Your music, beautifully tracked. All yours. (coming soon) teal.fm
teal-fm atproto

Move db functions to own place

+77 -101
+1
.gitignore
··· 14 14 15 15 # production 16 16 /build 17 + */dist 17 18 18 19 # misc 19 20 .DS_Store
+10 -10
apps/aqua/package.json
··· 6 6 "packageManager": "bun@1.0.0+", 7 7 "scripts": { 8 8 "dev": "tsx watch --clear-screen=false src/index.ts | pino-pretty", 9 - "bun:dev": "bun run src/index.ts | pino-pretty", 9 + "bun:dev": "bun run --hot src/index.ts | pino-pretty", 10 10 "build": "tsup", 11 11 "start": "node dist/index.js", 12 12 "lexgen": "lex gen-server ./src/lexicon ./lexicons/*", ··· 17 17 "db:studio": "drizzle-kit studio" 18 18 }, 19 19 "dependencies": { 20 - "@atproto/api": "^0.13.14", 20 + "@atproto/api": "^0.13.15", 21 21 "@atproto/common": "^0.4.4", 22 - "@atproto/identity": "^0.4.2", 22 + "@atproto/identity": "^0.4.3", 23 23 "@atproto/lexicon": "^0.4.2", 24 - "@atproto/oauth-client-node": "^0.1.4", 25 - "@atproto/sync": "^0.1.4", 24 + "@atproto/oauth-client-node": "^0.2.0", 25 + "@atproto/sync": "^0.1.5", 26 26 "@atproto/syntax": "^0.3.0", 27 27 "@atproto/xrpc-server": "^0.6.4", 28 - "@hono/node-server": "^1.13.4", 28 + "@hono/node-server": "^1.13.7", 29 29 "@libsql/client": "^0.14.0", 30 30 "dotenv": "^16.4.5", 31 - "drizzle-orm": "^0.36", 31 + "drizzle-orm": "^0.36.1", 32 32 "envalid": "^8.0.0", 33 - "hono": "^4.6.8", 33 + "hono": "^4.6.9", 34 34 "jose": "^5.9.6", 35 35 "pino": "^9.5.0", 36 36 "turbo": "^2.2.3", ··· 41 41 "devDependencies": { 42 42 "@teal/tsconfig": "*", 43 43 "@atproto/lex-cli": "^0.4.1", 44 - "@types/node": "^20.17.5", 45 - "drizzle-kit": "^0.27", 44 + "@types/node": "^20.17.6", 45 + "drizzle-kit": "^0.27.2", 46 46 "pino-pretty": "^11.3.0", 47 47 "rimraf": "^6.0.1", 48 48 "tsup": "^8.3.5",
+24 -24
apps/aqua/src/auth/client.ts
··· 1 1 import { NodeOAuthClient } from "@atproto/oauth-client-node"; 2 - import type { Database } from "@/db"; 2 + import type { Database } from "@teal/db/connect"; 3 3 import { env } from "@/lib/env"; 4 4 import { SessionStore, StateStore } from "./storage"; 5 5 6 - export const createClient = async (db: Database) => { 7 - const publicUrl = env.PUBLIC_URL; 8 - const url = publicUrl || `http://127.0.0.1:${env.PORT}`; 9 - const enc = encodeURIComponent; 10 - return new NodeOAuthClient({ 11 - clientMetadata: { 12 - client_name: "Teal", 13 - client_id: publicUrl 14 - ? `${url}/client-metadata.json` 15 - : `http://localhost?redirect_uri=${enc(`${url}/oauth/callback`)}&scope=${enc("atproto transition:generic")}`, 16 - client_uri: url, 17 - redirect_uris: [`${url}/oauth/callback`], 18 - scope: "atproto transition:generic", 19 - grant_types: ["authorization_code", "refresh_token"], 20 - response_types: ["code"], 21 - application_type: "web", 22 - token_endpoint_auth_method: "none", 23 - dpop_bound_access_tokens: true, 24 - }, 25 - stateStore: new StateStore(db), 26 - sessionStore: new SessionStore(db), 27 - }); 28 - }; 6 + import { db } from "@teal/db/connect"; 7 + 8 + const publicUrl = env.PUBLIC_URL; 9 + const url = publicUrl || `http://127.0.0.1:${env.PORT}`; 10 + const enc = encodeURIComponent; 11 + export const atclient = new NodeOAuthClient({ 12 + clientMetadata: { 13 + client_name: "teal", 14 + client_id: publicUrl 15 + ? `${url}/client-metadata.json` 16 + : `http://localhost?redirect_uri=${enc(`${url}/oauth/callback`)}&scope=${enc("atproto transition:generic")}`, 17 + client_uri: url, 18 + redirect_uris: [`${url}/oauth/callback`], 19 + scope: "atproto transition:generic", 20 + grant_types: ["authorization_code", "refresh_token"], 21 + response_types: ["code"], 22 + application_type: "web", 23 + token_endpoint_auth_method: "none", 24 + dpop_bound_access_tokens: true, 25 + }, 26 + stateStore: new StateStore(db), 27 + sessionStore: new SessionStore(db), 28 + });
+8
apps/aqua/src/auth/package.json
··· 1 + { 2 + "name": "@teal/db", 3 + "type": "module", 4 + "dependencies": { 5 + "drizzle-kit": "^0.27.1", 6 + "drizzle-orm": "^0.36.0" 7 + } 8 + }
+9 -10
apps/aqua/src/auth/router.ts
··· 1 - import { createClient } from "./client"; 2 - import { db } from "@/db"; 1 + import { atclient } from "./client"; 2 + import { db } from "@teal/db/connect"; 3 3 import { EnvWithCtx, TealContext } from "@/ctx"; 4 4 import { Hono } from "hono"; 5 5 import { tealSession } from "@teal/db/schema"; ··· 14 14 export const login = async (c: TealContext) => { 15 15 let body: LoginBody = await c.req.json(); 16 16 // Initiate the OAuth flow 17 - const auth = await createClient(db); 18 17 if (!body) return Response.json({ error: "Could not parse body" }); 19 18 // handle is the handle of the user 20 19 if (!body.handle && body.handle === undefined) 21 20 return Response.json({ error: "Handle is required" }); 22 21 try { 23 - const url = await auth.authorize(body.handle, { 22 + const url = await atclient.authorize(body.handle, { 24 23 scope: "atproto transition:generic", 25 24 state: crypto.randomUUID(), 26 25 }); ··· 32 31 }; 33 32 34 33 export async function loginGet(c: TealContext) { 35 - const handle = c.req.param('handle'); 34 + const handle = c.req.param("handle"); 35 + console.log("handle", handle); 36 36 // Initiate the OAuth flow 37 - const auth = await createClient(db); 38 37 try { 39 - const url = await auth.authorize(handle, { 38 + console.log("Calling authorize"); 39 + const url = await atclient.authorize(handle, { 40 40 scope: "atproto transition:generic", 41 41 }); 42 + console.log("Redirecting to oauth login page"); 42 43 return Response.redirect(url); 43 44 } catch (e) { 44 45 console.error(e); ··· 48 49 49 50 export async function callback(c: TealContext) { 50 51 // Initiate the OAuth flow 51 - const auth = await createClient(db); 52 52 try { 53 53 const honoParams = c.req.query(); 54 54 console.log("params", honoParams); 55 55 const params = new URLSearchParams(honoParams); 56 - const cb = await auth.callback(params); 56 + const cb = await atclient.callback(params); 57 57 58 58 let did = cb.session.did; 59 59 // gen opaque tealSessionKey ··· 86 86 } 87 87 88 88 const app = new Hono<EnvWithCtx>(); 89 - 90 89 91 90 app.get("/login/:handle", async (c) => loginGet(c)); 92 91
+1 -10
apps/aqua/src/auth/storage.ts
··· 4 4 NodeSavedState, 5 5 NodeSavedStateStore, 6 6 } from "@atproto/oauth-client-node"; 7 - import type { Database } from "@/db"; 7 + import type { Database } from "@teal/db/connect"; 8 8 import { atProtoSession, authState } from "@teal/db/schema"; 9 9 import { eq } from "drizzle-orm"; 10 10 ··· 17 17 .where(eq(authState.key, key)) 18 18 .limit(1) 19 19 .execute(); 20 - // .selectFrom("auth_state") 21 - // .selectAll() 22 - // .where("key", "=", key) 23 - // .executeTakeFirst(); 24 20 console.log("getting state", key, result); 25 21 if (!result[0]) return; 26 22 return JSON.parse(result[0].state) as NodeSavedState; ··· 36 32 target: authState.key, 37 33 }) 38 34 .execute(); 39 - // .insertInto("auth_state") 40 - // .values({ key, state }) 41 - // .onConflict((oc) => oc.doUpdateSet({ state })) 42 - // .execute(); 43 35 } 44 36 async del(key: string) { 45 37 await this.db.delete(authState).where(eq(authState.key, key)).execute(); ··· 76 68 .delete(atProtoSession) 77 69 .where(eq(atProtoSession.key, key)) 78 70 .execute(); 79 - //.deleteFrom("auth_session").where("key", "=", key).execute(); 80 71 } 81 72 }
+3 -3
apps/aqua/src/ctx.ts
··· 2 2 import { Client } from "@libsql/client/."; 3 3 import { LibSQLDatabase } from "drizzle-orm/libsql"; 4 4 import { Context, Next } from "hono"; 5 - import { createClient } from "./auth/client"; 6 5 import { Logger } from "pino"; 6 + import { atclient } from "./auth/client"; 7 7 8 8 export type TealContext = Context<EnvWithCtx, any, any>; 9 9 ··· 27 27 logger: Logger<never, boolean>, 28 28 next: Next, 29 29 ) => { 30 - const auth = await createClient(db); 30 + //const auth = await createClient(db); 31 31 32 32 c.set("db", db); 33 - c.set("auth", auth); 33 + c.set("auth", atclient); 34 34 c.set("logger", logger); 35 35 await next(); 36 36 };
-21
apps/aqua/src/db.ts
··· 1 - import { drizzle } from "drizzle-orm/libsql"; 2 - import * as schema from "@teal/db/schema"; 3 - import process from "node:process"; 4 - import path from "node:path"; 5 - 6 - console.log( 7 - "Loading SQLite file at", 8 - path.join(process.cwd(), "../../db.sqlite"), 9 - ); 10 - 11 - export const db = drizzle({ 12 - connection: 13 - // default is in project root / db.sqlite 14 - process.env.DATABASE_URL ?? 15 - "file:" + path.join(process.cwd(), "../../db.sqlite"), 16 - // doesn't seem to work? 17 - //casing: "snake_case", 18 - schema: schema, 19 - }); 20 - 21 - export type Database = typeof db;
+16 -13
apps/aqua/src/index.ts
··· 1 1 import { serve } from "@hono/node-server"; 2 2 import { Hono } from "hono"; 3 - import { db } from "@/db"; 3 + import { db } from "@teal/db/connect"; 4 4 import { getAuthRouter, loginGet } from "./auth/router"; 5 5 import pino from "pino"; 6 6 import { EnvWithCtx, setupContext } from "./ctx"; ··· 23 23 app.route("/oauth", getAuthRouter()); 24 24 25 25 const run = async () => { 26 - serveNode( 27 - { 28 - fetch: app.fetch, 29 - port: env.PORT, 30 - hostname: env.HOST, 31 - }, 32 - (info) => { 33 - console.log( 34 - `Listening on ${info.address == "::1" ? "http://localhost" : info.address}:${info.port} (${info.family})`, 35 - ); 36 - }, 37 - ); 26 + logger.info("Running in " + navigator.userAgent); 27 + if (navigator.userAgent.includes("Node")) { 28 + serve( 29 + { 30 + fetch: app.fetch, 31 + port: env.PORT, 32 + hostname: env.HOST, 33 + }, 34 + (info) => { 35 + logger.info( 36 + `Listening on ${info.address == "::1" ? "http://localhost" : info.address}:${info.port} (${info.family})`, 37 + ); 38 + }, 39 + ); 40 + } 38 41 }; 39 42 40 43 run();
+2 -2
apps/aqua/src/lib/auth.ts
··· 1 1 import { Ctx, EnvWithCtx } from "@/ctx"; 2 - import { db } from "@/db"; 2 + import { db } from "@teal/db/connect"; 3 3 import { Agent, lexicons } from "@atproto/api"; 4 4 import { NodeOAuthClient, Session } from "@atproto/oauth-client-node"; 5 5 import { tealSession } from "@teal/db/schema"; ··· 112 112 did: string, 113 113 ): Promise<Session> { 114 114 let auth: NodeOAuthClient = c.get("auth"); 115 - const jwt = await auth.sessionGetter.get(did); 115 + const jwt = await auth.restore(did); 116 116 if (jwt) { 117 117 return jwt; 118 118 }
+3 -8
apps/aqua/src/status.ts
··· 1 1 import { Hono } from "hono"; 2 2 import { EnvWithCtx } from "./ctx"; 3 + import { getSession } from "./lib/auth"; 3 4 4 5 const app = new Hono<EnvWithCtx>(); 5 6 ··· 8 9 }); 9 10 10 11 app.post("/status", async (c) => { 11 - const result = await c.db.query.status.insert({ 12 - uri: c.req.body.uri, 13 - authorDid: c.req.body.authorDid, 14 - status: c.req.body.status, 15 - createdAt: new Date().toISOString(), 16 - indexedAt: new Date().toISOString(), 17 - }); 18 - return c.json(result); 12 + let db = c.get("db"); 13 + return c.json({ status: "todo" }); 19 14 }); 20 15 21 16 export default app;
bun.lockb

This is a binary file and will not be displayed.