Public
HTTP
Forked from hunty/spotify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { generateRandomString } from "https://esm.town/v/greg0r/generateRandomString?v=1";
import { home, scopesRoute } from "https://esm.town/v/hunty/spotify_helpers";
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON";
import { querystring } from "https://esm.town/v/stevekrouse/querystring";
import { thisWebURL } from "https://esm.town/v/stevekrouse/thisWebURL";
import { Buffer } from "node:buffer";
import { eq, sql } from "npm:drizzle-orm";
import { drizzle } from "npm:drizzle-orm/libsql";
import { integer, sqliteTable, text } from "npm:drizzle-orm/sqlite-core";
import { getCookie, setCookie } from "npm:hono/cookie";
import { cors } from "npm:hono/cors";
import { Hono } from "npm:hono@4";
// Uncomment this line for one run of the Val to create the DB lol
// await sqlite.execute(`create table if not exists SPOTIFY_AUTH_2(id text primary key, data text)`);
export const db = drizzle(sqlite as any);
export const table = sqliteTable("SPOTIFY_AUTH_2", {
id: text("id").primaryKey(),
data: text("data"),
});
const thisURL = thisWebURL();
const redirect_uri = thisURL + "/callback";
export let spotifyRequestToken = ({ client_id, client_secret, code, redirect_uri }) =>
fetchJSON("https://accounts.spotify.com/api/token", {
method: "POST",
body: querystring({
code: code,
grant_type: "authorization_code",
redirect_uri,
}),
headers: {
"Authorization": "Basic " + (new Buffer(client_id + ":" + client_secret).toString("base64")),
"Content-Type": "application/x-www-form-urlencoded",
},
});
export let spotifyRefreshToken = async ({ refresh_token, client_id, client_secret }) =>
fetch("https://accounts.spotify.com/api/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refresh_token,
redirect_uri,
}),
headers: {
"Authorization": "Basic " + (new Buffer(client_id + ":" + client_secret).toString("base64")),
"Content-Type": "application/x-www-form-urlencoded",
},
});
const app = new Hono();
app.use("*", cors());
app.use("*", async (c, next) => {
const state = getCookie(c, "state") ?? generateRandomString(16);
c.set("state", state);
await next();
setCookie(c, "state", state);
});
app.get("/", home);
app.get("/scopes", scopesRoute);
app.get("/login", async (c) => {
const state = c.get("state");
const scopes = c.req.queries("scopes[]").join(" ");
if (typeof state !== "string") {
return c.html("State mismatch 1");
}
try {
const existingRecord = await db.select().from(table).where(eq(table.id, state)).limit(1);
if (existingRecord.length > 0) {
await db.update(table).set({ id: state }).where(eq(table.id, state)).execute();
} else {
await db.insert(table).values([{ id: state }]);
}
} catch (e) {
return c.json("Error: " + e);
}
return c.redirect(
`https://accounts.spotify.com/authorize?response_type=code&client_id=${
encodeURIComponent(Deno.env.get("SPOTIFY_CLIENT_ID"))
}&scope=${encodeURIComponent(scopes)}&redirect_uri=${encodeURIComponent(redirect_uri)}&state=${state}`,
);
});
app.get("/callback", async (c) => {
try {
const code = c.req.query("code");
const state = c.req.query("state") as string;
if (state !== c.get("state")) {
return c.html("State mismatch 1");
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
June 14, 2024