A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at feat/like-scrobble 188 lines 4.2 kB view raw
1import { ctx } from "context"; 2import { and, eq } from "drizzle-orm"; 3import { Hono } from "hono"; 4import jwt from "jsonwebtoken"; 5import { env } from "lib/env"; 6import crypto from "node:crypto"; 7import * as R from "ramda"; 8import apiKeys from "schema/api-keys"; 9import users from "schema/users"; 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.db 27 .select() 28 .from(users) 29 .where(eq(users.did, did)) 30 .limit(1) 31 .then((rows) => rows[0]); 32 33 if (!user) { 34 c.status(401); 35 return c.text("Unauthorized"); 36 } 37 38 const size = +c.req.query("size") || 20; 39 const offset = +c.req.query("offset") || 0; 40 41 const apikeysData = await ctx.db 42 .select() 43 .from(apiKeys) 44 .where(eq(apiKeys.userId, user.id)) 45 .limit(size) 46 .offset(offset); 47 48 return c.json(apikeysData.map((x) => R.omit(["userId"])(x))); 49}); 50 51app.post("/", async (c) => { 52 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 53 54 if (!bearer || bearer === "null") { 55 c.status(401); 56 return c.text("Unauthorized"); 57 } 58 59 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 60 ignoreExpiration: true, 61 }); 62 63 const user = await ctx.db 64 .select() 65 .from(users) 66 .where(eq(users.did, did)) 67 .limit(1) 68 .then((rows) => rows[0]); 69 70 if (!user) { 71 c.status(401); 72 return c.text("Unauthorized"); 73 } 74 75 const body = await c.req.json(); 76 const parsed = apiKeySchema.safeParse(body); 77 78 if (parsed.error) { 79 c.status(400); 80 return c.text("Invalid api key data: " + parsed.error.message); 81 } 82 const newApiKey = parsed.data; 83 84 if (!newApiKey.name) { 85 c.status(400); 86 return c.text("Missing required field: name"); 87 } 88 89 const apiKey = crypto.randomBytes(16).toString("hex"); 90 const sharedSecret = crypto.randomBytes(16).toString("hex"); 91 92 const [record] = await ctx.db 93 .insert(apiKeys) 94 .values({ 95 name: newApiKey.name, 96 description: newApiKey.description ?? "", 97 enabled: newApiKey.enabled ?? true, 98 apiKey, 99 sharedSecret, 100 userId: user.id, 101 }) 102 .returning(); 103 104 return c.json({ 105 id: record.id, 106 name: record.name, 107 description: record.description, 108 api_key: record.apiKey, 109 shared_secret: record.sharedSecret, 110 }); 111}); 112 113app.put("/:id", async (c) => { 114 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 115 116 if (!bearer || bearer === "null") { 117 c.status(401); 118 return c.text("Unauthorized"); 119 } 120 121 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 122 ignoreExpiration: true, 123 }); 124 125 const user = await ctx.db 126 .select() 127 .from(users) 128 .where(eq(users.did, did)) 129 .limit(1) 130 .then((rows) => rows[0]); 131 132 if (!user) { 133 c.status(401); 134 return c.text("Unauthorized"); 135 } 136 137 const data = await c.req.json(); 138 const id = c.req.param("id"); 139 140 const [record] = await ctx.db 141 .update(apiKeys) 142 .set(data) 143 .where(and(eq(apiKeys.id, id), eq(apiKeys.userId, user.id))) 144 .returning(); 145 146 return c.json({ 147 id: record.id, 148 name: record.name, 149 description: record.description, 150 api_key: record.apiKey, 151 shared_secret: record.sharedSecret, 152 }); 153}); 154 155app.delete("/:id", async (c) => { 156 const bearer = (c.req.header("authorization") || "").split(" ")[1]?.trim(); 157 158 if (!bearer || bearer === "null") { 159 c.status(401); 160 return c.text("Unauthorized"); 161 } 162 163 const { did } = jwt.verify(bearer, env.JWT_SECRET, { 164 ignoreExpiration: true, 165 }); 166 167 const user = await ctx.db 168 .select() 169 .from(users) 170 .where(eq(users.did, did)) 171 .limit(1) 172 .then((rows) => rows[0]); 173 174 if (!user) { 175 c.status(401); 176 return c.text("Unauthorized"); 177 } 178 179 const id = c.req.param("id"); 180 181 await ctx.db 182 .delete(apiKeys) 183 .where(and(eq(apiKeys.id, id), eq(apiKeys.userId, user.id))); 184 185 return c.json({ success: true }); 186}); 187 188export default app;