From 55054e7c9e4827f4eaa2eae779b8d29bb3c149b0 Mon Sep 17 00:00:00 2001 From: nin0 Date: Fri, 25 Apr 2025 04:46:30 -0400 Subject: [PATCH] init part 2 --- .gitignore | 3 +- .prettierrc.json | 8 +++++ Yapper.ts | 60 ++++++++++++++++++++++++++++++++++ index.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 21 ++++++++---- pnpm-lock.yaml | 51 +++++++++++++++++++++++++++++ 6 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 .prettierrc.json create mode 100644 Yapper.ts create mode 100644 index.ts diff --git a/.gitignore b/.gitignore index f94630c..50ad191 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ -.env \ No newline at end of file +.env +state.json \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..def18f1 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "tabWidth": 4, + "useTabs": true, + "semi": true, + "trailingComma": "none", + "singleQuote": false, + "jsxSingleQuote": false +} diff --git a/Yapper.ts b/Yapper.ts new file mode 100644 index 0000000..a10c3df --- /dev/null +++ b/Yapper.ts @@ -0,0 +1,60 @@ +function getDateTime() { + const now = new Date(); + const hours = String(now.getHours()).padStart(2, "0"); + const minutes = String(now.getMinutes()).padStart(2, "0"); + const seconds = String(now.getSeconds()).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const year = now.getFullYear(); + + return `${hours}:${minutes}:${seconds} ${day}-${month}-${year}`; +} + +export class Yapper { + constructor() {} + trace(obj) { + console.log("\x1b[90;1m TRC\x1b[90;1m \x1b[0;90m", getDateTime(), obj); + } + debug(obj) { + console.log( + "\x1b[90;1;47m DBG", + `\x1b[0m \x1b[90m${getDateTime()}`, + obj, + "\x1b[0m" + ); + } + info(obj) { + console.log( + "\x1b[44;1m INF", + `\x1b[0m \x1b[90m${getDateTime()}\x1b[0m`, + obj + ); + } + warn(obj) { + console.log( + "\x1b[43;1m WRN", + `\x1b[0m \x1b[90m${getDateTime()}\x1b[0m`, + obj + ); + } + error(obj) { + console.log( + "\x1b[41;1m ERR", + `\x1b[0m \x1b[90m${getDateTime()}\x1b[0m`, + obj + ); + } + fatal(obj) { + console.log( + "\x1b[40;31;1m FTL", + `\x1b[0m \x1b[31;1m${getDateTime()}\x1b[0;31m`, + obj, + "\x1b[0m" + ); + } + child() { + return new Yapper(); + } + level; // unused + silent; // unused +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..390a0a1 --- /dev/null +++ b/index.ts @@ -0,0 +1,84 @@ +import Fastify from "fastify"; +import { Yapper } from "./Yapper"; +import path from "path"; +import { readFileSync, writeFileSync } from "fs"; + +const state = new Proxy( + JSON.parse(readFileSync(path.join(__dirname, "state.json"), "utf-8")), + { + get(target, prop) { + const data = readFileSync( + path.join(__dirname, "state.json"), + "utf-8" + ); + return JSON.parse(data); + }, + set(target, prop, value) { + const data = JSON.parse( + readFileSync(path.join(__dirname, "state.json"), "utf-8") + ); + data[prop] = value; + writeFileSync( + path.join(__dirname, "state.json"), + JSON.stringify(data) + ); + return true; + } + } +); + +const fastify = Fastify({ + loggerInstance: new Yapper() +}); + +fastify.get("/", (request, reply) => { + reply.type("text/html"); + return 'auth'; +}); + +fastify.get("/auth", async (request, reply) => { + return reply.redirect( + `https://accounts.spotify.com/authorize?` + + new URLSearchParams({ + response_type: "code", + client_id: process.env.CLIENT_ID!, + scope: "user-library-read user-library-modify playlist-read-private playlist-read-collaborative playlist-modify-private playlist-modify-public", + redirect_uri: `https://${request.hostname}/callback`, + show_dialog: "true", + state: "ballsgamingmeoww" + }).toString() + ); +}); + +fastify.get("/callback", async (request, reply) => { + if ((request.query as any).error) return reply.code(400).send(); + + const tokenRes = await fetch("https://accounts.spotify.com/api/token", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: + "Basic " + + Buffer.from( + process.env.CLIENT_ID + ":" + process.env.CLIENT_SECRET + ).toString("base64") + }, + body: new URLSearchParams({ + code: (request.query as any).code, + redirect_uri: `https://${request.hostname}/callback`, + grant_type: "authorization_code" + }) + }); + + console.log(await tokenRes.json()); +}); + +try { + fastify.listen({ + port: 4929, + host: "0.0.0.0" + }); +} catch (err) { + fastify.log.error(err); + process.exit(1); +} diff --git a/package.json b/package.json index 4d5b75a..cb4eba4 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,17 @@ { - "name": "likedsongstoplaylist", - "packageManager": "pnpm@10.8.1", - "dependencies": { - "esbuild": "^0.25.3", - "fastify": "^5.3.2" - } + "name": "likedsongstoplaylist", + "packageManager": "pnpm@10.8.1", + "dependencies": { + "esbuild": "^0.25.3", + "fastify": "^5.3.2" + }, + "devDependencies": { + "@types/node": "^22.15.2", + "tsx": "^4.19.3" + }, + "scripts": { + "dev": "tsx --watch --env-file-if-exists=.env index.ts", + "build": "node build.mjs", + "start": "node build.mjs && node dist/index.js" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64aa4ce..87885ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,13 @@ importers: fastify: specifier: ^5.3.2 version: 5.3.2 + devDependencies: + '@types/node': + specifier: ^22.15.2 + version: 22.15.2 + tsx: + specifier: ^4.19.3 + version: 4.19.3 packages: @@ -185,6 +192,9 @@ packages: '@fastify/proxy-addr@5.0.0': resolution: {integrity: sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==} + '@types/node@22.15.2': + resolution: {integrity: sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A==} + abstract-logging@2.0.1: resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} @@ -248,6 +258,14 @@ packages: resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} engines: {node: '>=20'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + ipaddr.js@2.2.0: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} @@ -292,6 +310,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + ret@0.5.0: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} @@ -335,6 +356,14 @@ packages: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} + tsx@4.19.3: + resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + snapshots: '@esbuild/aix-ppc64@0.25.3': @@ -435,6 +464,10 @@ snapshots: '@fastify/forwarded': 3.0.0 ipaddr.js: 2.2.0 + '@types/node@22.15.2': + dependencies: + undici-types: 6.21.0 + abstract-logging@2.0.1: {} ajv-formats@3.0.1(ajv@8.17.1): @@ -536,6 +569,13 @@ snapshots: fast-querystring: 1.1.2 safe-regex2: 5.0.0 + fsevents@2.3.3: + optional: true + + get-tsconfig@4.10.0: + dependencies: + resolve-pkg-maps: 1.0.0 + ipaddr.js@2.2.0: {} json-schema-ref-resolver@2.0.1: @@ -582,6 +622,8 @@ snapshots: require-from-string@2.0.2: {} + resolve-pkg-maps@1.0.0: {} + ret@0.5.0: {} reusify@1.1.0: {} @@ -611,3 +653,12 @@ snapshots: real-require: 0.2.0 toad-cache@3.7.0: {} + + tsx@4.19.3: + dependencies: + esbuild: 0.25.3 + get-tsconfig: 4.10.0 + optionalDependencies: + fsevents: 2.3.3 + + undici-types@6.21.0: {}