@infuro/cms-core 1.0.28 → 1.0.29
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/dist/admin.cjs +1768 -1193
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +10 -1
- package/dist/admin.d.ts +10 -1
- package/dist/admin.js +1730 -1156
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +562 -13
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +566 -4
- package/dist/api.js.map +1 -1
- package/dist/cli.cjs +2 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/{index-CI6J9dxr.d.cts → index-BPQSXgXF.d.cts} +20 -1
- package/dist/{index-CMJZ5Fpr.d.ts → index-D9SdaDJ0.d.ts} +20 -1
- package/dist/index.cjs +2241 -1416
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -16
- package/dist/index.d.ts +106 -16
- package/dist/index.js +2210 -1383
- package/dist/index.js.map +1 -1
- package/dist/migrations/1775700000000-JobSchedules.ts +70 -0
- package/package.json +4 -2
package/dist/api.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as ChatPublicConfig, f as CmsApiHandlerConfig, g as CmsGetter, h as CrudHandlerOptions, D as DashboardStatsConfig, i as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, j as FormBySlugConfig, G as GetPublicSettingsGroupConfig, k as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, L as LlmAgentKnowledgeApiConfig, l as LlmAgentValidationRulesJson, P as ParsedLlmAgentValidation, m as SetPasswordConfig, n as SettingsApiConfig, p as StorefrontApiConfig, q as StorefrontOtpFlags, U as UploadHandlerConfig, r as UserAuthApiConfig, s as UserAvatarConfig, t as UserProfileConfig, u as UsersApiConfig, v as createAnalyticsHandlers, w as createBlogBySlugHandler, x as createChangePasswordHandler, y as createCmsApiHandler, z as createCrudByIdHandler, H as createCrudHandler,
|
|
1
|
+
export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as ChatPublicConfig, f as CmsApiHandlerConfig, g as CmsGetter, h as CrudHandlerOptions, D as DashboardStatsConfig, i as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, j as FormBySlugConfig, G as GetPublicSettingsGroupConfig, k as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, J as JobScheduleApiConfig, L as LlmAgentKnowledgeApiConfig, l as LlmAgentValidationRulesJson, P as ParsedLlmAgentValidation, m as SetPasswordConfig, n as SettingsApiConfig, p as StorefrontApiConfig, q as StorefrontOtpFlags, U as UploadHandlerConfig, r as UserAuthApiConfig, s as UserAvatarConfig, t as UserProfileConfig, u as UsersApiConfig, v as createAnalyticsHandlers, w as createBlogBySlugHandler, x as createChangePasswordHandler, y as createCmsApiHandler, z as createCrudByIdHandler, H as createCrudHandler, K as createDashboardStatsHandler, M as createEcommerceAnalyticsHandler, N as createForgotPasswordHandler, Q as createFormBySlugHandler, R as createInviteAcceptHandler, V as createJobScheduleHandlers, W as createLlmAgentKnowledgeHandlers, X as createMediaZipExtractHandler, Y as createSetPasswordHandler, Z as createSettingsApiHandlers, _ as createStorefrontApiHandler, $ as createUploadHandler, a0 as createUserAuthApiRouter, a1 as createUserAvatarHandler, a2 as createUserProfileHandler, a3 as createUsersApiHandlers, a5 as getPublicSettingsGroup, a7 as mergeGuardrailsIntoSystemPrompt, a8 as parseLlmAgentValidationRules, a9 as simpleDecrypt, aa as simpleEncrypt, ab as validateUserMessageAgainstAgentRules, ac as validateUserMessageAgainstStructuredRules } from './index-BPQSXgXF.cjs';
|
|
2
2
|
import 'typeorm';
|
|
3
3
|
import './helpers-dlrF_49e.cjs';
|
package/dist/api.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as ChatPublicConfig, f as CmsApiHandlerConfig, g as CmsGetter, h as CrudHandlerOptions, D as DashboardStatsConfig, i as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, j as FormBySlugConfig, G as GetPublicSettingsGroupConfig, k as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, L as LlmAgentKnowledgeApiConfig, l as LlmAgentValidationRulesJson, P as ParsedLlmAgentValidation, m as SetPasswordConfig, n as SettingsApiConfig, p as StorefrontApiConfig, q as StorefrontOtpFlags, U as UploadHandlerConfig, r as UserAuthApiConfig, s as UserAvatarConfig, t as UserProfileConfig, u as UsersApiConfig, v as createAnalyticsHandlers, w as createBlogBySlugHandler, x as createChangePasswordHandler, y as createCmsApiHandler, z as createCrudByIdHandler, H as createCrudHandler,
|
|
1
|
+
export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as ChatPublicConfig, f as CmsApiHandlerConfig, g as CmsGetter, h as CrudHandlerOptions, D as DashboardStatsConfig, i as EcommerceAnalyticsConfig, b as EntityMap, F as ForgotPasswordConfig, j as FormBySlugConfig, G as GetPublicSettingsGroupConfig, k as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, J as JobScheduleApiConfig, L as LlmAgentKnowledgeApiConfig, l as LlmAgentValidationRulesJson, P as ParsedLlmAgentValidation, m as SetPasswordConfig, n as SettingsApiConfig, p as StorefrontApiConfig, q as StorefrontOtpFlags, U as UploadHandlerConfig, r as UserAuthApiConfig, s as UserAvatarConfig, t as UserProfileConfig, u as UsersApiConfig, v as createAnalyticsHandlers, w as createBlogBySlugHandler, x as createChangePasswordHandler, y as createCmsApiHandler, z as createCrudByIdHandler, H as createCrudHandler, K as createDashboardStatsHandler, M as createEcommerceAnalyticsHandler, N as createForgotPasswordHandler, Q as createFormBySlugHandler, R as createInviteAcceptHandler, V as createJobScheduleHandlers, W as createLlmAgentKnowledgeHandlers, X as createMediaZipExtractHandler, Y as createSetPasswordHandler, Z as createSettingsApiHandlers, _ as createStorefrontApiHandler, $ as createUploadHandler, a0 as createUserAuthApiRouter, a1 as createUserAvatarHandler, a2 as createUserProfileHandler, a3 as createUsersApiHandlers, a5 as getPublicSettingsGroup, a7 as mergeGuardrailsIntoSystemPrompt, a8 as parseLlmAgentValidationRules, a9 as simpleDecrypt, aa as simpleEncrypt, ab as validateUserMessageAgainstAgentRules, ac as validateUserMessageAgainstStructuredRules } from './index-D9SdaDJ0.js';
|
|
2
2
|
import 'typeorm';
|
|
3
3
|
import './helpers-dlrF_49e.js';
|
package/dist/api.js
CHANGED
|
@@ -192,6 +192,81 @@ var init_email_queue = __esm({
|
|
|
192
192
|
}
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
// src/plugins/pg-boss/pg-boss-service.ts
|
|
196
|
+
var pg_boss_service_exports = {};
|
|
197
|
+
__export(pg_boss_service_exports, {
|
|
198
|
+
JOB_RUNNER_QUEUE: () => JOB_RUNNER_QUEUE,
|
|
199
|
+
PgBossService: () => PgBossService
|
|
200
|
+
});
|
|
201
|
+
import PgBoss from "pg-boss";
|
|
202
|
+
var JOB_RUNNER_QUEUE, PgBossService;
|
|
203
|
+
var init_pg_boss_service = __esm({
|
|
204
|
+
"src/plugins/pg-boss/pg-boss-service.ts"() {
|
|
205
|
+
"use strict";
|
|
206
|
+
JOB_RUNNER_QUEUE = "job-runner";
|
|
207
|
+
PgBossService = class {
|
|
208
|
+
boss;
|
|
209
|
+
started = false;
|
|
210
|
+
workRegistered = /* @__PURE__ */ new Set();
|
|
211
|
+
constructor(connectionString, schema) {
|
|
212
|
+
this.boss = new PgBoss({
|
|
213
|
+
connectionString,
|
|
214
|
+
...schema?.trim() ? { schema: schema.trim() } : {},
|
|
215
|
+
schedule: true
|
|
216
|
+
});
|
|
217
|
+
this.boss.on("error", (err) => {
|
|
218
|
+
console.error("[pg-boss]", err);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async start() {
|
|
222
|
+
if (this.started) return;
|
|
223
|
+
await this.boss.start();
|
|
224
|
+
await this.ensureQueue(JOB_RUNNER_QUEUE);
|
|
225
|
+
this.started = true;
|
|
226
|
+
}
|
|
227
|
+
async stop() {
|
|
228
|
+
if (!this.started) return;
|
|
229
|
+
await this.boss.stop({ graceful: true, timeout: 1e4 });
|
|
230
|
+
this.started = false;
|
|
231
|
+
this.workRegistered.clear();
|
|
232
|
+
}
|
|
233
|
+
get raw() {
|
|
234
|
+
return this.boss;
|
|
235
|
+
}
|
|
236
|
+
async ensureQueue(name) {
|
|
237
|
+
const existing = await this.boss.getQueue(name);
|
|
238
|
+
if (!existing) {
|
|
239
|
+
await this.boss.createQueue(name);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async send(queue, data) {
|
|
243
|
+
await this.ensureQueue(queue);
|
|
244
|
+
return this.boss.send(queue, data, { retryLimit: 2 });
|
|
245
|
+
}
|
|
246
|
+
async schedule(name, cron, data, options) {
|
|
247
|
+
await this.ensureQueue(name);
|
|
248
|
+
await this.boss.schedule(name, cron, data, options);
|
|
249
|
+
}
|
|
250
|
+
async unschedule(name) {
|
|
251
|
+
try {
|
|
252
|
+
await this.boss.unschedule(name);
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async registerWork(queue, handler) {
|
|
257
|
+
if (this.workRegistered.has(queue)) return;
|
|
258
|
+
await this.ensureQueue(queue);
|
|
259
|
+
await this.boss.work(queue, async (jobs) => {
|
|
260
|
+
for (const job of jobs) {
|
|
261
|
+
await handler(job.data);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
this.workRegistered.add(queue);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
195
270
|
// src/plugins/erp/erp-response-map.ts
|
|
196
271
|
function pickString(o, keys) {
|
|
197
272
|
for (const k of keys) {
|
|
@@ -7324,6 +7399,159 @@ RssFeed = __decorateClass([
|
|
|
7324
7399
|
Entity42("rss_feeds")
|
|
7325
7400
|
], RssFeed);
|
|
7326
7401
|
|
|
7402
|
+
// src/entities/job-schedule.entity.ts
|
|
7403
|
+
import {
|
|
7404
|
+
Entity as Entity44,
|
|
7405
|
+
PrimaryGeneratedColumn as PrimaryGeneratedColumn44,
|
|
7406
|
+
Column as Column44,
|
|
7407
|
+
CreateDateColumn as CreateDateColumn4,
|
|
7408
|
+
UpdateDateColumn as UpdateDateColumn2,
|
|
7409
|
+
OneToMany as OneToMany18
|
|
7410
|
+
} from "typeorm";
|
|
7411
|
+
|
|
7412
|
+
// src/entities/job-schedule-run.entity.ts
|
|
7413
|
+
import {
|
|
7414
|
+
Entity as Entity43,
|
|
7415
|
+
PrimaryGeneratedColumn as PrimaryGeneratedColumn43,
|
|
7416
|
+
Column as Column43,
|
|
7417
|
+
CreateDateColumn as CreateDateColumn3,
|
|
7418
|
+
ManyToOne as ManyToOne29,
|
|
7419
|
+
JoinColumn as JoinColumn29
|
|
7420
|
+
} from "typeorm";
|
|
7421
|
+
var JobScheduleRun = class {
|
|
7422
|
+
id;
|
|
7423
|
+
scheduleId;
|
|
7424
|
+
schedule;
|
|
7425
|
+
startedAt;
|
|
7426
|
+
finishedAt;
|
|
7427
|
+
status;
|
|
7428
|
+
error;
|
|
7429
|
+
result;
|
|
7430
|
+
triggeredBy;
|
|
7431
|
+
createdAt;
|
|
7432
|
+
};
|
|
7433
|
+
__decorateClass([
|
|
7434
|
+
PrimaryGeneratedColumn43("uuid")
|
|
7435
|
+
], JobScheduleRun.prototype, "id", 2);
|
|
7436
|
+
__decorateClass([
|
|
7437
|
+
Column43({ type: "uuid" })
|
|
7438
|
+
], JobScheduleRun.prototype, "scheduleId", 2);
|
|
7439
|
+
__decorateClass([
|
|
7440
|
+
ManyToOne29(() => JobSchedule, (s) => s.runs, { onDelete: "CASCADE" }),
|
|
7441
|
+
JoinColumn29({ name: "scheduleId" })
|
|
7442
|
+
], JobScheduleRun.prototype, "schedule", 2);
|
|
7443
|
+
__decorateClass([
|
|
7444
|
+
Column43({ type: "timestamp" })
|
|
7445
|
+
], JobScheduleRun.prototype, "startedAt", 2);
|
|
7446
|
+
__decorateClass([
|
|
7447
|
+
Column43({ type: "timestamp", nullable: true })
|
|
7448
|
+
], JobScheduleRun.prototype, "finishedAt", 2);
|
|
7449
|
+
__decorateClass([
|
|
7450
|
+
Column43({ type: "varchar", length: 32 })
|
|
7451
|
+
], JobScheduleRun.prototype, "status", 2);
|
|
7452
|
+
__decorateClass([
|
|
7453
|
+
Column43({ type: "text", nullable: true })
|
|
7454
|
+
], JobScheduleRun.prototype, "error", 2);
|
|
7455
|
+
__decorateClass([
|
|
7456
|
+
Column43({ type: "jsonb", nullable: true })
|
|
7457
|
+
], JobScheduleRun.prototype, "result", 2);
|
|
7458
|
+
__decorateClass([
|
|
7459
|
+
Column43({ type: "varchar", length: 16, default: "schedule" })
|
|
7460
|
+
], JobScheduleRun.prototype, "triggeredBy", 2);
|
|
7461
|
+
__decorateClass([
|
|
7462
|
+
CreateDateColumn3()
|
|
7463
|
+
], JobScheduleRun.prototype, "createdAt", 2);
|
|
7464
|
+
JobScheduleRun = __decorateClass([
|
|
7465
|
+
Entity43("job_schedule_runs")
|
|
7466
|
+
], JobScheduleRun);
|
|
7467
|
+
|
|
7468
|
+
// src/entities/job-schedule.entity.ts
|
|
7469
|
+
var JobSchedule = class {
|
|
7470
|
+
id;
|
|
7471
|
+
name;
|
|
7472
|
+
jobType;
|
|
7473
|
+
enabled;
|
|
7474
|
+
scheduleMode;
|
|
7475
|
+
intervalMinutes;
|
|
7476
|
+
runAtTime;
|
|
7477
|
+
runOnDays;
|
|
7478
|
+
timezone;
|
|
7479
|
+
cronExpression;
|
|
7480
|
+
payload;
|
|
7481
|
+
authorId;
|
|
7482
|
+
pgBossScheduleName;
|
|
7483
|
+
lastRunAt;
|
|
7484
|
+
lastRunStatus;
|
|
7485
|
+
lastRunError;
|
|
7486
|
+
nextRunAt;
|
|
7487
|
+
runs;
|
|
7488
|
+
createdAt;
|
|
7489
|
+
updatedAt;
|
|
7490
|
+
};
|
|
7491
|
+
__decorateClass([
|
|
7492
|
+
PrimaryGeneratedColumn44("uuid")
|
|
7493
|
+
], JobSchedule.prototype, "id", 2);
|
|
7494
|
+
__decorateClass([
|
|
7495
|
+
Column44({ type: "text" })
|
|
7496
|
+
], JobSchedule.prototype, "name", 2);
|
|
7497
|
+
__decorateClass([
|
|
7498
|
+
Column44({ type: "varchar", length: 64, default: "blog_generate" })
|
|
7499
|
+
], JobSchedule.prototype, "jobType", 2);
|
|
7500
|
+
__decorateClass([
|
|
7501
|
+
Column44({ type: "boolean", default: false })
|
|
7502
|
+
], JobSchedule.prototype, "enabled", 2);
|
|
7503
|
+
__decorateClass([
|
|
7504
|
+
Column44({ type: "varchar", length: 32, default: "daily" })
|
|
7505
|
+
], JobSchedule.prototype, "scheduleMode", 2);
|
|
7506
|
+
__decorateClass([
|
|
7507
|
+
Column44({ type: "int", nullable: true })
|
|
7508
|
+
], JobSchedule.prototype, "intervalMinutes", 2);
|
|
7509
|
+
__decorateClass([
|
|
7510
|
+
Column44({ type: "varchar", length: 8, nullable: true })
|
|
7511
|
+
], JobSchedule.prototype, "runAtTime", 2);
|
|
7512
|
+
__decorateClass([
|
|
7513
|
+
Column44({ type: "jsonb", nullable: true })
|
|
7514
|
+
], JobSchedule.prototype, "runOnDays", 2);
|
|
7515
|
+
__decorateClass([
|
|
7516
|
+
Column44({ type: "varchar", length: 64, default: "UTC" })
|
|
7517
|
+
], JobSchedule.prototype, "timezone", 2);
|
|
7518
|
+
__decorateClass([
|
|
7519
|
+
Column44({ type: "text", nullable: true })
|
|
7520
|
+
], JobSchedule.prototype, "cronExpression", 2);
|
|
7521
|
+
__decorateClass([
|
|
7522
|
+
Column44({ type: "jsonb", default: {} })
|
|
7523
|
+
], JobSchedule.prototype, "payload", 2);
|
|
7524
|
+
__decorateClass([
|
|
7525
|
+
Column44({ type: "int", nullable: true })
|
|
7526
|
+
], JobSchedule.prototype, "authorId", 2);
|
|
7527
|
+
__decorateClass([
|
|
7528
|
+
Column44({ type: "varchar", length: 128, unique: true })
|
|
7529
|
+
], JobSchedule.prototype, "pgBossScheduleName", 2);
|
|
7530
|
+
__decorateClass([
|
|
7531
|
+
Column44({ type: "timestamp", nullable: true })
|
|
7532
|
+
], JobSchedule.prototype, "lastRunAt", 2);
|
|
7533
|
+
__decorateClass([
|
|
7534
|
+
Column44({ type: "varchar", length: 32, nullable: true })
|
|
7535
|
+
], JobSchedule.prototype, "lastRunStatus", 2);
|
|
7536
|
+
__decorateClass([
|
|
7537
|
+
Column44({ type: "text", nullable: true })
|
|
7538
|
+
], JobSchedule.prototype, "lastRunError", 2);
|
|
7539
|
+
__decorateClass([
|
|
7540
|
+
Column44({ type: "timestamp", nullable: true })
|
|
7541
|
+
], JobSchedule.prototype, "nextRunAt", 2);
|
|
7542
|
+
__decorateClass([
|
|
7543
|
+
OneToMany18(() => JobScheduleRun, (r) => r.schedule)
|
|
7544
|
+
], JobSchedule.prototype, "runs", 2);
|
|
7545
|
+
__decorateClass([
|
|
7546
|
+
CreateDateColumn4()
|
|
7547
|
+
], JobSchedule.prototype, "createdAt", 2);
|
|
7548
|
+
__decorateClass([
|
|
7549
|
+
UpdateDateColumn2()
|
|
7550
|
+
], JobSchedule.prototype, "updatedAt", 2);
|
|
7551
|
+
JobSchedule = __decorateClass([
|
|
7552
|
+
Entity44("job_schedules")
|
|
7553
|
+
], JobSchedule);
|
|
7554
|
+
|
|
7327
7555
|
// src/entities/index.ts
|
|
7328
7556
|
var CMS_ENTITY_MAP = {
|
|
7329
7557
|
users: User,
|
|
@@ -7367,7 +7595,9 @@ var CMS_ENTITY_MAP = {
|
|
|
7367
7595
|
llm_agents: LlmAgent,
|
|
7368
7596
|
llm_agent_knowledge_documents: LlmAgentKnowledgeDocument,
|
|
7369
7597
|
rss_feeds: RssFeed,
|
|
7370
|
-
rss_articles: RssArticle
|
|
7598
|
+
rss_articles: RssArticle,
|
|
7599
|
+
job_schedules: JobSchedule,
|
|
7600
|
+
job_schedule_runs: JobScheduleRun
|
|
7371
7601
|
};
|
|
7372
7602
|
|
|
7373
7603
|
// src/plugins/blog-generator/blog-generator-service.ts
|
|
@@ -8570,6 +8800,302 @@ ${excerpt}`).trim().slice(0, 2200);
|
|
|
8570
8800
|
};
|
|
8571
8801
|
}
|
|
8572
8802
|
|
|
8803
|
+
// src/api/job-schedule-handlers.ts
|
|
8804
|
+
import { randomUUID } from "crypto";
|
|
8805
|
+
|
|
8806
|
+
// src/plugins/jobs/schedule-cron.ts
|
|
8807
|
+
function pgBossScheduleNameForId(scheduleId) {
|
|
8808
|
+
return `schedule:${scheduleId}`;
|
|
8809
|
+
}
|
|
8810
|
+
function buildCronFromSchedule(schedule) {
|
|
8811
|
+
if (schedule.scheduleMode === "cron" && schedule.cronExpression?.trim()) {
|
|
8812
|
+
return schedule.cronExpression.trim();
|
|
8813
|
+
}
|
|
8814
|
+
const time = parseRunAtTime(schedule.runAtTime);
|
|
8815
|
+
const minute = time?.minute ?? 0;
|
|
8816
|
+
const hour = time?.hour ?? 9;
|
|
8817
|
+
if (schedule.scheduleMode === "interval") {
|
|
8818
|
+
const mins = schedule.intervalMinutes ?? 60;
|
|
8819
|
+
if (mins >= 60 && mins % 60 === 0) {
|
|
8820
|
+
const h = Math.max(1, Math.min(23, Math.floor(mins / 60)));
|
|
8821
|
+
if (h === 1) return `${minute} * * * *`;
|
|
8822
|
+
return `${minute} */${h} * * *`;
|
|
8823
|
+
}
|
|
8824
|
+
if (mins <= 59) {
|
|
8825
|
+
return `*/${Math.max(1, mins)} * * * *`;
|
|
8826
|
+
}
|
|
8827
|
+
return `${minute} * * * *`;
|
|
8828
|
+
}
|
|
8829
|
+
if (schedule.scheduleMode === "weekly") {
|
|
8830
|
+
const days = Array.isArray(schedule.runOnDays) && schedule.runOnDays.length > 0 ? schedule.runOnDays.filter((d) => d >= 0 && d <= 6).join(",") : "1";
|
|
8831
|
+
return `${minute} ${hour} * * ${days}`;
|
|
8832
|
+
}
|
|
8833
|
+
return `${minute} ${hour} * * *`;
|
|
8834
|
+
}
|
|
8835
|
+
function parseRunAtTime(runAtTime) {
|
|
8836
|
+
if (!runAtTime?.trim()) return null;
|
|
8837
|
+
const m = runAtTime.trim().match(/^(\d{1,2}):(\d{2})$/);
|
|
8838
|
+
if (!m) return null;
|
|
8839
|
+
const hour = parseInt(m[1], 10);
|
|
8840
|
+
const minute = parseInt(m[2], 10);
|
|
8841
|
+
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
|
|
8842
|
+
return { hour, minute };
|
|
8843
|
+
}
|
|
8844
|
+
function validateScheduleInput(schedule) {
|
|
8845
|
+
if (schedule.scheduleMode === "cron") {
|
|
8846
|
+
if (!schedule.cronExpression?.trim()) return "cronExpression is required for cron mode";
|
|
8847
|
+
return null;
|
|
8848
|
+
}
|
|
8849
|
+
if (schedule.scheduleMode === "interval") {
|
|
8850
|
+
const mins = schedule.intervalMinutes;
|
|
8851
|
+
if (mins == null || !Number.isFinite(mins) || mins < 1 || mins > 10080) {
|
|
8852
|
+
return "intervalMinutes must be between 1 and 10080";
|
|
8853
|
+
}
|
|
8854
|
+
return null;
|
|
8855
|
+
}
|
|
8856
|
+
if (schedule.runAtTime?.trim()) {
|
|
8857
|
+
if (!parseRunAtTime(schedule.runAtTime)) return "runAtTime must be HH:mm (24h)";
|
|
8858
|
+
}
|
|
8859
|
+
return null;
|
|
8860
|
+
}
|
|
8861
|
+
|
|
8862
|
+
// src/plugins/jobs/schedule-sync.ts
|
|
8863
|
+
async function syncJobScheduleToPgBoss(cms, schedule, onRegisterQueue) {
|
|
8864
|
+
const boss = cms.getPlugin("pg_boss");
|
|
8865
|
+
if (!boss) return;
|
|
8866
|
+
const name = schedule.pgBossScheduleName;
|
|
8867
|
+
await boss.unschedule(name);
|
|
8868
|
+
if (!schedule.enabled) return;
|
|
8869
|
+
const cron = buildCronFromSchedule(schedule);
|
|
8870
|
+
const data = {
|
|
8871
|
+
scheduleId: schedule.id,
|
|
8872
|
+
triggeredBy: "schedule"
|
|
8873
|
+
};
|
|
8874
|
+
await boss.schedule(name, cron, data, { tz: schedule.timezone || "UTC" });
|
|
8875
|
+
if (onRegisterQueue) {
|
|
8876
|
+
await onRegisterQueue(name);
|
|
8877
|
+
}
|
|
8878
|
+
}
|
|
8879
|
+
async function queueJobScheduleNow(cms, scheduleId) {
|
|
8880
|
+
const boss = cms.getPlugin("pg_boss");
|
|
8881
|
+
if (!boss) {
|
|
8882
|
+
throw new Error("pg-boss is not configured (DATABASE_URL required)");
|
|
8883
|
+
}
|
|
8884
|
+
const { JOB_RUNNER_QUEUE: JOB_RUNNER_QUEUE2 } = await Promise.resolve().then(() => (init_pg_boss_service(), pg_boss_service_exports));
|
|
8885
|
+
await boss.send(JOB_RUNNER_QUEUE2, {
|
|
8886
|
+
scheduleId,
|
|
8887
|
+
triggeredBy: "manual"
|
|
8888
|
+
});
|
|
8889
|
+
}
|
|
8890
|
+
|
|
8891
|
+
// src/plugins/jobs/job-runner.ts
|
|
8892
|
+
init_pg_boss_service();
|
|
8893
|
+
|
|
8894
|
+
// src/plugins/jobs/blog-generate-job.ts
|
|
8895
|
+
import { In as In3 } from "typeorm";
|
|
8896
|
+
|
|
8897
|
+
// src/plugins/jobs/job-runner.ts
|
|
8898
|
+
var executeJobRef = null;
|
|
8899
|
+
async function ensureScheduleQueueWorker(cms, queueName) {
|
|
8900
|
+
const boss = cms.getPlugin("pg_boss");
|
|
8901
|
+
if (!boss || !executeJobRef) return;
|
|
8902
|
+
await boss.registerWork(queueName, executeJobRef);
|
|
8903
|
+
}
|
|
8904
|
+
|
|
8905
|
+
// src/api/job-schedule-handlers.ts
|
|
8906
|
+
function serializeSchedule(s) {
|
|
8907
|
+
return {
|
|
8908
|
+
id: s.id,
|
|
8909
|
+
name: s.name,
|
|
8910
|
+
jobType: s.jobType,
|
|
8911
|
+
enabled: s.enabled,
|
|
8912
|
+
scheduleMode: s.scheduleMode,
|
|
8913
|
+
intervalMinutes: s.intervalMinutes,
|
|
8914
|
+
runAtTime: s.runAtTime,
|
|
8915
|
+
runOnDays: s.runOnDays,
|
|
8916
|
+
timezone: s.timezone,
|
|
8917
|
+
cronExpression: s.cronExpression,
|
|
8918
|
+
cronResolved: buildCronFromSchedule(s),
|
|
8919
|
+
payload: s.payload ?? {},
|
|
8920
|
+
authorId: s.authorId,
|
|
8921
|
+
pgBossScheduleName: s.pgBossScheduleName,
|
|
8922
|
+
lastRunAt: s.lastRunAt?.toISOString() ?? null,
|
|
8923
|
+
lastRunStatus: s.lastRunStatus,
|
|
8924
|
+
lastRunError: s.lastRunError,
|
|
8925
|
+
nextRunAt: s.nextRunAt?.toISOString() ?? null,
|
|
8926
|
+
createdAt: s.createdAt.toISOString(),
|
|
8927
|
+
updatedAt: s.updatedAt.toISOString()
|
|
8928
|
+
};
|
|
8929
|
+
}
|
|
8930
|
+
function serializeRun(r) {
|
|
8931
|
+
return {
|
|
8932
|
+
id: r.id,
|
|
8933
|
+
scheduleId: r.scheduleId,
|
|
8934
|
+
startedAt: r.startedAt.toISOString(),
|
|
8935
|
+
finishedAt: r.finishedAt?.toISOString() ?? null,
|
|
8936
|
+
status: r.status,
|
|
8937
|
+
error: r.error,
|
|
8938
|
+
result: r.result,
|
|
8939
|
+
triggeredBy: r.triggeredBy,
|
|
8940
|
+
createdAt: r.createdAt.toISOString()
|
|
8941
|
+
};
|
|
8942
|
+
}
|
|
8943
|
+
function parseScheduleBody(body, existing) {
|
|
8944
|
+
const scheduleMode = typeof body.scheduleMode === "string" ? body.scheduleMode : existing?.scheduleMode ?? "daily";
|
|
8945
|
+
const patch = {
|
|
8946
|
+
name: typeof body.name === "string" ? body.name.trim().slice(0, 500) : existing?.name,
|
|
8947
|
+
jobType: body.jobType === "blog_generate" ? "blog_generate" : existing?.jobType ?? "blog_generate",
|
|
8948
|
+
enabled: typeof body.enabled === "boolean" ? body.enabled : existing?.enabled ?? false,
|
|
8949
|
+
scheduleMode,
|
|
8950
|
+
intervalMinutes: typeof body.intervalMinutes === "number" ? body.intervalMinutes : body.intervalMinutes != null ? Number(body.intervalMinutes) : existing?.intervalMinutes ?? null,
|
|
8951
|
+
runAtTime: typeof body.runAtTime === "string" ? body.runAtTime.trim() || null : body.runAtTime === null ? null : existing?.runAtTime ?? null,
|
|
8952
|
+
runOnDays: Array.isArray(body.runOnDays) ? body.runOnDays.map((d) => Number(d)).filter((d) => Number.isFinite(d)) : existing?.runOnDays ?? null,
|
|
8953
|
+
timezone: typeof body.timezone === "string" && body.timezone.trim() ? body.timezone.trim().slice(0, 64) : existing?.timezone ?? "UTC",
|
|
8954
|
+
cronExpression: typeof body.cronExpression === "string" ? body.cronExpression.trim() || null : existing?.cronExpression ?? null,
|
|
8955
|
+
payload: body.payload != null && typeof body.payload === "object" && !Array.isArray(body.payload) ? body.payload : existing?.payload ?? {},
|
|
8956
|
+
authorId: typeof body.authorId === "number" ? body.authorId : body.authorId != null ? Number(body.authorId) : existing?.authorId ?? null
|
|
8957
|
+
};
|
|
8958
|
+
return patch;
|
|
8959
|
+
}
|
|
8960
|
+
function createJobScheduleHandlers(apiConfig) {
|
|
8961
|
+
const { dataSource, entityMap, json, requireAuth, requireEntityPermission, getCms } = apiConfig;
|
|
8962
|
+
const ScheduleEntity = entityMap.job_schedules ?? JobSchedule;
|
|
8963
|
+
const RunEntity = entityMap.job_schedule_runs ?? JobScheduleRun;
|
|
8964
|
+
async function perm(req, action) {
|
|
8965
|
+
if (!requireEntityPermission) return null;
|
|
8966
|
+
return requireEntityPermission(req, "blogs", action);
|
|
8967
|
+
}
|
|
8968
|
+
async function syncAfterSave(cms, schedule) {
|
|
8969
|
+
await syncJobScheduleToPgBoss(cms, schedule, async (queueName) => {
|
|
8970
|
+
await ensureScheduleQueueWorker(cms, queueName);
|
|
8971
|
+
});
|
|
8972
|
+
}
|
|
8973
|
+
return {
|
|
8974
|
+
async list(req) {
|
|
8975
|
+
const a = await requireAuth(req);
|
|
8976
|
+
if (a) return a;
|
|
8977
|
+
const pe = await perm(req, "read");
|
|
8978
|
+
if (pe) return pe;
|
|
8979
|
+
const list = await dataSource.getRepository(ScheduleEntity).find({
|
|
8980
|
+
order: { updatedAt: "DESC" }
|
|
8981
|
+
});
|
|
8982
|
+
return json({ schedules: list.map(serializeSchedule) });
|
|
8983
|
+
},
|
|
8984
|
+
async getById(req, id) {
|
|
8985
|
+
const a = await requireAuth(req);
|
|
8986
|
+
if (a) return a;
|
|
8987
|
+
const pe = await perm(req, "read");
|
|
8988
|
+
if (pe) return pe;
|
|
8989
|
+
const row = await dataSource.getRepository(ScheduleEntity).findOne({ where: { id } });
|
|
8990
|
+
if (!row) return json({ error: "Schedule not found" }, { status: 404 });
|
|
8991
|
+
const runs = await dataSource.getRepository(RunEntity).find({
|
|
8992
|
+
where: { scheduleId: id },
|
|
8993
|
+
order: { startedAt: "DESC" },
|
|
8994
|
+
take: 20
|
|
8995
|
+
});
|
|
8996
|
+
return json({ schedule: serializeSchedule(row), runs: runs.map(serializeRun) });
|
|
8997
|
+
},
|
|
8998
|
+
async create(req) {
|
|
8999
|
+
const a = await requireAuth(req);
|
|
9000
|
+
if (a) return a;
|
|
9001
|
+
const pe = await perm(req, "create");
|
|
9002
|
+
if (pe) return pe;
|
|
9003
|
+
if (!getCms) return json({ error: "CMS not configured" }, { status: 503 });
|
|
9004
|
+
let body;
|
|
9005
|
+
try {
|
|
9006
|
+
body = await req.json();
|
|
9007
|
+
} catch {
|
|
9008
|
+
return json({ error: "Invalid JSON body" }, { status: 400 });
|
|
9009
|
+
}
|
|
9010
|
+
const patch = parseScheduleBody(body);
|
|
9011
|
+
if (!patch.name) return json({ error: "name is required" }, { status: 400 });
|
|
9012
|
+
const draft = Object.assign(new JobSchedule(), patch);
|
|
9013
|
+
const validationError = validateScheduleInput(draft);
|
|
9014
|
+
if (validationError) return json({ error: validationError }, { status: 400 });
|
|
9015
|
+
const repo = dataSource.getRepository(ScheduleEntity);
|
|
9016
|
+
let row = repo.create({
|
|
9017
|
+
...patch,
|
|
9018
|
+
pgBossScheduleName: `pending-${randomUUID()}`
|
|
9019
|
+
});
|
|
9020
|
+
row = await repo.save(row);
|
|
9021
|
+
row.pgBossScheduleName = pgBossScheduleNameForId(row.id);
|
|
9022
|
+
row = await repo.save(row);
|
|
9023
|
+
const cms = await getCms();
|
|
9024
|
+
await syncAfterSave(cms, row);
|
|
9025
|
+
return json({ schedule: serializeSchedule(row) }, { status: 201 });
|
|
9026
|
+
},
|
|
9027
|
+
async update(req, id) {
|
|
9028
|
+
const a = await requireAuth(req);
|
|
9029
|
+
if (a) return a;
|
|
9030
|
+
const pe = await perm(req, "update");
|
|
9031
|
+
if (pe) return pe;
|
|
9032
|
+
if (!getCms) return json({ error: "CMS not configured" }, { status: 503 });
|
|
9033
|
+
const repo = dataSource.getRepository(ScheduleEntity);
|
|
9034
|
+
const existing = await repo.findOne({ where: { id } });
|
|
9035
|
+
if (!existing) return json({ error: "Schedule not found" }, { status: 404 });
|
|
9036
|
+
let body;
|
|
9037
|
+
try {
|
|
9038
|
+
body = await req.json();
|
|
9039
|
+
} catch {
|
|
9040
|
+
return json({ error: "Invalid JSON body" }, { status: 400 });
|
|
9041
|
+
}
|
|
9042
|
+
const patch = parseScheduleBody(body, existing);
|
|
9043
|
+
Object.assign(existing, patch);
|
|
9044
|
+
const validationError = validateScheduleInput(existing);
|
|
9045
|
+
if (validationError) return json({ error: validationError }, { status: 400 });
|
|
9046
|
+
const row = await repo.save(existing);
|
|
9047
|
+
const cms = await getCms();
|
|
9048
|
+
await syncAfterSave(cms, row);
|
|
9049
|
+
return json({ schedule: serializeSchedule(row) });
|
|
9050
|
+
},
|
|
9051
|
+
async remove(req, id) {
|
|
9052
|
+
const a = await requireAuth(req);
|
|
9053
|
+
if (a) return a;
|
|
9054
|
+
const pe = await perm(req, "delete");
|
|
9055
|
+
if (pe) return pe;
|
|
9056
|
+
if (!getCms) return json({ error: "CMS not configured" }, { status: 503 });
|
|
9057
|
+
const repo = dataSource.getRepository(ScheduleEntity);
|
|
9058
|
+
const existing = await repo.findOne({ where: { id } });
|
|
9059
|
+
if (!existing) return json({ error: "Schedule not found" }, { status: 404 });
|
|
9060
|
+
const cms = await getCms();
|
|
9061
|
+
existing.enabled = false;
|
|
9062
|
+
await syncJobScheduleToPgBoss(cms, existing);
|
|
9063
|
+
await repo.delete({ id });
|
|
9064
|
+
return json({ ok: true });
|
|
9065
|
+
},
|
|
9066
|
+
async runNow(req, id) {
|
|
9067
|
+
const a = await requireAuth(req);
|
|
9068
|
+
if (a) return a;
|
|
9069
|
+
const pe = await perm(req, "create");
|
|
9070
|
+
if (pe) return pe;
|
|
9071
|
+
if (!getCms) return json({ error: "CMS not configured" }, { status: 503 });
|
|
9072
|
+
const repo = dataSource.getRepository(ScheduleEntity);
|
|
9073
|
+
const existing = await repo.findOne({ where: { id } });
|
|
9074
|
+
if (!existing) return json({ error: "Schedule not found" }, { status: 404 });
|
|
9075
|
+
try {
|
|
9076
|
+
const cms = await getCms();
|
|
9077
|
+
await queueJobScheduleNow(cms, id);
|
|
9078
|
+
return json({ ok: true, message: "Job queued" });
|
|
9079
|
+
} catch (e) {
|
|
9080
|
+
const message = e instanceof Error ? e.message : "Failed to queue job";
|
|
9081
|
+
return json({ error: message }, { status: 503 });
|
|
9082
|
+
}
|
|
9083
|
+
},
|
|
9084
|
+
async listRuns(req, id) {
|
|
9085
|
+
const a = await requireAuth(req);
|
|
9086
|
+
if (a) return a;
|
|
9087
|
+
const pe = await perm(req, "read");
|
|
9088
|
+
if (pe) return pe;
|
|
9089
|
+
const runs = await dataSource.getRepository(RunEntity).find({
|
|
9090
|
+
where: { scheduleId: id },
|
|
9091
|
+
order: { startedAt: "DESC" },
|
|
9092
|
+
take: 50
|
|
9093
|
+
});
|
|
9094
|
+
return json({ runs: runs.map(serializeRun) });
|
|
9095
|
+
}
|
|
9096
|
+
};
|
|
9097
|
+
}
|
|
9098
|
+
|
|
8573
9099
|
// src/api/cms-api-handler.ts
|
|
8574
9100
|
var KNOWLEDGE_SUFFIX = "knowledge";
|
|
8575
9101
|
var CMS_API_LOG = "[cms-api]";
|
|
@@ -8630,7 +9156,9 @@ var DEFAULT_EXCLUDE = /* @__PURE__ */ new Set([
|
|
|
8630
9156
|
"message_templates",
|
|
8631
9157
|
"llm_agent_knowledge_documents",
|
|
8632
9158
|
"rss_feeds",
|
|
8633
|
-
"rss_articles"
|
|
9159
|
+
"rss_articles",
|
|
9160
|
+
"job_schedules",
|
|
9161
|
+
"job_schedule_runs"
|
|
8634
9162
|
]);
|
|
8635
9163
|
function createCmsApiHandler(config) {
|
|
8636
9164
|
const {
|
|
@@ -8793,6 +9321,15 @@ function createCmsApiHandler(config) {
|
|
|
8793
9321
|
...llmAgentKnowledgeMerged,
|
|
8794
9322
|
requireEntityPermission: requireEntityPermissionEffective
|
|
8795
9323
|
}) : null;
|
|
9324
|
+
const jobScheduleHandlers = getCms ? createJobScheduleHandlers({
|
|
9325
|
+
dataSource,
|
|
9326
|
+
entityMap,
|
|
9327
|
+
json: config.json,
|
|
9328
|
+
requireAuth: config.requireAuth,
|
|
9329
|
+
requireEntityPermission: requireEntityPermissionEffective,
|
|
9330
|
+
getCms,
|
|
9331
|
+
config: typeof process !== "undefined" ? process.env : {}
|
|
9332
|
+
}) : null;
|
|
8796
9333
|
function resolveResource(segment) {
|
|
8797
9334
|
const model = pathToModel(segment);
|
|
8798
9335
|
return crudResources.includes(model) ? model : segment;
|
|
@@ -8823,6 +9360,30 @@ function createCmsApiHandler(config) {
|
|
|
8823
9360
|
if (g) return g;
|
|
8824
9361
|
return ecommerceAnalyticsGet(req);
|
|
8825
9362
|
}
|
|
9363
|
+
if (path2[0] === "job-schedules" && jobScheduleHandlers) {
|
|
9364
|
+
if (path2.length === 2 && m === "GET") {
|
|
9365
|
+
return jobScheduleHandlers.getById(req, path2[1]);
|
|
9366
|
+
}
|
|
9367
|
+
if (path2.length === 2 && m === "PATCH") {
|
|
9368
|
+
return jobScheduleHandlers.update(req, path2[1]);
|
|
9369
|
+
}
|
|
9370
|
+
if (path2.length === 2 && m === "DELETE") {
|
|
9371
|
+
return jobScheduleHandlers.remove(req, path2[1]);
|
|
9372
|
+
}
|
|
9373
|
+
if (path2.length === 3 && path2[2] === "run-now" && m === "POST") {
|
|
9374
|
+
return jobScheduleHandlers.runNow(req, path2[1]);
|
|
9375
|
+
}
|
|
9376
|
+
if (path2.length === 3 && path2[2] === "runs" && m === "GET") {
|
|
9377
|
+
return jobScheduleHandlers.listRuns(req, path2[1]);
|
|
9378
|
+
}
|
|
9379
|
+
if (path2.length === 1 && m === "GET") {
|
|
9380
|
+
return jobScheduleHandlers.list(req);
|
|
9381
|
+
}
|
|
9382
|
+
if (path2.length === 1 && m === "POST") {
|
|
9383
|
+
return jobScheduleHandlers.create(req);
|
|
9384
|
+
}
|
|
9385
|
+
return config.json({ error: "Not found" }, { status: 404 });
|
|
9386
|
+
}
|
|
8826
9387
|
if (path2[0] === "blog-generator" && path2[1] === "feeds" && path2.length === 3 && m === "DELETE" && getCms) {
|
|
8827
9388
|
const id = path2[2];
|
|
8828
9389
|
const uuidLooksValid = typeof id === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id);
|
|
@@ -9468,7 +10029,7 @@ function createCmsApiHandler(config) {
|
|
|
9468
10029
|
}
|
|
9469
10030
|
|
|
9470
10031
|
// src/api/storefront-handlers.ts
|
|
9471
|
-
import { In as
|
|
10032
|
+
import { In as In4, IsNull as IsNull4 } from "typeorm";
|
|
9472
10033
|
|
|
9473
10034
|
// src/lib/is-valid-signup-email.ts
|
|
9474
10035
|
var MAX_EMAIL = 254;
|
|
@@ -11082,7 +11643,7 @@ function createStorefrontApiHandler(config) {
|
|
|
11082
11643
|
const previewByOrder = {};
|
|
11083
11644
|
if (orderIds.length) {
|
|
11084
11645
|
const oItems = await orderItemRepo().find({
|
|
11085
|
-
where: { orderId:
|
|
11646
|
+
where: { orderId: In4(orderIds) },
|
|
11086
11647
|
relations: ["product"],
|
|
11087
11648
|
order: { id: "ASC" }
|
|
11088
11649
|
});
|
|
@@ -11218,6 +11779,7 @@ export {
|
|
|
11218
11779
|
createForgotPasswordHandler,
|
|
11219
11780
|
createFormBySlugHandler,
|
|
11220
11781
|
createInviteAcceptHandler,
|
|
11782
|
+
createJobScheduleHandlers,
|
|
11221
11783
|
createLlmAgentKnowledgeHandlers,
|
|
11222
11784
|
createMediaZipExtractHandler,
|
|
11223
11785
|
createSetPasswordHandler,
|