Number entries on publish, delete media, read the cover

- drafts stay unnumbered, the number is handed out when an entry goes live
- DELETE /api/v1/media/:id and a delete control in the media library
- cover_media_id in the draft read, coverMediaId and coverUrl in the archive read
This commit is contained in:
Matthias G
2026-08-01 12:25:01 +02:00
parent afaa323848
commit 2fd71fd406
31 changed files with 2286 additions and 76 deletions
+47 -14
View File
@@ -30,7 +30,7 @@ export type EntryFilter = {
export type EntryListItem = {
id: string
number: number
number: number | null
slug: string
title: string
teaser: string | null
@@ -180,26 +180,21 @@ export async function listEntryBlocks(postId: string): Promise<Block[]> {
export async function createEntry(values: EntryCreateValues): Promise<Post> {
const rows = await db.execute<{ id: string }>(sql`
with taken as (
select coalesce(max(${posts.number}), 0) + 1 as value
with used as (
select count(*) + 1 as value
from ${posts}
where ${posts.projectId} = ${values.projectId}::uuid
), counter as (
insert into post_counter (project_id, value)
select ${values.projectId}::uuid, taken.value from taken
on conflict (project_id) do update set value = greatest(post_counter.value + 1, excluded.value)
returning value
and (${posts.slug} = ${values.slug} or ${posts.slug} like ${values.slug} || '-%')
)
insert into post (project_id, number, slug, title, teaser, type_id, audience, author_name, cover_media_id)
insert into post (project_id, slug, title, teaser, type_id, audience, author_name, cover_media_id)
select
${values.projectId}::uuid,
counter.value,
case
when exists (
select 1 from post taken_slug
where taken_slug.project_id = ${values.projectId}::uuid and taken_slug.slug = ${values.slug}
)
then ${values.slug} || '-' || counter.value
then ${values.slug} || '-' || used.value
else ${values.slug}
end,
${values.title},
@@ -208,7 +203,7 @@ export async function createEntry(values: EntryCreateValues): Promise<Post> {
${values.audience}::post_audience,
${values.authorName},
${values.coverMediaId}::uuid
from counter
from used
returning id
`)
@@ -244,7 +239,7 @@ export async function moveEntryToProject(id: string, projectId: string): Promise
)
update post set
project_id = ${projectId}::uuid,
number = counter.value,
number = case when post.number is null then null else counter.value end,
slug = case
when exists (
select 1 from post taken_slug
@@ -286,6 +281,36 @@ export async function setEntryStatus(
.where(eq(posts.id, id))
.returning()
const post = rows[0]
if (!post || status !== 'published' || post.number !== null) {
return post
}
return assignEntryNumber(id)
}
export async function assignEntryNumber(id: string): Promise<Post | undefined> {
await db.execute(sql`
with target as (
select project_id from post where id = ${id}::uuid and number is null
), taken as (
select coalesce(max(${posts.number}), 0) + 1 as value
from ${posts}, target
where ${posts.projectId} = target.project_id
), counter as (
insert into post_counter (project_id, value)
select target.project_id, taken.value from target, taken
on conflict (project_id) do update set value = greatest(post_counter.value + 1, excluded.value)
returning value
)
update post set number = counter.value
from counter
where post.id = ${id}::uuid and post.number is null
`)
const rows = await db.select().from(posts).where(eq(posts.id, id)).limit(1)
return rows[0]
}
@@ -325,5 +350,13 @@ export async function publishDue(now: Date): Promise<PublishedEntry[]> {
title: posts.title,
})
return rows
const done: PublishedEntry[] = []
for (const row of rows) {
const numbered = row.number === null ? await assignEntryNumber(row.id) : undefined
done.push({ ...row, number: numbered?.number ?? row.number ?? 0 })
}
return done
}