feat: add SOUNDCLAW jukebox skill integration (#67)
Add the office jukebox flow so Spotify can be controlled from the SOUNDCLAW skill, manual jukebox UI, and local browser auth bridge during development. Made-with: Cursor
This commit is contained in:
+62
-5
@@ -1,4 +1,5 @@
|
||||
const http = require("node:http");
|
||||
const https = require("node:https");
|
||||
const next = require("next");
|
||||
|
||||
const { createAccessGate } = require("./access-gate");
|
||||
@@ -19,8 +20,52 @@ const resolvePathname = (url) => {
|
||||
return (idx === -1 ? raw : raw.slice(0, idx)) || "/";
|
||||
};
|
||||
|
||||
const CERT_DIR = require("node:path").join(__dirname, "..", ".certs");
|
||||
const CERT_PATH = require("node:path").join(CERT_DIR, "localhost.crt");
|
||||
const KEY_PATH = require("node:path").join(CERT_DIR, "localhost.key");
|
||||
|
||||
const generateHttpsCert = async () => {
|
||||
const fs = require("node:fs");
|
||||
|
||||
// Re-use a saved cert so the browser only needs to trust it once.
|
||||
if (fs.existsSync(CERT_PATH) && fs.existsSync(KEY_PATH)) {
|
||||
return {
|
||||
key: fs.readFileSync(KEY_PATH, "utf8"),
|
||||
cert: fs.readFileSync(CERT_PATH, "utf8"),
|
||||
};
|
||||
}
|
||||
|
||||
const selfsigned = require("selfsigned");
|
||||
const attrs = [{ name: "commonName", value: "localhost" }];
|
||||
const pems = await selfsigned.generate(attrs, {
|
||||
days: 825,
|
||||
keySize: 2048,
|
||||
algorithm: "sha256",
|
||||
extensions: [
|
||||
{
|
||||
name: "subjectAltName",
|
||||
altNames: [
|
||||
{ type: 2, value: "localhost" },
|
||||
{ type: 7, ip: "127.0.0.1" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
fs.mkdirSync(CERT_DIR, { recursive: true });
|
||||
fs.writeFileSync(CERT_PATH, pems.cert);
|
||||
fs.writeFileSync(KEY_PATH, pems.private);
|
||||
|
||||
console.info(`\nCert saved to ${CERT_DIR}`);
|
||||
console.info("To make browsers trust it (macOS), run:");
|
||||
console.info(` sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${CERT_PATH}"\n`);
|
||||
|
||||
return { key: pems.private, cert: pems.cert };
|
||||
};
|
||||
|
||||
async function main() {
|
||||
const dev = process.argv.includes("--dev");
|
||||
const useHttps = process.argv.includes("--https") || process.env.HTTPS === "true";
|
||||
const hostnames = Array.from(new Set(resolveHosts(process.env)));
|
||||
const hostname = hostnames[0] ?? "127.0.0.1";
|
||||
const port = resolvePort();
|
||||
@@ -65,11 +110,18 @@ async function main() {
|
||||
handleUpgrade(req, socket, head);
|
||||
};
|
||||
|
||||
const httpsCert = useHttps ? await generateHttpsCert() : null;
|
||||
|
||||
const createServer = () =>
|
||||
http.createServer((req, res) => {
|
||||
if (accessGate.handleHttp(req, res)) return;
|
||||
handle(req, res);
|
||||
});
|
||||
useHttps
|
||||
? https.createServer(httpsCert, (req, res) => {
|
||||
if (accessGate.handleHttp(req, res)) return;
|
||||
handle(req, res);
|
||||
})
|
||||
: http.createServer((req, res) => {
|
||||
if (accessGate.handleHttp(req, res)) return;
|
||||
handle(req, res);
|
||||
});
|
||||
|
||||
const servers = hostnames.map(() => createServer());
|
||||
|
||||
@@ -120,8 +172,13 @@ async function main() {
|
||||
? "localhost"
|
||||
: hostname;
|
||||
|
||||
const browserUrl = `http://${hostForBrowser}:${port}`;
|
||||
const protocol = useHttps ? "https" : "http";
|
||||
const browserUrl = `${protocol}://${hostForBrowser}:${port}`;
|
||||
console.info(`Open in browser: ${browserUrl}`);
|
||||
if (useHttps) {
|
||||
console.info("HTTPS mode: self-signed cert in use. You may need to accept a browser security warning once.");
|
||||
console.info(`Spotify redirect URI: ${browserUrl}/office`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
|
||||
Reference in New Issue
Block a user