raqim / artifacts /api-server /src /index.ts
RAQIM Deploy
Deploy RAQIM 2026-05-02 23:08
3e9069b
import app from "./app";
import { logger } from "./lib/logger";
import { db } from "@workspace/db";
import { conversionsTable, filesTable } from "@workspace/db";
import { eq, inArray, and, or } from "drizzle-orm";
const rawPort = process.env["PORT"];
if (!rawPort) {
throw new Error(
"PORT environment variable is required but was not provided.",
);
}
const port = Number(rawPort);
if (Number.isNaN(port) || port <= 0) {
throw new Error(`Invalid PORT value: "${rawPort}"`);
}
async function cleanupStuckJobs() {
try {
const stuckStatuses = ["queued", "analyzing", "routing", "ocr", "layout", "scoring", "merging", "cleanup"] as const;
const stuckConversions = await db.query.conversionsTable.findMany({
where: inArray(conversionsTable.status, stuckStatuses as any),
});
if (stuckConversions.length === 0) return;
logger.info({ count: stuckConversions.length }, "Marking stuck conversions as failed on startup");
for (const conv of stuckConversions) {
await db.update(conversionsTable)
.set({
status: "failed",
errorMessage: "انقطعت المعالجة بسبب إعادة تشغيل الخادم — يرجى المحاولة مجدداً",
completedAt: new Date(),
})
.where(eq(conversionsTable.id, conv.id));
if (conv.fileId) {
await db.update(filesTable)
.set({ status: "failed", updatedAt: new Date() })
.where(and(
eq(filesTable.id, conv.fileId),
inArray(filesTable.status, ["queued", "processing"] as any),
));
}
}
logger.info({ count: stuckConversions.length }, "Startup cleanup complete");
} catch (err) {
logger.error({ err }, "Startup cleanup failed — continuing anyway");
}
}
app.listen(port, async (err) => {
if (err) {
logger.error({ err }, "Error listening on port");
process.exit(1);
}
logger.info({ port }, "Server listening");
await cleanupStuckJobs();
});