Add v2 schedule API endpoint
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { prisma } from '$lib/db';
|
||||
import { clientIp, jsonOk, rateLimited } from '$lib/server/v2-helpers';
|
||||
import { rateLimit } from '$lib/server/v2-rate-limit';
|
||||
import { listBlocks } from '$lib/server/schedule';
|
||||
import { serializeScheduleBlock } from '$lib/server/v2-serializers';
|
||||
|
||||
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
/**
|
||||
* Machine-to-app endpoint used by the RadiantWave kiosk. Authenticates with
|
||||
* `email` + `licenseKey` query parameters and validates only that the pair
|
||||
* belong together — license active/entitlement is deliberately not checked
|
||||
* here, since an invalid license would already lock the kiosk out.
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ request, url }) => {
|
||||
const ip = clientIp(request);
|
||||
const rl = rateLimit({ key: `schedule:${ip}`, limit: 10, windowMs: 60_000 });
|
||||
if (!rl.ok) {
|
||||
return rateLimited(rl.retryAfterMs);
|
||||
}
|
||||
|
||||
const email = (url.searchParams.get('email') ?? '').trim().toLowerCase();
|
||||
const licenseKey = (
|
||||
url.searchParams.get('licenseKey') ??
|
||||
url.searchParams.get('key') ??
|
||||
''
|
||||
).trim();
|
||||
|
||||
if (!email || !licenseKey || email.length > 254 || !EMAIL_RE.test(email)) {
|
||||
throw error(400, 'not-authorized');
|
||||
}
|
||||
|
||||
const storedKey = await prisma.licenseKey.findUnique({ where: { key: licenseKey } });
|
||||
if (!storedKey) {
|
||||
throw error(400, 'not-authorized');
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { email } });
|
||||
if (!user) {
|
||||
throw error(400, 'not-authorized');
|
||||
}
|
||||
|
||||
// Licenses created before accounts were linked may carry only an email.
|
||||
const belongs =
|
||||
storedKey.userId === user.id ||
|
||||
(storedKey.userId === null && storedKey.email?.toLowerCase() === email);
|
||||
if (!belongs) {
|
||||
throw error(400, "this key doesn't appear to belong to you");
|
||||
}
|
||||
|
||||
const blocks = await listBlocks(user.id);
|
||||
|
||||
const serialized = blocks.map((block) =>
|
||||
serializeScheduleBlock(
|
||||
block,
|
||||
block.proclamations.map((entry) => entry.proclamation.id)
|
||||
)
|
||||
);
|
||||
|
||||
return jsonOk({ email, blocks: serialized });
|
||||
};
|
||||
Reference in New Issue
Block a user