HTTP-Endpunkte und Anwendungsaufbau
Eingaben werden mit zod validiert, jede aendernde Aktion schreibt ins Audit-Log. Beim ersten Start entsteht ein Administrator samt Startwerten fuer die Produkterkennung; solange ein Erstpasswort gilt, bleiben schreibende Wege bis auf den Passwortwechsel gesperrt. Nicht-API-Pfade fallen auf die SPA zurueck, damit Deep-Links funktionieren.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import { z } from 'zod';
|
||||
import { requireAdmin } from '../auth/guard.js';
|
||||
import { badRequest } from '../errors.js';
|
||||
import {
|
||||
applyMapping,
|
||||
commitImport,
|
||||
createImport,
|
||||
discardImport,
|
||||
getImport,
|
||||
listImports,
|
||||
parseUpload,
|
||||
} from '../services/imports.js';
|
||||
import { contextOf, idParam, parseWith } from './helpers.js';
|
||||
|
||||
const mappingBody = z.object({
|
||||
playerKey: z.string().min(1, 'Spalte fuer die Spieler-Kennung fehlt'),
|
||||
playerName: z.string().optional(),
|
||||
product: z.string().min(1, 'Spalte fuer den Produktnamen fehlt'),
|
||||
orderId: z.string().optional(),
|
||||
purchasedAt: z.string().optional(),
|
||||
createMissingPlayers: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export async function importRoutes(app: FastifyInstance): Promise<void> {
|
||||
// Der Import veraendert Guthaben in grosser Zahl und bleibt deshalb Admins vorbehalten.
|
||||
app.post('/api/imports', { preHandler: requireAdmin }, async (request, reply) => {
|
||||
const file = await request.file();
|
||||
if (!file) throw badRequest('NO_FILE', 'Es wurde keine Datei hochgeladen');
|
||||
|
||||
const content = await file.toBuffer();
|
||||
const parsed = parseUpload(file.filename, content);
|
||||
const importId = await createImport(file.filename, parsed, contextOf(request));
|
||||
|
||||
reply.code(201);
|
||||
return {
|
||||
importId,
|
||||
columns: parsed.columns,
|
||||
rowCount: parsed.rows.length,
|
||||
sample: parsed.rows.slice(0, 5),
|
||||
};
|
||||
});
|
||||
|
||||
app.get('/api/imports', { preHandler: requireAdmin }, async () => {
|
||||
return { items: await listImports() };
|
||||
});
|
||||
|
||||
app.get('/api/imports/:id', { preHandler: requireAdmin }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
return getImport(id);
|
||||
});
|
||||
|
||||
app.post('/api/imports/:id/mapping', { preHandler: requireAdmin }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
const body = parseWith(mappingBody, request.body);
|
||||
const summary = await applyMapping(id, body, contextOf(request));
|
||||
const record = await getImport(id);
|
||||
return { summary, import: record };
|
||||
});
|
||||
|
||||
app.post('/api/imports/:id/commit', { preHandler: requireAdmin }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
return commitImport(id, contextOf(request));
|
||||
});
|
||||
|
||||
app.post('/api/imports/:id/discard', { preHandler: requireAdmin }, async (request) => {
|
||||
const { id } = parseWith(idParam, request.params);
|
||||
await discardImport(id, contextOf(request));
|
||||
return { ok: true };
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user