@prenta/core 7.8.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/api/openapi.d.ts.map +1 -1
- package/dist/api/openapi.js +35 -11
- package/dist/api/openapi.js.map +1 -1
- package/dist/api/routes/media.d.ts.map +1 -1
- package/dist/api/routes/media.js +4 -2
- package/dist/api/routes/media.js.map +1 -1
- package/dist/api/routes/scheduling.d.ts.map +1 -1
- package/dist/api/routes/scheduling.js +6 -47
- package/dist/api/routes/scheduling.js.map +1 -1
- package/dist/content-health/cron.d.ts +16 -0
- package/dist/content-health/cron.d.ts.map +1 -1
- package/dist/content-health/cron.js +18 -4
- package/dist/content-health/cron.js.map +1 -1
- package/dist/db/model-types.d.ts +15 -0
- package/dist/db/model-types.d.ts.map +1 -1
- package/dist/generated/browser.d.ts +8 -0
- package/dist/generated/client.d.ts +8 -0
- package/dist/generated/internal/class.d.ts +11 -0
- package/dist/generated/internal/class.js +4 -4
- package/dist/generated/internal/prismaNamespace.d.ts +99 -1
- package/dist/generated/internal/prismaNamespace.js +22 -1
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +23 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +22 -1
- package/dist/generated/models/Job.d.ts +1243 -0
- package/dist/generated/models/Job.js +1 -0
- package/dist/generated/models.d.ts +1 -0
- package/dist/jobs/handlers.d.ts +4 -0
- package/dist/jobs/handlers.d.ts.map +1 -0
- package/dist/jobs/handlers.js +95 -0
- package/dist/jobs/handlers.js.map +1 -0
- package/dist/jobs/memory-store.d.ts +13 -0
- package/dist/jobs/memory-store.d.ts.map +1 -0
- package/dist/jobs/memory-store.js +55 -0
- package/dist/jobs/memory-store.js.map +1 -0
- package/dist/jobs/prisma-store.d.ts +13 -0
- package/dist/jobs/prisma-store.d.ts.map +1 -0
- package/dist/jobs/prisma-store.js +113 -0
- package/dist/jobs/prisma-store.js.map +1 -0
- package/dist/jobs/queue.d.ts +17 -0
- package/dist/jobs/queue.d.ts.map +1 -0
- package/dist/jobs/queue.js +134 -0
- package/dist/jobs/queue.js.map +1 -0
- package/dist/jobs/routes.d.ts +10 -0
- package/dist/jobs/routes.d.ts.map +1 -0
- package/dist/jobs/routes.js +61 -0
- package/dist/jobs/routes.js.map +1 -0
- package/dist/jobs/seo-scan-tick.d.ts +12 -0
- package/dist/jobs/seo-scan-tick.d.ts.map +1 -0
- package/dist/jobs/seo-scan-tick.js +41 -0
- package/dist/jobs/seo-scan-tick.js.map +1 -0
- package/dist/jobs/types.d.ts +52 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +2 -0
- package/dist/jobs/types.js.map +1 -0
- package/package.json +1 -1
- package/prisma/migrations/0022_jobs/migration.sql +20 -0
- package/prisma/schema.prisma +22 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -37,4 +37,5 @@ export type * from './models/ContentIssue.js';
|
|
|
37
37
|
export type * from './models/DocumentCRDT.js';
|
|
38
38
|
export type * from './models/DocumentComment.js';
|
|
39
39
|
export type * from './models/DocumentNotification.js';
|
|
40
|
+
export type * from './models/Job.js';
|
|
40
41
|
export type * from './commonInputTypes.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/jobs/handlers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAErD,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,YAAY,CAAA;AAgBzD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CA8E1E"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { processContentHealthScan, resolveUnseenContentIssues } from '../content-health/cron.js';
|
|
2
|
+
import { getPrentaConfig } from '../config/runtime.js';
|
|
3
|
+
import { runSeoScanTick } from './seo-scan-tick.js';
|
|
4
|
+
/** Documents per content-health slice. Small enough to finish inside the worker budget. */
|
|
5
|
+
const CONTENT_HEALTH_SLICE = 25;
|
|
6
|
+
function siteOrigin() {
|
|
7
|
+
const config = getPrentaConfig();
|
|
8
|
+
return config?.site?.url ?? config?.seo?.siteUrl ?? process.env.NEXT_PUBLIC_SITE_URL ?? null;
|
|
9
|
+
}
|
|
10
|
+
function stringList(value) {
|
|
11
|
+
return Array.isArray(value)
|
|
12
|
+
? value.filter((item) => typeof item === 'string')
|
|
13
|
+
: [];
|
|
14
|
+
}
|
|
15
|
+
export function createJobHandlers(db) {
|
|
16
|
+
return {
|
|
17
|
+
'seo-scan': async () => {
|
|
18
|
+
const tick = await runSeoScanTick(db);
|
|
19
|
+
const failed = typeof tick.audit.error === 'string';
|
|
20
|
+
if (failed)
|
|
21
|
+
throw new Error(tick.audit.error);
|
|
22
|
+
const message = typeof tick.audit.skipped === 'string'
|
|
23
|
+
? tick.audit.skipped
|
|
24
|
+
: `Scanned ${String(tick.audit.pagesScanned ?? 0)} pages`;
|
|
25
|
+
return {
|
|
26
|
+
done: true,
|
|
27
|
+
progress: { completed: 1, total: 1, message },
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
'content-health': async (job) => {
|
|
31
|
+
const seen = new Set(stringList(job.payload.seen));
|
|
32
|
+
const scannedBefore = typeof job.payload.scanned === 'number' ? job.payload.scanned : 0;
|
|
33
|
+
const cursor = typeof job.payload.cursor === 'string' ? job.payload.cursor : undefined;
|
|
34
|
+
const result = await processContentHealthScan(db, {
|
|
35
|
+
maxDocuments: CONTENT_HEALTH_SLICE,
|
|
36
|
+
startCursor: cursor,
|
|
37
|
+
seen,
|
|
38
|
+
prune: false,
|
|
39
|
+
siteOrigin: siteOrigin(),
|
|
40
|
+
});
|
|
41
|
+
const scanned = scannedBefore + result.scanned;
|
|
42
|
+
const progress = {
|
|
43
|
+
completed: scanned,
|
|
44
|
+
total: result.truncated ? scanned + 1 : scanned,
|
|
45
|
+
message: result.truncated
|
|
46
|
+
? `Scanned ${scanned} documents, more remain`
|
|
47
|
+
: `Scanned ${scanned} documents`,
|
|
48
|
+
};
|
|
49
|
+
if (result.truncated && result.cursor) {
|
|
50
|
+
return {
|
|
51
|
+
done: false,
|
|
52
|
+
progress,
|
|
53
|
+
payload: {
|
|
54
|
+
...job.payload,
|
|
55
|
+
cursor: result.cursor,
|
|
56
|
+
seen: result.seenKeys,
|
|
57
|
+
scanned,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const resolved = await resolveUnseenContentIssues(db, result.seenKeys);
|
|
62
|
+
return {
|
|
63
|
+
done: true,
|
|
64
|
+
progress: {
|
|
65
|
+
...progress,
|
|
66
|
+
message: `Scanned ${scanned} documents, resolved ${resolved}`,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
'batch-ai': async (job) => {
|
|
71
|
+
const items = Array.isArray(job.payload.items) ? job.payload.items : [];
|
|
72
|
+
const index = typeof job.payload.index === 'number' ? job.payload.index : 0;
|
|
73
|
+
if (index >= items.length) {
|
|
74
|
+
return {
|
|
75
|
+
done: true,
|
|
76
|
+
progress: { completed: items.length, total: items.length, message: 'Batch finished' },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const next = index + 1;
|
|
80
|
+
const progress = {
|
|
81
|
+
completed: next,
|
|
82
|
+
total: items.length,
|
|
83
|
+
message: `Processed ${next} of ${items.length}`,
|
|
84
|
+
};
|
|
85
|
+
if (next >= items.length)
|
|
86
|
+
return { done: true, progress };
|
|
87
|
+
return {
|
|
88
|
+
done: false,
|
|
89
|
+
progress,
|
|
90
|
+
payload: { ...job.payload, index: next },
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/jobs/handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JobRecord, JobStore } from './types.js';
|
|
2
|
+
/** In-process store for the queue tests. Production uses {@link PrismaJobStore}. */
|
|
3
|
+
export declare class MemoryJobStore implements JobStore {
|
|
4
|
+
private readonly jobs;
|
|
5
|
+
insert(job: JobRecord): Promise<void>;
|
|
6
|
+
findActive(type: string): Promise<JobRecord | null>;
|
|
7
|
+
claimNext(now: Date): Promise<JobRecord | null>;
|
|
8
|
+
findStaleRunning(cutoff: Date): Promise<JobRecord[]>;
|
|
9
|
+
save(job: JobRecord): Promise<void>;
|
|
10
|
+
list(limit: number): Promise<JobRecord[]>;
|
|
11
|
+
private clone;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=memory-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/jobs/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErD,oFAAoF;AACpF,qBAAa,cAAe,YAAW,QAAQ;IAC7C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IAE9C,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IASnD,SAAS,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAe/C,gBAAgB,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAWpD,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAO/C,OAAO,CAAC,KAAK;CAWd"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** In-process store for the queue tests. Production uses {@link PrismaJobStore}. */
|
|
2
|
+
export class MemoryJobStore {
|
|
3
|
+
jobs = new Map();
|
|
4
|
+
async insert(job) {
|
|
5
|
+
this.jobs.set(job.id, { ...job, payload: { ...job.payload }, progress: { ...job.progress } });
|
|
6
|
+
}
|
|
7
|
+
async findActive(type) {
|
|
8
|
+
for (const job of this.jobs.values()) {
|
|
9
|
+
if (job.type === type && (job.status === 'queued' || job.status === 'running')) {
|
|
10
|
+
return this.clone(job);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
async claimNext(now) {
|
|
16
|
+
const due = [...this.jobs.values()]
|
|
17
|
+
.filter((job) => job.status === 'queued' && job.runAt.getTime() <= now.getTime())
|
|
18
|
+
.sort((a, b) => a.runAt.getTime() - b.runAt.getTime() || a.createdAt.getTime() - b.createdAt.getTime());
|
|
19
|
+
const job = due[0];
|
|
20
|
+
if (!job)
|
|
21
|
+
return null;
|
|
22
|
+
job.status = 'running';
|
|
23
|
+
job.lockedAt = now;
|
|
24
|
+
job.updatedAt = now;
|
|
25
|
+
return this.clone(job);
|
|
26
|
+
}
|
|
27
|
+
async findStaleRunning(cutoff) {
|
|
28
|
+
return [...this.jobs.values()]
|
|
29
|
+
.filter((job) => job.status === 'running' &&
|
|
30
|
+
job.lockedAt !== null &&
|
|
31
|
+
job.lockedAt.getTime() < cutoff.getTime())
|
|
32
|
+
.map((job) => this.clone(job));
|
|
33
|
+
}
|
|
34
|
+
async save(job) {
|
|
35
|
+
this.jobs.set(job.id, { ...job, payload: { ...job.payload }, progress: { ...job.progress } });
|
|
36
|
+
}
|
|
37
|
+
async list(limit) {
|
|
38
|
+
return [...this.jobs.values()]
|
|
39
|
+
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
|
|
40
|
+
.slice(0, limit)
|
|
41
|
+
.map((job) => this.clone(job));
|
|
42
|
+
}
|
|
43
|
+
clone(job) {
|
|
44
|
+
return {
|
|
45
|
+
...job,
|
|
46
|
+
payload: { ...job.payload },
|
|
47
|
+
progress: { ...job.progress },
|
|
48
|
+
runAt: new Date(job.runAt),
|
|
49
|
+
lockedAt: job.lockedAt ? new Date(job.lockedAt) : null,
|
|
50
|
+
createdAt: new Date(job.createdAt),
|
|
51
|
+
updatedAt: new Date(job.updatedAt),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=memory-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../src/jobs/memory-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PrentaDB } from '../db/client-types.js';
|
|
2
|
+
import type { JobRecord, JobStore } from './types.js';
|
|
3
|
+
export declare class PrismaJobStore implements JobStore {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(db: PrentaDB);
|
|
6
|
+
insert(job: JobRecord): Promise<void>;
|
|
7
|
+
findActive(type: string): Promise<JobRecord | null>;
|
|
8
|
+
claimNext(now: Date): Promise<JobRecord | null>;
|
|
9
|
+
findStaleRunning(cutoff: Date): Promise<JobRecord[]>;
|
|
10
|
+
save(job: JobRecord): Promise<void>;
|
|
11
|
+
list(limit: number): Promise<JobRecord[]>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=prisma-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-store.d.ts","sourceRoot":"","sources":["../../src/jobs/prisma-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,KAAK,EAAe,SAAS,EAAa,QAAQ,EAAE,MAAM,YAAY,CAAA;AAqD7E,qBAAa,cAAe,YAAW,QAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEnC,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBrC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAQnD,SAAS,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAc/C,gBAAgB,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQpD,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBnC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;CAOhD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
function asRecord(value) {
|
|
2
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
return {};
|
|
6
|
+
}
|
|
7
|
+
function asProgress(value) {
|
|
8
|
+
const record = asRecord(value);
|
|
9
|
+
return {
|
|
10
|
+
completed: typeof record.completed === 'number' ? record.completed : 0,
|
|
11
|
+
total: typeof record.total === 'number' ? record.total : 0,
|
|
12
|
+
...(typeof record.message === 'string' ? { message: record.message } : {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function asStatus(value) {
|
|
16
|
+
if (value === 'running' || value === 'succeeded' || value === 'dead')
|
|
17
|
+
return value;
|
|
18
|
+
return 'queued';
|
|
19
|
+
}
|
|
20
|
+
function toRecord(row) {
|
|
21
|
+
return {
|
|
22
|
+
id: row.id,
|
|
23
|
+
type: row.type,
|
|
24
|
+
payload: asRecord(row.payload),
|
|
25
|
+
status: asStatus(row.status),
|
|
26
|
+
attempts: row.attempts,
|
|
27
|
+
maxAttempts: row.maxAttempts,
|
|
28
|
+
runAt: row.runAt,
|
|
29
|
+
lockedAt: row.lockedAt,
|
|
30
|
+
lastError: row.lastError,
|
|
31
|
+
progress: asProgress(row.progress),
|
|
32
|
+
createdAt: row.createdAt,
|
|
33
|
+
updatedAt: row.updatedAt,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export class PrismaJobStore {
|
|
37
|
+
db;
|
|
38
|
+
constructor(db) {
|
|
39
|
+
this.db = db;
|
|
40
|
+
}
|
|
41
|
+
async insert(job) {
|
|
42
|
+
await this.db.job.create({
|
|
43
|
+
data: {
|
|
44
|
+
id: job.id,
|
|
45
|
+
type: job.type,
|
|
46
|
+
payload: job.payload,
|
|
47
|
+
status: job.status,
|
|
48
|
+
attempts: job.attempts,
|
|
49
|
+
maxAttempts: job.maxAttempts,
|
|
50
|
+
runAt: job.runAt,
|
|
51
|
+
lockedAt: job.lockedAt,
|
|
52
|
+
lastError: job.lastError,
|
|
53
|
+
progress: job.progress,
|
|
54
|
+
createdAt: job.createdAt,
|
|
55
|
+
updatedAt: job.updatedAt,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async findActive(type) {
|
|
60
|
+
const row = await this.db.job.findFirst({
|
|
61
|
+
where: { type, status: { in: ['queued', 'running'] } },
|
|
62
|
+
orderBy: { createdAt: 'asc' },
|
|
63
|
+
});
|
|
64
|
+
return row ? toRecord(row) : null;
|
|
65
|
+
}
|
|
66
|
+
async claimNext(now) {
|
|
67
|
+
const row = await this.db.job.findFirst({
|
|
68
|
+
where: { status: 'queued', runAt: { lte: now } },
|
|
69
|
+
orderBy: [{ runAt: 'asc' }, { createdAt: 'asc' }],
|
|
70
|
+
});
|
|
71
|
+
if (!row)
|
|
72
|
+
return null;
|
|
73
|
+
const claimed = await this.db.job.updateMany({
|
|
74
|
+
where: { id: row.id, status: 'queued' },
|
|
75
|
+
data: { status: 'running', lockedAt: now, updatedAt: now },
|
|
76
|
+
});
|
|
77
|
+
if (claimed.count !== 1)
|
|
78
|
+
return null;
|
|
79
|
+
return toRecord({ ...row, status: 'running', lockedAt: now, updatedAt: now });
|
|
80
|
+
}
|
|
81
|
+
async findStaleRunning(cutoff) {
|
|
82
|
+
const rows = await this.db.job.findMany({
|
|
83
|
+
where: { status: 'running', lockedAt: { lt: cutoff } },
|
|
84
|
+
orderBy: { lockedAt: 'asc' },
|
|
85
|
+
});
|
|
86
|
+
return rows.map((row) => toRecord(row));
|
|
87
|
+
}
|
|
88
|
+
async save(job) {
|
|
89
|
+
await this.db.job.update({
|
|
90
|
+
where: { id: job.id },
|
|
91
|
+
data: {
|
|
92
|
+
type: job.type,
|
|
93
|
+
payload: job.payload,
|
|
94
|
+
status: job.status,
|
|
95
|
+
attempts: job.attempts,
|
|
96
|
+
maxAttempts: job.maxAttempts,
|
|
97
|
+
runAt: job.runAt,
|
|
98
|
+
lockedAt: job.lockedAt,
|
|
99
|
+
lastError: job.lastError,
|
|
100
|
+
progress: job.progress,
|
|
101
|
+
updatedAt: job.updatedAt,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async list(limit) {
|
|
106
|
+
const rows = await this.db.job.findMany({
|
|
107
|
+
orderBy: { updatedAt: 'desc' },
|
|
108
|
+
take: limit,
|
|
109
|
+
});
|
|
110
|
+
return rows.map((row) => toRecord(row));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=prisma-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-store.js","sourceRoot":"","sources":["../../src/jobs/prisma-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JobHandler, JobRecord, JobStore, QueueTickResult } from './types.js';
|
|
2
|
+
export declare function enqueueJob(store: JobStore, input: {
|
|
3
|
+
type: string;
|
|
4
|
+
payload?: Record<string, unknown>;
|
|
5
|
+
maxAttempts?: number;
|
|
6
|
+
now?: Date;
|
|
7
|
+
dedupe?: boolean;
|
|
8
|
+
}): Promise<JobRecord>;
|
|
9
|
+
export declare function processJobQueue(options: {
|
|
10
|
+
store: JobStore;
|
|
11
|
+
handlers: Record<string, JobHandler>;
|
|
12
|
+
now?: Date;
|
|
13
|
+
budgetMs?: number;
|
|
14
|
+
staleLockMs?: number;
|
|
15
|
+
clock?: () => Date;
|
|
16
|
+
}): Promise<QueueTickResult>;
|
|
17
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/jobs/queue.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAe,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAiB/F,wBAAsB,UAAU,CAC9B,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,GACA,OAAO,CAAC,SAAS,CAAC,CAsBpB;AAkED,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,KAAK,EAAE,QAAQ,CAAA;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACpC,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;CACnB,GAAG,OAAO,CAAC,eAAe,CAAC,CAuD3B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
const BACKOFF_BASE_MS = 30_000;
|
|
3
|
+
const BACKOFF_CAP_MS = 15 * 60_000;
|
|
4
|
+
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
5
|
+
const DEFAULT_BUDGET_MS = 20_000;
|
|
6
|
+
const DEFAULT_STALE_LOCK_MS = 10 * 60_000;
|
|
7
|
+
function emptyProgress() {
|
|
8
|
+
return { completed: 0, total: 0 };
|
|
9
|
+
}
|
|
10
|
+
function backoffMs(attempts) {
|
|
11
|
+
const exp = Math.min(Math.max(attempts - 1, 0), 8);
|
|
12
|
+
return Math.min(BACKOFF_CAP_MS, BACKOFF_BASE_MS * 2 ** exp);
|
|
13
|
+
}
|
|
14
|
+
export async function enqueueJob(store, input) {
|
|
15
|
+
const now = input.now ?? new Date();
|
|
16
|
+
if (input.dedupe !== false) {
|
|
17
|
+
const active = await store.findActive(input.type);
|
|
18
|
+
if (active)
|
|
19
|
+
return active;
|
|
20
|
+
}
|
|
21
|
+
const job = {
|
|
22
|
+
id: randomUUID(),
|
|
23
|
+
type: input.type,
|
|
24
|
+
payload: input.payload ?? {},
|
|
25
|
+
status: 'queued',
|
|
26
|
+
attempts: 0,
|
|
27
|
+
maxAttempts: input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
|
|
28
|
+
runAt: now,
|
|
29
|
+
lockedAt: null,
|
|
30
|
+
lastError: null,
|
|
31
|
+
progress: emptyProgress(),
|
|
32
|
+
createdAt: now,
|
|
33
|
+
updatedAt: now,
|
|
34
|
+
};
|
|
35
|
+
await store.insert(job);
|
|
36
|
+
return job;
|
|
37
|
+
}
|
|
38
|
+
/** Count a failure. Returns the row to queued with backoff, or dead-letters it. */
|
|
39
|
+
async function failJob(store, job, error, now) {
|
|
40
|
+
const attempts = job.attempts + 1;
|
|
41
|
+
const dead = attempts >= job.maxAttempts;
|
|
42
|
+
const next = {
|
|
43
|
+
...job,
|
|
44
|
+
attempts,
|
|
45
|
+
status: dead ? 'dead' : 'queued',
|
|
46
|
+
runAt: dead ? job.runAt : new Date(now.getTime() + backoffMs(attempts)),
|
|
47
|
+
lockedAt: null,
|
|
48
|
+
lastError: error,
|
|
49
|
+
updatedAt: now,
|
|
50
|
+
};
|
|
51
|
+
await store.save(next);
|
|
52
|
+
return next;
|
|
53
|
+
}
|
|
54
|
+
/** A voluntary slice. Attempts stay put so a long job is not punished for yielding. */
|
|
55
|
+
async function yieldJob(store, job, progress, payload, now) {
|
|
56
|
+
const next = {
|
|
57
|
+
...job,
|
|
58
|
+
status: 'queued',
|
|
59
|
+
runAt: now,
|
|
60
|
+
lockedAt: null,
|
|
61
|
+
progress,
|
|
62
|
+
payload,
|
|
63
|
+
updatedAt: now,
|
|
64
|
+
};
|
|
65
|
+
await store.save(next);
|
|
66
|
+
return next;
|
|
67
|
+
}
|
|
68
|
+
async function completeJob(store, job, progress, now) {
|
|
69
|
+
const next = {
|
|
70
|
+
...job,
|
|
71
|
+
status: 'succeeded',
|
|
72
|
+
lockedAt: null,
|
|
73
|
+
progress: progress ?? {
|
|
74
|
+
completed: job.progress.total || 1,
|
|
75
|
+
total: job.progress.total || 1,
|
|
76
|
+
message: job.progress.message,
|
|
77
|
+
},
|
|
78
|
+
updatedAt: now,
|
|
79
|
+
};
|
|
80
|
+
await store.save(next);
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
export async function processJobQueue(options) {
|
|
84
|
+
const clock = options.clock ?? (() => new Date());
|
|
85
|
+
const started = options.now ?? clock();
|
|
86
|
+
const budgetMs = options.budgetMs ?? DEFAULT_BUDGET_MS;
|
|
87
|
+
const deadline = new Date(started.getTime() + budgetMs);
|
|
88
|
+
const staleLockMs = options.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
|
|
89
|
+
const result = { succeeded: 0, continued: 0, retried: 0, dead: 0 };
|
|
90
|
+
const cutoff = new Date(started.getTime() - staleLockMs);
|
|
91
|
+
for (const stale of await options.store.findStaleRunning(cutoff)) {
|
|
92
|
+
const failed = await failJob(options.store, stale, 'Worker stopped before the job finished', started);
|
|
93
|
+
if (failed.status === 'dead')
|
|
94
|
+
result.dead++;
|
|
95
|
+
else
|
|
96
|
+
result.retried++;
|
|
97
|
+
}
|
|
98
|
+
while (clock().getTime() < deadline.getTime()) {
|
|
99
|
+
const now = clock();
|
|
100
|
+
const job = await options.store.claimNext(now);
|
|
101
|
+
if (!job)
|
|
102
|
+
break;
|
|
103
|
+
const handler = options.handlers[job.type];
|
|
104
|
+
if (!handler) {
|
|
105
|
+
const failed = await failJob(options.store, job, `Unknown job type: ${job.type}`, now);
|
|
106
|
+
if (failed.status === 'dead')
|
|
107
|
+
result.dead++;
|
|
108
|
+
else
|
|
109
|
+
result.retried++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const outcome = await handler(job, { now, deadline });
|
|
114
|
+
if (outcome.done) {
|
|
115
|
+
await completeJob(options.store, job, outcome.progress, clock());
|
|
116
|
+
result.succeeded++;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
await yieldJob(options.store, job, outcome.progress, outcome.payload ?? job.payload, clock());
|
|
120
|
+
result.continued++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
125
|
+
const failed = await failJob(options.store, job, message, clock());
|
|
126
|
+
if (failed.status === 'dead')
|
|
127
|
+
result.dead++;
|
|
128
|
+
else
|
|
129
|
+
result.retried++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/jobs/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ApiRouter } from '../api/router.js';
|
|
2
|
+
import { guardedDb } from '../api/guarded-db.js';
|
|
3
|
+
export declare function registerJobRoutes(router: ApiRouter): void;
|
|
4
|
+
/** Queue a cron's work and return immediately. An active job of the same type is reused. */
|
|
5
|
+
export declare function enqueueCronJob(db: ReturnType<typeof guardedDb>, type: string): Promise<{
|
|
6
|
+
queued: true;
|
|
7
|
+
jobId: string;
|
|
8
|
+
type: string;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/jobs/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAwBhD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgCzD;AAED,4FAA4F;AAC5F,wBAAsB,cAAc,CAClC,EAAE,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,EAChC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAGxD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isAuthorizedCronRequest } from '../cron/index.js';
|
|
2
|
+
import { guardedDb } from '../api/guarded-db.js';
|
|
3
|
+
import { errorResponse, internalError, json, requireAuth } from '../api/route-helpers.js';
|
|
4
|
+
import { createJobHandlers } from './handlers.js';
|
|
5
|
+
import { PrismaJobStore } from './prisma-store.js';
|
|
6
|
+
import { enqueueJob, processJobQueue } from './queue.js';
|
|
7
|
+
const WORKER_BUDGET_MS = 20_000;
|
|
8
|
+
function serializeJob(job) {
|
|
9
|
+
return {
|
|
10
|
+
id: job.id,
|
|
11
|
+
type: job.type,
|
|
12
|
+
status: job.status,
|
|
13
|
+
attempts: job.attempts,
|
|
14
|
+
maxAttempts: job.maxAttempts,
|
|
15
|
+
lastError: job.lastError,
|
|
16
|
+
progress: job.progress,
|
|
17
|
+
runAt: job.runAt.toISOString(),
|
|
18
|
+
createdAt: job.createdAt.toISOString(),
|
|
19
|
+
updatedAt: job.updatedAt.toISOString(),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function registerJobRoutes(router) {
|
|
23
|
+
const db = guardedDb;
|
|
24
|
+
const cronJobs = async (request) => {
|
|
25
|
+
try {
|
|
26
|
+
if (!isAuthorizedCronRequest(request.headers.get('authorization'))) {
|
|
27
|
+
return errorResponse('Unauthorized', 401);
|
|
28
|
+
}
|
|
29
|
+
const client = db();
|
|
30
|
+
const summary = await processJobQueue({
|
|
31
|
+
store: new PrismaJobStore(client),
|
|
32
|
+
handlers: createJobHandlers(client),
|
|
33
|
+
budgetMs: WORKER_BUDGET_MS,
|
|
34
|
+
});
|
|
35
|
+
return json({ data: summary });
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return internalError(err, 'cron jobs');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
router.get('/cron/jobs', cronJobs);
|
|
42
|
+
router.post('/cron/jobs', cronJobs);
|
|
43
|
+
router.get('/jobs', async (request) => {
|
|
44
|
+
try {
|
|
45
|
+
const auth = await requireAuth(request);
|
|
46
|
+
if (auth.error)
|
|
47
|
+
return auth.error;
|
|
48
|
+
const jobs = await new PrismaJobStore(db()).list(50);
|
|
49
|
+
return json({ data: { jobs: jobs.map(serializeJob) } });
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return internalError(err, 'list jobs');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Queue a cron's work and return immediately. An active job of the same type is reused. */
|
|
57
|
+
export async function enqueueCronJob(db, type) {
|
|
58
|
+
const job = await enqueueJob(new PrismaJobStore(db), { type });
|
|
59
|
+
return { queued: true, jobId: job.id, type: job.type };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/jobs/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type DigestSendResult } from '../seo/digest.js';
|
|
2
|
+
import type { PrentaDB } from '../db/client-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* One SEO audit + digest attempt. Failures stay in the result so a retry
|
|
5
|
+
* policy can see them, and a thrown audit error does not discard the digest.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runSeoScanTick(db: PrentaDB): Promise<{
|
|
8
|
+
swept: number;
|
|
9
|
+
audit: Record<string, unknown>;
|
|
10
|
+
digest: DigestSendResult;
|
|
11
|
+
}>;
|
|
12
|
+
//# sourceMappingURL=seo-scan-tick.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-scan-tick.d.ts","sourceRoot":"","sources":["../../src/jobs/seo-scan-tick.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAErD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,MAAM,EAAE,gBAAgB,CAAA;CACzB,CAAC,CAgCD"}
|