A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at fix/spotify 160 lines 3.9 kB view raw
1import { equals } from "@xata.io/client"; 2import { ctx } from "context"; 3import { and, eq } from "drizzle-orm"; 4import { Hono } from "hono"; 5import jwt from "jsonwebtoken"; 6import { env } from "lib/env"; 7import crypto from "node:crypto"; 8import * as R from "ramda"; 9import tables from "schema"; 10import { apiKeySchema } from "types/apikey"; 11 12const app = new Hono(); 13 14app.get("/", async (c) => { 15 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 16 17 if (!bearer || bearer === "null") { 18 c.status(401); 19 return c.text("Unauthorized"); 20 } 21 22 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 23 ignoreExpiration: true, 24 }); 25 26 const user = await ctx.client.db.users.filter("did", equals(did)).getFirst(); 27 if (!user) { 28 c.status(401); 29 return c.text("Unauthorized"); 30 } 31 32 const size = +c.req.query("size") || 20; 33 const offset = +c.req.query("offset") || 0; 34 35 const apikeys = await ctx.db 36 .select() 37 .from(tables.apiKeys) 38 .where(eq(tables.apiKeys.userId, user.xata_id)) 39 .limit(size) 40 .offset(offset) 41 .execute(); 42 43 return c.json(apikeys.map((x) => R.omit(["userId"])(x))); 44}); 45 46app.post("/", async (c) => { 47 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 48 49 if (!bearer || bearer === "null") { 50 c.status(401); 51 return c.text("Unauthorized"); 52 } 53 54 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 55 ignoreExpiration: true, 56 }); 57 58 const user = await ctx.client.db.users.filter("did", equals(did)).getFirst(); 59 if (!user) { 60 c.status(401); 61 return c.text("Unauthorized"); 62 } 63 64 const body = await c.req.json(); 65 const parsed = apiKeySchema.safeParse(body); 66 67 if (parsed.error) { 68 c.status(400); 69 return c.text("Invalid api key data: " + parsed.error.message); 70 } 71 const newApiKey = parsed.data; 72 73 const api_key = crypto.randomBytes(16).toString("hex"); 74 const shared_secret = crypto.randomBytes(16).toString("hex"); 75 76 const record = await ctx.client.db.api_keys.create({ 77 ...newApiKey, 78 api_key, 79 shared_secret, 80 user_id: user.xata_id, 81 }); 82 83 return c.json({ 84 id: record.xata_id, 85 name: record.name, 86 description: record.description, 87 api_key: record.api_key, 88 shared_secret: record.shared_secret, 89 }); 90}); 91 92app.put("/:id", async (c) => { 93 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 94 95 if (!bearer || bearer === "null") { 96 c.status(401); 97 return c.text("Unauthorized"); 98 } 99 100 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 101 ignoreExpiration: true, 102 }); 103 104 const user = await ctx.client.db.users.filter("did", equals(did)).getFirst(); 105 if (!user) { 106 c.status(401); 107 return c.text("Unauthorized"); 108 } 109 110 const data = await c.req.json(); 111 const id = c.req.param("id"); 112 113 const record = await ctx.db 114 .update(tables.apiKeys) 115 .set(data) 116 .where( 117 and(eq(tables.apiKeys.id, id), eq(tables.apiKeys.userId, user.xata_id)), 118 ) 119 .execute(); 120 121 return c.json({ 122 id: record.xata_id, 123 name: record.name, 124 description: record.description, 125 api_key: record.api_key, 126 shared_secret: record.shared_secret, 127 }); 128}); 129 130app.delete("/:id", async (c) => { 131 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 132 133 if (!bearer || bearer === "null") { 134 c.status(401); 135 return c.text("Unauthorized"); 136 } 137 138 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 139 ignoreExpiration: true, 140 }); 141 142 const user = await ctx.client.db.users.filter("did", equals(did)).getFirst(); 143 if (!user) { 144 c.status(401); 145 return c.text("Unauthorized"); 146 } 147 148 const id = c.req.param("id"); 149 150 await ctx.db 151 .delete(tables.apiKeys) 152 .where( 153 and(eq(tables.apiKeys.id, id), eq(tables.apiKeys.userId, user.xata_id)), 154 ) 155 .execute(); 156 157 return c.json({ success: true }); 158}); 159 160export default app;