Files
Synapsis/src/app/api/auth/me/route.ts
T
Christopher 5903022f8a Add encrypted key support for chat and nodes
Introduces encrypted private key storage for nodes and users, updates chat message schema to support sender-side encryption, and adds supporting libraries and tests for cryptographic signing and identity unlock flows. Includes new database migrations, API route updates, and React components for identity unlock prompts.
2026-01-27 17:13:28 -08:00

123 lines
4.6 KiB
TypeScript

import { NextResponse } from 'next/server';
import { getSession, requireAuth } from '@/lib/auth';
import { db, users } from '@/db';
import { eq } from 'drizzle-orm';
import { z } from 'zod';
const updateProfileSchema = z.object({
displayName: z.string().min(1).max(50).optional(),
bio: z.string().max(160).optional().nullable(),
avatarUrl: z.string().url().or(z.string().length(0)).optional().nullable(),
headerUrl: z.string().url().or(z.string().length(0)).optional().nullable(),
website: z.string().url().or(z.string().length(0)).optional().nullable(),
});
export async function GET() {
try {
// Return null user if no database is connected (for UI testing)
if (!db) {
return NextResponse.json({ user: null });
}
const session = await getSession();
if (!session) {
return NextResponse.json({ user: null });
}
return NextResponse.json({
user: {
id: session.user.id,
handle: session.user.handle,
displayName: session.user.displayName,
avatarUrl: session.user.avatarUrl,
bio: session.user.bio,
website: session.user.website,
did: session.user.did,
publicKey: session.user.publicKey,
privateKeyEncrypted: session.user.privateKeyEncrypted,
},
});
} catch (error) {
console.error('Session check error:', error);
return NextResponse.json({ user: null });
}
}
export async function PATCH(request: Request) {
try {
if (!db) {
return NextResponse.json({ error: 'Database not available' }, { status: 503 });
}
const currentUser = await requireAuth();
const body = await request.json();
const data = updateProfileSchema.parse(body);
const updateData: {
displayName?: string;
bio?: string | null;
avatarUrl?: string | null;
headerUrl?: string | null;
website?: string | null;
updatedAt?: Date;
} = {};
if (data.displayName !== undefined) updateData.displayName = data.displayName;
if (data.bio !== undefined) updateData.bio = data.bio === '' ? null : data.bio;
if (data.avatarUrl !== undefined) updateData.avatarUrl = data.avatarUrl === '' ? null : data.avatarUrl;
if (data.headerUrl !== undefined) updateData.headerUrl = data.headerUrl === '' ? null : data.headerUrl;
if (data.website !== undefined) updateData.website = data.website === '' ? null : data.website;
if (Object.keys(updateData).length === 0) {
return NextResponse.json({
user: {
id: currentUser.id,
handle: currentUser.handle,
displayName: currentUser.displayName,
avatarUrl: currentUser.avatarUrl,
bio: currentUser.bio,
headerUrl: currentUser.headerUrl,
website: currentUser.website,
followersCount: currentUser.followersCount,
followingCount: currentUser.followingCount,
postsCount: currentUser.postsCount,
createdAt: currentUser.createdAt,
},
});
}
updateData.updatedAt = new Date();
const [updatedUser] = await db.update(users)
.set(updateData)
.where(eq(users.id, currentUser.id))
.returning();
return NextResponse.json({
user: {
id: updatedUser.id,
handle: updatedUser.handle,
displayName: updatedUser.displayName,
avatarUrl: updatedUser.avatarUrl,
bio: updatedUser.bio,
headerUrl: updatedUser.headerUrl,
website: updatedUser.website,
followersCount: updatedUser.followersCount,
followingCount: updatedUser.followingCount,
postsCount: updatedUser.postsCount,
createdAt: updatedUser.createdAt,
},
});
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ error: 'Invalid input', details: error.issues }, { status: 400 });
}
if (error instanceof Error && error.message === 'Authentication required') {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
}
console.error('Profile update error:', error);
return NextResponse.json({ error: 'Failed to update profile' }, { status: 500 });
}
}