@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.
- package/lib/acl/available-action.js +1 -1
- package/lib/aes-encryptor.js +4 -0
- package/lib/app-command.d.ts +1 -0
- package/lib/app-command.js +3 -0
- package/lib/app-supervisor.d.ts +7 -2
- package/lib/app-supervisor.js +54 -15
- package/lib/application.d.ts +4 -6
- package/lib/application.js +19 -11
- package/lib/commands/index.js +2 -0
- package/lib/commands/pm.js +11 -0
- package/lib/commands/repair.d.ts +11 -0
- package/lib/commands/repair.js +43 -0
- package/lib/commands/start.js +1 -1
- package/lib/event-queue.js +7 -3
- package/lib/gateway/errors.js +10 -5
- package/lib/gateway/index.d.ts +8 -0
- package/lib/gateway/index.js +26 -2
- package/lib/helper.d.ts +359 -0
- package/lib/helper.js +58 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +6 -3
- package/lib/locale/locale.js +1 -1
- package/lib/locale/resource.js +6 -9
- package/lib/main-data-source.d.ts +11 -0
- package/lib/main-data-source.js +128 -0
- package/lib/middlewares/data-template.js +1 -6
- package/lib/middlewares/parse-variables.js +2 -49
- package/lib/plugin-manager/options/resource.js +48 -25
- package/lib/plugin-manager/plugin-manager.d.ts +1 -0
- package/lib/plugin-manager/plugin-manager.js +36 -1
- package/lib/pub-sub-manager/pub-sub-manager.d.ts +1 -1
- package/lib/pub-sub-manager/pub-sub-manager.js +8 -8
- package/package.json +17 -16
- package/lib/background-job-manager.d.ts +0 -40
- package/lib/background-job-manager.js +0 -111
package/lib/main-data-source.js
CHANGED
|
@@ -32,6 +32,7 @@ __export(main_data_source_exports, {
|
|
|
32
32
|
module.exports = __toCommonJS(main_data_source_exports);
|
|
33
33
|
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
34
34
|
const _MainDataSource = class _MainDataSource extends import_data_source_manager.SequelizeDataSource {
|
|
35
|
+
status = "loaded";
|
|
35
36
|
init(options = {}) {
|
|
36
37
|
const { acl, resourceManager, database } = options;
|
|
37
38
|
this.acl = acl;
|
|
@@ -48,6 +49,133 @@ const _MainDataSource = class _MainDataSource extends import_data_source_manager
|
|
|
48
49
|
this.resourceManager.use(this.acl.middleware(), { group: "acl", after: "auth" });
|
|
49
50
|
}
|
|
50
51
|
}
|
|
52
|
+
async readTables() {
|
|
53
|
+
const allTables = await this.introspector.getTableList();
|
|
54
|
+
const existsCollections = this.collectionManager.db.collections;
|
|
55
|
+
const existsTables = Array.from(existsCollections.values()).map(
|
|
56
|
+
(collection) => collection.model.tableName
|
|
57
|
+
);
|
|
58
|
+
const diffTables = allTables.filter((table) => !existsTables.includes(table));
|
|
59
|
+
return diffTables.map((name) => ({ name }));
|
|
60
|
+
}
|
|
61
|
+
async tables2Collections(tableNames) {
|
|
62
|
+
const db = this.collectionManager.db;
|
|
63
|
+
const results = await Promise.all(
|
|
64
|
+
tableNames.map(async (tableName) => {
|
|
65
|
+
let tableInfo;
|
|
66
|
+
if (typeof tableName === "string") {
|
|
67
|
+
tableInfo = { tableName };
|
|
68
|
+
if (db.options.schema) {
|
|
69
|
+
tableInfo.schema = db.options.schema;
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
tableInfo = tableName;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return await this.introspector.getCollection({ tableInfo });
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e.message.includes("No description found for")) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
return results.filter(Boolean);
|
|
85
|
+
}
|
|
86
|
+
async loadTables(ctx, tables) {
|
|
87
|
+
const repo = this.collectionManager.db.getRepository("collections");
|
|
88
|
+
const existsCollections = this.collectionManager.db.collections;
|
|
89
|
+
const existsTables = Array.from(existsCollections.values()).map(
|
|
90
|
+
(collection) => collection.model.tableName
|
|
91
|
+
);
|
|
92
|
+
const toAddTables = tables.filter((table) => !existsTables.includes(table));
|
|
93
|
+
if (toAddTables.length) {
|
|
94
|
+
try {
|
|
95
|
+
this.status = "loading";
|
|
96
|
+
const results = await this.tables2Collections(toAddTables);
|
|
97
|
+
const values = results.map((result) => ({
|
|
98
|
+
...result,
|
|
99
|
+
underscored: false
|
|
100
|
+
}));
|
|
101
|
+
await repo.create({ values, context: ctx });
|
|
102
|
+
} catch (e) {
|
|
103
|
+
throw e;
|
|
104
|
+
} finally {
|
|
105
|
+
this.status = "loaded";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async getLoadedCollections(filter) {
|
|
110
|
+
const db = this.collectionManager.db;
|
|
111
|
+
const loadedCollections = await db.getRepository("collections").find({
|
|
112
|
+
appends: ["fields"],
|
|
113
|
+
filter: {
|
|
114
|
+
hidden: false,
|
|
115
|
+
...filter
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const collections = loadedCollections.filter((collection) => {
|
|
119
|
+
var _a;
|
|
120
|
+
return ((_a = collection.options) == null ? void 0 : _a.from) !== "db2cm";
|
|
121
|
+
});
|
|
122
|
+
const loadedData = {};
|
|
123
|
+
for (const collection of collections) {
|
|
124
|
+
const c = db.getCollection(collection.name);
|
|
125
|
+
loadedData[c.tableName()] = {
|
|
126
|
+
...collection.toJSON(),
|
|
127
|
+
fields: collection.fields.map((field) => {
|
|
128
|
+
const f = c.getField(field.name);
|
|
129
|
+
return {
|
|
130
|
+
columnName: f == null ? void 0 : f.columnName(),
|
|
131
|
+
...field.toJSON()
|
|
132
|
+
};
|
|
133
|
+
})
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return loadedData;
|
|
137
|
+
}
|
|
138
|
+
async syncFieldsFromDatabase(ctx, collectionNames) {
|
|
139
|
+
let filter = {};
|
|
140
|
+
if (collectionNames == null ? void 0 : collectionNames.length) {
|
|
141
|
+
filter = {
|
|
142
|
+
name: collectionNames
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const db = this.collectionManager.db;
|
|
146
|
+
const loadedCollections = await this.getLoadedCollections(filter);
|
|
147
|
+
const tableNames = Object.values(loadedCollections).map(({ name }) => {
|
|
148
|
+
const collection = db.getCollection(name);
|
|
149
|
+
return collection.getTableNameWithSchema();
|
|
150
|
+
});
|
|
151
|
+
let collections = [];
|
|
152
|
+
try {
|
|
153
|
+
collections = await this.tables2Collections(tableNames);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
ctx.log.error(err);
|
|
156
|
+
}
|
|
157
|
+
const toLoadCollections = this.mergeWithLoadedCollections(collections, loadedCollections);
|
|
158
|
+
for (const values of toLoadCollections) {
|
|
159
|
+
const existsFields = loadedCollections[values.tableName].fields;
|
|
160
|
+
const deletedFields = existsFields.filter((field) => !values.fields.find((f) => f.name === field.name));
|
|
161
|
+
await db.sequelize.transaction(async (transaction) => {
|
|
162
|
+
for (const field of deletedFields) {
|
|
163
|
+
await db.getRepository("fields").destroy({
|
|
164
|
+
filterByTk: field.key,
|
|
165
|
+
context: ctx,
|
|
166
|
+
transaction
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
await db.getRepository("collections").update({
|
|
170
|
+
filterByTk: values.name,
|
|
171
|
+
values,
|
|
172
|
+
updateAssociationValues: ["fields"],
|
|
173
|
+
context: ctx,
|
|
174
|
+
transaction
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
51
179
|
};
|
|
52
180
|
__name(_MainDataSource, "MainDataSource");
|
|
53
181
|
let MainDataSource = _MainDataSource;
|
|
@@ -126,12 +126,7 @@ const traverseJSON = /* @__PURE__ */ __name((data, options) => {
|
|
|
126
126
|
include: subInclude
|
|
127
127
|
});
|
|
128
128
|
} else if (field.type === "belongsTo") {
|
|
129
|
-
result[key] =
|
|
130
|
-
collection: collection.db.getCollection(field.target),
|
|
131
|
-
// exclude: [field.foreignKey],
|
|
132
|
-
include: subInclude,
|
|
133
|
-
excludePk: false
|
|
134
|
-
});
|
|
129
|
+
result[key] = data[key];
|
|
135
130
|
} else if (field.type === "belongsToMany") {
|
|
136
131
|
result[key] = traverseBelongsToMany(data[key], {
|
|
137
132
|
collection: collection.db.getCollection(field.target),
|
|
@@ -31,60 +31,13 @@ __export(parse_variables_exports, {
|
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(parse_variables_exports);
|
|
33
33
|
var import_utils = require("@nocobase/utils");
|
|
34
|
-
|
|
35
|
-
return async ({ fields }) => {
|
|
36
|
-
var _a, _b;
|
|
37
|
-
const userFields = fields.filter((f) => f && ctx.db.getFieldByPath("users." + f));
|
|
38
|
-
(_a = ctx.logger) == null ? void 0 : _a.info("filter-parse: ", { userFields });
|
|
39
|
-
if (!ctx.state.currentUser) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (!userFields.length) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const user = await ctx.db.getRepository("users").findOne({
|
|
46
|
-
filterByTk: ctx.state.currentUser.id,
|
|
47
|
-
fields: userFields
|
|
48
|
-
});
|
|
49
|
-
(_b = ctx.logger) == null ? void 0 : _b.info("filter-parse: ", {
|
|
50
|
-
$user: user == null ? void 0 : user.toJSON()
|
|
51
|
-
});
|
|
52
|
-
return user;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
__name(getUser, "getUser");
|
|
56
|
-
function isNumeric(str) {
|
|
57
|
-
if (typeof str === "number") return true;
|
|
58
|
-
if (typeof str != "string") return false;
|
|
59
|
-
return !isNaN(str) && !isNaN(parseFloat(str));
|
|
60
|
-
}
|
|
61
|
-
__name(isNumeric, "isNumeric");
|
|
34
|
+
var import_helper = require("../helper");
|
|
62
35
|
async function parseVariables(ctx, next) {
|
|
63
36
|
const filter = ctx.action.params.filter;
|
|
64
37
|
if (!filter) {
|
|
65
38
|
return next();
|
|
66
39
|
}
|
|
67
|
-
ctx.action.params.filter = await (0, import_utils.parseFilter)(filter,
|
|
68
|
-
timezone: ctx.get("x-timezone"),
|
|
69
|
-
now: (/* @__PURE__ */ new Date()).toISOString(),
|
|
70
|
-
getField: /* @__PURE__ */ __name((path) => {
|
|
71
|
-
const fieldPath = path.split(".").filter((p) => !p.startsWith("$") && !isNumeric(p)).join(".");
|
|
72
|
-
const { resourceName } = ctx.action;
|
|
73
|
-
return ctx.db.getFieldByPath(`${resourceName}.${fieldPath}`);
|
|
74
|
-
}, "getField"),
|
|
75
|
-
vars: {
|
|
76
|
-
// @deprecated
|
|
77
|
-
$system: {
|
|
78
|
-
now: (/* @__PURE__ */ new Date()).toISOString()
|
|
79
|
-
},
|
|
80
|
-
// @deprecated
|
|
81
|
-
$date: (0, import_utils.getDateVars)(),
|
|
82
|
-
// 新的命名方式,防止和 formily 内置变量冲突
|
|
83
|
-
$nDate: (0, import_utils.getDateVars)(),
|
|
84
|
-
$user: getUser(ctx),
|
|
85
|
-
$nRole: ctx.state.currentRole === "__union__" ? ctx.state.currentRoles : ctx.state.currentRole
|
|
86
|
-
}
|
|
87
|
-
});
|
|
40
|
+
ctx.action.params.filter = await (0, import_utils.parseFilter)(filter, (0, import_helper.createContextVariablesScope)(ctx));
|
|
88
41
|
await next();
|
|
89
42
|
}
|
|
90
43
|
__name(parseVariables, "parseVariables");
|
|
@@ -13,6 +13,8 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
13
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
14
|
var __getProtoOf = Object.getPrototypeOf;
|
|
15
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
17
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
16
18
|
var __export = (target, all) => {
|
|
17
19
|
for (var name in all)
|
|
18
20
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -34,6 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
34
36
|
mod
|
|
35
37
|
));
|
|
36
38
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
39
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
37
40
|
var resource_exports = {};
|
|
38
41
|
__export(resource_exports, {
|
|
39
42
|
default: () => resource_default
|
|
@@ -43,6 +46,33 @@ var import_utils = require("@nocobase/utils");
|
|
|
43
46
|
var import_fs = __toESM(require("fs"));
|
|
44
47
|
var import_fs_extra = __toESM(require("fs-extra"));
|
|
45
48
|
var import_path = __toESM(require("path"));
|
|
49
|
+
const _PackageUrls = class _PackageUrls {
|
|
50
|
+
static async get(packageName, version) {
|
|
51
|
+
if (!this.items[packageName]) {
|
|
52
|
+
this.items[packageName] = await this.fetch(packageName, version);
|
|
53
|
+
}
|
|
54
|
+
return this.items[packageName];
|
|
55
|
+
}
|
|
56
|
+
static async fetch(packageName, version) {
|
|
57
|
+
const PLUGIN_CLIENT_ENTRY_FILE = "dist/client/index.js";
|
|
58
|
+
const pkgPath = import_path.default.resolve(process.env.NODE_MODULES_PATH, packageName);
|
|
59
|
+
const r = await import_fs_extra.default.exists(pkgPath);
|
|
60
|
+
if (r) {
|
|
61
|
+
let t = "";
|
|
62
|
+
const dist = import_path.default.resolve(pkgPath, PLUGIN_CLIENT_ENTRY_FILE);
|
|
63
|
+
const distExists = await import_fs_extra.default.exists(dist);
|
|
64
|
+
if (distExists) {
|
|
65
|
+
const fsState = await import_fs_extra.default.stat(distExists ? dist : pkgPath);
|
|
66
|
+
t = `&t=${fsState.mtime.getTime()}`;
|
|
67
|
+
}
|
|
68
|
+
const url = `${process.env.APP_SERVER_BASE_URL}${process.env.PLUGIN_STATICS_PATH}${packageName}/${PLUGIN_CLIENT_ENTRY_FILE}?version=${version}${t}`;
|
|
69
|
+
return url;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
__name(_PackageUrls, "PackageUrls");
|
|
74
|
+
__publicField(_PackageUrls, "items", {});
|
|
75
|
+
let PackageUrls = _PackageUrls;
|
|
46
76
|
var resource_default = {
|
|
47
77
|
name: "pm",
|
|
48
78
|
actions: {
|
|
@@ -152,33 +182,26 @@ var resource_default = {
|
|
|
152
182
|
await next();
|
|
153
183
|
},
|
|
154
184
|
async listEnabled(ctx, next) {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (distExists) {
|
|
171
|
-
const fsState = await import_fs_extra.default.stat(distExists ? dist : pkgPath);
|
|
172
|
-
t = `&t=${fsState.mtime.getTime()}`;
|
|
185
|
+
const toArr = /* @__PURE__ */ __name(async () => {
|
|
186
|
+
const pm = ctx.db.getRepository("applicationPlugins");
|
|
187
|
+
const items = await pm.find({
|
|
188
|
+
filter: {
|
|
189
|
+
enabled: true
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
const arr = [];
|
|
193
|
+
for (const item of items) {
|
|
194
|
+
const url = await PackageUrls.get(item.packageName, item.version);
|
|
195
|
+
if (url) {
|
|
196
|
+
arr.push({
|
|
197
|
+
...item.toJSON(),
|
|
198
|
+
url
|
|
199
|
+
});
|
|
173
200
|
}
|
|
174
|
-
const url = `${process.env.APP_SERVER_BASE_URL}${process.env.PLUGIN_STATICS_PATH}${item.packageName}/${PLUGIN_CLIENT_ENTRY_FILE}?version=${item.version}${t}`;
|
|
175
|
-
arr.push({
|
|
176
|
-
...item.toJSON(),
|
|
177
|
-
url
|
|
178
|
-
});
|
|
179
201
|
}
|
|
180
|
-
|
|
181
|
-
|
|
202
|
+
return arr;
|
|
203
|
+
}, "toArr");
|
|
204
|
+
ctx.body = await toArr();
|
|
182
205
|
await next();
|
|
183
206
|
},
|
|
184
207
|
async get(ctx, next) {
|
|
@@ -90,7 +90,7 @@ const _PluginManager = class _PluginManager {
|
|
|
90
90
|
namespace: "core/pm",
|
|
91
91
|
directory: (0, import_path.resolve)(__dirname, "../migrations")
|
|
92
92
|
});
|
|
93
|
-
this.app.
|
|
93
|
+
this.app.resourceManager.use(import_middleware.uploadMiddleware, { tag: "upload", after: "acl" });
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
96
|
* @internal
|
|
@@ -718,6 +718,41 @@ const _PluginManager = class _PluginManager {
|
|
|
718
718
|
await removeDir();
|
|
719
719
|
}
|
|
720
720
|
}
|
|
721
|
+
async pull(urlOrName, options, emitStartedEvent = true) {
|
|
722
|
+
if (Array.isArray(urlOrName)) {
|
|
723
|
+
for (const packageName of urlOrName) {
|
|
724
|
+
await this.addViaCLI(packageName, import_lodash.default.omit(options, "name"), false);
|
|
725
|
+
}
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if ((0, import_utils.isURL)(urlOrName)) {
|
|
729
|
+
await this.addByCompressedFileUrl(
|
|
730
|
+
{
|
|
731
|
+
...options,
|
|
732
|
+
compressedFileUrl: urlOrName
|
|
733
|
+
},
|
|
734
|
+
emitStartedEvent
|
|
735
|
+
);
|
|
736
|
+
} else if (await import_fs_extra.default.exists(urlOrName)) {
|
|
737
|
+
await this.addByCompressedFileUrl(
|
|
738
|
+
{
|
|
739
|
+
...options,
|
|
740
|
+
compressedFileUrl: urlOrName
|
|
741
|
+
},
|
|
742
|
+
emitStartedEvent
|
|
743
|
+
);
|
|
744
|
+
} else if (options == null ? void 0 : options.registry) {
|
|
745
|
+
const { name, packageName } = await _PluginManager.parseName(urlOrName);
|
|
746
|
+
options["name"] = name;
|
|
747
|
+
await this.addByNpm(
|
|
748
|
+
{
|
|
749
|
+
...options,
|
|
750
|
+
packageName
|
|
751
|
+
},
|
|
752
|
+
emitStartedEvent
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
721
756
|
/**
|
|
722
757
|
* @internal
|
|
723
758
|
*/
|
|
@@ -17,8 +17,8 @@ export declare class PubSubManager {
|
|
|
17
17
|
protected adapter: IPubSubAdapter;
|
|
18
18
|
protected handlerManager: HandlerManager;
|
|
19
19
|
constructor(app: Application, options?: PubSubManagerOptions);
|
|
20
|
-
get channelPrefix(): string;
|
|
21
20
|
setAdapter(adapter: IPubSubAdapter): void;
|
|
21
|
+
getFullChannel(channel: string): string;
|
|
22
22
|
isConnected(): Promise<boolean>;
|
|
23
23
|
connect(): Promise<void>;
|
|
24
24
|
close(): Promise<any>;
|
|
@@ -55,13 +55,13 @@ const _PubSubManager = class _PubSubManager {
|
|
|
55
55
|
publisherId;
|
|
56
56
|
adapter;
|
|
57
57
|
handlerManager;
|
|
58
|
-
get channelPrefix() {
|
|
59
|
-
var _a;
|
|
60
|
-
return ((_a = this.options) == null ? void 0 : _a.channelPrefix) ? `${this.options.channelPrefix}.` : "";
|
|
61
|
-
}
|
|
62
58
|
setAdapter(adapter) {
|
|
63
59
|
this.adapter = adapter;
|
|
64
60
|
}
|
|
61
|
+
getFullChannel(channel) {
|
|
62
|
+
var _a;
|
|
63
|
+
return [this.app.name, (_a = this.options) == null ? void 0 : _a.channelPrefix, channel].filter(Boolean).join(".");
|
|
64
|
+
}
|
|
65
65
|
async isConnected() {
|
|
66
66
|
if (this.adapter) {
|
|
67
67
|
return this.adapter.isConnected();
|
|
@@ -79,7 +79,7 @@ const _PubSubManager = class _PubSubManager {
|
|
|
79
79
|
await this.adapter.connect();
|
|
80
80
|
await this.handlerManager.each(async (channel, headler) => {
|
|
81
81
|
this.app.logger.debug(`[PubSubManager] subscribe ${channel} added before connected`);
|
|
82
|
-
await this.adapter.subscribe(
|
|
82
|
+
await this.adapter.subscribe(this.getFullChannel(channel), headler);
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
async close() {
|
|
@@ -93,7 +93,7 @@ const _PubSubManager = class _PubSubManager {
|
|
|
93
93
|
const handler = this.handlerManager.set(channel, callback, options);
|
|
94
94
|
if (await this.isConnected()) {
|
|
95
95
|
this.app.logger.debug(`[PubSubManager] subscribe ${channel} added after connected`);
|
|
96
|
-
await this.adapter.subscribe(
|
|
96
|
+
await this.adapter.subscribe(this.getFullChannel(channel), handler);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
async unsubscribe(channel, callback) {
|
|
@@ -101,7 +101,7 @@ const _PubSubManager = class _PubSubManager {
|
|
|
101
101
|
if (!this.adapter || !handler) {
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
|
-
return this.adapter.unsubscribe(
|
|
104
|
+
return this.adapter.unsubscribe(this.getFullChannel(channel), handler);
|
|
105
105
|
}
|
|
106
106
|
async publish(channel, message, options) {
|
|
107
107
|
var _a;
|
|
@@ -116,7 +116,7 @@ const _PubSubManager = class _PubSubManager {
|
|
|
116
116
|
...options,
|
|
117
117
|
message
|
|
118
118
|
});
|
|
119
|
-
await this.adapter.publish(
|
|
119
|
+
await this.adapter.publish(this.getFullChannel(channel), wrappedMessage);
|
|
120
120
|
this.app.logger.trace(`[PubSubManager] published message to channel ${channel}`);
|
|
121
121
|
}
|
|
122
122
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/server",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.51",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "./lib/index.d.ts",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -10,20 +10,20 @@
|
|
|
10
10
|
"@koa/cors": "^5.0.0",
|
|
11
11
|
"@koa/multer": "^3.1.0",
|
|
12
12
|
"@koa/router": "^13.1.0",
|
|
13
|
-
"@nocobase/acl": "2.0.0-alpha.
|
|
14
|
-
"@nocobase/actions": "2.0.0-alpha.
|
|
15
|
-
"@nocobase/auth": "2.0.0-alpha.
|
|
16
|
-
"@nocobase/cache": "2.0.0-alpha.
|
|
17
|
-
"@nocobase/data-source-manager": "2.0.0-alpha.
|
|
18
|
-
"@nocobase/database": "2.0.0-alpha.
|
|
19
|
-
"@nocobase/evaluators": "2.0.0-alpha.
|
|
20
|
-
"@nocobase/lock-manager": "2.0.0-alpha.
|
|
21
|
-
"@nocobase/logger": "2.0.0-alpha.
|
|
22
|
-
"@nocobase/resourcer": "2.0.0-alpha.
|
|
23
|
-
"@nocobase/sdk": "2.0.0-alpha.
|
|
24
|
-
"@nocobase/snowflake-id": "2.0.0-alpha.
|
|
25
|
-
"@nocobase/telemetry": "2.0.0-alpha.
|
|
26
|
-
"@nocobase/utils": "2.0.0-alpha.
|
|
13
|
+
"@nocobase/acl": "2.0.0-alpha.51",
|
|
14
|
+
"@nocobase/actions": "2.0.0-alpha.51",
|
|
15
|
+
"@nocobase/auth": "2.0.0-alpha.51",
|
|
16
|
+
"@nocobase/cache": "2.0.0-alpha.51",
|
|
17
|
+
"@nocobase/data-source-manager": "2.0.0-alpha.51",
|
|
18
|
+
"@nocobase/database": "2.0.0-alpha.51",
|
|
19
|
+
"@nocobase/evaluators": "2.0.0-alpha.51",
|
|
20
|
+
"@nocobase/lock-manager": "2.0.0-alpha.51",
|
|
21
|
+
"@nocobase/logger": "2.0.0-alpha.51",
|
|
22
|
+
"@nocobase/resourcer": "2.0.0-alpha.51",
|
|
23
|
+
"@nocobase/sdk": "2.0.0-alpha.51",
|
|
24
|
+
"@nocobase/snowflake-id": "2.0.0-alpha.51",
|
|
25
|
+
"@nocobase/telemetry": "2.0.0-alpha.51",
|
|
26
|
+
"@nocobase/utils": "2.0.0-alpha.51",
|
|
27
27
|
"@types/decompress": "4.2.7",
|
|
28
28
|
"@types/ini": "^1.3.31",
|
|
29
29
|
"@types/koa-send": "^4.1.3",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"lodash": "^4.17.21",
|
|
50
50
|
"multer": "^1.4.5-lts.2",
|
|
51
51
|
"nanoid": "^3.3.11",
|
|
52
|
+
"p-queue": "^6.6.2",
|
|
52
53
|
"semver": "^7.7.1",
|
|
53
54
|
"serve-handler": "^6.1.6",
|
|
54
55
|
"ws": "^8.13.0",
|
|
@@ -59,5 +60,5 @@
|
|
|
59
60
|
"@types/serve-handler": "^6.1.1",
|
|
60
61
|
"@types/ws": "^8.5.5"
|
|
61
62
|
},
|
|
62
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "a1e34dd97f370d54f3d80a6b83ab7ddb9c72dc18"
|
|
63
64
|
}
|
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
import { QueueEventOptions, QueueMessageOptions } from './event-queue';
|
|
11
|
-
export interface BackgroundJobManagerOptions {
|
|
12
|
-
channel?: string;
|
|
13
|
-
}
|
|
14
|
-
type BackgroundJobEventOptions = Pick<QueueEventOptions, 'process' | 'idle'>;
|
|
15
|
-
declare class BackgroundJobManager {
|
|
16
|
-
private app;
|
|
17
|
-
private options;
|
|
18
|
-
static DEFAULT_CHANNEL: string;
|
|
19
|
-
private subscriptions;
|
|
20
|
-
private processing;
|
|
21
|
-
private get channel();
|
|
22
|
-
private onAfterStart;
|
|
23
|
-
private onBeforeStop;
|
|
24
|
-
private process;
|
|
25
|
-
constructor(app: Application, options?: BackgroundJobManagerOptions);
|
|
26
|
-
private get idle();
|
|
27
|
-
/**
|
|
28
|
-
* 订阅指定主题的任务处理器
|
|
29
|
-
* @param options 订阅选项
|
|
30
|
-
*/
|
|
31
|
-
subscribe(topic: string, options: BackgroundJobEventOptions): void;
|
|
32
|
-
/**
|
|
33
|
-
* 取消订阅指定主题
|
|
34
|
-
* @param topic 主题名称
|
|
35
|
-
*/
|
|
36
|
-
unsubscribe(topic: string): void;
|
|
37
|
-
publish(topic: string, payload: any, options?: QueueMessageOptions): Promise<void>;
|
|
38
|
-
}
|
|
39
|
-
export { BackgroundJobManager };
|
|
40
|
-
export default BackgroundJobManager;
|
|
@@ -1,111 +0,0 @@
|
|
|
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 __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
15
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
16
|
-
var __export = (target, all) => {
|
|
17
|
-
for (var name in all)
|
|
18
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
-
};
|
|
20
|
-
var __copyProps = (to, from, except, desc) => {
|
|
21
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
-
for (let key of __getOwnPropNames(from))
|
|
23
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
-
}
|
|
26
|
-
return to;
|
|
27
|
-
};
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
30
|
-
var background_job_manager_exports = {};
|
|
31
|
-
__export(background_job_manager_exports, {
|
|
32
|
-
BackgroundJobManager: () => BackgroundJobManager,
|
|
33
|
-
default: () => background_job_manager_default
|
|
34
|
-
});
|
|
35
|
-
module.exports = __toCommonJS(background_job_manager_exports);
|
|
36
|
-
const _BackgroundJobManager = class _BackgroundJobManager {
|
|
37
|
-
constructor(app, options = {}) {
|
|
38
|
-
this.app = app;
|
|
39
|
-
this.options = options;
|
|
40
|
-
this.app.on("afterStart", this.onAfterStart);
|
|
41
|
-
this.app.on("beforeStop", this.onBeforeStop);
|
|
42
|
-
}
|
|
43
|
-
subscriptions = /* @__PURE__ */ new Map();
|
|
44
|
-
// topic -> handler
|
|
45
|
-
processing = null;
|
|
46
|
-
get channel() {
|
|
47
|
-
return this.options.channel ?? _BackgroundJobManager.DEFAULT_CHANNEL;
|
|
48
|
-
}
|
|
49
|
-
onAfterStart = /* @__PURE__ */ __name(() => {
|
|
50
|
-
this.app.eventQueue.subscribe(this.channel, {
|
|
51
|
-
idle: /* @__PURE__ */ __name(() => this.idle, "idle"),
|
|
52
|
-
process: this.process
|
|
53
|
-
});
|
|
54
|
-
}, "onAfterStart");
|
|
55
|
-
onBeforeStop = /* @__PURE__ */ __name(() => {
|
|
56
|
-
this.app.eventQueue.unsubscribe(this.channel);
|
|
57
|
-
}, "onBeforeStop");
|
|
58
|
-
process = /* @__PURE__ */ __name(async ({ topic, payload }, options) => {
|
|
59
|
-
const event = this.subscriptions.get(topic);
|
|
60
|
-
if (!event) {
|
|
61
|
-
this.app.logger.warn(`No handler found for topic: ${topic}, event skipped.`);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
this.processing = event.process(payload, options);
|
|
65
|
-
try {
|
|
66
|
-
await this.processing;
|
|
67
|
-
this.app.logger.debug(`Completed background job ${topic}:${options.id}`);
|
|
68
|
-
} catch (error) {
|
|
69
|
-
this.app.logger.error(`Failed to process background job ${topic}:${options.id}`, error);
|
|
70
|
-
throw error;
|
|
71
|
-
} finally {
|
|
72
|
-
this.processing = null;
|
|
73
|
-
}
|
|
74
|
-
}, "process");
|
|
75
|
-
get idle() {
|
|
76
|
-
return !this.processing && [...this.subscriptions.values()].every((event) => event.idle());
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* 订阅指定主题的任务处理器
|
|
80
|
-
* @param options 订阅选项
|
|
81
|
-
*/
|
|
82
|
-
subscribe(topic, options) {
|
|
83
|
-
if (this.subscriptions.has(topic)) {
|
|
84
|
-
this.app.logger.warn(`Topic "${topic}" already has a handler, skip...`);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
this.subscriptions.set(topic, options);
|
|
88
|
-
this.app.logger.debug(`Subscribed to background job topic: ${topic}`);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* 取消订阅指定主题
|
|
92
|
-
* @param topic 主题名称
|
|
93
|
-
*/
|
|
94
|
-
unsubscribe(topic) {
|
|
95
|
-
if (this.subscriptions.has(topic)) {
|
|
96
|
-
this.subscriptions.delete(topic);
|
|
97
|
-
this.app.logger.debug(`Unsubscribed from background job topic: ${topic}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async publish(topic, payload, options) {
|
|
101
|
-
await this.app.eventQueue.publish(this.channel, { topic, payload }, options);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
__name(_BackgroundJobManager, "BackgroundJobManager");
|
|
105
|
-
__publicField(_BackgroundJobManager, "DEFAULT_CHANNEL", "background-jobs");
|
|
106
|
-
let BackgroundJobManager = _BackgroundJobManager;
|
|
107
|
-
var background_job_manager_default = BackgroundJobManager;
|
|
108
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
109
|
-
0 && (module.exports = {
|
|
110
|
-
BackgroundJobManager
|
|
111
|
-
});
|