@nocobase/server 2.0.0-alpha.5 → 2.0.0-alpha.51

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.
@@ -56,7 +56,7 @@ const availableActions = {
56
56
  update: {
57
57
  displayName: '{{t("Edit")}}',
58
58
  type: "old-data",
59
- aliases: ["update", "move", "add", "set", "remove", "toggle"],
59
+ aliases: ["update", "move"],
60
60
  allowConfigureFields: true
61
61
  },
62
62
  destroy: {
@@ -109,6 +109,10 @@ const _AesEncryptor = class _AesEncryptor {
109
109
  return appKeyPath;
110
110
  }
111
111
  static async create(app) {
112
+ if (process.env.APP_AES_SECRET_KEY) {
113
+ const key2 = Buffer.from(process.env.APP_AES_SECRET_KEY, "hex");
114
+ return new _AesEncryptor(key2);
115
+ }
112
116
  const KEY_PATH = process.env.APP_AES_SECRET_KEY_PATH;
113
117
  const keyPath = KEY_PATH ? (0, import_path.resolve)(process.cwd(), KEY_PATH) : await this.getKeyPath(app.name);
114
118
  const key = await _AesEncryptor.getOrGenerateKey(keyPath);
@@ -14,6 +14,7 @@ export declare class AppCommand extends Command {
14
14
  auth(): this;
15
15
  preload(): this;
16
16
  hasCommand(name: string): boolean;
17
+ findCommand(name: string): any;
17
18
  isHandleByIPCServer(): boolean;
18
19
  createCommand(name?: string): AppCommand;
19
20
  parseHandleByIPCServer(argv: any, parseOptions?: any): Boolean;
@@ -51,6 +51,9 @@ const _AppCommand = class _AppCommand extends import_commander.Command {
51
51
  const names = this.commands.map((c) => c.name());
52
52
  return names.includes(name);
53
53
  }
54
+ findCommand(name) {
55
+ return this._findCommand(name);
56
+ }
54
57
  isHandleByIPCServer() {
55
58
  return this._handleByIPCServer;
56
59
  }
@@ -8,6 +8,7 @@
8
8
  */
9
9
  /// <reference types="node" />
10
10
  import { AsyncEmitter } from '@nocobase/utils';
11
+ import { Mutex } from 'async-mutex';
11
12
  import { EventEmitter } from 'events';
12
13
  import Application, { ApplicationOptions } from './application';
13
14
  type BootOptions = {
@@ -16,9 +17,10 @@ type BootOptions = {
16
17
  appSupervisor: AppSupervisor;
17
18
  };
18
19
  type AppBootstrapper = (bootOptions: BootOptions) => Promise<void>;
19
- type AppStatus = 'initializing' | 'initialized' | 'running' | 'commanding' | 'stopped' | 'error' | 'not_found';
20
+ export type AppStatus = 'preparing' | 'initializing' | 'initialized' | 'running' | 'commanding' | 'stopped' | 'error' | 'not_found';
20
21
  export declare class AppSupervisor extends EventEmitter implements AsyncEmitter {
21
22
  private static instance;
23
+ private bootstrapQueue;
22
24
  runningMode: 'single' | 'multiple';
23
25
  singleAppName: string | null;
24
26
  emitAsync: (event: string | symbol, ...args: any[]) => Promise<boolean>;
@@ -48,7 +50,8 @@ export declare class AppSupervisor extends EventEmitter implements AsyncEmitter
48
50
  reset(): Promise<void>;
49
51
  destroy(): Promise<void>;
50
52
  setAppStatus(appName: string, status: AppStatus, options?: {}): void;
51
- getMutexOfApp(appName: string): any;
53
+ getMutexOfApp(appName: string): Mutex;
54
+ private _bootStrapApp;
52
55
  bootStrapApp(appName: string, options?: {}): Promise<void>;
53
56
  getApp(appName: string, options?: {
54
57
  withOutBootStrap?: boolean;
@@ -61,6 +64,8 @@ export declare class AppSupervisor extends EventEmitter implements AsyncEmitter
61
64
  touchApp(appName: string): void;
62
65
  addApp(app: Application): Application<import("./application").DefaultState, import("./application").DefaultContext>;
63
66
  getAppsNames(): Promise<string[]>;
67
+ startApp(appName: string): Promise<void>;
68
+ stopApp(appName: string): Promise<void>;
64
69
  removeApp(appName: string): Promise<void>;
65
70
  subApps(): Application<import("./application").DefaultState, import("./application").DefaultContext>[];
66
71
  on(eventName: string | symbol, listener: (...args: any[]) => void): this;
@@ -47,7 +47,9 @@ var import_async_mutex = require("async-mutex");
47
47
  var import_events = require("events");
48
48
  var import_application = __toESM(require("./application"));
49
49
  var import_handler = require("./errors/handler");
50
+ var import_p_queue = __toESM(require("p-queue"));
50
51
  const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
52
+ bootstrapQueue;
51
53
  runningMode = "multiple";
52
54
  singleAppName = null;
53
55
  apps = {};
@@ -64,6 +66,12 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
64
66
  this.runningMode = "single";
65
67
  this.singleAppName = process.env.STARTUP_SUBAPP;
66
68
  }
69
+ this.bootstrapQueue = new import_p_queue.default({
70
+ concurrency: process.env.SUBAPP_BOOTSTRAP_CONCURRENCY ? parseInt(process.env.SUBAPP_BOOTSTRAP_CONCURRENCY) : Infinity,
71
+ intervalCap: process.env.SUBAPP_BOOTSTRAP_INTERVAL_CAP ? parseInt(process.env.SUBAPP_BOOTSTRAP_INTERVAL_CAP) : Infinity,
72
+ interval: process.env.SUBAPP_BOOTSTRAP_INTERVAL ? parseInt(process.env.SUBAPP_BOOTSTRAP_INTERVAL) : 0,
73
+ timeout: 1 * 60 * 1e3
74
+ });
67
75
  }
68
76
  static getInstance() {
69
77
  if (!_AppSupervisor.instance) {
@@ -113,24 +121,42 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
113
121
  }
114
122
  return this.appMutexes[appName];
115
123
  }
124
+ async _bootStrapApp(appName, options = {}) {
125
+ this.setAppStatus(appName, "initializing");
126
+ if (this.appBootstrapper) {
127
+ await this.appBootstrapper({
128
+ appSupervisor: this,
129
+ appName,
130
+ options
131
+ });
132
+ }
133
+ if (!this.hasApp(appName)) {
134
+ this.setAppStatus(appName, "not_found");
135
+ } else if (!this.getAppStatus(appName) || this.getAppStatus(appName) === "initializing") {
136
+ this.setAppStatus(appName, "initialized");
137
+ }
138
+ }
116
139
  async bootStrapApp(appName, options = {}) {
117
- await this.getMutexOfApp(appName).runExclusive(async () => {
118
- if (!this.hasApp(appName)) {
119
- this.setAppStatus(appName, "initializing");
120
- if (this.appBootstrapper) {
121
- await this.appBootstrapper({
122
- appSupervisor: this,
123
- appName,
124
- options
125
- });
140
+ const mutex = this.getMutexOfApp(appName);
141
+ try {
142
+ await (0, import_async_mutex.tryAcquire)(mutex).runExclusive(async () => {
143
+ if (this.hasApp(appName) && this.getAppStatus(appName) !== "preparing") {
144
+ return;
126
145
  }
127
- if (!this.hasApp(appName)) {
128
- this.setAppStatus(appName, "not_found");
129
- } else if (!this.getAppStatus(appName) || this.getAppStatus(appName) == "initializing") {
130
- this.setAppStatus(appName, "initialized");
146
+ if (appName === "main") {
147
+ return this._bootStrapApp(appName, options);
131
148
  }
149
+ this.setAppStatus(appName, "preparing");
150
+ await this.bootstrapQueue.add(async () => {
151
+ await this._bootStrapApp(appName, options);
152
+ });
153
+ });
154
+ } catch (e) {
155
+ console.log(e);
156
+ if (e === import_async_mutex.E_ALREADY_LOCKED) {
157
+ return;
132
158
  }
133
- });
159
+ }
134
160
  }
135
161
  async getApp(appName, options = {}) {
136
162
  if (!options.withOutBootStrap) {
@@ -170,7 +196,7 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
170
196
  this.apps[app.name] = app;
171
197
  this.emit("afterAppAdded", app);
172
198
  if (!this.getAppStatus(app.name) || this.getAppStatus(app.name) == "not_found") {
173
- this.setAppStatus(app.name, "initialized");
199
+ this.setAppStatus(app.name, "preparing");
174
200
  }
175
201
  return app;
176
202
  }
@@ -179,12 +205,25 @@ const _AppSupervisor = class _AppSupervisor extends import_events.EventEmitter {
179
205
  const apps = Object.values(this.apps);
180
206
  return apps.map((app) => app.name);
181
207
  }
208
+ async startApp(appName) {
209
+ const appInstance = await _AppSupervisor.getInstance().getApp(appName);
210
+ await appInstance.runCommand("start", "--quickstart");
211
+ }
212
+ async stopApp(appName) {
213
+ if (!this.apps[appName]) {
214
+ console.log(`app ${appName} not exists`);
215
+ return;
216
+ }
217
+ await this.apps[appName].runCommand("stop");
218
+ this.apps[appName] = null;
219
+ }
182
220
  async removeApp(appName) {
183
221
  if (!this.apps[appName]) {
184
222
  console.log(`app ${appName} not exists`);
185
223
  return;
186
224
  }
187
225
  await this.apps[appName].runCommand("destroy");
226
+ this.apps[appName] = null;
188
227
  }
189
228
  subApps() {
190
229
  return Object.values(this.apps).filter((app) => app.name !== "main");
@@ -34,10 +34,9 @@ import { SyncMessageManager } from './sync-message-manager';
34
34
  import AesEncryptor from './aes-encryptor';
35
35
  import { AuditManager } from './audit-manager';
36
36
  import { Environment } from './environment';
37
- import { ServiceContainer } from './service-container';
38
37
  import { EventQueue, EventQueueOptions } from './event-queue';
39
- import { BackgroundJobManager, BackgroundJobManagerOptions } from './background-job-manager';
40
38
  import { RedisConfig, RedisConnectionManager } from './redis-connection-manager';
39
+ import { ServiceContainer } from './service-container';
41
40
  import { WorkerIdAllocator } from './worker-id-allocator';
42
41
  export type PluginType = string | typeof Plugin;
43
42
  export type PluginConfiguration = PluginType | [PluginType, any];
@@ -89,7 +88,6 @@ export interface ApplicationOptions {
89
88
  auditManager?: AuditManager;
90
89
  lockManager?: LockManagerOptions;
91
90
  eventQueue?: EventQueueOptions;
92
- backgroundJobManager?: BackgroundJobManagerOptions;
93
91
  /**
94
92
  * @internal
95
93
  */
@@ -190,9 +188,9 @@ export declare class Application<StateT = DefaultState, ContextT = DefaultContex
190
188
  container: ServiceContainer;
191
189
  lockManager: LockManager;
192
190
  eventQueue: EventQueue;
193
- backgroundJobManager: BackgroundJobManager;
194
191
  constructor(options: ApplicationOptions);
195
192
  private static staticCommands;
193
+ static registerStaticCommand(callback: (app: Application) => void): void;
196
194
  static addCommand(callback: (app: Application) => void): void;
197
195
  private _sqlLogger;
198
196
  get instanceId(): number;
@@ -219,7 +217,7 @@ export declare class Application<StateT = DefaultState, ContextT = DefaultContex
219
217
  get environment(): Environment;
220
218
  protected _cronJobManager: CronJobManager;
221
219
  get cronJobManager(): CronJobManager;
222
- get mainDataSource(): SequelizeDataSource;
220
+ get mainDataSource(): SequelizeDataSource<import("@nocobase/data-source-manager").DatabaseIntrospector>;
223
221
  get db(): Database;
224
222
  get resourceManager(): import("@nocobase/resourcer").ResourceManager;
225
223
  /**
@@ -283,7 +281,7 @@ export declare class Application<StateT = DefaultState, ContextT = DefaultContex
283
281
  setMaintainingMessage(message: string): void;
284
282
  /**
285
283
  * This method is deprecated and should not be used.
286
- * Use {@link #this.version.get()} instead.
284
+ * Use {@link #this.getPackageVersion} instead.
287
285
  * @deprecated
288
286
  */
289
287
  getVersion(): string;
@@ -50,8 +50,8 @@ var import_database = __toESM(require("@nocobase/database"));
50
50
  var import_logger = require("@nocobase/logger");
51
51
  var import_telemetry = require("@nocobase/telemetry");
52
52
  var import_lock_manager = require("@nocobase/lock-manager");
53
- var import_utils = require("@nocobase/utils");
54
53
  var import_snowflake_id = require("@nocobase/snowflake-id");
54
+ var import_utils = require("@nocobase/utils");
55
55
  var import_crypto = require("crypto");
56
56
  var import_glob = __toESM(require("glob"));
57
57
  var import_koa = __toESM(require("koa"));
@@ -81,12 +81,11 @@ var import_available_action = require("./acl/available-action");
81
81
  var import_aes_encryptor = __toESM(require("./aes-encryptor"));
82
82
  var import_audit_manager = require("./audit-manager");
83
83
  var import_environment = require("./environment");
84
- var import_service_container = require("./service-container");
85
84
  var import_event_queue = require("./event-queue");
86
- var import_background_job_manager = require("./background-job-manager");
87
85
  var import_redis_connection_manager = require("./redis-connection-manager");
88
- var import_worker_id_allocator = require("./worker-id-allocator");
86
+ var import_service_container = require("./service-container");
89
87
  var import_snowflake_id_field = require("./snowflake-id-field");
88
+ var import_worker_id_allocator = require("./worker-id-allocator");
90
89
  const _Application = class _Application extends import_koa.default {
91
90
  constructor(options) {
92
91
  super();
@@ -142,7 +141,9 @@ const _Application = class _Application extends import_koa.default {
142
141
  container = new import_service_container.ServiceContainer();
143
142
  lockManager;
144
143
  eventQueue;
145
- backgroundJobManager;
144
+ static registerStaticCommand(callback) {
145
+ this.staticCommands.push(callback);
146
+ }
146
147
  static addCommand(callback) {
147
148
  this.staticCommands.push(callback);
148
149
  }
@@ -336,7 +337,7 @@ const _Application = class _Application extends import_koa.default {
336
337
  }
337
338
  /**
338
339
  * This method is deprecated and should not be used.
339
- * Use {@link #this.version.get()} instead.
340
+ * Use {@link #this.getPackageVersion} instead.
340
341
  * @deprecated
341
342
  */
342
343
  getVersion() {
@@ -777,6 +778,12 @@ const _Application = class _Application extends import_koa.default {
777
778
  }
778
779
  async upgrade(options = {}) {
779
780
  this.log.info("upgrading...");
781
+ const pkgVersion = this.getPackageVersion();
782
+ const appVersion = await this.version.get();
783
+ if (process.env.SKIP_SAME_VERSION_UPGRADE === "true" && pkgVersion === appVersion) {
784
+ this.log.info(`app is already the latest version (${appVersion})`);
785
+ return;
786
+ }
780
787
  await this.reInit();
781
788
  const migrator1 = await this.loadCoreMigrations();
782
789
  await migrator1.beforeLoad.up();
@@ -800,7 +807,9 @@ const _Application = class _Application extends import_koa.default {
800
807
  await migrator3.afterLoad.up();
801
808
  await this.pm.repository.updateVersions();
802
809
  await this.version.update();
803
- await this.emitAsync("afterUpgrade", this, options);
810
+ if (!options.quickstart) {
811
+ await this.emitAsync("afterUpgrade", this, options);
812
+ }
804
813
  await this.restart();
805
814
  }
806
815
  toJSON() {
@@ -911,7 +920,6 @@ const _Application = class _Application extends import_koa.default {
911
920
  this.pubSubManager = (0, import_pub_sub_manager.createPubSubManager)(this, options.pubSubManager);
912
921
  this.syncMessageManager = new import_sync_message_manager.SyncMessageManager(this, options.syncMessageManager);
913
922
  this.eventQueue = new import_event_queue.EventQueue(this, options.eventQueue);
914
- this.backgroundJobManager = new import_background_job_manager.BackgroundJobManager(this, options.backgroundJobManager);
915
923
  this.lockManager = new import_lock_manager.LockManager({
916
924
  defaultAdapter: process.env.LOCK_ADAPTER_DEFAULT,
917
925
  ...options.lockManager
@@ -927,8 +935,8 @@ const _Application = class _Application extends import_koa.default {
927
935
  plugins: plugins || []
928
936
  });
929
937
  this._telemetry = new import_telemetry.Telemetry({
930
- serviceName: `nocobase-${this.name}`,
931
- version: this.getVersion(),
938
+ appName: this.name,
939
+ version: this.getPackageVersion(),
932
940
  ...options.telemetry
933
941
  });
934
942
  this._authManager = new import_auth.AuthManager({
@@ -948,7 +956,7 @@ const _Application = class _Application extends import_koa.default {
948
956
  }
949
957
  }
950
958
  });
951
- this._dataSourceManager.use(this._authManager.middleware(), { tag: "auth" });
959
+ this._dataSourceManager.use(this._authManager.middleware(), { tag: "auth", before: "default" });
952
960
  this._dataSourceManager.use(import_validate_filter_params.default, { tag: "validate-filter-params", before: ["auth"] });
953
961
  this._dataSourceManager.use(import_middlewares.parseVariables, {
954
962
  group: "parseVariables",
@@ -53,6 +53,7 @@ var import_start = __toESM(require("./start"));
53
53
  var import_stop = __toESM(require("./stop"));
54
54
  var import_upgrade = __toESM(require("./upgrade"));
55
55
  var import_console = __toESM(require("./console"));
56
+ var import_repair = __toESM(require("./repair"));
56
57
  /* istanbul ignore file -- @preserve */
57
58
  function registerCli(app) {
58
59
  (0, import_console.default)(app);
@@ -68,6 +69,7 @@ function registerCli(app) {
68
69
  (0, import_destroy.default)(app);
69
70
  (0, import_start.default)(app);
70
71
  (0, import_refresh.default)(app);
72
+ (0, import_repair.default)(app);
71
73
  app.command("build").argument("[packages...]");
72
74
  app.command("clean");
73
75
  app.command("dev").usage("[options]").option("-p, --port [port]").option("--client").option("--server");
@@ -38,6 +38,17 @@ var pm_default = /* @__PURE__ */ __name((app) => {
38
38
  pm.command("create").argument("plugin").option("--force-recreate").action(async (plugin, options) => {
39
39
  await app.pm.create(plugin, options);
40
40
  });
41
+ pm.command("pull").arguments("<packageNames...>").option("--registry [registry]").option("--auth-token [authToken]").option("--version [version]").action(async (packageNames, options, cli) => {
42
+ try {
43
+ let name = packageNames;
44
+ if (Array.isArray(packageNames) && packageNames.length === 1) {
45
+ name = packageNames[0];
46
+ }
47
+ await app.pm.pull(name, { ...options });
48
+ } catch (error) {
49
+ throw new import_plugin_command_error.PluginCommandError(`Failed to pull plugin`, { cause: error });
50
+ }
51
+ });
41
52
  pm.command("add").ipc().preload().arguments("<packageNames...>").option("--registry [registry]").option("--auth-token [authToken]").option("--version [version]").action(async (packageNames, options, cli) => {
42
53
  try {
43
54
  let name = packageNames;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import Application from '../application';
10
+ declare const _default: (app: Application) => void;
11
+ export default _default;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var repair_exports = {};
29
+ __export(repair_exports, {
30
+ default: () => repair_default
31
+ });
32
+ module.exports = __toCommonJS(repair_exports);
33
+ var repair_default = /* @__PURE__ */ __name((app) => {
34
+ app.command("repair").auth().preload().action(async (options) => {
35
+ app.log.info("start repair data...");
36
+ const Collection = app.db.getCollection("collections");
37
+ if (Collection) {
38
+ await Collection.repository.setApp(app);
39
+ await Collection.repository.load();
40
+ }
41
+ await app.emitAsync("repair", options);
42
+ });
43
+ }, "default");
@@ -61,7 +61,7 @@ var start_default = /* @__PURE__ */ __name((app) => {
61
61
  }
62
62
  } else if (options.quickstart) {
63
63
  if (await app.isInstalled()) {
64
- await app.upgrade();
64
+ await app.upgrade({ quickstart: true });
65
65
  } else {
66
66
  await app.install();
67
67
  }
@@ -300,13 +300,13 @@ const _EventQueue = class _EventQueue {
300
300
  return this.adapter.isConnected();
301
301
  }
302
302
  async connect() {
303
- if (!this.adapter) {
304
- throw new Error("no adapter set, cannot connect");
305
- }
306
303
  if (!this.app.serving()) {
307
304
  this.app.logger.warn("app is not serving, will not connect to event queue");
308
305
  return;
309
306
  }
307
+ if (!this.adapter) {
308
+ throw new Error("no adapter set, cannot connect");
309
+ }
310
310
  await this.adapter.connect();
311
311
  this.app.logger.debug(`connected to adapter, using memory? ${this.adapter instanceof MemoryEventQueueAdapter}`);
312
312
  for (const [channel, event] of this.events.entries()) {
@@ -342,6 +342,10 @@ const _EventQueue = class _EventQueue {
342
342
  }
343
343
  }
344
344
  async publish(channel, message, options = {}) {
345
+ if (!this.app.serving()) {
346
+ this.app.logger.warn("app is not serving, will not publish message to event queue");
347
+ return;
348
+ }
345
349
  if (!this.adapter) {
346
350
  throw new Error("no adapter set, cannot publish");
347
351
  }
@@ -53,7 +53,7 @@ const errors = {
53
53
  APP_ERROR: {
54
54
  status: 503,
55
55
  message: /* @__PURE__ */ __name(({ app }) => {
56
- const error = import_app_supervisor.AppSupervisor.getInstance().appErrors[app.name];
56
+ const error = import_app_supervisor.AppSupervisor.getInstance().appErrors[app == null ? void 0 : app.name];
57
57
  if (!error) {
58
58
  return "";
59
59
  }
@@ -64,12 +64,17 @@ const errors = {
64
64
  return message;
65
65
  }, "message"),
66
66
  code: /* @__PURE__ */ __name(({ app }) => {
67
- const error = import_app_supervisor.AppSupervisor.getInstance().appErrors[app.name];
67
+ const error = import_app_supervisor.AppSupervisor.getInstance().appErrors[app == null ? void 0 : app.name];
68
68
  return error["code"] || "APP_ERROR";
69
69
  }, "code"),
70
70
  command: /* @__PURE__ */ __name(({ app }) => app.getMaintaining().command, "command"),
71
71
  maintaining: true
72
72
  },
73
+ APP_PREPARING: {
74
+ status: 503,
75
+ message: /* @__PURE__ */ __name(({ appName }) => `application ${appName} is preparing, please wait patiently`, "message"),
76
+ maintaining: true
77
+ },
73
78
  APP_STARTING: {
74
79
  status: 503,
75
80
  message: /* @__PURE__ */ __name(({ app }) => app.maintainingMessage, "message"),
@@ -77,12 +82,12 @@ const errors = {
77
82
  },
78
83
  APP_STOPPED: {
79
84
  status: 503,
80
- message: /* @__PURE__ */ __name(({ app }) => `application ${app.name} is stopped`, "message"),
85
+ message: /* @__PURE__ */ __name(({ app }) => `application ${app == null ? void 0 : app.name} is stopped`, "message"),
81
86
  maintaining: true
82
87
  },
83
88
  APP_INITIALIZED: {
84
89
  status: 503,
85
- message: /* @__PURE__ */ __name(({ app }) => `application ${app.name} is initialized, waiting for command`, "message"),
90
+ message: /* @__PURE__ */ __name(({ app }) => `application ${app == null ? void 0 : app.name} is initialized, please refresh the page`, "message"),
86
91
  maintaining: true
87
92
  },
88
93
  APP_INITIALIZING: {
@@ -111,7 +116,7 @@ const errors = {
111
116
  APP_RUNNING: {
112
117
  status: 200,
113
118
  maintaining: false,
114
- message: /* @__PURE__ */ __name(({ message, app }) => message || `application ${app.name} is running`, "message")
119
+ message: /* @__PURE__ */ __name(({ message, app }) => message || `application ${app == null ? void 0 : app.name} is running`, "message")
115
120
  },
116
121
  UNKNOWN_ERROR: {
117
122
  status: 500,
@@ -23,6 +23,12 @@ export interface IncomingRequest {
23
23
  url: string;
24
24
  headers: any;
25
25
  }
26
+ export interface GatewayRequestContext {
27
+ req: IncomingMessage;
28
+ res: ServerResponse;
29
+ appName: string;
30
+ }
31
+ type GatewayMiddleware = (ctx: GatewayRequestContext, next: () => Promise<void>) => Promise<void> | void;
26
32
  export type AppSelector = (req: IncomingRequest) => string | Promise<string>;
27
33
  export type AppSelectorMiddleware = (ctx: AppSelectorMiddlewareContext, next: () => Promise<void>) => void;
28
34
  interface StartHttpServerOptions {
@@ -39,6 +45,7 @@ export interface AppSelectorMiddlewareContext {
39
45
  }
40
46
  export declare class Gateway extends EventEmitter {
41
47
  private static instance;
48
+ middlewares: Toposort<GatewayMiddleware>;
42
49
  /**
43
50
  * use main app as default app to handle request
44
51
  */
@@ -54,6 +61,7 @@ export declare class Gateway extends EventEmitter {
54
61
  private onTerminate;
55
62
  private constructor();
56
63
  static getInstance(options?: any): Gateway;
64
+ use(middleware: GatewayMiddleware, options?: ToposortOptions): void;
57
65
  static getIPCSocketClient(): Promise<false | IPCSocketClient>;
58
66
  destroy(): void;
59
67
  reset(): void;
@@ -75,6 +75,7 @@ function getSocketPath() {
75
75
  }
76
76
  __name(getSocketPath, "getSocketPath");
77
77
  const _Gateway = class _Gateway extends import_events.EventEmitter {
78
+ middlewares;
78
79
  /**
79
80
  * use main app as default app to handle request
80
81
  */
@@ -124,6 +125,9 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
124
125
  }
125
126
  return _Gateway.instance;
126
127
  }
128
+ use(middleware, options) {
129
+ this.middlewares.add(middleware, options);
130
+ }
127
131
  static async getIPCSocketClient() {
128
132
  const socketPath = getSocketPath();
129
133
  try {
@@ -139,6 +143,7 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
139
143
  _Gateway.instance = null;
140
144
  }
141
145
  reset() {
146
+ this.middlewares = new import_utils.Toposort();
142
147
  this.selectorMiddlewares = new import_utils.Toposort();
143
148
  this.addAppSelectorMiddleware(
144
149
  async (ctx, next) => {
@@ -202,7 +207,15 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
202
207
  res.end(JSON.stringify({ error }));
203
208
  }
204
209
  responseErrorWithCode(code, res, options) {
210
+ const log = this.getLogger(options.appName, res);
205
211
  const error = (0, import_errors.applyErrorWithArgs)((0, import_errors.getErrorWithCode)(code), options);
212
+ log.error(error.message, {
213
+ method: "responseErrorWithCode",
214
+ code,
215
+ error,
216
+ statusCode: res.statusCode,
217
+ appName: options.appName
218
+ });
206
219
  this.responseError(res, error);
207
220
  }
208
221
  async requestHandler(req, res) {
@@ -256,11 +269,15 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
256
269
  if (!hasApp) {
257
270
  void import_app_supervisor.AppSupervisor.getInstance().bootStrapApp(handleApp);
258
271
  }
259
- let appStatus = import_app_supervisor.AppSupervisor.getInstance().getAppStatus(handleApp, "initializing");
272
+ let appStatus = import_app_supervisor.AppSupervisor.getInstance().getAppStatus(handleApp, "preparing");
260
273
  if (appStatus === "not_found") {
261
274
  this.responseErrorWithCode("APP_NOT_FOUND", res, { appName: handleApp });
262
275
  return;
263
276
  }
277
+ if (appStatus === "preparing") {
278
+ this.responseErrorWithCode("APP_PREPARING", res, { appName: handleApp });
279
+ return;
280
+ }
264
281
  if (appStatus === "initializing") {
265
282
  this.responseErrorWithCode("APP_INITIALIZING", res, { appName: handleApp });
266
283
  return;
@@ -283,7 +300,14 @@ const _Gateway = class _Gateway extends import_events.EventEmitter {
283
300
  if (handleApp !== "main") {
284
301
  import_app_supervisor.AppSupervisor.getInstance().touchApp(handleApp);
285
302
  }
286
- app.callback()(req, res);
303
+ const ctx = { req, res, appName: handleApp };
304
+ const fn = (0, import_koa_compose.default)([
305
+ ...this.middlewares.nodes,
306
+ async (_ctx) => {
307
+ await app.callback()(req, res);
308
+ }
309
+ ]);
310
+ await fn(ctx);
287
311
  }
288
312
  getAppSelectorMiddlewares() {
289
313
  return this.selectorMiddlewares;