Copy overhaul + qr designs
This commit is contained in:
129
src/app/(main)/api/design-presets/route.ts
Normal file
129
src/app/(main)/api/design-presets/route.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getSessionUserId } from '@/lib/session';
|
||||
import { db } from '@/lib/db';
|
||||
import { csrfProtection } from '@/lib/csrf';
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Saved QR design presets.
|
||||
*
|
||||
* Business-only. The value here is repeatability, not novelty: an agency running
|
||||
* several clients needs client A to look identical across every code, including
|
||||
* the 500 that came out of one spreadsheet upload.
|
||||
*/
|
||||
|
||||
const MAX_PRESETS = 50;
|
||||
|
||||
const presetSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required').max(60),
|
||||
style: z.record(z.any()),
|
||||
});
|
||||
|
||||
function isAllowed(plan: string | undefined): boolean {
|
||||
return plan === 'BUSINESS' || plan === 'ENTERPRISE';
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const userId = getSessionUserId();
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const presets = await db.qRDesignPreset.findMany({
|
||||
where: { userId },
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
});
|
||||
|
||||
return NextResponse.json(presets);
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const csrfCheck = csrfProtection(request);
|
||||
if (!csrfCheck.valid) {
|
||||
return NextResponse.json({ error: csrfCheck.error }, { status: 403 });
|
||||
}
|
||||
|
||||
const userId = getSessionUserId();
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { plan: true },
|
||||
});
|
||||
|
||||
if (!isAllowed(user?.plan)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Upgrade required',
|
||||
message: 'Saved design presets are part of the Business plan.',
|
||||
plan: user?.plan ?? 'FREE',
|
||||
},
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = presetSchema.parse(await request.json());
|
||||
} catch (err) {
|
||||
if (err instanceof z.ZodError) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid input', details: err.errors },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
return NextResponse.json({ error: 'Invalid input' }, { status: 400 });
|
||||
}
|
||||
|
||||
const count = await db.qRDesignPreset.count({ where: { userId } });
|
||||
const existing = await db.qRDesignPreset.findFirst({
|
||||
where: { userId, name: data.name },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!existing && count >= MAX_PRESETS) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Preset limit reached',
|
||||
message: `You can keep up to ${MAX_PRESETS} presets. Delete one to save another.`,
|
||||
},
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
// Same name overwrites rather than creating a near-duplicate. Someone saving
|
||||
// "Client A" twice means "update it", not "keep both".
|
||||
const preset = await db.qRDesignPreset.upsert({
|
||||
where: { userId_name: { userId, name: data.name } },
|
||||
create: { userId, name: data.name, style: data.style },
|
||||
update: { style: data.style },
|
||||
});
|
||||
|
||||
return NextResponse.json(preset, { status: existing ? 200 : 201 });
|
||||
}
|
||||
|
||||
export async function DELETE(request: NextRequest) {
|
||||
const csrfCheck = csrfProtection(request);
|
||||
if (!csrfCheck.valid) {
|
||||
return NextResponse.json({ error: csrfCheck.error }, { status: 403 });
|
||||
}
|
||||
|
||||
const userId = getSessionUserId();
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const id = new URL(request.url).searchParams.get('id');
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: 'Missing id' }, { status: 400 });
|
||||
}
|
||||
|
||||
const deleted = await db.qRDesignPreset.deleteMany({ where: { id, userId } });
|
||||
if (deleted.count === 0) {
|
||||
return NextResponse.json({ error: 'Preset not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
Reference in New Issue
Block a user