81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
|
|
import { fail, redirect } from '@sveltejs/kit';
|
||
|
|
import type { Actions, PageServerLoad } from './$types';
|
||
|
|
import { db } from '$lib/server/db';
|
||
|
|
import { projects, projectLinks } from '$lib/server/db/schema';
|
||
|
|
import { LINK_KINDS } from '$lib/config';
|
||
|
|
import { newId, slugify } from '$lib/markdown';
|
||
|
|
import { eq } from 'drizzle-orm';
|
||
|
|
|
||
|
|
export const load: PageServerLoad = async ({ locals }) => {
|
||
|
|
if (!locals.user) redirect(302, '/login');
|
||
|
|
return {};
|
||
|
|
};
|
||
|
|
|
||
|
|
export const actions: Actions = {
|
||
|
|
default: async ({ request, locals }) => {
|
||
|
|
const data = await request.formData();
|
||
|
|
const title = (data.get('title') ?? '').toString().trim();
|
||
|
|
const slugRaw = (data.get('slug') ?? '').toString().trim();
|
||
|
|
const summary = (data.get('summary') ?? '').toString().trim();
|
||
|
|
const bodyMd = (data.get('body_md') ?? '').toString();
|
||
|
|
const coverUrl = (data.get('cover_url') ?? '').toString().trim() || null;
|
||
|
|
|
||
|
|
const values = { title, slug: slugRaw, summary, body_md: bodyMd, cover_url: coverUrl };
|
||
|
|
|
||
|
|
if (!locals.user) return fail(401, { error: 'Not authenticated', values });
|
||
|
|
if (!title) return fail(400, { error: 'Title is required.', values });
|
||
|
|
if (!summary) return fail(400, { error: 'Summary is required.', values });
|
||
|
|
|
||
|
|
const slug = slugify(slugRaw || title);
|
||
|
|
if (!slug) return fail(400, { error: 'Slug could not be generated.', values });
|
||
|
|
|
||
|
|
const exists = db.select({ id: projects.id }).from(projects).where(eq(projects.slug, slug)).get();
|
||
|
|
if (exists) {
|
||
|
|
return fail(400, { error: `A project with slug "${slug}" already exists.`, values });
|
||
|
|
}
|
||
|
|
|
||
|
|
const kinds = data.getAll('link_kind').map(String);
|
||
|
|
const labels = data.getAll('link_label').map(String);
|
||
|
|
const urls = data.getAll('link_url').map(String);
|
||
|
|
|
||
|
|
const linksToInsert: { kind: string; label: string; url: string; position: number }[] = [];
|
||
|
|
for (let i = 0; i < kinds.length; i++) {
|
||
|
|
const k = kinds[i];
|
||
|
|
const l = (labels[i] ?? '').trim();
|
||
|
|
const u = (urls[i] ?? '').trim();
|
||
|
|
if (!u) continue;
|
||
|
|
if (!(LINK_KINDS as readonly string[]).includes(k)) continue;
|
||
|
|
linksToInsert.push({ kind: k, label: l || u, url: u, position: i });
|
||
|
|
}
|
||
|
|
|
||
|
|
const id = newId('prj');
|
||
|
|
db.insert(projects)
|
||
|
|
.values({
|
||
|
|
id,
|
||
|
|
slug,
|
||
|
|
title,
|
||
|
|
summary,
|
||
|
|
bodyMd,
|
||
|
|
coverUrl,
|
||
|
|
authorId: locals.user.id,
|
||
|
|
status: 'published'
|
||
|
|
})
|
||
|
|
.run();
|
||
|
|
|
||
|
|
for (const link of linksToInsert) {
|
||
|
|
db.insert(projectLinks)
|
||
|
|
.values({
|
||
|
|
id: newId('lnk'),
|
||
|
|
projectId: id,
|
||
|
|
kind: link.kind as (typeof LINK_KINDS)[number],
|
||
|
|
label: link.label,
|
||
|
|
url: link.url,
|
||
|
|
position: link.position
|
||
|
|
})
|
||
|
|
.run();
|
||
|
|
}
|
||
|
|
|
||
|
|
redirect(303, `/projects/${slug}`);
|
||
|
|
}
|
||
|
|
};
|