import { Hono } from 'hono' import { jwt, sign, verify } from 'hono/jwt' import { drizzle } from 'drizzle-orm/d1' import { eq, and, inArray, desc } from 'drizzle-orm' import { usersTable, postsTable, followsTable, likesTable } from './db/schema' type Bindings = { DB: D1Database JWT_SECRET: string } const app = new Hono<{ Bindings: Bindings }>() // Middleware for JWT protection on some routes app.use('/api/protected/*', async (c, next) => { const jwtMiddleware = jwt({ secret: c.env.JWT_SECRET || 'supersecret', alg: 'HS256' }) return jwtMiddleware(c, next) }) // --- AUTH --- app.post('/api/signup', async (c) => { const { username, password } = await c.req.json() const db = drizzle(c.env.DB) try { const result = await db.insert(usersTable).values({ username, passwordHash: password, // In a real app, hash this! }).returning() return c.json({ success: true, user: result[0] }) } catch (e) { console.error('Signup error:', e) return c.json({ error: 'Username taken or database error', details: String(e) }, 400) } }) app.post('/api/login', async (c) => { const { username, password } = await c.req.json() const db = drizzle(c.env.DB) const user = await db.select().from(usersTable) .where(and(eq(usersTable.username, username), eq(usersTable.passwordHash, password))) .get() if (!user) return c.json({ error: 'Invalid credentials' }, 401) const token = await sign({ id: user.id, username: user.username }, c.env.JWT_SECRET || 'supersecret', 'HS256') return c.json({ token, user }) }) // --- POSTS & FEED --- app.get('/api/protected/feed', async (c) => { const payload = c.get('jwtPayload') const db = drizzle(c.env.DB) try { // Get followed users const followed = await db.select({ id: followsTable.followingId }) .from(followsTable) .where(eq(followsTable.followerId, payload.id)) .all() const followedIds = [payload.id, ...followed.map(f => f.id)] const feed = await db.select({ id: postsTable.id, content: postsTable.content, createdAt: postsTable.createdAt, username: usersTable.username, userId: usersTable.id }) .from(postsTable) .leftJoin(usersTable, eq(postsTable.userId, usersTable.id)) .where(inArray(postsTable.userId, followedIds)) .orderBy(desc(postsTable.createdAt)) .all() return c.json(feed) } catch (e) { console.error('Feed error:', e) return c.json({ error: 'Failed to fetch feed', details: String(e) }, 500) } }) app.post('/api/protected/posts', async (c) => { const payload = c.get('jwtPayload') const { content } = await c.req.json() const db = drizzle(c.env.DB) try { const result = await db.insert(postsTable).values({ userId: payload.id, content, }).returning() return c.json(result[0]) } catch (e) { console.error('Post creation error:', e) return c.json({ error: 'Failed to create post', details: String(e) }, 500) } }) // --- FOLLOW & LIKE --- app.post('/api/protected/follow/:id', async (c) => { const payload = c.get('jwtPayload') const targetId = parseInt(c.req.param('id')) const db = drizzle(c.env.DB) await db.insert(followsTable).values({ followerId: payload.id, followingId: targetId, }).run() return c.json({ success: true }) }) app.post('/api/protected/like/:id', async (c) => { const payload = c.get('jwtPayload') const postId = parseInt(c.req.param('id')) const db = drizzle(c.env.DB) await db.insert(likesTable).values({ userId: payload.id, postId, }).run() return c.json({ success: true }) }) // Serve React App app.get('*', async (c) => { return c.html(` Social Media App ${import.meta.env?.PROD ? '' : ` `}
`) }) export default app