Release 0.3.0
This commit is contained in:
1 parent
08e8875cb5
commit
7590be66a5
31 files changed
+3270
-151
No files matched your search
@@ -0,0 +1,171 @@
|
||||
import { error, fail, redirect } from '@sveltejs/kit';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
import { requireUser } from '#lib/server/auth.js';
|
||||
import {
|
||||
deletePieceImage,
|
||||
duplicatePiece,
|
||||
getPiece,
|
||||
getTaxonomy,
|
||||
savePiece,
|
||||
setPieceArchived,
|
||||
uploadPieceImage,
|
||||
type EditablePieceStatus,
|
||||
type PieceInput,
|
||||
type PieceSourceInput
|
||||
} from '#lib/server/content.js';
|
||||
import { renderMarkdown } from '#lib/server/markdown.js';
|
||||
|
||||
function renderedFields(
|
||||
piece: Pick<
|
||||
PieceInput,
|
||||
'intro' | 'researchText' | 'moderationNotes' | 'transitionText' | 'presentationText'
|
||||
>
|
||||
) {
|
||||
return {
|
||||
intro: renderMarkdown(piece.intro),
|
||||
research: renderMarkdown(piece.researchText),
|
||||
moderation: renderMarkdown(piece.moderationNotes),
|
||||
transition: renderMarkdown(piece.transitionText),
|
||||
presentation: renderMarkdown(piece.presentationText)
|
||||
};
|
||||
}
|
||||
|
||||
function text(data: FormData, name: string) {
|
||||
return String(data.get(name) || '');
|
||||
}
|
||||
|
||||
function pieceInput(data: FormData): PieceInput {
|
||||
const labels = data.getAll('sourceLabel').map(String);
|
||||
const urls = data.getAll('sourceUrl').map(String);
|
||||
const timecodes = data.getAll('sourceTimecode').map(String);
|
||||
const sources: PieceSourceInput[] = [];
|
||||
for (let index = 0; index < Math.max(labels.length, urls.length, timecodes.length); index += 1) {
|
||||
const label = labels[index]?.trim() ?? '';
|
||||
const url = urls[index]?.trim() ?? '';
|
||||
const timecode = timecodes[index]?.trim() ?? '';
|
||||
if (!label && !url && !timecode) continue;
|
||||
sources.push({
|
||||
label,
|
||||
url,
|
||||
timecodeSeconds: timecode ? Number(timecode) : null
|
||||
});
|
||||
}
|
||||
return {
|
||||
title: text(data, 'title'),
|
||||
summary: text(data, 'summary'),
|
||||
formatId: text(data, 'formatId') || null,
|
||||
status: text(data, 'status') as EditablePieceStatus,
|
||||
durationSeconds: Number(data.get('durationSeconds')),
|
||||
intro: text(data, 'intro'),
|
||||
researchText: text(data, 'researchText'),
|
||||
moderationNotes: text(data, 'moderationNotes'),
|
||||
transitionText: text(data, 'transitionText'),
|
||||
presentationText: text(data, 'presentationText'),
|
||||
topicIds: data.getAll('topicIds').map(String),
|
||||
categoryIds: data.getAll('categoryIds').map(String),
|
||||
sources
|
||||
};
|
||||
}
|
||||
|
||||
function saveError(cause: unknown) {
|
||||
const code = cause instanceof Error ? cause.message : '';
|
||||
if (code === 'CONFLICT')
|
||||
return {
|
||||
status: 409,
|
||||
error: 'Eine neuere Änderung liegt vor. Deine Eingaben wurden nicht überschrieben.',
|
||||
conflict: true
|
||||
};
|
||||
if (code === 'INVALID_TITLE') return { status: 400, error: 'Bitte einen Titel eingeben.' };
|
||||
if (code === 'INVALID_DURATION')
|
||||
return { status: 400, error: 'Die Dauer muss zwischen 0 und 86.400 Sekunden liegen.' };
|
||||
if (code === 'INVALID_SOURCES')
|
||||
return {
|
||||
status: 400,
|
||||
error: 'Quellen benötigen einen Namen und eine vollständige HTTP- oder HTTPS-Adresse.'
|
||||
};
|
||||
if (code.startsWith('INVALID_'))
|
||||
return { status: 400, error: 'Mindestens ein Feld ist ungültig oder zu lang.' };
|
||||
return {
|
||||
status: 500,
|
||||
error: 'Speichern fehlgeschlagen. Deine Eingaben bleiben im Formular erhalten.'
|
||||
};
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = ({ locals, params }) => {
|
||||
requireUser(locals.user);
|
||||
const piece = getPiece(params.id);
|
||||
if (!piece) error(404, 'Piece nicht gefunden');
|
||||
if (piece.status === 'archived') redirect(303, '/archiv');
|
||||
return { piece, taxonomy: getTaxonomy(), rendered: renderedFields(piece) };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
save: async ({ locals, params, request }) => {
|
||||
requireUser(locals.user);
|
||||
const data = await request.formData();
|
||||
const input = pieceInput(data);
|
||||
try {
|
||||
const saved = savePiece(params.id, Number(data.get('revision')), input);
|
||||
return { intent: 'save', saved: true, ...saved, rendered: renderedFields(input) };
|
||||
} catch (cause) {
|
||||
const problem = saveError(cause);
|
||||
return fail(problem.status, { intent: 'save', ...problem });
|
||||
}
|
||||
},
|
||||
upload: async ({ locals, params, request }) => {
|
||||
requireUser(locals.user);
|
||||
const data = await request.formData();
|
||||
const image = data.get('image');
|
||||
if (!(image instanceof File))
|
||||
return fail(400, { intent: 'upload', error: 'Bitte ein Bild auswählen.' });
|
||||
try {
|
||||
await uploadPieceImage(params.id, Number(data.get('revision')), image, text(data, 'altText'));
|
||||
} catch (cause) {
|
||||
const code = cause instanceof Error ? cause.message : '';
|
||||
if (code === 'CONFLICT')
|
||||
return fail(409, {
|
||||
intent: 'upload',
|
||||
error: 'Das Piece wurde inzwischen geändert. Bitte neu laden.'
|
||||
});
|
||||
if (code === 'INVALID_IMAGE_SIZE')
|
||||
return fail(400, {
|
||||
intent: 'upload',
|
||||
error: 'Das Bild darf höchstens 5 MB groß und nicht leer sein.'
|
||||
});
|
||||
if (code === 'INVALID_IMAGE_TYPE')
|
||||
return fail(400, { intent: 'upload', error: 'Erlaubt sind JPEG, PNG, WebP und GIF.' });
|
||||
return fail(400, { intent: 'upload', error: 'Bild konnte nicht gespeichert werden.' });
|
||||
}
|
||||
redirect(303, `/fundus/${params.id}`);
|
||||
},
|
||||
deleteImage: async ({ locals, params, request }) => {
|
||||
requireUser(locals.user);
|
||||
const data = await request.formData();
|
||||
try {
|
||||
await deletePieceImage(params.id, text(data, 'mediaId'), Number(data.get('revision')));
|
||||
} catch (cause) {
|
||||
return fail(409, { intent: 'deleteImage', error: saveError(cause).error });
|
||||
}
|
||||
redirect(303, `/fundus/${params.id}`);
|
||||
},
|
||||
duplicate: async ({ locals, params }) => {
|
||||
requireUser(locals.user);
|
||||
let id: string;
|
||||
try {
|
||||
id = await duplicatePiece(params.id);
|
||||
} catch {
|
||||
return fail(400, { intent: 'duplicate', error: 'Das Piece konnte nicht dupliziert werden.' });
|
||||
}
|
||||
redirect(303, `/fundus/${id}`);
|
||||
},
|
||||
archive: async ({ locals, params, request }) => {
|
||||
requireUser(locals.user);
|
||||
const data = await request.formData();
|
||||
try {
|
||||
setPieceArchived(params.id, Number(data.get('revision')), true);
|
||||
} catch (cause) {
|
||||
return fail(409, { intent: 'archive', error: saveError(cause).error });
|
||||
}
|
||||
redirect(303, '/archiv');
|
||||
}
|
||||
} satisfies Actions;
|
||||
Reference in new issue
Block a user