@ruiapp/rapid-core 0.8.9 → 0.8.11

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.
@@ -4,6 +4,7 @@ import { HttpStatus } from "./http-types";
4
4
  import { IRpdServer } from "./server";
5
5
  import { IDatabaseAccessor, IDatabaseClient } from "../types";
6
6
  export type Next = () => Promise<void>;
7
+ export type TransactionState = "uninited" | "inited" | "started";
7
8
  export declare class RouteContext {
8
9
  #private;
9
10
  readonly request: RapidRequest;
@@ -22,7 +23,8 @@ export declare class RouteContext {
22
23
  json(obj: any, status?: HttpStatus, headers?: HeadersInit): void;
23
24
  redirect(url: string, status?: HttpStatus): void;
24
25
  getDbTransactionClient(): IDatabaseClient | undefined;
25
- beginDbTransaction(): Promise<IDatabaseClient>;
26
+ initDbTransactionClient(): Promise<IDatabaseClient>;
27
+ beginDbTransaction(): Promise<void>;
26
28
  commitDbTransaction(): Promise<void>;
27
29
  rollbackDbTransaction(): Promise<void>;
28
30
  }
package/dist/index.js CHANGED
@@ -1052,12 +1052,14 @@ class RouteContext {
1052
1052
  routeConfig;
1053
1053
  #server;
1054
1054
  #dbTransactionClient;
1055
+ #dbTransactionState;
1055
1056
  static newSystemOperationContext(server) {
1056
1057
  return new RouteContext(server);
1057
1058
  }
1058
1059
  constructor(server, request) {
1059
1060
  this.#server = server;
1060
1061
  this.databaseAccessor = server.getDatabaseAccessor();
1062
+ this.#dbTransactionState = "uninited";
1061
1063
  this.request = request;
1062
1064
  this.state = {};
1063
1065
  this.response = new RapidResponse();
@@ -1091,27 +1093,45 @@ class RouteContext {
1091
1093
  getDbTransactionClient() {
1092
1094
  return this.#dbTransactionClient;
1093
1095
  }
1094
- async beginDbTransaction() {
1096
+ async initDbTransactionClient() {
1095
1097
  let dbClient = this.#dbTransactionClient;
1096
1098
  if (dbClient) {
1097
- throw new Error("Database transaction has been started. You can not start a transaction more than once in a request context.");
1099
+ return dbClient;
1098
1100
  }
1099
1101
  dbClient = await this.databaseAccessor.getClient();
1100
- await this.databaseAccessor.queryDatabaseObject("BEGIN", [], dbClient);
1102
+ this.#dbTransactionState = "inited";
1101
1103
  this.#dbTransactionClient = dbClient;
1102
1104
  return dbClient;
1103
1105
  }
1106
+ async beginDbTransaction() {
1107
+ if (!this.#dbTransactionClient) {
1108
+ throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1109
+ }
1110
+ if (this.#dbTransactionState === "started") {
1111
+ throw new Error("Database transaction has been started. You can not begin a new transaction before you commit or rollback it.");
1112
+ }
1113
+ await this.databaseAccessor.queryDatabaseObject("BEGIN", [], this.#dbTransactionClient);
1114
+ this.#dbTransactionState = "started";
1115
+ }
1104
1116
  async commitDbTransaction() {
1105
1117
  if (!this.#dbTransactionClient) {
1118
+ throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1119
+ }
1120
+ if (this.#dbTransactionState !== "started") {
1106
1121
  throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
1107
1122
  }
1108
1123
  await this.databaseAccessor.queryDatabaseObject("COMMIT", [], this.#dbTransactionClient);
1124
+ this.#dbTransactionState = "inited";
1109
1125
  }
1110
1126
  async rollbackDbTransaction() {
1111
1127
  if (!this.#dbTransactionClient) {
1128
+ throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1129
+ }
1130
+ if (this.#dbTransactionState !== "started") {
1112
1131
  throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
1113
1132
  }
1114
1133
  await this.databaseAccessor.queryDatabaseObject("ROLLBACK", [], this.#dbTransactionClient);
1134
+ this.#dbTransactionState = "inited";
1115
1135
  }
1116
1136
  }
1117
1137
 
@@ -2478,6 +2498,9 @@ function getNowStringWithTimezone() {
2478
2498
  }
2479
2499
  function getDateString(timeString) {
2480
2500
  return dayjs__default["default"](timeString).format("YYYY-MM-DD");
2501
+ }
2502
+ function formatDateTimeWithTimezone(source) {
2503
+ return dayjs__default["default"](source).format("YYYY-MM-DD HH:mm:ss.SSSZ");
2481
2504
  }
2482
2505
 
2483
2506
  // TODO Generate mapper and cache it.
@@ -5593,7 +5616,8 @@ async function runCollectionEntityActionHandler(ctx, options, code, autoMergeInp
5593
5616
  if (runInTransaction) {
5594
5617
  let transactionDbClient;
5595
5618
  try {
5596
- transactionDbClient = await routeContext.beginDbTransaction();
5619
+ transactionDbClient = await routeContext.initDbTransactionClient();
5620
+ await routeContext.beginDbTransaction();
5597
5621
  const result = handleEntityAction(entityManager, autoMergeInput ? mergedInput : input);
5598
5622
  if (result instanceof Promise) {
5599
5623
  ctx.output = await result;
@@ -6239,7 +6263,7 @@ var SequenceAutoIncrementRecord = {
6239
6263
  ],
6240
6264
  };
6241
6265
 
6242
- var pluginModels$6 = [SequenceRule, SequenceAutoIncrementRecord];
6266
+ var pluginModels$7 = [SequenceRule, SequenceAutoIncrementRecord];
6243
6267
 
6244
6268
  var generateSn = {
6245
6269
  namespace: "svc",
@@ -6478,7 +6502,7 @@ class SequencePlugin$1 {
6478
6502
  }
6479
6503
  }
6480
6504
  async configureModels(server, applicationConfig) {
6481
- server.appendApplicationConfig({ models: pluginModels$6 });
6505
+ server.appendApplicationConfig({ models: pluginModels$7 });
6482
6506
  }
6483
6507
  async configureServices(server, applicationConfig) {
6484
6508
  this.#sequenceService = new SequenceService(server);
@@ -6962,7 +6986,7 @@ var AccessToken = {
6962
6986
  ],
6963
6987
  };
6964
6988
 
6965
- var pluginModels$5 = [AccessToken];
6989
+ var pluginModels$6 = [AccessToken];
6966
6990
 
6967
6991
  var changePassword = {
6968
6992
  namespace: "auth",
@@ -7082,7 +7106,7 @@ class AuthPlugin {
7082
7106
  }
7083
7107
  }
7084
7108
  async configureModels(server, applicationConfig) {
7085
- server.appendApplicationConfig({ models: pluginModels$5 });
7109
+ server.appendApplicationConfig({ models: pluginModels$6 });
7086
7110
  }
7087
7111
  async configureServices(server, applicationConfig) {
7088
7112
  this.#authService = new AuthService(server, server.config.jwtKey);
@@ -7357,7 +7381,7 @@ var getLicense$1 = /*#__PURE__*/Object.freeze({
7357
7381
 
7358
7382
  var pluginActionHandlers$6 = [getLicense$1];
7359
7383
 
7360
- var pluginModels$4 = [];
7384
+ var pluginModels$5 = [];
7361
7385
 
7362
7386
  var getLicense = {
7363
7387
  namespace: "svc",
@@ -7528,7 +7552,7 @@ class LicensePlugin {
7528
7552
  }
7529
7553
  }
7530
7554
  async configureModels(server, applicationConfig) {
7531
- server.appendApplicationConfig({ models: pluginModels$4 });
7555
+ server.appendApplicationConfig({ models: pluginModels$5 });
7532
7556
  }
7533
7557
  async configureServices(server, applicationConfig) {
7534
7558
  this.#licenseService = new LicenseService(server, this.#encryptionKey);
@@ -7544,7 +7568,7 @@ class LicensePlugin {
7544
7568
 
7545
7569
  var pluginActionHandlers$5 = [];
7546
7570
 
7547
- var pluginModels$3 = [];
7571
+ var pluginModels$4 = [];
7548
7572
 
7549
7573
  var pluginRoutes$4 = [];
7550
7574
 
@@ -7610,7 +7634,7 @@ class MailPlugin {
7610
7634
  }
7611
7635
  }
7612
7636
  async configureModels(server, applicationConfig) {
7613
- server.appendApplicationConfig({ models: pluginModels$3 });
7637
+ server.appendApplicationConfig({ models: pluginModels$4 });
7614
7638
  }
7615
7639
  async configureServices(server, applicationConfig) {
7616
7640
  this.#mailService = new MailService(server, this.#config.smtpServer);
@@ -7683,7 +7707,7 @@ var Notification = {
7683
7707
  ],
7684
7708
  };
7685
7709
 
7686
- var pluginModels$2 = [Notification];
7710
+ var pluginModels$3 = [Notification];
7687
7711
 
7688
7712
  var pluginRoutes$3 = [];
7689
7713
 
@@ -7735,7 +7759,7 @@ class SequencePlugin {
7735
7759
  }
7736
7760
  }
7737
7761
  async configureModels(server, applicationConfig) {
7738
- server.appendApplicationConfig({ models: pluginModels$2 });
7762
+ server.appendApplicationConfig({ models: pluginModels$3 });
7739
7763
  }
7740
7764
  async configureServices(server, applicationConfig) {
7741
7765
  this.#notificationService = new NotificationService(server);
@@ -8253,7 +8277,7 @@ var UserSettingItemSetting = {
8253
8277
  ],
8254
8278
  };
8255
8279
 
8256
- var pluginModels$1 = [SystemSettingGroupSetting, SystemSettingItem, SystemSettingItemSetting, UserSettingGroupSetting, UserSettingItem, UserSettingItemSetting];
8280
+ var pluginModels$2 = [SystemSettingGroupSetting, SystemSettingItem, SystemSettingItemSetting, UserSettingGroupSetting, UserSettingItem, UserSettingItemSetting];
8257
8281
 
8258
8282
  var getUserSettingValues = {
8259
8283
  namespace: "svc",
@@ -8508,7 +8532,7 @@ class SettingPlugin {
8508
8532
  }
8509
8533
  }
8510
8534
  async configureModels(server, applicationConfig) {
8511
- server.appendApplicationConfig({ models: pluginModels$1 });
8535
+ server.appendApplicationConfig({ models: pluginModels$2 });
8512
8536
  }
8513
8537
  async configureServices(server, applicationConfig) {
8514
8538
  this.#settingService = new SettingService(server);
@@ -8520,6 +8544,136 @@ class SettingPlugin {
8520
8544
  async onApplicationLoaded(server, applicationConfig) { }
8521
8545
  }
8522
8546
 
8547
+ var CronJob = {
8548
+ maintainedBy: "cronJob",
8549
+ namespace: "sys",
8550
+ name: "sys_cron_job",
8551
+ singularCode: "sys_cron_job",
8552
+ pluralCode: "sys_cron_jobs",
8553
+ schema: "public",
8554
+ tableName: "sys_cron_jobs",
8555
+ properties: [
8556
+ {
8557
+ name: "id",
8558
+ code: "id",
8559
+ columnName: "id",
8560
+ type: "integer",
8561
+ required: true,
8562
+ autoIncrement: true,
8563
+ },
8564
+ {
8565
+ name: "code",
8566
+ code: "code",
8567
+ columnName: "code",
8568
+ type: "text",
8569
+ required: true,
8570
+ },
8571
+ {
8572
+ name: "description",
8573
+ code: "description",
8574
+ columnName: "description",
8575
+ type: "text",
8576
+ required: false,
8577
+ },
8578
+ {
8579
+ name: "cronTime",
8580
+ code: "cronTime",
8581
+ columnName: "cron_time",
8582
+ type: "text",
8583
+ required: true,
8584
+ },
8585
+ {
8586
+ name: "disabled",
8587
+ code: "disabled",
8588
+ columnName: "disabled",
8589
+ type: "boolean",
8590
+ required: true,
8591
+ defaultValue: "false",
8592
+ },
8593
+ {
8594
+ name: "jobOptions",
8595
+ code: "jobOptions",
8596
+ columnName: "job_options",
8597
+ type: "json",
8598
+ required: false,
8599
+ },
8600
+ {
8601
+ name: "isRunning",
8602
+ code: "isRunning",
8603
+ columnName: "is_running",
8604
+ type: "boolean",
8605
+ required: true,
8606
+ defaultValue: "false",
8607
+ },
8608
+ {
8609
+ name: "nextRunningTime",
8610
+ code: "nextRunningTime",
8611
+ columnName: "next_running_time",
8612
+ type: "datetime",
8613
+ required: false,
8614
+ },
8615
+ {
8616
+ name: "lastRunningTime",
8617
+ code: "lastRunningTime",
8618
+ columnName: "last_running_time",
8619
+ type: "datetime",
8620
+ required: false,
8621
+ },
8622
+ // success, failed, error
8623
+ {
8624
+ name: "lastRunningResult",
8625
+ code: "lastRunningResult",
8626
+ columnName: "last_running_result",
8627
+ type: "text",
8628
+ required: false,
8629
+ },
8630
+ {
8631
+ name: "lastErrorMessage",
8632
+ code: "lastErrorMessage",
8633
+ columnName: "last_error_message",
8634
+ type: "text",
8635
+ required: false,
8636
+ },
8637
+ {
8638
+ name: "lastErrorStack",
8639
+ code: "lastErrorStack",
8640
+ columnName: "last_error_stack",
8641
+ type: "text",
8642
+ required: false,
8643
+ },
8644
+ {
8645
+ name: "actionHandlerCode",
8646
+ code: "actionHandlerCode",
8647
+ columnName: "action_handler_code",
8648
+ type: "text",
8649
+ required: false,
8650
+ },
8651
+ {
8652
+ name: "handler",
8653
+ code: "handler",
8654
+ columnName: "handler",
8655
+ type: "text",
8656
+ required: false,
8657
+ },
8658
+ {
8659
+ name: "handleOptions",
8660
+ code: "handleOptions",
8661
+ columnName: "handle_options",
8662
+ type: "json",
8663
+ required: false,
8664
+ },
8665
+ {
8666
+ name: "onError",
8667
+ code: "onError",
8668
+ columnName: "on_error",
8669
+ type: "text",
8670
+ required: false,
8671
+ },
8672
+ ],
8673
+ };
8674
+
8675
+ var pluginModels$1 = [CronJob];
8676
+
8523
8677
  const code$1 = "runCronJob";
8524
8678
  async function handler$1(plugin, ctx, options) {
8525
8679
  const { server, routerContext } = ctx;
@@ -8572,9 +8726,30 @@ var runCronJob = {
8572
8726
 
8573
8727
  var pluginRoutes$1 = [runCronJob];
8574
8728
 
8729
+ var cronJobEntityWatchers = [
8730
+ {
8731
+ eventName: "entity.update",
8732
+ modelSingularCode: "sys_cron_job",
8733
+ handler: async (ctx) => {
8734
+ const { server, payload, routerContext: routeContext } = ctx;
8735
+ const cronJobService = server.getService("cronJobService");
8736
+ const changes = payload.changes;
8737
+ const after = payload.after;
8738
+ await cronJobService.updateJobConfig(routeContext, {
8739
+ code: after.code,
8740
+ cronTime: changes.cronTime,
8741
+ disabled: changes.disabled,
8742
+ jobOptions: changes.jobOptions,
8743
+ });
8744
+ },
8745
+ },
8746
+ ];
8747
+
8748
+ var pluginEntityWatchers = [...cronJobEntityWatchers];
8749
+
8575
8750
  class CronJobService {
8576
8751
  #server;
8577
- #jobInstances;
8752
+ #namedJobInstances;
8578
8753
  constructor(server) {
8579
8754
  this.#server = server;
8580
8755
  }
@@ -8586,32 +8761,80 @@ class CronJobService {
8586
8761
  getJobConfigurationByCode(code) {
8587
8762
  return lodash.find(this.#server.listCronJobs(), (job) => job.code === code);
8588
8763
  }
8764
+ #createJobInstance(job) {
8765
+ return cron.CronJob.from({
8766
+ ...(job.jobOptions || {}),
8767
+ cronTime: job.cronTime,
8768
+ onTick: async () => {
8769
+ await this.tryExecuteJob(job);
8770
+ },
8771
+ });
8772
+ }
8773
+ async #startJobInstance(routeContext, jobConfiguration, jobInstance) {
8774
+ const server = this.#server;
8775
+ const jobCode = jobConfiguration.code;
8776
+ const cronJobManager = server.getEntityManager("sys_cron_job");
8777
+ const cronJobInDb = await cronJobManager.findEntity({
8778
+ routeContext,
8779
+ filters: [{ operator: "eq", field: "code", value: jobCode }],
8780
+ });
8781
+ if (cronJobInDb) {
8782
+ let nextRunningTime;
8783
+ nextRunningTime = formatDateTimeWithTimezone(jobInstance.nextDate().toISO());
8784
+ await cronJobManager.updateEntityById({
8785
+ routeContext,
8786
+ id: cronJobInDb.id,
8787
+ entityToSave: {
8788
+ nextRunningTime,
8789
+ },
8790
+ });
8791
+ }
8792
+ jobInstance.start();
8793
+ }
8794
+ async #setJobNextRunningTime(routeContext, jobCode, nextRunningTime) {
8795
+ const server = this.#server;
8796
+ const cronJobManager = server.getEntityManager("sys_cron_job");
8797
+ const cronJobInDb = await cronJobManager.findEntity({
8798
+ routeContext,
8799
+ filters: [{ operator: "eq", field: "code", value: jobCode }],
8800
+ });
8801
+ await cronJobManager.updateEntityById({
8802
+ routeContext,
8803
+ id: cronJobInDb.id,
8804
+ entityToSave: {
8805
+ nextRunningTime,
8806
+ },
8807
+ });
8808
+ }
8589
8809
  /**
8590
8810
  * 重新加载定时任务
8591
8811
  */
8592
- reloadJobs() {
8812
+ async reloadJobs() {
8593
8813
  const server = this.#server;
8594
- if (this.#jobInstances) {
8595
- for (const job of this.#jobInstances) {
8814
+ const routeContext = RouteContext.newSystemOperationContext(server);
8815
+ if (this.#namedJobInstances) {
8816
+ for (const job of this.#namedJobInstances) {
8596
8817
  job.instance.stop();
8597
8818
  }
8598
8819
  }
8599
- this.#jobInstances = [];
8600
- const cronJobs = server.listCronJobs();
8601
- for (const job of cronJobs) {
8602
- if (job.disabled) {
8820
+ this.#namedJobInstances = [];
8821
+ const cronJobManager = server.getEntityManager("sys_cron_job");
8822
+ const cronJobConfigurationsInDb = await cronJobManager.findEntities({ routeContext });
8823
+ const cronJobConfigurations = server.listCronJobs();
8824
+ for (const cronJobConfig of cronJobConfigurations) {
8825
+ const jobCode = cronJobConfig.code;
8826
+ const jobConfigInDb = lodash.find(cronJobConfigurationsInDb, { code: jobCode });
8827
+ if (jobConfigInDb) {
8828
+ overrideJobConfig(cronJobConfig, jobConfigInDb);
8829
+ }
8830
+ if (cronJobConfig.disabled) {
8831
+ await this.#setJobNextRunningTime(routeContext, jobCode, null);
8603
8832
  continue;
8604
8833
  }
8605
- const jobInstance = cron.CronJob.from({
8606
- ...(job.jobOptions || {}),
8607
- cronTime: job.cronTime,
8608
- onTick: async () => {
8609
- await this.tryExecuteJob(job);
8610
- },
8611
- });
8612
- jobInstance.start();
8613
- this.#jobInstances.push({
8614
- code: job.code,
8834
+ const jobInstance = this.#createJobInstance(cronJobConfig);
8835
+ await this.#startJobInstance(routeContext, cronJobConfig, jobInstance);
8836
+ this.#namedJobInstances.push({
8837
+ code: jobCode,
8615
8838
  instance: jobInstance,
8616
8839
  });
8617
8840
  }
@@ -8619,7 +8842,8 @@ class CronJobService {
8619
8842
  async tryExecuteJob(job) {
8620
8843
  const server = this.#server;
8621
8844
  const logger = server.getLogger();
8622
- logger.info(`Executing cron job '${job.code}'...`);
8845
+ const jobCode = job.code;
8846
+ logger.info(`Executing cron job '${jobCode}'...`);
8623
8847
  let handlerContext = {
8624
8848
  logger,
8625
8849
  routerContext: RouteContext.newSystemOperationContext(server),
@@ -8628,21 +8852,54 @@ class CronJobService {
8628
8852
  applicationConfig: null,
8629
8853
  input: null,
8630
8854
  };
8855
+ let result;
8856
+ let lastErrorMessage;
8857
+ let lastErrorStack;
8631
8858
  try {
8632
8859
  await this.executeJob(handlerContext, job);
8633
- logger.info(`Completed cron job '${job.code}'...`);
8860
+ result = "success";
8861
+ logger.info(`Completed cron job '${jobCode}'...`);
8634
8862
  }
8635
8863
  catch (ex) {
8636
- logger.error('Cron job "%s" execution error: %s', job.code, ex.message);
8864
+ logger.error('Cron job "%s" execution error: %s', jobCode, ex.message);
8865
+ if (lodash.isString(ex)) {
8866
+ lastErrorMessage = ex;
8867
+ }
8868
+ else {
8869
+ lastErrorMessage = ex.message;
8870
+ lastErrorStack = ex.stack;
8871
+ }
8872
+ result = "failed";
8637
8873
  if (job.onError) {
8638
8874
  try {
8639
8875
  await job.onError(handlerContext, ex);
8640
8876
  }
8641
8877
  catch (ex) {
8642
- logger.error('Error handler of cron job "%s" execution failed: %s', job.code, ex.message);
8878
+ logger.error('Error handler of cron job "%s" execution failed: %s', jobCode, ex.message);
8643
8879
  }
8644
8880
  }
8645
8881
  }
8882
+ const cronJobManager = server.getEntityManager("sys_cron_job");
8883
+ const cronJobInDb = await cronJobManager.findEntity({
8884
+ filters: [{ operator: "eq", field: "code", value: jobCode }],
8885
+ });
8886
+ if (cronJobInDb) {
8887
+ let nextRunningTime;
8888
+ const namedJobInstance = lodash.find(this.#namedJobInstances, { code: jobCode });
8889
+ if (namedJobInstance && namedJobInstance.instance) {
8890
+ nextRunningTime = formatDateTimeWithTimezone(namedJobInstance.instance.nextDate().toISO());
8891
+ }
8892
+ await cronJobManager.updateEntityById({
8893
+ id: cronJobInDb.id,
8894
+ entityToSave: {
8895
+ nextRunningTime,
8896
+ lastRunningResult: result,
8897
+ lastRunningTime: getNowStringWithTimezone(),
8898
+ lastErrorMessage,
8899
+ lastErrorStack,
8900
+ },
8901
+ });
8902
+ }
8646
8903
  }
8647
8904
  /**
8648
8905
  * 执行指定任务
@@ -8659,6 +8916,50 @@ class CronJobService {
8659
8916
  await job.handler(handlerContext, job.handleOptions);
8660
8917
  }
8661
8918
  }
8919
+ async updateJobConfig(routeContext, options) {
8920
+ const server = this.#server;
8921
+ const cronJobs = server.listCronJobs();
8922
+ const jobCode = options.code;
8923
+ if (!jobCode) {
8924
+ throw new Error(`options.code is required.`);
8925
+ }
8926
+ const cronJobConfig = lodash.find(cronJobs, { code: jobCode });
8927
+ if (!cronJobConfig) {
8928
+ throw new Error(`Cron job with code "${jobCode}" not found.`);
8929
+ }
8930
+ if (!["cronTime", "disabled", "jobOptions"].some((field) => !lodash.isNil(options[field]))) {
8931
+ return;
8932
+ }
8933
+ overrideJobConfig(cronJobConfig, options);
8934
+ const namedJobInstance = lodash.find(this.#namedJobInstances, { code: jobCode });
8935
+ if (namedJobInstance && namedJobInstance.instance) {
8936
+ namedJobInstance.instance.stop();
8937
+ namedJobInstance.instance = null;
8938
+ }
8939
+ if (cronJobConfig.disabled) {
8940
+ await this.#setJobNextRunningTime(routeContext, jobCode, null);
8941
+ }
8942
+ else {
8943
+ const jobInstance = this.#createJobInstance(cronJobConfig);
8944
+ await this.#startJobInstance(routeContext, cronJobConfig, jobInstance);
8945
+ if (namedJobInstance) {
8946
+ namedJobInstance.instance = jobInstance;
8947
+ }
8948
+ else {
8949
+ this.#namedJobInstances.push({
8950
+ code: cronJobConfig.code,
8951
+ instance: jobInstance,
8952
+ });
8953
+ }
8954
+ }
8955
+ }
8956
+ }
8957
+ function overrideJobConfig(original, overrides) {
8958
+ ["cronTime", "disabled", "jobOptions"].forEach((field) => {
8959
+ if (!lodash.isNil(overrides[field])) {
8960
+ original[field] = overrides[field];
8961
+ }
8962
+ });
8662
8963
  }
8663
8964
 
8664
8965
  class CronJobPlugin {
@@ -8689,11 +8990,16 @@ class CronJobPlugin {
8689
8990
  server.registerActionHandler(this, actionHandler);
8690
8991
  }
8691
8992
  }
8692
- async registerEventHandlers(server) { }
8993
+ async registerEventHandlers(server) {
8994
+ for (const entityWatcher of pluginEntityWatchers)
8995
+ server.registerEntityWatcher(entityWatcher);
8996
+ }
8693
8997
  async registerMessageHandlers(server) { }
8694
8998
  async registerTaskProcessors(server) { }
8695
8999
  async onLoadingApplication(server, applicationConfig) { }
8696
- async configureModels(server, applicationConfig) { }
9000
+ async configureModels(server, applicationConfig) {
9001
+ server.appendApplicationConfig({ models: pluginModels$1 });
9002
+ }
8697
9003
  async configureModelProperties(server, applicationConfig) { }
8698
9004
  async configureServices(server, applicationConfig) {
8699
9005
  this.#cronJobService = new CronJobService(server);
@@ -8702,9 +9008,24 @@ class CronJobPlugin {
8702
9008
  async configureRoutes(server, applicationConfig) {
8703
9009
  server.appendApplicationConfig({ routes: pluginRoutes$1 });
8704
9010
  }
8705
- async onApplicationLoaded(server, applicationConfig) { }
9011
+ async onApplicationLoaded(server, applicationConfig) {
9012
+ await saveCronJobsToDatabase(server);
9013
+ }
8706
9014
  async onApplicationReady(server, applicationConfig) {
8707
- this.#cronJobService.reloadJobs();
9015
+ await this.#cronJobService.reloadJobs();
9016
+ }
9017
+ }
9018
+ async function saveCronJobsToDatabase(server) {
9019
+ const cronJobManager = server.getEntityManager("sys_cron_job");
9020
+ for (const cronJobToSave of server.listCronJobs()) {
9021
+ const currentCronJob = await cronJobManager.findEntity({
9022
+ filters: [{ operator: "eq", field: "code", value: cronJobToSave.code }],
9023
+ });
9024
+ if (!currentCronJob) {
9025
+ await cronJobManager.createEntity({
9026
+ entity: lodash.pick(cronJobToSave, ["code", "description", "cronTime", "disabled"]),
9027
+ });
9028
+ }
8708
9029
  }
8709
9030
  }
8710
9031
 
@@ -9120,6 +9441,7 @@ exports.createJwt = createJwt;
9120
9441
  exports.decodeJwt = decodeJwt;
9121
9442
  exports.deleteCookie = deleteCookie;
9122
9443
  exports.detectChangedFieldsOfEntity = detectChangedFieldsOfEntity;
9444
+ exports.formatDateTimeWithTimezone = formatDateTimeWithTimezone;
9123
9445
  exports.generateJwtSecretKey = generateJwtSecretKey;
9124
9446
  exports.getCookies = getCookies;
9125
9447
  exports.getDateString = getDateString;
@@ -1,4 +1,5 @@
1
1
  import { CronJob } from "cron";
2
+ import { CronJobConfiguration } from "../../types/cron-job-types";
2
3
  export type RunCronJobActionHandlerOptions = {
3
4
  code?: string;
4
5
  };
@@ -10,3 +11,28 @@ export type NamedCronJobInstance = {
10
11
  code: string;
11
12
  instance: CronJob;
12
13
  };
14
+ export type JobRunningResult = "success" | "failed" | "error";
15
+ export type SysCronJob = {
16
+ id: number;
17
+ code: string;
18
+ description: string;
19
+ cronTime: string;
20
+ disabled: boolean;
21
+ jobOptions: CronJobConfiguration["jobOptions"];
22
+ isRunning: boolean;
23
+ nextRunningTime: string;
24
+ lastRunningTime: string;
25
+ lastRunningResult?: JobRunningResult;
26
+ lastErrorMessage?: string;
27
+ lastErrorStack?: string;
28
+ actionHandlerCode?: string;
29
+ handler?: string;
30
+ handleOptions?: Record<string, any>;
31
+ onError?: string;
32
+ };
33
+ export type UpdateJobConfigOptions = {
34
+ code: string;
35
+ cronTime?: string;
36
+ disabled?: boolean;
37
+ jobOptions?: CronJobConfiguration["jobOptions"];
38
+ };
@@ -0,0 +1,7 @@
1
+ import type { EntityWatchHandlerContext } from "../../../types";
2
+ declare const _default: {
3
+ eventName: string;
4
+ modelSingularCode: string;
5
+ handler: (ctx: EntityWatchHandlerContext<"entity.update">) => Promise<void>;
6
+ }[];
7
+ export default _default;
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ eventName: string;
3
+ modelSingularCode: string;
4
+ handler: (ctx: import("../../../types").EntityWatchHandlerContext<"entity.update">) => Promise<void>;
5
+ }[];
6
+ export default _default;