Initial commit
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db, users } from '@/db';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const handle = searchParams.get('handle')?.toLowerCase().trim();
|
||||
|
||||
if (!handle || handle.length < 3) {
|
||||
return NextResponse.json({ available: false, error: 'Handle too short' });
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(handle)) {
|
||||
return NextResponse.json({ available: false, error: 'Invalid characters' });
|
||||
}
|
||||
|
||||
const existingUser = await db.query.users.findFirst({
|
||||
where: eq(users.handle, handle),
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
available: !existingUser,
|
||||
handle
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Check handle error:', error);
|
||||
return NextResponse.json({ error: 'Failed to check handle' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { authenticateUser, createSession } from '@/lib/auth';
|
||||
import { z } from 'zod';
|
||||
|
||||
const loginSchema = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
});
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const data = loginSchema.parse(body);
|
||||
|
||||
const user = await authenticateUser(data.email, data.password);
|
||||
|
||||
// Create session
|
||||
await createSession(user.id);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
handle: user.handle,
|
||||
displayName: user.displayName,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid input', details: error.issues },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'Login failed' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { destroySession } from '@/lib/auth';
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
await destroySession();
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Logout failed' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
} 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 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { registerUser, createSession } from '@/lib/auth';
|
||||
import { db, nodes, users } from '@/db';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { z } from 'zod';
|
||||
|
||||
const registerSchema = z.object({
|
||||
handle: z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/),
|
||||
email: z.string().email(),
|
||||
password: z.string().min(8),
|
||||
displayName: z.string().optional(),
|
||||
});
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const data = registerSchema.parse(body);
|
||||
|
||||
const user = await registerUser(
|
||||
data.handle,
|
||||
data.email,
|
||||
data.password,
|
||||
data.displayName
|
||||
);
|
||||
|
||||
// Check if this is an NSFW node and auto-enable NSFW settings
|
||||
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
||||
const node = await db.query.nodes.findFirst({
|
||||
where: eq(nodes.domain, nodeDomain),
|
||||
});
|
||||
|
||||
if (node?.isNsfw) {
|
||||
// Auto-enable NSFW viewing and mark account as NSFW for users on NSFW nodes
|
||||
await db.update(users)
|
||||
.set({
|
||||
nsfwEnabled: true,
|
||||
isNsfw: true
|
||||
})
|
||||
.where(eq(users.id, user.id));
|
||||
}
|
||||
|
||||
// Create session for new user
|
||||
await createSession(user.id);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
handle: user.handle,
|
||||
displayName: user.displayName,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Registration error:', error);
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid input', details: error.issues },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'Registration failed' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user