21258edd95
Hop-State: A_06FP5KEDBTB4A9ZT7QB498G Hop-Proposal: R_06FP5KDWMCKVVMQZT4MVZ28 Hop-Task: T_06FP5DZ7T0G45FG93PT90B8 Hop-Attempt: AT_06FP5DZ7T0PKQW99V27JCV8
142 lines
5.0 KiB
TypeScript
142 lines
5.0 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { registerUser, createSession } from '@/lib/auth';
|
|
import { createStorageSession } from '@/lib/storage/session';
|
|
import { db, nodes, users } from '@/db';
|
|
import { eq } from 'drizzle-orm';
|
|
import { verifyTurnstileToken } from '@/lib/turnstile';
|
|
import { testS3Credentials } from '@/lib/storage/s3';
|
|
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(),
|
|
turnstileToken: z.string().nullable().optional(),
|
|
// S3-compatible storage credentials
|
|
storageProvider: z.string().min(1),
|
|
storageEndpoint: z.string().nullable().optional(),
|
|
storagePublicBaseUrl: z.string().nullable().optional(),
|
|
storageRegion: z.string().min(1),
|
|
storageBucket: z.string().min(1),
|
|
storageAccessKey: z.string().min(10),
|
|
storageSecretKey: z.string().min(10),
|
|
});
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const body = await request.json();
|
|
|
|
// Log registration attempt (excluding password)
|
|
const { password, ...logData } = body;
|
|
console.log('Registration attempt details:', logData);
|
|
|
|
const data = registerSchema.parse(body);
|
|
|
|
// Verify Turnstile token if provided
|
|
if (data.turnstileToken) {
|
|
const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || undefined;
|
|
const isValid = await verifyTurnstileToken(data.turnstileToken, ip);
|
|
if (!isValid) {
|
|
console.error('Turnstile verification failed for handle:', data.handle);
|
|
return NextResponse.json(
|
|
{ error: 'Bot verification failed. Please try again.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Test S3 credentials before creating account
|
|
console.log('[Register] Testing S3 credentials...');
|
|
const s3Test = await testS3Credentials(
|
|
data.storageEndpoint || null,
|
|
data.storageRegion,
|
|
data.storageBucket,
|
|
data.storageAccessKey,
|
|
data.storageSecretKey
|
|
);
|
|
|
|
if (!s3Test.success) {
|
|
console.error('[Register] S3 credential test failed:', s3Test.error);
|
|
return NextResponse.json(
|
|
{ error: `Storage connection failed: ${s3Test.error}` },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
console.log('[Register] S3 credentials verified successfully');
|
|
|
|
const user = await registerUser(
|
|
data.handle,
|
|
data.email,
|
|
data.password,
|
|
data.displayName,
|
|
data.storageProvider,
|
|
data.storageEndpoint || null,
|
|
data.storagePublicBaseUrl || null,
|
|
data.storageRegion,
|
|
data.storageBucket,
|
|
data.storageAccessKey,
|
|
data.storageSecretKey
|
|
);
|
|
|
|
// Check if this is an NSFW node and auto-enable NSFW settings
|
|
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:43821';
|
|
const node = await db.query.nodes.findFirst({
|
|
where: { 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);
|
|
await createStorageSession(user, data.password);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
user: {
|
|
id: user.id,
|
|
handle: user.handle,
|
|
displayName: user.displayName,
|
|
did: user.did,
|
|
publicKey: user.publicKey,
|
|
privateKeyEncrypted: user.privateKeyEncrypted, // Client will decrypt with password
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Registration error detailed:', error);
|
|
|
|
if (error instanceof z.ZodError) {
|
|
console.error('Validation error:', error.issues);
|
|
return NextResponse.json(
|
|
{ error: 'Invalid input', details: error.issues },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const errorMessage = error instanceof Error ? error.message : 'Registration failed';
|
|
|
|
// Return 400 for known business logic errors
|
|
if (errorMessage.includes('taken') || errorMessage.includes('registered') || errorMessage.includes('Handle must be')) {
|
|
return NextResponse.json(
|
|
{ error: errorMessage },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Return 500 for everything else so we can see it's a server error
|
|
return NextResponse.json(
|
|
{ error: `Server error: ${errorMessage}` },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|