145 lines
4.6 KiB
TypeScript
145 lines
4.6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { cookies } from 'next/headers';
|
|
import { db } from '@/lib/db';
|
|
|
|
// Reserved subdomains that cannot be used
|
|
const RESERVED_SUBDOMAINS = [
|
|
'www', 'app', 'api', 'admin', 'mail', 'email',
|
|
'ftp', 'smtp', 'pop', 'imap', 'dns', 'ns1', 'ns2',
|
|
'blog', 'shop', 'store', 'help', 'support', 'dashboard',
|
|
'login', 'signup', 'auth', 'cdn', 'static', 'assets',
|
|
'dev', 'staging', 'test', 'demo', 'beta', 'alpha'
|
|
];
|
|
|
|
// Validate subdomain format
|
|
function isValidSubdomain(subdomain: string): { valid: boolean; error?: string } {
|
|
if (!subdomain) {
|
|
return { valid: false, error: 'Subdomain is required' };
|
|
}
|
|
|
|
// Must be lowercase
|
|
if (subdomain !== subdomain.toLowerCase()) {
|
|
return { valid: false, error: 'Subdomain must be lowercase' };
|
|
}
|
|
|
|
// Length check
|
|
if (subdomain.length < 3 || subdomain.length > 30) {
|
|
return { valid: false, error: 'Subdomain must be 3-30 characters' };
|
|
}
|
|
|
|
// Alphanumeric and hyphens only, no leading/trailing hyphens
|
|
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(subdomain)) {
|
|
return { valid: false, error: 'Only lowercase letters, numbers, and hyphens allowed' };
|
|
}
|
|
|
|
// No consecutive hyphens
|
|
if (/--/.test(subdomain)) {
|
|
return { valid: false, error: 'No consecutive hyphens allowed' };
|
|
}
|
|
|
|
// Check reserved
|
|
if (RESERVED_SUBDOMAINS.includes(subdomain)) {
|
|
return { valid: false, error: 'This subdomain is reserved' };
|
|
}
|
|
|
|
return { valid: true };
|
|
}
|
|
|
|
// GET /api/user/subdomain - Get current subdomain
|
|
export async function GET() {
|
|
try {
|
|
const userId = cookies().get('userId')?.value;
|
|
if (!userId) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const user = await db.user.findUnique({
|
|
where: { id: userId },
|
|
select: { subdomain: true },
|
|
});
|
|
|
|
if (!user) {
|
|
return NextResponse.json({ error: 'User not found' }, { status: 404 });
|
|
}
|
|
|
|
return NextResponse.json({ subdomain: user.subdomain });
|
|
} catch (error) {
|
|
console.error('Error fetching subdomain:', error);
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// POST /api/user/subdomain - Set subdomain
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const userId = cookies().get('userId')?.value;
|
|
if (!userId) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const body = await request.json();
|
|
const subdomain = body.subdomain?.trim().toLowerCase();
|
|
|
|
// Validate
|
|
const validation = isValidSubdomain(subdomain);
|
|
if (!validation.valid) {
|
|
return NextResponse.json({ error: validation.error }, { status: 400 });
|
|
}
|
|
|
|
// Check if already taken by another user
|
|
const existing = await db.user.findFirst({
|
|
where: {
|
|
subdomain,
|
|
NOT: { id: userId },
|
|
},
|
|
});
|
|
|
|
if (existing) {
|
|
return NextResponse.json({ error: 'This subdomain is already taken' }, { status: 409 });
|
|
}
|
|
|
|
// Update user
|
|
try {
|
|
const updatedUser = await db.user.update({
|
|
where: { id: userId },
|
|
data: { subdomain },
|
|
select: { subdomain: true } // Only select needed fields
|
|
});
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
subdomain: updatedUser.subdomain,
|
|
url: `https://${updatedUser.subdomain}.qrmaster.net`
|
|
});
|
|
} catch (error: any) {
|
|
if (error.code === 'P2025') {
|
|
return NextResponse.json({ error: 'User not found' }, { status: 404 });
|
|
}
|
|
throw error;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error setting subdomain:', error);
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// DELETE /api/user/subdomain - Remove subdomain
|
|
export async function DELETE() {
|
|
try {
|
|
const userId = cookies().get('userId')?.value;
|
|
if (!userId) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
await db.user.update({
|
|
where: { id: userId },
|
|
data: { subdomain: null },
|
|
});
|
|
|
|
return NextResponse.json({ success: true });
|
|
} catch (error) {
|
|
console.error('Error removing subdomain:', error);
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|