feat: comprehensive CMS improvements
- 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:
1 parent
b810b16672
commit
30c95b1a5c
78 files changed
+2252
-134
No files matched your search
+45
-2
@@ -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(),
|
||||
|
||||
Reference in new issue
Block a user