- Add sortOrder column to ProclamationCategory with migration - Implement drag-and-drop reorder in AdminTable with persist-to-DB - Refactor AdminTable to use $derived + dragOverride (no $effect sync) - Replace window.location.reload with invalidateAll for reorder - Convert license key create to modal with subscription level dropdown - Add LicenseKeyForm.svelte following the CategoryForm pattern - Add replace action (delete + generate new key with same level) - Remove deactivateOld checkbox; each key has its own level - Use use:enhance with update() to refresh list after create/replace
102 lines
2.8 KiB
TypeScript
102 lines
2.8 KiB
TypeScript
import type { PageServerLoad } from './$types';
|
|
import { getModelConfig } from '$lib/svelte-admin/registry';
|
|
import { loadRecords } from '$lib/svelte-admin/crud';
|
|
import { prisma } from '$lib/db';
|
|
import { sendMail } from '$lib/server/resend';
|
|
import { error } from '@sveltejs/kit';
|
|
|
|
const config = getModelConfig('license-keys')!;
|
|
|
|
export const load: PageServerLoad = async ({ locals }) => {
|
|
const records = await loadRecords(locals, config);
|
|
|
|
const subscriptionLevels = await prisma.subscriptionLevel.findMany({
|
|
select: { id: true, name: true },
|
|
orderBy: { name: 'asc' }
|
|
});
|
|
|
|
return {
|
|
...records,
|
|
subscriptionLevels: subscriptionLevels.map((s: { id: number; name: string }) => ({
|
|
label: s.name,
|
|
value: s.name
|
|
}))
|
|
};
|
|
};
|
|
|
|
export const actions = {
|
|
createKey: async ({ request, locals }) => {
|
|
if (!locals.user) throw error(401, 'Unauthorized');
|
|
|
|
const formData = await request.formData();
|
|
const email = formData.get('email')?.toString();
|
|
const level = formData.get('level')?.toString();
|
|
|
|
if (!email) {
|
|
return { success: false, error: 'Email is required' };
|
|
}
|
|
if (!level) {
|
|
return { success: false, error: 'Subscription level is required' };
|
|
}
|
|
|
|
const user = await prisma.user.findUnique({ where: { email } });
|
|
if (!user) {
|
|
return { success: false, error: 'No user found with that email' };
|
|
}
|
|
|
|
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
|
|
const key = `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
|
|
|
|
await prisma.licenseKey.create({
|
|
data: {
|
|
key,
|
|
active: true,
|
|
level,
|
|
userId: user.id
|
|
}
|
|
});
|
|
|
|
try {
|
|
const profile = await prisma.profile.findUnique({ where: { userId: user.id } });
|
|
await sendMail({
|
|
to: email,
|
|
subject: 'Your License Key',
|
|
data: {
|
|
firstName: profile?.firstName ?? user.name ?? 'Valued Customer',
|
|
licenseKey: key
|
|
}
|
|
});
|
|
} catch (e) {
|
|
console.error('Failed to send license key email:', e);
|
|
}
|
|
|
|
return { success: true, error: undefined };
|
|
},
|
|
replace: async ({ request, locals }) => {
|
|
if (!locals.user) throw error(401, 'Unauthorized');
|
|
|
|
const formData = await request.formData();
|
|
const keyId = formData.get('id')?.toString();
|
|
if (!keyId) return { success: false, error: 'Missing key id' };
|
|
|
|
const existing = await prisma.licenseKey.findUnique({ where: { id: keyId } });
|
|
if (!existing) return { success: false, error: 'License key not found' };
|
|
|
|
await prisma.licenseKey.delete({ where: { id: keyId } });
|
|
|
|
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
|
|
const key = `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
|
|
|
|
await prisma.licenseKey.create({
|
|
data: {
|
|
key,
|
|
active: true,
|
|
level: existing.level,
|
|
userId: existing.userId
|
|
}
|
|
});
|
|
|
|
return { success: true, error: undefined };
|
|
}
|
|
};
|