@ruiapp/rapid-core 0.9.6 → 0.9.8
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/core/routes/healthz.d.ts +12 -0
- package/dist/core/routes/index.d.ts +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +184 -41
- package/dist/types.d.ts +2 -0
- package/dist/utilities/fsUtility.d.ts +8 -0
- package/package.json +1 -1
- package/src/core/routes/healthz.ts +20 -0
- package/src/core/routes/index.ts +3 -0
- package/src/core/routesBuilder.ts +29 -11
- package/src/index.ts +1 -0
- package/src/plugins/fileManage/actionHandlers/uploadFile.ts +2 -2
- package/src/server.ts +6 -4
- package/src/types.ts +2 -0
- package/src/utilities/fsUtility.ts +76 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ActionHandlerContext } from "../actionHandler";
|
|
2
|
+
declare const _default: {
|
|
3
|
+
namespace: string;
|
|
4
|
+
name: string;
|
|
5
|
+
code: string;
|
|
6
|
+
type: "RESTful";
|
|
7
|
+
method: "GET";
|
|
8
|
+
endpoint: string;
|
|
9
|
+
handler: typeof handler;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
12
|
+
export declare function handler(ctx: ActionHandlerContext): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { default as EntityManager } from "./dataAccess/entityManager";
|
|
|
13
13
|
export * from "./dataAccess/entityManager";
|
|
14
14
|
export * from "./utilities/accessControlUtility";
|
|
15
15
|
export * from "./utilities/entityUtility";
|
|
16
|
+
export * from "./utilities/fsUtility";
|
|
16
17
|
export * from "./utilities/jwtUtility";
|
|
17
18
|
export * from "./utilities/timeUtility";
|
|
18
19
|
export * from "./utilities/passwordUtility";
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,11 @@ var events = require('events');
|
|
|
7
7
|
var Router = require('koa-tree-router');
|
|
8
8
|
var qs = require('qs');
|
|
9
9
|
var dayjs = require('dayjs');
|
|
10
|
+
var fs = require('fs');
|
|
11
|
+
var path = require('path');
|
|
10
12
|
var jsonwebtoken = require('jsonwebtoken');
|
|
11
13
|
var crypto = require('crypto');
|
|
12
14
|
var bcrypt = require('bcryptjs');
|
|
13
|
-
var path = require('path');
|
|
14
|
-
var fs = require('fs');
|
|
15
15
|
var uuid = require('uuid');
|
|
16
16
|
var nodemailer = require('nodemailer');
|
|
17
17
|
var cron = require('cron');
|
|
@@ -22,10 +22,10 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
22
22
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
23
23
|
var qs__default = /*#__PURE__*/_interopDefaultLegacy(qs);
|
|
24
24
|
var dayjs__default = /*#__PURE__*/_interopDefaultLegacy(dayjs);
|
|
25
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
26
|
+
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
25
27
|
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
26
28
|
var bcrypt__default = /*#__PURE__*/_interopDefaultLegacy(bcrypt);
|
|
27
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
28
|
-
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
29
29
|
var nodemailer__default = /*#__PURE__*/_interopDefaultLegacy(nodemailer);
|
|
30
30
|
|
|
31
31
|
function fixBigIntJSONSerialize() {
|
|
@@ -1032,17 +1032,37 @@ async function buildRoutes(server, applicationConfig) {
|
|
|
1032
1032
|
input,
|
|
1033
1033
|
};
|
|
1034
1034
|
await server.beforeRunRouteActions(handlerContext);
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
throw new Error("Unknown handler: " + actionCode);
|
|
1035
|
+
let handler = routeConfig.handler;
|
|
1036
|
+
if (handler) {
|
|
1037
|
+
if (lodash.isString(handler)) {
|
|
1038
|
+
handler = new Function(`return (${routeConfig.handler})`);
|
|
1040
1039
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1040
|
+
if (lodash.isFunction(handler)) {
|
|
1041
|
+
const result = handler(handlerContext);
|
|
1042
|
+
if (result instanceof Promise) {
|
|
1043
|
+
await result;
|
|
1044
|
+
}
|
|
1045
1045
|
}
|
|
1046
|
+
else {
|
|
1047
|
+
throw new Error(`Invalid handler for route ${routeConfig.code}: ${routeConfig.handler}`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
else if (routeConfig.actions) {
|
|
1051
|
+
for (const actionConfig of routeConfig.actions) {
|
|
1052
|
+
const actionCode = actionConfig.code;
|
|
1053
|
+
const handler = server.getActionHandlerByCode(actionCode);
|
|
1054
|
+
if (!handler) {
|
|
1055
|
+
throw new Error("Unknown handler: " + actionCode);
|
|
1056
|
+
}
|
|
1057
|
+
await server.beforeRunActionHandler(handlerContext, actionConfig);
|
|
1058
|
+
const result = handler(handlerContext, actionConfig.config);
|
|
1059
|
+
if (result instanceof Promise) {
|
|
1060
|
+
await result;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
throw new Error(`No handler or actions defined for route ${routeConfig.code}`);
|
|
1046
1066
|
}
|
|
1047
1067
|
if (!isNullOrUndefined(handlerContext.output)) {
|
|
1048
1068
|
routerContext.json(handlerContext.output, handlerContext.status);
|
|
@@ -3963,6 +3983,21 @@ class EntityManager {
|
|
|
3963
3983
|
}
|
|
3964
3984
|
}
|
|
3965
3985
|
|
|
3986
|
+
var healthz = {
|
|
3987
|
+
namespace: "sys",
|
|
3988
|
+
name: "sys.healthz",
|
|
3989
|
+
code: "sys.healthz",
|
|
3990
|
+
type: "RESTful",
|
|
3991
|
+
method: "GET",
|
|
3992
|
+
endpoint: "/healthz",
|
|
3993
|
+
handler: handler$x,
|
|
3994
|
+
};
|
|
3995
|
+
async function handler$x(ctx) {
|
|
3996
|
+
ctx.output = {};
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
var coreRoutes = [healthz];
|
|
4000
|
+
|
|
3966
4001
|
class RapidServer {
|
|
3967
4002
|
#logger;
|
|
3968
4003
|
#facilityFactories;
|
|
@@ -4237,7 +4272,12 @@ class RapidServer {
|
|
|
4237
4272
|
this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
|
|
4238
4273
|
}
|
|
4239
4274
|
}
|
|
4275
|
+
this.#applicationConfig = lodash.cloneDeep(this.#bootstrapApplicationConfig);
|
|
4276
|
+
this.appendApplicationConfig({
|
|
4277
|
+
routes: coreRoutes,
|
|
4278
|
+
});
|
|
4240
4279
|
await this.configureApplication();
|
|
4280
|
+
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
4241
4281
|
if (!this.#disableCronJobs) {
|
|
4242
4282
|
await pluginManager.registerCronJobs();
|
|
4243
4283
|
}
|
|
@@ -4245,7 +4285,6 @@ class RapidServer {
|
|
|
4245
4285
|
await pluginManager.onApplicationReady(this.#applicationConfig);
|
|
4246
4286
|
}
|
|
4247
4287
|
async configureApplication() {
|
|
4248
|
-
this.#applicationConfig = lodash.cloneDeep(this.#bootstrapApplicationConfig);
|
|
4249
4288
|
const pluginManager = this.#pluginManager;
|
|
4250
4289
|
await pluginManager.onLoadingApplication(this.#applicationConfig);
|
|
4251
4290
|
await pluginManager.configureModels(this.#applicationConfig);
|
|
@@ -4254,7 +4293,6 @@ class RapidServer {
|
|
|
4254
4293
|
await pluginManager.configureRoutes(this.#applicationConfig);
|
|
4255
4294
|
// TODO: check application configuration.
|
|
4256
4295
|
await pluginManager.onApplicationLoaded(this.#applicationConfig);
|
|
4257
|
-
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
4258
4296
|
}
|
|
4259
4297
|
registerFacilityFactory(factory) {
|
|
4260
4298
|
this.#facilityFactories.set(factory.name, factory);
|
|
@@ -4903,6 +4941,128 @@ function getEntityRelationTargetId(entity, propName, targetColumnName) {
|
|
|
4903
4941
|
}
|
|
4904
4942
|
}
|
|
4905
4943
|
|
|
4944
|
+
async function readFile(path) {
|
|
4945
|
+
return new Promise((resolve, reject) => {
|
|
4946
|
+
fs__default["default"].readFile(path, null, (err, data) => {
|
|
4947
|
+
if (err) {
|
|
4948
|
+
reject(err);
|
|
4949
|
+
}
|
|
4950
|
+
else {
|
|
4951
|
+
resolve(data);
|
|
4952
|
+
}
|
|
4953
|
+
});
|
|
4954
|
+
});
|
|
4955
|
+
}
|
|
4956
|
+
async function copyFile(fromPath, toPath) {
|
|
4957
|
+
return new Promise((resolve, reject) => {
|
|
4958
|
+
fs__default["default"].copyFile(fromPath, toPath, (err) => {
|
|
4959
|
+
if (err) {
|
|
4960
|
+
reject(err);
|
|
4961
|
+
}
|
|
4962
|
+
else {
|
|
4963
|
+
resolve();
|
|
4964
|
+
}
|
|
4965
|
+
});
|
|
4966
|
+
});
|
|
4967
|
+
}
|
|
4968
|
+
async function removeFile(path) {
|
|
4969
|
+
return new Promise((resolve, reject) => {
|
|
4970
|
+
fs__default["default"].rm(path, (err) => {
|
|
4971
|
+
if (err) {
|
|
4972
|
+
reject(err);
|
|
4973
|
+
}
|
|
4974
|
+
else {
|
|
4975
|
+
resolve();
|
|
4976
|
+
}
|
|
4977
|
+
});
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4980
|
+
async function moveFile(fromPath, toPath) {
|
|
4981
|
+
return new Promise((resolve, reject) => {
|
|
4982
|
+
fs__default["default"].rename(fromPath, toPath, (err) => {
|
|
4983
|
+
if (err) {
|
|
4984
|
+
reject(err);
|
|
4985
|
+
}
|
|
4986
|
+
else {
|
|
4987
|
+
resolve();
|
|
4988
|
+
}
|
|
4989
|
+
});
|
|
4990
|
+
});
|
|
4991
|
+
}
|
|
4992
|
+
async function appendFile(path, data) {
|
|
4993
|
+
return new Promise((resolve, reject) => {
|
|
4994
|
+
fs__default["default"].appendFile(path, Buffer.from(data), (err) => {
|
|
4995
|
+
if (err) {
|
|
4996
|
+
reject(err);
|
|
4997
|
+
}
|
|
4998
|
+
else {
|
|
4999
|
+
resolve();
|
|
5000
|
+
}
|
|
5001
|
+
});
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
5004
|
+
async function writeFile(path, data) {
|
|
5005
|
+
return new Promise((resolve, reject) => {
|
|
5006
|
+
fs__default["default"].writeFile(path, Buffer.from(data), (err) => {
|
|
5007
|
+
if (err) {
|
|
5008
|
+
reject(err);
|
|
5009
|
+
}
|
|
5010
|
+
else {
|
|
5011
|
+
resolve();
|
|
5012
|
+
}
|
|
5013
|
+
});
|
|
5014
|
+
});
|
|
5015
|
+
}
|
|
5016
|
+
function ensureDirectoryExists(dirPath) {
|
|
5017
|
+
if (!fs__default["default"].existsSync(dirPath)) {
|
|
5018
|
+
const parentDirPath = path__default["default"].dirname(dirPath);
|
|
5019
|
+
if (parentDirPath == dirPath) {
|
|
5020
|
+
return;
|
|
5021
|
+
}
|
|
5022
|
+
ensureDirectoryExists(parentDirPath);
|
|
5023
|
+
fs__default["default"].mkdirSync(dirPath);
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
function enumFileBaseNamesInDirectory(options) {
|
|
5027
|
+
const { dirPath, prefix, fileNameFilter } = options;
|
|
5028
|
+
let fileNames = [];
|
|
5029
|
+
let resolvedDirPath = dirPath;
|
|
5030
|
+
const isRelative = dirPath.startsWith(".") || dirPath.startsWith("..");
|
|
5031
|
+
if (isRelative) {
|
|
5032
|
+
resolvedDirPath = path__default["default"].join(process.cwd(), dirPath);
|
|
5033
|
+
}
|
|
5034
|
+
if (!fs__default["default"].existsSync(resolvedDirPath)) {
|
|
5035
|
+
console.warn(`Directory '${resolvedDirPath}' not found.`);
|
|
5036
|
+
return [];
|
|
5037
|
+
}
|
|
5038
|
+
const files = fs__default["default"].readdirSync(resolvedDirPath);
|
|
5039
|
+
for (const fileName of files) {
|
|
5040
|
+
const filePathName = path__default["default"].join(resolvedDirPath, fileName);
|
|
5041
|
+
const fileStat = fs__default["default"].statSync(filePathName);
|
|
5042
|
+
if (fileStat.isDirectory()) {
|
|
5043
|
+
fileNames = fileNames.concat(enumFileBaseNamesInDirectory({
|
|
5044
|
+
dirPath: filePathName,
|
|
5045
|
+
prefix: prefix ? `${prefix}/${fileName}` : fileName,
|
|
5046
|
+
fileNameFilter,
|
|
5047
|
+
}));
|
|
5048
|
+
}
|
|
5049
|
+
else if (fileStat.isFile()) {
|
|
5050
|
+
if (fileNameFilter && !fileNameFilter(fileName)) {
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
5053
|
+
const baseName = path__default["default"].parse(fileName).name;
|
|
5054
|
+
if (prefix) {
|
|
5055
|
+
fileNames.push(`${prefix}/${baseName}`);
|
|
5056
|
+
}
|
|
5057
|
+
else {
|
|
5058
|
+
fileNames.push(baseName);
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
fileNames.sort();
|
|
5063
|
+
return fileNames;
|
|
5064
|
+
}
|
|
5065
|
+
|
|
4906
5066
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
4907
5067
|
// This module is browser compatible.
|
|
4908
5068
|
/**
|
|
@@ -7335,31 +7495,6 @@ class AuthPlugin {
|
|
|
7335
7495
|
}
|
|
7336
7496
|
}
|
|
7337
7497
|
|
|
7338
|
-
async function readFile(path) {
|
|
7339
|
-
return new Promise((resolve, reject) => {
|
|
7340
|
-
fs__default["default"].readFile(path, null, (err, data) => {
|
|
7341
|
-
if (err) {
|
|
7342
|
-
reject(err);
|
|
7343
|
-
}
|
|
7344
|
-
else {
|
|
7345
|
-
resolve(data);
|
|
7346
|
-
}
|
|
7347
|
-
});
|
|
7348
|
-
});
|
|
7349
|
-
}
|
|
7350
|
-
async function appendFile(path, data) {
|
|
7351
|
-
return new Promise((resolve, reject) => {
|
|
7352
|
-
fs__default["default"].appendFile(path, Buffer.from(data), (err) => {
|
|
7353
|
-
if (err) {
|
|
7354
|
-
reject(err);
|
|
7355
|
-
}
|
|
7356
|
-
else {
|
|
7357
|
-
resolve();
|
|
7358
|
-
}
|
|
7359
|
-
});
|
|
7360
|
-
});
|
|
7361
|
-
}
|
|
7362
|
-
|
|
7363
7498
|
function getFileBaseName(pathname) {
|
|
7364
7499
|
const extName = path__default["default"].extname(pathname);
|
|
7365
7500
|
return path__default["default"].basename(pathname, extName);
|
|
@@ -7473,7 +7608,7 @@ async function handler$8(plugin, ctx, options) {
|
|
|
7473
7608
|
const fileKey = `${uuid.v1()}${extName}`;
|
|
7474
7609
|
const filePathName = path__default["default"].join(server.config.localFileStoragePath, fileKey);
|
|
7475
7610
|
const fileBuffer = await file.arrayBuffer();
|
|
7476
|
-
await
|
|
7611
|
+
await writeFile(filePathName, fileBuffer);
|
|
7477
7612
|
ctx.output = { ok: true, fileKey };
|
|
7478
7613
|
}
|
|
7479
7614
|
|
|
@@ -9668,12 +9803,16 @@ exports.ServerOperationPlugin = ServerOperationPlugin;
|
|
|
9668
9803
|
exports.SettingPlugin = SettingPlugin;
|
|
9669
9804
|
exports.StateMachinePlugin = StateMachinePlugin;
|
|
9670
9805
|
exports.WebhooksPlugin = WebhooksPlugin;
|
|
9806
|
+
exports.appendFile = appendFile;
|
|
9671
9807
|
exports.bootstrapApplicationConfig = bootstrapApplicationConfig$1;
|
|
9808
|
+
exports.copyFile = copyFile;
|
|
9672
9809
|
exports.createJwt = createJwt;
|
|
9673
9810
|
exports.decodeJwt = decodeJwt;
|
|
9674
9811
|
exports.deleteCookie = deleteCookie;
|
|
9675
9812
|
exports.detectChangedFieldsOfEntity = detectChangedFieldsOfEntity;
|
|
9813
|
+
exports.ensureDirectoryExists = ensureDirectoryExists;
|
|
9676
9814
|
exports.entityHelper = entityHelper;
|
|
9815
|
+
exports.enumFileBaseNamesInDirectory = enumFileBaseNamesInDirectory;
|
|
9677
9816
|
exports.formatDateTimeWithTimezone = formatDateTimeWithTimezone;
|
|
9678
9817
|
exports.generateJwtSecretKey = generateJwtSecretKey;
|
|
9679
9818
|
exports.generatePasswordHash = generatePasswordHash;
|
|
@@ -9687,8 +9826,12 @@ exports.isAccessAllowed = isAccessAllowed;
|
|
|
9687
9826
|
exports.licenseHelper = licenseHelper;
|
|
9688
9827
|
exports.mapDbRowToEntity = mapDbRowToEntity;
|
|
9689
9828
|
exports.metaHelper = metaHelper;
|
|
9829
|
+
exports.moveFile = moveFile;
|
|
9830
|
+
exports.readFile = readFile;
|
|
9831
|
+
exports.removeFile = removeFile;
|
|
9690
9832
|
exports.setCookie = setCookie;
|
|
9691
9833
|
exports.tryValidateLicense = tryValidateLicense;
|
|
9692
9834
|
exports.validateLicense = validateLicense;
|
|
9693
9835
|
exports.validatePassword = validatePassword;
|
|
9694
9836
|
exports.verifyJwt = verifyJwt;
|
|
9837
|
+
exports.writeFile = writeFile;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActionHandlerContext } from "./core/actionHandler";
|
|
1
2
|
import { RouteContext } from "./core/routeContext";
|
|
2
3
|
import { IRpdServer, RapidPlugin } from "./core/server";
|
|
3
4
|
import { ColumnSelectOptions, CountRowOptions, FindRowOptions, RowFilterOptions } from "./dataAccess/dataAccessTypes";
|
|
@@ -483,6 +484,7 @@ export interface RpdRoute {
|
|
|
483
484
|
method: RpdHttpMethod;
|
|
484
485
|
endpoint: string;
|
|
485
486
|
actions?: RpdRouteActionConfig[];
|
|
487
|
+
handler?: string | ((ctx: ActionHandlerContext) => Promise<void>);
|
|
486
488
|
}
|
|
487
489
|
export type RpdHttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
488
490
|
export interface RpdRouteActionConfig {
|
|
@@ -4,3 +4,11 @@ export declare function copyFile(fromPath: string, toPath: string): Promise<void
|
|
|
4
4
|
export declare function removeFile(path: string): Promise<void>;
|
|
5
5
|
export declare function moveFile(fromPath: string, toPath: string): Promise<void>;
|
|
6
6
|
export declare function appendFile(path: string, data: ArrayBuffer): Promise<void>;
|
|
7
|
+
export declare function writeFile(path: string, data: ArrayBuffer): Promise<void>;
|
|
8
|
+
export declare function ensureDirectoryExists(dirPath: string): void;
|
|
9
|
+
export type EnumFileBaseNamesOptions = {
|
|
10
|
+
dirPath: string;
|
|
11
|
+
prefix?: string;
|
|
12
|
+
fileNameFilter?: (fileName: string) => boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function enumFileBaseNamesInDirectory(options: EnumFileBaseNamesOptions): string[];
|
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { RpdRoute } from "~/types";
|
|
2
|
+
import { ActionHandlerContext } from "../actionHandler";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
namespace: "sys",
|
|
6
|
+
name: "sys.healthz",
|
|
7
|
+
code: "sys.healthz",
|
|
8
|
+
type: "RESTful",
|
|
9
|
+
method: "GET",
|
|
10
|
+
endpoint: "/healthz",
|
|
11
|
+
handler,
|
|
12
|
+
} satisfies RpdRoute;
|
|
13
|
+
|
|
14
|
+
export async function handler(ctx: ActionHandlerContext) {
|
|
15
|
+
const { server, input, routerContext: routeContext, logger } = ctx;
|
|
16
|
+
const { response } = routeContext;
|
|
17
|
+
const {} = input;
|
|
18
|
+
|
|
19
|
+
ctx.output = {};
|
|
20
|
+
}
|
|
@@ -5,7 +5,7 @@ import { IRpdServer } from "~/core/server";
|
|
|
5
5
|
import { RpdApplicationConfig } from "~/types";
|
|
6
6
|
import { isNullOrUndefined } from "~/utilities/typeUtility";
|
|
7
7
|
import { Next, RouteContext } from "./routeContext";
|
|
8
|
-
import { cloneDeep } from "lodash";
|
|
8
|
+
import { cloneDeep, isFunction, isString } from "lodash";
|
|
9
9
|
|
|
10
10
|
export async function buildRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig) {
|
|
11
11
|
const logger = server.getLogger();
|
|
@@ -67,19 +67,37 @@ export async function buildRoutes(server: IRpdServer, applicationConfig: RpdAppl
|
|
|
67
67
|
|
|
68
68
|
await server.beforeRunRouteActions(handlerContext);
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
throw new Error("Unknown handler: " + actionCode);
|
|
70
|
+
let handler = routeConfig.handler as any;
|
|
71
|
+
if (handler) {
|
|
72
|
+
if (isString(handler)) {
|
|
73
|
+
handler = new Function(`return (${routeConfig.handler})`) as any;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
if (isFunction(handler)) {
|
|
77
|
+
const result = handler(handlerContext);
|
|
78
|
+
if (result instanceof Promise) {
|
|
79
|
+
await result;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
throw new Error(`Invalid handler for route ${routeConfig.code}: ${routeConfig.handler}`);
|
|
83
|
+
}
|
|
84
|
+
} else if (routeConfig.actions) {
|
|
85
|
+
for (const actionConfig of routeConfig.actions) {
|
|
86
|
+
const actionCode = actionConfig.code;
|
|
87
|
+
const handler = server.getActionHandlerByCode(actionCode);
|
|
88
|
+
if (!handler) {
|
|
89
|
+
throw new Error("Unknown handler: " + actionCode);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await server.beforeRunActionHandler(handlerContext, actionConfig);
|
|
93
|
+
|
|
94
|
+
const result = handler(handlerContext, actionConfig.config);
|
|
95
|
+
if (result instanceof Promise) {
|
|
96
|
+
await result;
|
|
97
|
+
}
|
|
82
98
|
}
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error(`No handler or actions defined for route ${routeConfig.code}`);
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
if (!isNullOrUndefined(handlerContext.output)) {
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export * from "./dataAccess/entityManager";
|
|
|
20
20
|
|
|
21
21
|
export * from "./utilities/accessControlUtility";
|
|
22
22
|
export * from "./utilities/entityUtility";
|
|
23
|
+
export * from "./utilities/fsUtility";
|
|
23
24
|
export * from "./utilities/jwtUtility";
|
|
24
25
|
export * from "./utilities/timeUtility";
|
|
25
26
|
export * from "./utilities/passwordUtility";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v1 as uuidv1 } from "uuid";
|
|
2
|
-
import {
|
|
2
|
+
import { writeFile } from "~/utilities/fsUtility";
|
|
3
3
|
import { ActionHandlerContext } from "~/core/actionHandler";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { isArray } from "lodash";
|
|
@@ -27,7 +27,7 @@ export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, op
|
|
|
27
27
|
const filePathName = path.join(server.config.localFileStoragePath, fileKey);
|
|
28
28
|
|
|
29
29
|
const fileBuffer = await file.arrayBuffer();
|
|
30
|
-
await
|
|
30
|
+
await writeFile(filePathName, fileBuffer);
|
|
31
31
|
|
|
32
32
|
ctx.output = { ok: true, fileKey };
|
|
33
33
|
}
|
package/src/server.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { bind, cloneDeep, find, forEach, isString, merge, omit } from "lodash";
|
|
|
35
35
|
import { Logger } from "./facilities/log/LogFacility";
|
|
36
36
|
import { FacilityFactory } from "./core/facility";
|
|
37
37
|
import { CronJobConfiguration } from "./types/cron-job-types";
|
|
38
|
+
import coreRoutes from "./core/routes";
|
|
38
39
|
|
|
39
40
|
export interface InitServerOptions {
|
|
40
41
|
logger: Logger;
|
|
@@ -367,7 +368,12 @@ export class RapidServer implements IRpdServer {
|
|
|
367
368
|
}
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
this.#applicationConfig = cloneDeep(this.#bootstrapApplicationConfig) as RpdApplicationConfig;
|
|
372
|
+
this.appendApplicationConfig({
|
|
373
|
+
routes: coreRoutes,
|
|
374
|
+
});
|
|
370
375
|
await this.configureApplication();
|
|
376
|
+
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
371
377
|
|
|
372
378
|
if (!this.#disableCronJobs) {
|
|
373
379
|
await pluginManager.registerCronJobs();
|
|
@@ -378,8 +384,6 @@ export class RapidServer implements IRpdServer {
|
|
|
378
384
|
}
|
|
379
385
|
|
|
380
386
|
async configureApplication() {
|
|
381
|
-
this.#applicationConfig = cloneDeep(this.#bootstrapApplicationConfig) as RpdApplicationConfig;
|
|
382
|
-
|
|
383
387
|
const pluginManager = this.#pluginManager;
|
|
384
388
|
await pluginManager.onLoadingApplication(this.#applicationConfig);
|
|
385
389
|
await pluginManager.configureModels(this.#applicationConfig);
|
|
@@ -390,8 +394,6 @@ export class RapidServer implements IRpdServer {
|
|
|
390
394
|
// TODO: check application configuration.
|
|
391
395
|
|
|
392
396
|
await pluginManager.onApplicationLoaded(this.#applicationConfig);
|
|
393
|
-
|
|
394
|
-
this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
|
|
395
397
|
}
|
|
396
398
|
|
|
397
399
|
registerFacilityFactory(factory: FacilityFactory) {
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActionHandlerContext } from "./core/actionHandler";
|
|
1
2
|
import { RouteContext } from "./core/routeContext";
|
|
2
3
|
import { IRpdServer, RapidPlugin } from "./core/server";
|
|
3
4
|
import { ColumnSelectOptions, CountRowOptions, FindRowOptions, RowFilterOptions } from "./dataAccess/dataAccessTypes";
|
|
@@ -586,6 +587,7 @@ export interface RpdRoute {
|
|
|
586
587
|
method: RpdHttpMethod;
|
|
587
588
|
endpoint: string;
|
|
588
589
|
actions?: RpdRouteActionConfig[];
|
|
590
|
+
handler?: string | ((ctx: ActionHandlerContext) => Promise<void>);
|
|
589
591
|
}
|
|
590
592
|
|
|
591
593
|
export type RpdHttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
2
3
|
|
|
3
4
|
export async function readFile(path: string): Promise<Buffer> {
|
|
4
5
|
return new Promise((resolve, reject) => {
|
|
@@ -59,3 +60,78 @@ export async function appendFile(path: string, data: ArrayBuffer): Promise<void>
|
|
|
59
60
|
});
|
|
60
61
|
});
|
|
61
62
|
}
|
|
63
|
+
|
|
64
|
+
export async function writeFile(path: string, data: ArrayBuffer): Promise<void> {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
fs.writeFile(path, Buffer.from(data), (err) => {
|
|
67
|
+
if (err) {
|
|
68
|
+
reject(err);
|
|
69
|
+
} else {
|
|
70
|
+
resolve();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function ensureDirectoryExists(dirPath: string) {
|
|
77
|
+
if (!fs.existsSync(dirPath)) {
|
|
78
|
+
const parentDirPath = path.dirname(dirPath);
|
|
79
|
+
if (parentDirPath == dirPath) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
ensureDirectoryExists(parentDirPath);
|
|
83
|
+
|
|
84
|
+
fs.mkdirSync(dirPath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type EnumFileBaseNamesOptions = {
|
|
89
|
+
dirPath: string;
|
|
90
|
+
prefix?: string;
|
|
91
|
+
fileNameFilter?: (fileName: string) => boolean;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export function enumFileBaseNamesInDirectory(options: EnumFileBaseNamesOptions): string[] {
|
|
95
|
+
const { dirPath, prefix, fileNameFilter } = options;
|
|
96
|
+
let fileNames = [];
|
|
97
|
+
|
|
98
|
+
let resolvedDirPath = dirPath;
|
|
99
|
+
const isRelative = dirPath.startsWith(".") || dirPath.startsWith("..");
|
|
100
|
+
if (isRelative) {
|
|
101
|
+
resolvedDirPath = path.join(process.cwd(), dirPath);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!fs.existsSync(resolvedDirPath)) {
|
|
105
|
+
console.warn(`Directory '${resolvedDirPath}' not found.`);
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const files = fs.readdirSync(resolvedDirPath);
|
|
110
|
+
for (const fileName of files) {
|
|
111
|
+
const filePathName = path.join(resolvedDirPath, fileName);
|
|
112
|
+
const fileStat = fs.statSync(filePathName);
|
|
113
|
+
if (fileStat.isDirectory()) {
|
|
114
|
+
fileNames = fileNames.concat(
|
|
115
|
+
enumFileBaseNamesInDirectory({
|
|
116
|
+
dirPath: filePathName,
|
|
117
|
+
prefix: prefix ? `${prefix}/${fileName}` : fileName,
|
|
118
|
+
fileNameFilter,
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
} else if (fileStat.isFile()) {
|
|
122
|
+
if (fileNameFilter && !fileNameFilter(fileName)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const baseName = path.parse(fileName).name;
|
|
127
|
+
if (prefix) {
|
|
128
|
+
fileNames.push(`${prefix}/${baseName}`);
|
|
129
|
+
} else {
|
|
130
|
+
fileNames.push(baseName);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
fileNames.sort();
|
|
136
|
+
return fileNames;
|
|
137
|
+
}
|