Deleting a digest makes it sendable again

The dispatch timestamp survived the deletion, so removing a message in Slack
left the digest permanently swallowed. Deleting now rewinds the window to the
newest digest that still stands.
This commit is contained in:
Matthias G
2026-08-03 13:32:01 +02:00
parent c1cfd41ad3
commit 5ef9cd704e
3 changed files with 58 additions and 3 deletions
+12 -1
View File
@@ -1,4 +1,4 @@
import { desc, eq, inArray } from 'drizzle-orm'
import { and, desc, eq, inArray, isNull } from 'drizzle-orm'
import { db } from '../db'
import { appSettings, reportRuns, type ReportRun } from '../schema'
@@ -54,6 +54,17 @@ export async function markReportRunDeleted(id: string): Promise<void> {
await db.update(reportRuns).set({ deletedAt: new Date() }).where(eq(reportRuns.id, id))
}
export async function findLatestSentRun(): Promise<ReportRun | undefined> {
const rows = await db
.select()
.from(reportRuns)
.where(and(eq(reportRuns.sent, true), isNull(reportRuns.deletedAt)))
.orderBy(desc(reportRuns.toAt))
.limit(1)
return rows[0]
}
export async function listReportRuns(limit: number): Promise<ReportRun[]> {
return db.select().from(reportRuns).orderBy(desc(reportRuns.createdAt)).limit(limit)
}
+9
View File
@@ -1,6 +1,7 @@
import { recordAudit } from '~/data/repositories/audit'
import {
clearSetting,
findLatestSentRun,
findReportRun,
markReportRunDeleted,
readSettings,
@@ -167,6 +168,14 @@ export async function performDeleteMessage(viewer: Viewer, formData: FormData):
await markReportRunDeleted(run.id)
const previous = await findLatestSentRun()
if (previous) {
await writeSetting('slack.lastSentAt', previous.toAt.toISOString())
} else {
await clearSetting('slack.lastSentAt')
}
await recordAudit({
actorId: viewer.id,
actorLabel: viewer.email,
+37 -2
View File
@@ -3,6 +3,7 @@ import { db } from '~/data/db'
import { appSettings } from '~/data/schema'
import { listReportRuns, writeSetting } from '~/data/repositories/settings'
import { readSlackConfig, reportScope, runWeeklyReport } from '~/lib/report-settings'
import { performDeleteMessage } from '~/lib/admin-settings'
import { makeBrand, makeProject } from '../support/admin'
import { makePost } from '../support/entries'
@@ -129,10 +130,10 @@ describe('Doppelter Versand', () => {
await makePost(project.id, { number: 1, slug: 'eins', status: 'published', publishAt: new Date() })
await writeSetting('slack.botToken', 'xoxb-1234')
await writeSetting('slack.channel', 'C123')
await writeSetting('slack.channel', 'C0123456')
vi.stubGlobal('fetch', vi.fn(async () => new Response(
JSON.stringify({ ok: true, channel: 'C123', ts: '1785600000.000100' }),
JSON.stringify({ ok: true, channel: 'C0123456', ts: '1785600000.000100' }),
{ headers: { 'content-type': 'application/json' } },
)))
@@ -143,3 +144,37 @@ describe('Doppelter Versand', () => {
expect(second.reason).toBe('nothing_published')
})
})
describe('Löschen nimmt den Versand zurück', () => {
it('macht den Bericht nach dem Löschen wieder sendbar', async () => {
const brand = await makeBrand()
const project = await makeProject(brand.id)
await makePost(project.id, { number: 1, slug: 'eins', status: 'published', publishAt: new Date() })
await writeSetting('slack.botToken', 'xoxb-1234')
await writeSetting('slack.channel', 'C0123456')
vi.stubGlobal('fetch', vi.fn(async () => new Response(
JSON.stringify({ ok: true, channel: 'C0123456', ts: '1785600000.000100' }),
{ headers: { 'content-type': 'application/json' } },
)))
const first = await runWeeklyReport({ origin, trigger: 'test' })
expect(first.reason).toBe('sent')
expect((await runWeeklyReport({ origin, trigger: 'test' })).reason).toBe('nothing_published')
const form = new FormData()
form.set('runId', first.run.id)
const deleted = await performDeleteMessage(
{ id: 'u1', name: 'Admin', email: '[email protected]', isAdmin: true, assignments: [] },
form,
)
expect(deleted.message).toBe('messageDeleted')
expect(deleted.status).toBe('ok')
expect((await runWeeklyReport({ origin, trigger: 'test' })).reason).toBe('sent')
})
})