@ruiapp/rapid-core 0.9.2 → 0.9.3
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/pluginManager.d.ts +3 -1
- package/dist/core/routeContext.d.ts +2 -2
- package/dist/core/server.d.ts +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +90 -54
- package/dist/server.d.ts +2 -1
- package/dist/utilities/passwordUtility.d.ts +14 -0
- package/package.json +2 -2
- package/src/core/pluginManager.ts +10 -1
- package/src/core/routeContext.ts +2 -2
- package/src/core/routesBuilder.ts +2 -0
- package/src/core/server.ts +4 -0
- package/src/index.ts +1 -0
- package/src/plugins/auth/actionHandlers/changePassword.ts +3 -5
- package/src/plugins/auth/actionHandlers/createSession.ts +2 -3
- package/src/plugins/auth/actionHandlers/resetPassword.ts +2 -4
- package/src/plugins/dataManage/DataManagePlugin.ts +51 -51
- package/src/server.ts +5 -0
- package/src/utilities/passwordUtility.ts +26 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateEntityOptions, RpdApplicationConfig, RpdDataModel, UpdateEntityByIdOptions } from "../types";
|
|
1
|
+
import { CreateEntityOptions, RpdApplicationConfig, RpdDataModel, RpdRouteActionConfig, UpdateEntityByIdOptions } from "../types";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "./server";
|
|
3
3
|
import { RouteContext } from "./routeContext";
|
|
4
4
|
import { ActionHandlerContext } from "./actionHandler";
|
|
@@ -38,6 +38,8 @@ declare class PluginManager {
|
|
|
38
38
|
onPrepareRouteContext(routeContext: RouteContext): Promise<void>;
|
|
39
39
|
/** 在接收到HTTP请求,执行 actions 前调用。 */
|
|
40
40
|
beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
|
|
41
|
+
/** 在执行 action hanlder 前调用。 */
|
|
42
|
+
beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
|
|
41
43
|
/** 在创建实体前调用。 */
|
|
42
44
|
beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
|
|
43
45
|
/** 在更新实体前调用。 */
|
|
@@ -2,7 +2,7 @@ import { RapidRequest } from "./request";
|
|
|
2
2
|
import { RapidResponse } from "./response";
|
|
3
3
|
import { HttpStatus } from "./http-types";
|
|
4
4
|
import { IRpdServer } from "./server";
|
|
5
|
-
import { IDatabaseAccessor, IDatabaseClient } from "../types";
|
|
5
|
+
import { IDatabaseAccessor, IDatabaseClient, RpdRoute } from "../types";
|
|
6
6
|
export type Next = () => Promise<void>;
|
|
7
7
|
export type TransactionState = "uninited" | "inited" | "started";
|
|
8
8
|
export declare class RouteContext {
|
|
@@ -14,7 +14,7 @@ export declare class RouteContext {
|
|
|
14
14
|
method: string;
|
|
15
15
|
path: string;
|
|
16
16
|
params: Record<string, string>;
|
|
17
|
-
routeConfig:
|
|
17
|
+
routeConfig: RpdRoute;
|
|
18
18
|
static newSystemOperationContext(server: IRpdServer): RouteContext;
|
|
19
19
|
constructor(server: IRpdServer, request?: RapidRequest);
|
|
20
20
|
clone(): RouteContext;
|
package/dist/core/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateEntityOptions, EmitServerEventOptions, EntityWatcherType, GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseClient, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RapidServerConfig, RpdApplicationConfig, RpdDataModel, RpdDataModelProperty, RpdServerEventTypes, UpdateEntityByIdOptions } from "../types";
|
|
1
|
+
import { CreateEntityOptions, EmitServerEventOptions, EntityWatcherType, GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseClient, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RapidServerConfig, RpdApplicationConfig, RpdDataModel, RpdDataModelProperty, RpdRouteActionConfig, RpdServerEventTypes, UpdateEntityByIdOptions } from "../types";
|
|
2
2
|
import { IPluginActionHandler, ActionHandler, ActionHandlerContext } from "./actionHandler";
|
|
3
3
|
import { Next, RouteContext } from "./routeContext";
|
|
4
4
|
import EntityManager from "../dataAccess/entityManager";
|
|
@@ -32,6 +32,7 @@ export interface IRpdServer {
|
|
|
32
32
|
emitEvent<TEventName extends keyof RpdServerEventTypes>(event: EmitServerEventOptions<TEventName>): void;
|
|
33
33
|
handleRequest(request: RapidRequest, next: Next): Promise<Response>;
|
|
34
34
|
beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
|
|
35
|
+
beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
|
|
35
36
|
beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
|
|
36
37
|
beforeUpdateEntity(model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any): Promise<void>;
|
|
37
38
|
registerCronJob(job: CronJobConfiguration): void;
|
|
@@ -108,6 +109,8 @@ export interface RapidPlugin {
|
|
|
108
109
|
onPrepareRouteContext?: (server: IRpdServer, routeContext: RouteContext) => Promise<any>;
|
|
109
110
|
/** 在接收到HTTP请求,执行 actions 前调用。 */
|
|
110
111
|
beforeRunRouteActions?: (server: IRpdServer, handlerContext: ActionHandlerContext) => Promise<any>;
|
|
112
|
+
/** 在执行 action hanlder 前调用。 */
|
|
113
|
+
beforeRunActionHandler?: (server: IRpdServer, handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig) => Promise<any>;
|
|
111
114
|
/** 在创建实体前调用。 */
|
|
112
115
|
beforeCreateEntity?: (server: IRpdServer, model: RpdDataModel, options: CreateEntityOptions) => Promise<any>;
|
|
113
116
|
/** 在更新实体前调用。 */
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./utilities/accessControlUtility";
|
|
|
15
15
|
export * from "./utilities/entityUtility";
|
|
16
16
|
export * from "./utilities/jwtUtility";
|
|
17
17
|
export * from "./utilities/timeUtility";
|
|
18
|
+
export * from "./utilities/passwordUtility";
|
|
18
19
|
export * from "./helpers/entityHelpers";
|
|
19
20
|
export * from "./helpers/licenseHelper";
|
|
20
21
|
export * from "./deno-std/http/cookie";
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var qs = require('qs');
|
|
|
9
9
|
var dayjs = require('dayjs');
|
|
10
10
|
var jsonwebtoken = require('jsonwebtoken');
|
|
11
11
|
var crypto = require('crypto');
|
|
12
|
-
var bcrypt = require('
|
|
12
|
+
var bcrypt = require('bcryptjs');
|
|
13
13
|
var path = require('path');
|
|
14
14
|
var fs = require('fs');
|
|
15
15
|
var uuid = require('uuid');
|
|
@@ -928,6 +928,14 @@ class PluginManager {
|
|
|
928
928
|
}
|
|
929
929
|
}
|
|
930
930
|
}
|
|
931
|
+
/** 在执行 action hanlder 前调用。 */
|
|
932
|
+
async beforeRunActionHandler(handlerContext, actionConfig) {
|
|
933
|
+
for (const plugin of this.#plugins) {
|
|
934
|
+
if (plugin.beforeRunActionHandler) {
|
|
935
|
+
await plugin.beforeRunActionHandler(this.#server, handlerContext, actionConfig);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
931
939
|
/** 在创建实体前调用。 */
|
|
932
940
|
async beforeCreateEntity(model, options) {
|
|
933
941
|
for (const plugin of this.#plugins) {
|
|
@@ -1030,6 +1038,7 @@ async function buildRoutes(server, applicationConfig) {
|
|
|
1030
1038
|
if (!handler) {
|
|
1031
1039
|
throw new Error("Unknown handler: " + actionCode);
|
|
1032
1040
|
}
|
|
1041
|
+
await server.beforeRunActionHandler(handlerContext, actionConfig);
|
|
1033
1042
|
const result = handler(handlerContext, actionConfig.config);
|
|
1034
1043
|
if (result instanceof Promise) {
|
|
1035
1044
|
await result;
|
|
@@ -4301,6 +4310,9 @@ class RapidServer {
|
|
|
4301
4310
|
async beforeRunRouteActions(handlerContext) {
|
|
4302
4311
|
await this.#pluginManager.beforeRunRouteActions(handlerContext);
|
|
4303
4312
|
}
|
|
4313
|
+
async beforeRunActionHandler(handlerContext, actionConfig) {
|
|
4314
|
+
await this.#pluginManager.beforeRunActionHandler(handlerContext, actionConfig);
|
|
4315
|
+
}
|
|
4304
4316
|
async beforeCreateEntity(model, options) {
|
|
4305
4317
|
await this.#pluginManager.beforeCreateEntity(model, options);
|
|
4306
4318
|
}
|
|
@@ -5015,6 +5027,30 @@ async function generateJwtSecretKey() {
|
|
|
5015
5027
|
return encode(exportedKey);
|
|
5016
5028
|
}
|
|
5017
5029
|
|
|
5030
|
+
/**
|
|
5031
|
+
* Generates password hash.
|
|
5032
|
+
* @param password
|
|
5033
|
+
* @param salt
|
|
5034
|
+
* @returns
|
|
5035
|
+
*/
|
|
5036
|
+
async function generatePasswordHash(password, salt) {
|
|
5037
|
+
if (!salt) {
|
|
5038
|
+
salt = 10;
|
|
5039
|
+
}
|
|
5040
|
+
const passwordHash = await bcrypt__default["default"].hash(password, salt);
|
|
5041
|
+
return passwordHash;
|
|
5042
|
+
}
|
|
5043
|
+
/**
|
|
5044
|
+
* Validates the password against the hash.
|
|
5045
|
+
* @param password
|
|
5046
|
+
* @param passwordHash
|
|
5047
|
+
* @returns
|
|
5048
|
+
*/
|
|
5049
|
+
async function validatePassword(password, passwordHash) {
|
|
5050
|
+
const isMatch = await bcrypt__default["default"].compare(password, passwordHash);
|
|
5051
|
+
return isMatch;
|
|
5052
|
+
}
|
|
5053
|
+
|
|
5018
5054
|
function validateLicense(server) {
|
|
5019
5055
|
const licenseService = server.getService("licenseService");
|
|
5020
5056
|
const license = licenseService.getLicense();
|
|
@@ -6010,66 +6046,66 @@ var queryDatabase = /*#__PURE__*/Object.freeze({
|
|
|
6010
6046
|
* This plugin provide:
|
|
6011
6047
|
* - routes for manage data in database.
|
|
6012
6048
|
*/
|
|
6013
|
-
const
|
|
6049
|
+
const entityOperationConfigs = [
|
|
6014
6050
|
{
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6051
|
+
operationCode: "createBatch",
|
|
6052
|
+
httpMethod: "POST",
|
|
6053
|
+
requestEndpoint: "/operations/create_batch",
|
|
6054
|
+
actionHandlerCode: "createCollectionEntitiesBatch",
|
|
6019
6055
|
},
|
|
6020
6056
|
{
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6057
|
+
operationCode: "find",
|
|
6058
|
+
httpMethod: "POST",
|
|
6059
|
+
requestEndpoint: "/operations/find",
|
|
6060
|
+
actionHandlerCode: "findCollectionEntities",
|
|
6025
6061
|
},
|
|
6026
6062
|
{
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6063
|
+
operationCode: "count",
|
|
6064
|
+
httpMethod: "POST",
|
|
6065
|
+
requestEndpoint: "/operations/count",
|
|
6066
|
+
actionHandlerCode: "countCollectionEntities",
|
|
6031
6067
|
},
|
|
6032
6068
|
{
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6069
|
+
operationCode: "delete",
|
|
6070
|
+
httpMethod: "POST",
|
|
6071
|
+
requestEndpoint: "/operations/delete",
|
|
6072
|
+
actionHandlerCode: "deleteCollectionEntities",
|
|
6037
6073
|
},
|
|
6038
6074
|
{
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6075
|
+
operationCode: "addRelations",
|
|
6076
|
+
httpMethod: "POST",
|
|
6077
|
+
requestEndpoint: "/operations/add_relations",
|
|
6078
|
+
actionHandlerCode: "addEntityRelations",
|
|
6043
6079
|
},
|
|
6044
6080
|
{
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6081
|
+
operationCode: "removeRelations",
|
|
6082
|
+
httpMethod: "POST",
|
|
6083
|
+
requestEndpoint: "/operations/remove_relations",
|
|
6084
|
+
actionHandlerCode: "removeEntityRelations",
|
|
6049
6085
|
},
|
|
6050
6086
|
{
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6087
|
+
operationCode: "getById",
|
|
6088
|
+
httpMethod: "GET",
|
|
6089
|
+
requestEndpoint: "/:id",
|
|
6090
|
+
actionHandlerCode: "findCollectionEntityById",
|
|
6055
6091
|
},
|
|
6056
6092
|
{
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6093
|
+
operationCode: "create",
|
|
6094
|
+
httpMethod: "POST",
|
|
6095
|
+
requestEndpoint: "",
|
|
6096
|
+
actionHandlerCode: "createCollectionEntity",
|
|
6061
6097
|
},
|
|
6062
6098
|
{
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6099
|
+
operationCode: "updateById",
|
|
6100
|
+
httpMethod: "PATCH",
|
|
6101
|
+
requestEndpoint: "/:id",
|
|
6102
|
+
actionHandlerCode: "updateCollectionEntityById",
|
|
6067
6103
|
},
|
|
6068
6104
|
{
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6105
|
+
operationCode: "deleteById",
|
|
6106
|
+
httpMethod: "DELETE",
|
|
6107
|
+
requestEndpoint: "/:id",
|
|
6108
|
+
actionHandlerCode: "deleteCollectionEntityById",
|
|
6073
6109
|
},
|
|
6074
6110
|
];
|
|
6075
6111
|
class DataManager {
|
|
@@ -6106,17 +6142,17 @@ class DataManager {
|
|
|
6106
6142
|
const routes = [];
|
|
6107
6143
|
models.forEach((model) => {
|
|
6108
6144
|
const { namespace, singularCode, pluralCode } = model;
|
|
6109
|
-
|
|
6145
|
+
entityOperationConfigs.forEach((entityOperationConfig) => {
|
|
6110
6146
|
routes.push({
|
|
6111
6147
|
namespace,
|
|
6112
|
-
name: `${namespace}.${singularCode}.${
|
|
6113
|
-
code: `${namespace}.${singularCode}.${
|
|
6148
|
+
name: `${namespace}.${singularCode}.${entityOperationConfig.operationCode}`,
|
|
6149
|
+
code: `${namespace}.${singularCode}.${entityOperationConfig.operationCode}`,
|
|
6114
6150
|
type: "RESTful",
|
|
6115
|
-
method:
|
|
6116
|
-
endpoint: `/${namespace}/${pluralCode}${
|
|
6151
|
+
method: entityOperationConfig.httpMethod,
|
|
6152
|
+
endpoint: `/${namespace}/${pluralCode}${entityOperationConfig.requestEndpoint}`,
|
|
6117
6153
|
actions: [
|
|
6118
6154
|
{
|
|
6119
|
-
code:
|
|
6155
|
+
code: entityOperationConfig.actionHandlerCode,
|
|
6120
6156
|
config: {
|
|
6121
6157
|
namespace,
|
|
6122
6158
|
singularCode,
|
|
@@ -6892,12 +6928,11 @@ async function handler$e(plugin, ctx, options) {
|
|
|
6892
6928
|
if (!user) {
|
|
6893
6929
|
throw new Error("User not found.");
|
|
6894
6930
|
}
|
|
6895
|
-
const isMatch = await
|
|
6931
|
+
const isMatch = await validatePassword(oldPassword, user.password);
|
|
6896
6932
|
if (!isMatch) {
|
|
6897
6933
|
throw new Error("旧密码错误。");
|
|
6898
6934
|
}
|
|
6899
|
-
const
|
|
6900
|
-
const passwordHash = await bcrypt__default["default"].hash(newPassword, saltRounds);
|
|
6935
|
+
const passwordHash = await generatePasswordHash(newPassword);
|
|
6901
6936
|
await userDataAccessor.updateById(user.id, {
|
|
6902
6937
|
password: passwordHash,
|
|
6903
6938
|
}, routeContext?.getDbTransactionClient());
|
|
@@ -6939,7 +6974,7 @@ async function handler$d(plugin, ctx, options) {
|
|
|
6939
6974
|
if (user.state !== "enabled") {
|
|
6940
6975
|
throw new Error("用户已被禁用,不允许登录。");
|
|
6941
6976
|
}
|
|
6942
|
-
const isMatch = await
|
|
6977
|
+
const isMatch = await validatePassword(password, user.password);
|
|
6943
6978
|
if (!isMatch) {
|
|
6944
6979
|
throw new Error("用户名或密码错误。");
|
|
6945
6980
|
}
|
|
@@ -7040,8 +7075,7 @@ async function handler$a(plugin, ctx, options) {
|
|
|
7040
7075
|
if (!user) {
|
|
7041
7076
|
throw new Error("User not found.");
|
|
7042
7077
|
}
|
|
7043
|
-
const
|
|
7044
|
-
const passwordHash = await bcrypt__default["default"].hash(password, saltRounds);
|
|
7078
|
+
const passwordHash = await generatePasswordHash(password);
|
|
7045
7079
|
await userDataAccessor.updateById(user.id, {
|
|
7046
7080
|
password: passwordHash,
|
|
7047
7081
|
}, routeContext?.getDbTransactionClient());
|
|
@@ -9570,6 +9604,7 @@ exports.deleteCookie = deleteCookie;
|
|
|
9570
9604
|
exports.detectChangedFieldsOfEntity = detectChangedFieldsOfEntity;
|
|
9571
9605
|
exports.formatDateTimeWithTimezone = formatDateTimeWithTimezone;
|
|
9572
9606
|
exports.generateJwtSecretKey = generateJwtSecretKey;
|
|
9607
|
+
exports.generatePasswordHash = generatePasswordHash;
|
|
9573
9608
|
exports.getCookies = getCookies;
|
|
9574
9609
|
exports.getDateString = getDateString;
|
|
9575
9610
|
exports.getEntityRelationTargetId = getEntityRelationTargetId;
|
|
@@ -9581,4 +9616,5 @@ exports.mapDbRowToEntity = mapDbRowToEntity;
|
|
|
9581
9616
|
exports.setCookie = setCookie;
|
|
9582
9617
|
exports.tryValidateLicense = tryValidateLicense;
|
|
9583
9618
|
exports.validateLicense = validateLicense;
|
|
9619
|
+
exports.validatePassword = validatePassword;
|
|
9584
9620
|
exports.verifyJwt = verifyJwt;
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RpdApplicationConfig, RpdDataModel, RpdServerEventTypes, RapidServerConfig, RpdDataModelProperty, CreateEntityOptions, UpdateEntityByIdOptions, EntityWatcherType, EmitServerEventOptions, IDatabaseClient } from "./types";
|
|
1
|
+
import { GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RpdApplicationConfig, RpdDataModel, RpdServerEventTypes, RapidServerConfig, RpdDataModelProperty, CreateEntityOptions, UpdateEntityByIdOptions, EntityWatcherType, EmitServerEventOptions, IDatabaseClient, RpdRouteActionConfig } from "./types";
|
|
2
2
|
import { ActionHandler, ActionHandlerContext, IPluginActionHandler } from "./core/actionHandler";
|
|
3
3
|
import { IRpdServer, RapidPlugin } from "./core/server";
|
|
4
4
|
import { Next } from "./core/routeContext";
|
|
@@ -58,6 +58,7 @@ export declare class RapidServer implements IRpdServer {
|
|
|
58
58
|
get middlewares(): any[];
|
|
59
59
|
handleRequest(rapidRequest: RapidRequest, next: Next): Promise<Response>;
|
|
60
60
|
beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
|
|
61
|
+
beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
|
|
61
62
|
beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
|
|
62
63
|
beforeUpdateEntity(model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any): Promise<void>;
|
|
63
64
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates password hash.
|
|
3
|
+
* @param password
|
|
4
|
+
* @param salt
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export declare function generatePasswordHash(password: string, salt?: number | string): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Validates the password against the hash.
|
|
10
|
+
* @param password
|
|
11
|
+
* @param passwordHash
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
export declare function validatePassword(password: string, passwordHash: string): Promise<boolean>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruiapp/rapid-core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"typescript": "^4.8.4"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"
|
|
22
|
+
"bcryptjs": "^3.0.2",
|
|
23
23
|
"cron": "^3.1.7",
|
|
24
24
|
"dayjs": "^1.11.7",
|
|
25
25
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateEntityOptions, RpdApplicationConfig, RpdDataModel, UpdateEntityByIdOptions } from "~/types";
|
|
1
|
+
import { CreateEntityOptions, RpdApplicationConfig, RpdDataModel, RpdRouteActionConfig, UpdateEntityByIdOptions } from "~/types";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "./server";
|
|
3
3
|
import { RouteContext } from "./routeContext";
|
|
4
4
|
import { ActionHandlerContext } from "./actionHandler";
|
|
@@ -162,6 +162,15 @@ class PluginManager {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/** 在执行 action hanlder 前调用。 */
|
|
166
|
+
async beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig) {
|
|
167
|
+
for (const plugin of this.#plugins) {
|
|
168
|
+
if (plugin.beforeRunActionHandler) {
|
|
169
|
+
await plugin.beforeRunActionHandler(this.#server, handlerContext, actionConfig);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
165
174
|
/** 在创建实体前调用。 */
|
|
166
175
|
async beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions) {
|
|
167
176
|
for (const plugin of this.#plugins) {
|
package/src/core/routeContext.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { RapidRequest } from "./request";
|
|
|
2
2
|
import { RapidResponse } from "./response";
|
|
3
3
|
import { HttpStatus } from "./http-types";
|
|
4
4
|
import { IRpdServer } from "./server";
|
|
5
|
-
import { IDatabaseAccessor, IDatabaseClient } from "~/types";
|
|
5
|
+
import { IDatabaseAccessor, IDatabaseClient, RpdRoute } from "~/types";
|
|
6
6
|
|
|
7
7
|
export type Next = () => Promise<void>;
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ export class RouteContext {
|
|
|
18
18
|
method: string;
|
|
19
19
|
path: string;
|
|
20
20
|
params: Record<string, string>;
|
|
21
|
-
routeConfig:
|
|
21
|
+
routeConfig: RpdRoute;
|
|
22
22
|
#server: IRpdServer;
|
|
23
23
|
#dbTransactionClient: IDatabaseClient | undefined;
|
|
24
24
|
#dbTransactionState: TransactionState;
|
|
@@ -74,6 +74,8 @@ export async function buildRoutes(server: IRpdServer, applicationConfig: RpdAppl
|
|
|
74
74
|
throw new Error("Unknown handler: " + actionCode);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
await server.beforeRunActionHandler(handlerContext, actionConfig);
|
|
78
|
+
|
|
77
79
|
const result = handler(handlerContext, actionConfig.config);
|
|
78
80
|
if (result instanceof Promise) {
|
|
79
81
|
await result;
|
package/src/core/server.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
RpdApplicationConfig,
|
|
14
14
|
RpdDataModel,
|
|
15
15
|
RpdDataModelProperty,
|
|
16
|
+
RpdRouteActionConfig,
|
|
16
17
|
RpdServerEventTypes,
|
|
17
18
|
UpdateEntityByIdOptions,
|
|
18
19
|
} from "~/types";
|
|
@@ -53,6 +54,7 @@ export interface IRpdServer {
|
|
|
53
54
|
emitEvent<TEventName extends keyof RpdServerEventTypes>(event: EmitServerEventOptions<TEventName>): void;
|
|
54
55
|
handleRequest(request: RapidRequest, next: Next): Promise<Response>;
|
|
55
56
|
beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
|
|
57
|
+
beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig): Promise<void>;
|
|
56
58
|
beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions): Promise<void>;
|
|
57
59
|
beforeUpdateEntity(model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any): Promise<void>;
|
|
58
60
|
|
|
@@ -145,6 +147,8 @@ export interface RapidPlugin {
|
|
|
145
147
|
onPrepareRouteContext?: (server: IRpdServer, routeContext: RouteContext) => Promise<any>;
|
|
146
148
|
/** 在接收到HTTP请求,执行 actions 前调用。 */
|
|
147
149
|
beforeRunRouteActions?: (server: IRpdServer, handlerContext: ActionHandlerContext) => Promise<any>;
|
|
150
|
+
/** 在执行 action hanlder 前调用。 */
|
|
151
|
+
beforeRunActionHandler?: (server: IRpdServer, handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig) => Promise<any>;
|
|
148
152
|
/** 在创建实体前调用。 */
|
|
149
153
|
beforeCreateEntity?: (server: IRpdServer, model: RpdDataModel, options: CreateEntityOptions) => Promise<any>;
|
|
150
154
|
/** 在更新实体前调用。 */
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export * from "./utilities/accessControlUtility";
|
|
|
22
22
|
export * from "./utilities/entityUtility";
|
|
23
23
|
export * from "./utilities/jwtUtility";
|
|
24
24
|
export * from "./utilities/timeUtility";
|
|
25
|
+
export * from "./utilities/passwordUtility";
|
|
25
26
|
|
|
26
27
|
export * from "./helpers/entityHelpers";
|
|
27
28
|
export * from "./helpers/licenseHelper";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import bcrypt from "bcrypt";
|
|
2
1
|
import { ActionHandlerContext } from "~/core/actionHandler";
|
|
3
|
-
import { RapidPlugin } from "~/core/server";
|
|
4
2
|
import AuthPlugin from "../AuthPlugin";
|
|
3
|
+
import { generatePasswordHash, validatePassword } from "~/utilities/passwordUtility";
|
|
5
4
|
|
|
6
5
|
export const code = "changePassword";
|
|
7
6
|
|
|
@@ -43,13 +42,12 @@ export async function handler(plugin: AuthPlugin, ctx: ActionHandlerContext, opt
|
|
|
43
42
|
throw new Error("User not found.");
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
const isMatch = await
|
|
45
|
+
const isMatch = await validatePassword(oldPassword, user.password);
|
|
47
46
|
if (!isMatch) {
|
|
48
47
|
throw new Error("旧密码错误。");
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
const
|
|
52
|
-
const passwordHash = await bcrypt.hash(newPassword, saltRounds);
|
|
50
|
+
const passwordHash = await generatePasswordHash(newPassword);
|
|
53
51
|
|
|
54
52
|
await userDataAccessor.updateById(
|
|
55
53
|
user.id,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import bcrypt from "bcrypt";
|
|
2
1
|
import { setCookie } from "~/deno-std/http/cookie";
|
|
3
2
|
import { ActionHandlerContext } from "~/core/actionHandler";
|
|
4
|
-
import { RapidPlugin } from "~/core/server";
|
|
5
3
|
import AuthService from "../services/AuthService";
|
|
6
4
|
import { validateLicense } from "~/helpers/licenseHelper";
|
|
7
5
|
import AuthPlugin from "../AuthPlugin";
|
|
6
|
+
import { validatePassword } from "~/utilities/passwordUtility";
|
|
8
7
|
|
|
9
8
|
export const code = "createSession";
|
|
10
9
|
|
|
@@ -45,7 +44,7 @@ export async function handler(plugin: AuthPlugin, ctx: ActionHandlerContext, opt
|
|
|
45
44
|
throw new Error("用户已被禁用,不允许登录。");
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
const isMatch = await
|
|
47
|
+
const isMatch = await validatePassword(password, user.password);
|
|
49
48
|
if (!isMatch) {
|
|
50
49
|
throw new Error("用户名或密码错误。");
|
|
51
50
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import bcrypt from "bcrypt";
|
|
2
1
|
import { ActionHandlerContext } from "~/core/actionHandler";
|
|
3
|
-
import { RapidPlugin } from "~/core/server";
|
|
4
2
|
import AuthPlugin from "../AuthPlugin";
|
|
3
|
+
import { generatePasswordHash } from "~/utilities/passwordUtility";
|
|
5
4
|
|
|
6
5
|
export const code = "resetPassword";
|
|
7
6
|
|
|
@@ -32,8 +31,7 @@ export async function handler(plugin: AuthPlugin, ctx: ActionHandlerContext, opt
|
|
|
32
31
|
throw new Error("User not found.");
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
const
|
|
36
|
-
const passwordHash = await bcrypt.hash(password, saltRounds);
|
|
34
|
+
const passwordHash = await generatePasswordHash(password);
|
|
37
35
|
|
|
38
36
|
await userDataAccessor.updateById(
|
|
39
37
|
user.id,
|
|
@@ -25,71 +25,71 @@ import {
|
|
|
25
25
|
RapidPlugin,
|
|
26
26
|
} from "~/core/server";
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
const entityOperationConfigs: {
|
|
29
|
+
operationCode: string;
|
|
30
|
+
httpMethod: RpdHttpMethod;
|
|
31
|
+
requestEndpoint: string;
|
|
32
|
+
actionHandlerCode: string;
|
|
33
33
|
}[] = [
|
|
34
34
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
operationCode: "createBatch",
|
|
36
|
+
httpMethod: "POST",
|
|
37
|
+
requestEndpoint: "/operations/create_batch",
|
|
38
|
+
actionHandlerCode: "createCollectionEntitiesBatch",
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
operationCode: "find",
|
|
42
|
+
httpMethod: "POST",
|
|
43
|
+
requestEndpoint: "/operations/find",
|
|
44
|
+
actionHandlerCode: "findCollectionEntities",
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
operationCode: "count",
|
|
48
|
+
httpMethod: "POST",
|
|
49
|
+
requestEndpoint: "/operations/count",
|
|
50
|
+
actionHandlerCode: "countCollectionEntities",
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
operationCode: "delete",
|
|
54
|
+
httpMethod: "POST",
|
|
55
|
+
requestEndpoint: "/operations/delete",
|
|
56
|
+
actionHandlerCode: "deleteCollectionEntities",
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
operationCode: "addRelations",
|
|
60
|
+
httpMethod: "POST",
|
|
61
|
+
requestEndpoint: "/operations/add_relations",
|
|
62
|
+
actionHandlerCode: "addEntityRelations",
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
operationCode: "removeRelations",
|
|
66
|
+
httpMethod: "POST",
|
|
67
|
+
requestEndpoint: "/operations/remove_relations",
|
|
68
|
+
actionHandlerCode: "removeEntityRelations",
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
operationCode: "getById",
|
|
72
|
+
httpMethod: "GET",
|
|
73
|
+
requestEndpoint: "/:id",
|
|
74
|
+
actionHandlerCode: "findCollectionEntityById",
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
operationCode: "create",
|
|
78
|
+
httpMethod: "POST",
|
|
79
|
+
requestEndpoint: "",
|
|
80
|
+
actionHandlerCode: "createCollectionEntity",
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
operationCode: "updateById",
|
|
84
|
+
httpMethod: "PATCH",
|
|
85
|
+
requestEndpoint: "/:id",
|
|
86
|
+
actionHandlerCode: "updateCollectionEntityById",
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
operationCode: "deleteById",
|
|
90
|
+
httpMethod: "DELETE",
|
|
91
|
+
requestEndpoint: "/:id",
|
|
92
|
+
actionHandlerCode: "deleteCollectionEntityById",
|
|
93
93
|
},
|
|
94
94
|
];
|
|
95
95
|
|
|
@@ -135,17 +135,17 @@ class DataManager implements RapidPlugin {
|
|
|
135
135
|
models.forEach((model) => {
|
|
136
136
|
const { namespace, singularCode, pluralCode } = model;
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
entityOperationConfigs.forEach((entityOperationConfig) => {
|
|
139
139
|
routes.push({
|
|
140
140
|
namespace,
|
|
141
|
-
name: `${namespace}.${singularCode}.${
|
|
142
|
-
code: `${namespace}.${singularCode}.${
|
|
141
|
+
name: `${namespace}.${singularCode}.${entityOperationConfig.operationCode}`,
|
|
142
|
+
code: `${namespace}.${singularCode}.${entityOperationConfig.operationCode}`,
|
|
143
143
|
type: "RESTful",
|
|
144
|
-
method:
|
|
145
|
-
endpoint: `/${namespace}/${pluralCode}${
|
|
144
|
+
method: entityOperationConfig.httpMethod,
|
|
145
|
+
endpoint: `/${namespace}/${pluralCode}${entityOperationConfig.requestEndpoint}`,
|
|
146
146
|
actions: [
|
|
147
147
|
{
|
|
148
|
-
code:
|
|
148
|
+
code: entityOperationConfig.actionHandlerCode,
|
|
149
149
|
config: {
|
|
150
150
|
namespace,
|
|
151
151
|
singularCode,
|
package/src/server.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
RpdEntityCreateEventPayload,
|
|
19
19
|
EmitServerEventOptions,
|
|
20
20
|
IDatabaseClient,
|
|
21
|
+
RpdRouteActionConfig,
|
|
21
22
|
} from "./types";
|
|
22
23
|
|
|
23
24
|
import QueryBuilder from "./queryBuilder/queryBuilder";
|
|
@@ -475,6 +476,10 @@ export class RapidServer implements IRpdServer {
|
|
|
475
476
|
await this.#pluginManager.beforeRunRouteActions(handlerContext);
|
|
476
477
|
}
|
|
477
478
|
|
|
479
|
+
async beforeRunActionHandler(handlerContext: ActionHandlerContext, actionConfig: RpdRouteActionConfig) {
|
|
480
|
+
await this.#pluginManager.beforeRunActionHandler(handlerContext, actionConfig);
|
|
481
|
+
}
|
|
482
|
+
|
|
478
483
|
async beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions) {
|
|
479
484
|
await this.#pluginManager.beforeCreateEntity(model, options);
|
|
480
485
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates password hash.
|
|
5
|
+
* @param password
|
|
6
|
+
* @param salt
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export async function generatePasswordHash(password: string, salt?: number | string): Promise<string> {
|
|
10
|
+
if (!salt) {
|
|
11
|
+
salt = 10;
|
|
12
|
+
}
|
|
13
|
+
const passwordHash = await bcrypt.hash(password, salt);
|
|
14
|
+
return passwordHash;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validates the password against the hash.
|
|
19
|
+
* @param password
|
|
20
|
+
* @param passwordHash
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
export async function validatePassword(password: string, passwordHash: string): Promise<boolean> {
|
|
24
|
+
const isMatch = await bcrypt.compare(password, passwordHash);
|
|
25
|
+
return isMatch;
|
|
26
|
+
}
|