An atproto based writing game loosely inspired by Fiasco!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

quick refactor

+116 -91
+10 -52
src/api/server.ts
··· 3 3 import { redirect } from "@solidjs/router" 4 4 import { isValidHandle } from '@atproto/syntax' 5 5 import { Agent } from '@atproto/api' 6 - import { drizzle } from 'drizzle-orm/node-postgres' 7 - import { eq } from 'drizzle-orm' 8 - import { getOAuthClient } from '~/auth' 9 - import { userTable, userSessionTable } from '~/db/schema' 10 - import { getRequestEvent } from 'solid-js/web' 6 + import { getOAuthClient, getSession } from '~/auth' 7 + import { getUserByDid, getUserSessionBySessionId, insertUser, insertUserSession, deleteUserSession } from '~/db' 11 8 12 - const db = drizzle(process.env.DATABASE_URL!) 13 9 14 10 export async function initiateLogin(formData: FormData) { 15 11 const handle = String(formData.get('handle')) ··· 36 32 try { 37 33 const oauthClient = await getOAuthClient() 38 34 const { session: oauthSession } = await oauthClient.callback(params) 35 + const existingUser = await getUserByDid(oauthSession.did); 39 36 40 - // Get or create user 41 - const existingUser = await db 42 - .select() 43 - .from(userTable) 44 - .where(eq(userTable.did, oauthSession.did)) 45 - .limit(1) 46 37 47 38 if (existingUser.length === 0) { 48 39 // Fetch handle from the agent 49 40 const agent = new Agent(oauthSession) 50 41 const profile = await agent.getProfile({ actor: oauthSession.did }) 51 42 52 - await db.insert(userTable).values({ 53 - did: oauthSession.did, 54 - handle: profile.data.handle, 55 - }) 43 + await insertUser(oauthSession.did, profile.data.handle) 56 44 } 57 45 58 46 // Create user session 59 47 const sessionId = crypto.randomUUID() 60 48 const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days 61 49 62 - await db.insert(userSessionTable).values({ 63 - sessionId, 64 - did: oauthSession.did, 65 - expiresAt, 66 - }) 50 + await insertUserSession(sessionId, oauthSession.did, expiresAt) 67 51 68 52 return { sessionId, did: oauthSession.did } 69 53 } catch (err) { ··· 73 57 } 74 58 75 59 export async function logout() { 76 - const { getSession } = await import('~/auth') 77 60 const session = await getSession() 78 61 79 62 if (session.data.sessionId) { 80 - await db 81 - .delete(userSessionTable) 82 - .where(eq(userSessionTable.sessionId, session.data.sessionId)) 83 - 84 - await session.update((d) => { 85 - d.sessionId = undefined 86 - }) 63 + await deleteUserSession(session.data.sessionId) 87 64 } 88 65 89 66 throw redirect('/login') ··· 96 73 if (!session.data.sessionId) return null 97 74 98 75 try { 99 - const userSession = await db 100 - .select() 101 - .from(userSessionTable) 102 - .where(eq(userSessionTable.sessionId, session.data.sessionId)) 103 - .limit(1) 76 + const userSession = await getUserSessionBySessionId(session.data.sessionId) 104 77 105 78 if (!userSession || userSession.length === 0) { 106 79 await session.update((d) => { ··· 113 86 114 87 // Check if session expired 115 88 if (new Date() > sessionData.expiresAt) { 116 - await db 117 - .delete(userSessionTable) 118 - .where(eq(userSessionTable.sessionId, session.data.sessionId)) 119 - await session.update((d) => { 120 - d.sessionId = undefined 121 - }) 122 - return null 89 + await deleteUserSession(session.data.sessionId) 123 90 } 124 91 125 - const user = await db 126 - .select() 127 - .from(userTable) 128 - .where(eq(userTable.did, sessionData.did)) 129 - .limit(1) 92 + const user = await getUserByDid(sessionData.did) 130 93 131 94 if (!user || user.length === 0) { 132 95 await session.update((d) => { ··· 143 106 } 144 107 145 108 export async function getAgent() { 146 - const { getSession } = await import('~/auth') 147 109 const session = await getSession() 148 110 149 111 if (!session.data.sessionId) return null 150 112 151 113 try { 152 - const userSession = await db 153 - .select() 154 - .from(userSessionTable) 155 - .where(eq(userSessionTable.sessionId, session.data.sessionId)) 156 - .limit(1) 114 + const userSession = await getUserSessionBySessionId(session.data.sessionId) 157 115 158 116 if (!userSession || userSession.length === 0) return null 159 117
+2
src/auth/client.ts
··· 1 + "use server" 2 + 1 3 import { NodeOAuthClient } from '@atproto/oauth-client-node' 2 4 import { SessionStore, StateStore } from './storage' 3 5
+9 -35
src/auth/storage.ts
··· 1 + "use server"; 2 + 1 3 import type { 2 4 NodeSavedSession, 3 5 NodeSavedSessionStore, 4 6 NodeSavedState, 5 7 NodeSavedStateStore, 6 8 } from '@atproto/oauth-client-node' 7 - import { drizzle } from 'drizzle-orm/node-postgres' 8 - import { eq } from 'drizzle-orm' 9 - import { authSessionTable, authStateTable } from '~/db/schema' 10 - 11 - const db = drizzle(process.env.DATABASE_URL!) 9 + import { getAuthState, setAuthState, deleteAuthState, getAuthSession, setAuthSession, deleteAuthSession } from '~/db' 12 10 13 11 export class StateStore implements NodeSavedStateStore { 14 12 async get(key: string): Promise<NodeSavedState | undefined> { 15 - const result = await db 16 - .select() 17 - .from(authStateTable) 18 - .where(eq(authStateTable.key, key)) 19 - .limit(1) 13 + const result = await getAuthState(key) 20 14 21 15 if (!result || result.length === 0) return undefined 22 16 return JSON.parse(result[0].state) as NodeSavedState ··· 24 18 25 19 async set(key: string, val: NodeSavedState) { 26 20 const state = JSON.stringify(val) 27 - await db 28 - .insert(authStateTable) 29 - .values({ key, state }) 30 - .onConflictDoUpdate({ 31 - target: authStateTable.key, 32 - set: { state } 33 - }) 21 + await setAuthState(key, state) 34 22 } 35 23 36 24 async del(key: string) { 37 - await db 38 - .delete(authStateTable) 39 - .where(eq(authStateTable.key, key)) 25 + await deleteAuthState(key) 40 26 } 41 27 } 42 28 43 29 export class SessionStore implements NodeSavedSessionStore { 44 30 async get(key: string): Promise<NodeSavedSession | undefined> { 45 - const result = await db 46 - .select() 47 - .from(authSessionTable) 48 - .where(eq(authSessionTable.key, key)) 49 - .limit(1) 31 + const result = await getAuthSession(key) 50 32 51 33 if (!result || result.length === 0) return undefined 52 34 return JSON.parse(result[0].session) as NodeSavedSession ··· 54 36 55 37 async set(key: string, val: NodeSavedSession) { 56 38 const session = JSON.stringify(val) 57 - await db 58 - .insert(authSessionTable) 59 - .values({ key, session }) 60 - .onConflictDoUpdate({ 61 - target: authSessionTable.key, 62 - set: { session } 63 - }) 39 + await setAuthSession(key, session) 64 40 } 65 41 66 42 async del(key: string) { 67 - await db 68 - .delete(authSessionTable) 69 - .where(eq(authSessionTable.key, key)) 43 + await deleteAuthSession(key) 70 44 } 71 45 }
+92 -3
src/db/index.ts
··· 1 1 import 'dotenv/config'; 2 2 import { drizzle } from 'drizzle-orm/node-postgres'; 3 - import { postTable } from './schema'; 3 + import { eq } from 'drizzle-orm'; 4 + import { postTable, userTable, userSessionTable, authStateTable, authSessionTable } from './schema'; 5 + 6 + export * from './schema'; 4 7 5 - const db = drizzle(process.env.DATABASE_URL!); 8 + export const db = drizzle(process.env.DATABASE_URL!); 6 9 7 10 interface InsertPostParams { 8 11 uri?: string; ··· 26 29 27 30 export const getPosts = async () => { 28 31 return db.select().from(postTable) 29 - } 32 + } 33 + 34 + export const getUserByDid = async (did: string) => { 35 + return db 36 + .select() 37 + .from(userTable) 38 + .where(eq(userTable.did, did)) 39 + .limit(1) 40 + } 41 + 42 + export const getUserSessionBySessionId = async (sessionId: string) => { 43 + return db 44 + .select() 45 + .from(userSessionTable) 46 + .where(eq(userSessionTable.sessionId, sessionId)) 47 + .limit(1) 48 + } 49 + 50 + export const insertUser = async (did: string, handle: string) => { 51 + return db.insert(userTable).values({ 52 + did, 53 + handle, 54 + }) 55 + } 56 + 57 + export const insertUserSession = async (sessionId: string, did: string, expiresAt: Date) => { 58 + return db.insert(userSessionTable).values({ 59 + sessionId, 60 + did, 61 + expiresAt, 62 + }) 63 + } 64 + 65 + export const deleteUserSession = async (sessionId: string) => { 66 + return db 67 + .delete(userSessionTable) 68 + .where(eq(userSessionTable.sessionId, sessionId)) 69 + } 70 + 71 + export const getAuthState = async (key: string) => { 72 + return db 73 + .select() 74 + .from(authStateTable) 75 + .where(eq(authStateTable.key, key)) 76 + .limit(1) 77 + } 78 + 79 + export const setAuthState = async (key: string, state: string) => { 80 + return db 81 + .insert(authStateTable) 82 + .values({ key, state }) 83 + .onConflictDoUpdate({ 84 + target: authStateTable.key, 85 + set: { state } 86 + }) 87 + } 88 + 89 + export const deleteAuthState = async (key: string) => { 90 + return db 91 + .delete(authStateTable) 92 + .where(eq(authStateTable.key, key)) 93 + } 94 + 95 + export const getAuthSession = async (key: string) => { 96 + return db 97 + .select() 98 + .from(authSessionTable) 99 + .where(eq(authSessionTable.key, key)) 100 + .limit(1) 101 + } 102 + 103 + export const setAuthSession = async (key: string, session: string) => { 104 + return db 105 + .insert(authSessionTable) 106 + .values({ key, session }) 107 + .onConflictDoUpdate({ 108 + target: authSessionTable.key, 109 + set: { session } 110 + }) 111 + } 112 + 113 + export const deleteAuthSession = async (key: string) => { 114 + return db 115 + .delete(authSessionTable) 116 + .where(eq(authSessionTable.key, key)) 117 + } 118 +
+2
src/db/schema.ts
··· 1 + "use server"; 2 + 1 3 import { integer, pgTable, varchar, text, timestamp } from "drizzle-orm/pg-core"; 2 4 3 5 export const postTable = pgTable("post", {
+1 -1
src/routes/index.tsx
··· 1 1 import { createAsync, type RouteDefinition } from "@solidjs/router"; 2 2 import { For, Show } from "solid-js"; 3 3 import { action } from "@solidjs/router"; 4 - import { getPosts, insertPost } from "~/db"; 4 + import { getPosts } from "~/db"; 5 5 import { getUser, logout } from "~/api"; 6 6 import { createPost } from "./post-action"; 7 7