feat: comprehensive CMS improvements
CI / runtime-diagnostics (push) Skipped
CI / release (push) Skipped
CI / check (push) Failing after 0s
CI / deploy (push) Skipped

- Fix DOMPurify SSR crash (use isomorphic-dompurify)
- Fix SanitizedHtml to sanitize by default
- Add auth guards to studio/catalog maintenance pages
- Add update/edit to vouchers CRUD
- Add update/edit to rare-values CRUD
- Add approve workflow to applications page
- Add edit form to guilds detail page
- Add SEO metadata to all public pages (21 pages)
- Fix mobile nav accessibility (focus trap, aria attributes)
- Fix missing labels and table accessibility
- Add dynamic imports for heavy client components (6 components)
- Fix silent error swallowing (40+ locations)
- Add content scheduling for articles (publishAt, status)
- Wire up 12 missing webhook notification triggers
- Add global search to admin panel
- Add bulk actions to admin users table
- Fix JSON formatting and a11y issues
This commit is contained in:
openhands committed 2026-09-03 16:00:32 +02:00
1 parent b810b16672
commit 30c95b1a5c
78 files changed
+2252 -134

No files matched your search

+45 -2
View File
@@ -1,8 +1,13 @@
import "./load-env";
import { Cron } from "croner";
import { lt, sql } from "drizzle-orm";
import { and, lt, or, sql } from "drizzle-orm";
import { env } from "../src/env";
import { db, PasswordReset, WebsiteLoginLogs } from "../src/lib/db";
import {
db,
PasswordReset,
WebsiteArticles,
WebsiteLoginLogs,
} from "../src/lib/db";
import { logger } from "../src/lib/logger";
import { redis } from "../src/lib/redis";
import { emulatorOffline, healthDegraded } from "../src/lib/services/alert";
@@ -224,6 +229,35 @@ async function cleanupOldSessions(): Promise<void> {
}
}
async function publishScheduledArticles(): Promise<void> {
try {
const now = new Date();
const result = await db
.update(WebsiteArticles)
.set({
status: "published",
publishedAt: now,
})
.where(
and(
sql`${WebsiteArticles.status} = 'scheduled'`,
or(
sql`${WebsiteArticles.publishAt} IS NULL`,
sql`${WebsiteArticles.publishAt} <= ${now}`,
),
),
);
const info = result as unknown as { affectedRows?: number };
if (info.affectedRows && info.affectedRows > 0) {
logger.info(`Published ${info.affectedRows} scheduled article(s)`, {
module: "jobs",
});
}
} catch (err) {
captureWorkerError(err, "Scheduled article publish failed");
}
}
async function main() {
logger.info("Worker started", { module: "jobs" });
@@ -257,6 +291,15 @@ async function main() {
});
logger.info("Scheduled: ops health probe (every 5 min)", { module: "jobs" });
new Cron("* * * * *", () => {
publishScheduledArticles().catch((e) =>
captureWorkerError(e, "ScheduledArticlePublish"),
);
});
logger.info("Scheduled: publish scheduled articles (every minute)", {
module: "jobs",
});
await Promise.all([
backupEmulatorJar(),
cleanupOldLogs(),