Portal: per-license schedule service, shared validator, and key-only GET
Implements the shared validateSchedule validator (daily + weekly, midnight spillover, Sunday->Monday wrap, adjacency, empty-block rejection), a per-License-Key schedule service (get/set type, CRUD blocks, weekly prefill with confirmation), the content-hash version/ETag serializer, the new GET /api/v2/licenses/[licenseKey]/schedule (200/304/404, type null), and additive evolution of the legacy GET /api/v2/schedule. Adds validator, serializer (against docs/fixtures), endpoint ETag/304, and service tests.
This commit is contained in:
@@ -3,16 +3,24 @@ 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';
|
||||
import { getScheduleForLicense, scheduleBlocksToInput } from '$lib/server/schedule';
|
||||
import { buildSchedulePayload } from '$lib/server/v2-serializers';
|
||||
|
||||
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
function scheduleType(value: string | null | undefined) {
|
||||
return value === 'daily' || value === 'weekly' ? value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Extended additively with `version`, `type`, `daily`, and `weekly`. `email`
|
||||
* and `blocks` keep their exact previous meaning (daily flattening; empty for
|
||||
* weekly), so old Kiosks are unaffected. No ETag/304 here.
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ request, url }) => {
|
||||
const ip = clientIp(request);
|
||||
@@ -50,14 +58,10 @@ export const GET: RequestHandler = async ({ request, url }) => {
|
||||
throw error(400, "this key doesn't appear to belong to you");
|
||||
}
|
||||
|
||||
const blocks = await listBlocks(user.id);
|
||||
const schedule = await getScheduleForLicense(storedKey.id);
|
||||
const type = scheduleType(schedule?.type);
|
||||
const blocks = schedule ? scheduleBlocksToInput(schedule.blocks) : [];
|
||||
const payload = buildSchedulePayload(type, blocks);
|
||||
|
||||
const serialized = blocks.map((block) =>
|
||||
serializeScheduleBlock(
|
||||
block,
|
||||
block.proclamations.map((entry) => entry.proclamation.id)
|
||||
)
|
||||
);
|
||||
|
||||
return jsonOk({ email, blocks: serialized });
|
||||
return jsonOk({ email, ...payload });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user