@ruiapp/rapid-core 0.1.6 → 0.1.7
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 -0
- package/dist/core/request.d.ts +2 -1
- package/dist/core/server.d.ts +15 -13
- package/dist/index.d.ts +6 -6
- package/dist/index.js +278 -208
- package/dist/plugins/auth/{mod.d.ts → AuthPlugin.d.ts} +4 -2
- package/dist/plugins/auth/routes/index.d.ts +2 -2
- package/dist/utilities/jwtUtility.d.ts +2 -2
- package/package.json +1 -1
- package/src/core/pluginManager.ts +13 -0
- package/src/core/request.ts +16 -3
- package/src/core/server.ts +15 -14
- package/src/index.ts +6 -6
- package/src/plugins/auth/{mod.ts → AuthPlugin.ts} +27 -2
- package/src/server.ts +1 -0
- package/src/utilities/jwtUtility.ts +3 -3
- /package/dist/plugins/dataManage/{mod.d.ts → DataManagePlugin.d.ts} +0 -0
- /package/dist/plugins/fileManage/{mod.d.ts → FileManagePlugin.d.ts} +0 -0
- /package/dist/plugins/metaManage/{mod.d.ts → MetaManagePlugin.d.ts} +0 -0
- /package/dist/plugins/routeManage/{mod.d.ts → RouteManagePlugin.d.ts} +0 -0
- /package/dist/plugins/webhooks/{mod.d.ts → WebhooksPlugin.d.ts} +0 -0
- /package/src/plugins/dataManage/{mod.ts → DataManagePlugin.ts} +0 -0
- /package/src/plugins/fileManage/{mod.ts → FileManagePlugin.ts} +0 -0
- /package/src/plugins/metaManage/{mod.ts → MetaManagePlugin.ts} +0 -0
- /package/src/plugins/routeManage/{mod.ts → RouteManagePlugin.ts} +0 -0
- /package/src/plugins/webhooks/{mod.ts → WebhooksPlugin.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RpdApplicationConfig } from "~/types";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "./server";
|
|
3
|
+
import { RouteContext } from "./routeContext";
|
|
3
4
|
declare class PluginManager {
|
|
4
5
|
#private;
|
|
5
6
|
constructor(server: IRpdServer);
|
|
@@ -28,5 +29,7 @@ declare class PluginManager {
|
|
|
28
29
|
onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<void>;
|
|
29
30
|
/** 在应用准备完成后调用。此时服务器已经可以处理网络请求。 */
|
|
30
31
|
onApplicationReady(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<void>;
|
|
32
|
+
/** 在接收到HTTP请求,准备路由上下文时调用。 */
|
|
33
|
+
onPrepareRouteContext(server: IRpdServer, routeContext: RouteContext): Promise<void>;
|
|
31
34
|
}
|
|
32
35
|
export default PluginManager;
|
package/dist/core/request.d.ts
CHANGED
|
@@ -10,9 +10,10 @@ export declare class RapidRequest {
|
|
|
10
10
|
#private;
|
|
11
11
|
method: string;
|
|
12
12
|
url: URL;
|
|
13
|
-
headers: Headers;
|
|
14
13
|
constructor(req: Request);
|
|
15
14
|
parseBody(): Promise<void>;
|
|
16
15
|
get rawRequest(): Request;
|
|
16
|
+
get headers(): Headers;
|
|
17
|
+
get cookies(): Record<string, string>;
|
|
17
18
|
get body(): RapidRequestBody;
|
|
18
19
|
}
|
package/dist/core/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GetDataAccessorOptions, GetModelOptions, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RapidServerConfig, RpdApplicationConfig, RpdDataModel, RpdServerEventTypes } from "~/types";
|
|
2
2
|
import { IPluginActionHandler, ActionHandler } from "./actionHandler";
|
|
3
|
-
import { Next } from "./routeContext";
|
|
3
|
+
import { Next, RouteContext } from "./routeContext";
|
|
4
4
|
export interface IRpdServer {
|
|
5
5
|
config: RapidServerConfig;
|
|
6
6
|
databaseConfig: IDatabaseConfig;
|
|
@@ -58,27 +58,29 @@ export interface RapidPlugin {
|
|
|
58
58
|
/** 插件的全局配置项 */
|
|
59
59
|
get configurations(): RpdConfigurationItemOptions[];
|
|
60
60
|
/** 初始化插件时调用。插件可以在此时进行一些内部对象的初始化工作。 */
|
|
61
|
-
initPlugin(server: IRpdServer)
|
|
61
|
+
initPlugin?: (server: IRpdServer) => Promise<any>;
|
|
62
62
|
/** 注册中间件 */
|
|
63
|
-
registerMiddlewares(server: IRpdServer)
|
|
63
|
+
registerMiddlewares?: (server: IRpdServer) => Promise<any>;
|
|
64
64
|
/** 注册接口动作处理程序 */
|
|
65
|
-
registerActionHandlers(server: IRpdServer)
|
|
65
|
+
registerActionHandlers?: (server: IRpdServer) => Promise<any>;
|
|
66
66
|
/** 注册事件处理程序 */
|
|
67
|
-
registerEventHandlers(server: IRpdServer)
|
|
67
|
+
registerEventHandlers?: (server: IRpdServer) => Promise<any>;
|
|
68
68
|
/** 注册消息处理程序 */
|
|
69
|
-
registerMessageHandlers(server: IRpdServer)
|
|
69
|
+
registerMessageHandlers?: (server: IRpdServer) => Promise<any>;
|
|
70
70
|
/** 注册任务处理程序 */
|
|
71
|
-
registerTaskProcessors(server: IRpdServer)
|
|
71
|
+
registerTaskProcessors?: (server: IRpdServer) => Promise<any>;
|
|
72
72
|
/** 在加载应用前调用。 */
|
|
73
|
-
onLoadingApplication(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
73
|
+
onLoadingApplication?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
74
74
|
/** 配置数据集合 */
|
|
75
|
-
configureModels(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
75
|
+
configureModels?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
76
76
|
/** 配置模型属性 */
|
|
77
|
-
configureModelProperties(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
77
|
+
configureModelProperties?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
78
78
|
/** 配置路由 */
|
|
79
|
-
configureRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
79
|
+
configureRoutes?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
80
80
|
/** 在应用配置加载完成后调用。此时插件可以进行一些数据的初始化工作。 */
|
|
81
|
-
onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
81
|
+
onApplicationLoaded?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
82
82
|
/** 在应用准备完成后调用。此时服务器已经可以处理网络请求,可以对外广播消息。 */
|
|
83
|
-
onApplicationReady(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
83
|
+
onApplicationReady?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
84
|
+
/** 在接收到HTTP请求,准备路由上下文时调用。 */
|
|
85
|
+
onPrepareRouteContext?: (server: IRpdServer, routeContext: RouteContext) => Promise<any>;
|
|
84
86
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ export * from "./core/http-types";
|
|
|
7
7
|
export * from "./core/actionHandler";
|
|
8
8
|
export * from "./utilities/jwtUtility";
|
|
9
9
|
export * as bootstrapApplicationConfig from "./bootstrapApplicationConfig";
|
|
10
|
-
export { default as MetaManagePlugin } from "./plugins/metaManage/
|
|
11
|
-
export { default as DataManagePlugin } from "./plugins/dataManage/
|
|
12
|
-
export { default as RouteManagePlugin } from "./plugins/routeManage/
|
|
13
|
-
export { default as WebhooksPlugin } from "./plugins/webhooks/
|
|
14
|
-
export { default as AuthPlugin } from "./plugins/auth/
|
|
15
|
-
export { default as FileManagePlugin } from "./plugins/fileManage/
|
|
10
|
+
export { default as MetaManagePlugin } from "./plugins/metaManage/MetaManagePlugin";
|
|
11
|
+
export { default as DataManagePlugin } from "./plugins/dataManage/DataManagePlugin";
|
|
12
|
+
export { default as RouteManagePlugin } from "./plugins/routeManage/RouteManagePlugin";
|
|
13
|
+
export { default as WebhooksPlugin } from "./plugins/webhooks/WebhooksPlugin";
|
|
14
|
+
export { default as AuthPlugin } from "./plugins/auth/AuthPlugin";
|
|
15
|
+
export { default as FileManagePlugin } from "./plugins/fileManage/FileManagePlugin";
|
package/dist/index.js
CHANGED
|
@@ -544,6 +544,14 @@ class PluginManager {
|
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
|
+
/** 在接收到HTTP请求,准备路由上下文时调用。 */
|
|
548
|
+
async onPrepareRouteContext(server, routeContext) {
|
|
549
|
+
for (const plugin of this.#plugins) {
|
|
550
|
+
if (plugin.onPrepareRouteContext) {
|
|
551
|
+
await plugin.onPrepareRouteContext(server, routeContext);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
547
555
|
}
|
|
548
556
|
|
|
549
557
|
class EventManager {
|
|
@@ -769,19 +777,255 @@ const convertToNewArray = (form, key, value) => {
|
|
|
769
777
|
form[key] = [form[key], value];
|
|
770
778
|
};
|
|
771
779
|
|
|
780
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
781
|
+
class AssertionError extends Error {
|
|
782
|
+
name = "AssertionError";
|
|
783
|
+
constructor(message) {
|
|
784
|
+
super(message);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
789
|
+
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
|
|
790
|
+
function assert(expr, msg = "") {
|
|
791
|
+
if (!expr) {
|
|
792
|
+
throw new AssertionError(msg);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
797
|
+
// This module is browser compatible.
|
|
798
|
+
/**
|
|
799
|
+
* Formats the given date to IMF date time format. (Reference:
|
|
800
|
+
* https://tools.ietf.org/html/rfc7231#section-7.1.1.1).
|
|
801
|
+
* IMF is the time format to use when generating times in HTTP
|
|
802
|
+
* headers. The time being formatted must be in UTC for Format to
|
|
803
|
+
* generate the correct format.
|
|
804
|
+
*
|
|
805
|
+
* @example
|
|
806
|
+
* ```ts
|
|
807
|
+
* import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/to_imf.ts";
|
|
808
|
+
*
|
|
809
|
+
* toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT"
|
|
810
|
+
* ```
|
|
811
|
+
* @param date Date to parse
|
|
812
|
+
* @return IMF date formatted string
|
|
813
|
+
*/
|
|
814
|
+
function toIMF(date) {
|
|
815
|
+
function dtPad(v, lPad = 2) {
|
|
816
|
+
return v.padStart(lPad, "0");
|
|
817
|
+
}
|
|
818
|
+
const d = dtPad(date.getUTCDate().toString());
|
|
819
|
+
const h = dtPad(date.getUTCHours().toString());
|
|
820
|
+
const min = dtPad(date.getUTCMinutes().toString());
|
|
821
|
+
const s = dtPad(date.getUTCSeconds().toString());
|
|
822
|
+
const y = date.getUTCFullYear();
|
|
823
|
+
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
824
|
+
const months = [
|
|
825
|
+
"Jan",
|
|
826
|
+
"Feb",
|
|
827
|
+
"Mar",
|
|
828
|
+
"Apr",
|
|
829
|
+
"May",
|
|
830
|
+
"Jun",
|
|
831
|
+
"Jul",
|
|
832
|
+
"Aug",
|
|
833
|
+
"Sep",
|
|
834
|
+
"Oct",
|
|
835
|
+
"Nov",
|
|
836
|
+
"Dec",
|
|
837
|
+
];
|
|
838
|
+
return `${days[date.getUTCDay()]}, ${d} ${months[date.getUTCMonth()]} ${y} ${h}:${min}:${s} GMT`;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
842
|
+
const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;
|
|
843
|
+
function toString(cookie) {
|
|
844
|
+
if (!cookie.name) {
|
|
845
|
+
return "";
|
|
846
|
+
}
|
|
847
|
+
const out = [];
|
|
848
|
+
validateName(cookie.name);
|
|
849
|
+
validateValue(cookie.name, cookie.value);
|
|
850
|
+
out.push(`${cookie.name}=${cookie.value}`);
|
|
851
|
+
// Fallback for invalid Set-Cookie
|
|
852
|
+
// ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
|
|
853
|
+
if (cookie.name.startsWith("__Secure")) {
|
|
854
|
+
cookie.secure = true;
|
|
855
|
+
}
|
|
856
|
+
if (cookie.name.startsWith("__Host")) {
|
|
857
|
+
cookie.path = "/";
|
|
858
|
+
cookie.secure = true;
|
|
859
|
+
delete cookie.domain;
|
|
860
|
+
}
|
|
861
|
+
if (cookie.secure) {
|
|
862
|
+
out.push("Secure");
|
|
863
|
+
}
|
|
864
|
+
if (cookie.httpOnly) {
|
|
865
|
+
out.push("HttpOnly");
|
|
866
|
+
}
|
|
867
|
+
if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) {
|
|
868
|
+
assert(cookie.maxAge >= 0, "Max-Age must be an integer superior or equal to 0");
|
|
869
|
+
out.push(`Max-Age=${cookie.maxAge}`);
|
|
870
|
+
}
|
|
871
|
+
if (cookie.domain) {
|
|
872
|
+
validateDomain(cookie.domain);
|
|
873
|
+
out.push(`Domain=${cookie.domain}`);
|
|
874
|
+
}
|
|
875
|
+
if (cookie.sameSite) {
|
|
876
|
+
out.push(`SameSite=${cookie.sameSite}`);
|
|
877
|
+
}
|
|
878
|
+
if (cookie.path) {
|
|
879
|
+
validatePath(cookie.path);
|
|
880
|
+
out.push(`Path=${cookie.path}`);
|
|
881
|
+
}
|
|
882
|
+
if (cookie.expires) {
|
|
883
|
+
const { expires } = cookie;
|
|
884
|
+
const dateString = toIMF(typeof expires === "number" ? new Date(expires) : expires);
|
|
885
|
+
out.push(`Expires=${dateString}`);
|
|
886
|
+
}
|
|
887
|
+
if (cookie.unparsed) {
|
|
888
|
+
out.push(cookie.unparsed.join("; "));
|
|
889
|
+
}
|
|
890
|
+
return out.join("; ");
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Validate Cookie Name.
|
|
894
|
+
* @param name Cookie name.
|
|
895
|
+
*/
|
|
896
|
+
function validateName(name) {
|
|
897
|
+
if (name && !FIELD_CONTENT_REGEXP.test(name)) {
|
|
898
|
+
throw new TypeError(`Invalid cookie name: "${name}".`);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Validate Path Value.
|
|
903
|
+
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1.2.4}.
|
|
904
|
+
* @param path Path value.
|
|
905
|
+
*/
|
|
906
|
+
function validatePath(path) {
|
|
907
|
+
if (path == null) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
for (let i = 0; i < path.length; i++) {
|
|
911
|
+
const c = path.charAt(i);
|
|
912
|
+
if (c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7E) || c == ";") {
|
|
913
|
+
throw new Error(path + ": Invalid cookie path char '" + c + "'");
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Validate Cookie Value.
|
|
919
|
+
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1}.
|
|
920
|
+
* @param value Cookie value.
|
|
921
|
+
*/
|
|
922
|
+
function validateValue(name, value) {
|
|
923
|
+
if (value == null || name == null)
|
|
924
|
+
return;
|
|
925
|
+
for (let i = 0; i < value.length; i++) {
|
|
926
|
+
const c = value.charAt(i);
|
|
927
|
+
if (c < String.fromCharCode(0x21) || c == String.fromCharCode(0x22) ||
|
|
928
|
+
c == String.fromCharCode(0x2c) || c == String.fromCharCode(0x3b) ||
|
|
929
|
+
c == String.fromCharCode(0x5c) || c == String.fromCharCode(0x7f)) {
|
|
930
|
+
throw new Error("RFC2616 cookie '" + name + "' cannot contain character '" + c + "'");
|
|
931
|
+
}
|
|
932
|
+
if (c > String.fromCharCode(0x80)) {
|
|
933
|
+
throw new Error("RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" +
|
|
934
|
+
c.charCodeAt(0).toString(16));
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Validate Cookie Domain.
|
|
940
|
+
* See {@link https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3}.
|
|
941
|
+
* @param domain Cookie domain.
|
|
942
|
+
*/
|
|
943
|
+
function validateDomain(domain) {
|
|
944
|
+
if (domain == null) {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
const char1 = domain.charAt(0);
|
|
948
|
+
const charN = domain.charAt(domain.length - 1);
|
|
949
|
+
if (char1 == "-" || charN == "." || charN == "-") {
|
|
950
|
+
throw new Error("Invalid first/last char in cookie domain: " + domain);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Parse cookies of a header
|
|
955
|
+
*
|
|
956
|
+
* @example
|
|
957
|
+
* ```ts
|
|
958
|
+
* import { getCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
959
|
+
*
|
|
960
|
+
* const headers = new Headers();
|
|
961
|
+
* headers.set("Cookie", "full=of; tasty=chocolate");
|
|
962
|
+
*
|
|
963
|
+
* const cookies = getCookies(headers);
|
|
964
|
+
* console.log(cookies); // { full: "of", tasty: "chocolate" }
|
|
965
|
+
* ```
|
|
966
|
+
*
|
|
967
|
+
* @param headers The headers instance to get cookies from
|
|
968
|
+
* @return Object with cookie names as keys
|
|
969
|
+
*/
|
|
970
|
+
function getCookies(headers) {
|
|
971
|
+
const cookie = headers.get("Cookie");
|
|
972
|
+
if (cookie != null) {
|
|
973
|
+
const out = {};
|
|
974
|
+
const c = cookie.split(";");
|
|
975
|
+
for (const kv of c) {
|
|
976
|
+
const [cookieKey, ...cookieVal] = kv.split("=");
|
|
977
|
+
assert(cookieKey != null);
|
|
978
|
+
const key = cookieKey.trim();
|
|
979
|
+
out[key] = cookieVal.join("=");
|
|
980
|
+
}
|
|
981
|
+
return out;
|
|
982
|
+
}
|
|
983
|
+
return {};
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Set the cookie header properly in the headers
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* ```ts
|
|
990
|
+
* import {
|
|
991
|
+
* Cookie,
|
|
992
|
+
* setCookie,
|
|
993
|
+
* } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
994
|
+
*
|
|
995
|
+
* const headers = new Headers();
|
|
996
|
+
* const cookie: Cookie = { name: "Space", value: "Cat" };
|
|
997
|
+
* setCookie(headers, cookie);
|
|
998
|
+
*
|
|
999
|
+
* const cookieHeader = headers.get("set-cookie");
|
|
1000
|
+
* console.log(cookieHeader); // Space=Cat
|
|
1001
|
+
* ```
|
|
1002
|
+
*
|
|
1003
|
+
* @param headers The headers instance to set the cookie to
|
|
1004
|
+
* @param cookie Cookie to set
|
|
1005
|
+
*/
|
|
1006
|
+
function setCookie(headers, cookie) {
|
|
1007
|
+
// Parsing cookie headers to make consistent set-cookie header
|
|
1008
|
+
// ref: https://tools.ietf.org/html/rfc6265#section-4.1.1
|
|
1009
|
+
const v = toString(cookie);
|
|
1010
|
+
if (v) {
|
|
1011
|
+
headers.append("Set-Cookie", v);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
772
1015
|
const GlobalRequest = global.Request;
|
|
773
1016
|
class RapidRequest {
|
|
774
1017
|
#raw;
|
|
775
1018
|
#bodyParsed;
|
|
776
1019
|
#body;
|
|
1020
|
+
#headers;
|
|
1021
|
+
#parsedCookies;
|
|
777
1022
|
method;
|
|
778
1023
|
url;
|
|
779
|
-
headers;
|
|
780
1024
|
constructor(req) {
|
|
781
1025
|
this.#raw = req;
|
|
782
1026
|
this.method = req.method;
|
|
783
1027
|
this.url = new URL(req.url);
|
|
784
|
-
this
|
|
1028
|
+
this.#headers = req.headers;
|
|
785
1029
|
}
|
|
786
1030
|
async parseBody() {
|
|
787
1031
|
if (this.#bodyParsed) {
|
|
@@ -791,7 +1035,7 @@ class RapidRequest {
|
|
|
791
1035
|
const requestMethod = this.method;
|
|
792
1036
|
if (requestMethod === "POST" || requestMethod === "PUT" || requestMethod === "PATCH") {
|
|
793
1037
|
const req = this.#raw;
|
|
794
|
-
const contentType = this
|
|
1038
|
+
const contentType = this.#headers.get("Content-Type");
|
|
795
1039
|
if (contentType.includes("json")) {
|
|
796
1040
|
this.#body = {
|
|
797
1041
|
type: "json",
|
|
@@ -820,6 +1064,15 @@ class RapidRequest {
|
|
|
820
1064
|
get rawRequest() {
|
|
821
1065
|
return this.#raw;
|
|
822
1066
|
}
|
|
1067
|
+
get headers() {
|
|
1068
|
+
return this.#headers;
|
|
1069
|
+
}
|
|
1070
|
+
get cookies() {
|
|
1071
|
+
if (!this.#parsedCookies) {
|
|
1072
|
+
this.#parsedCookies = getCookies(this.#headers);
|
|
1073
|
+
}
|
|
1074
|
+
return this.#parsedCookies;
|
|
1075
|
+
}
|
|
823
1076
|
get body() {
|
|
824
1077
|
if (!this.#bodyParsed) {
|
|
825
1078
|
throw new Error("Request body not parsed, you should call 'parseBody()' method before getting the body.");
|
|
@@ -3450,209 +3703,6 @@ class WebhooksPlugin {
|
|
|
3450
3703
|
}
|
|
3451
3704
|
}
|
|
3452
3705
|
|
|
3453
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
3454
|
-
class AssertionError extends Error {
|
|
3455
|
-
name = "AssertionError";
|
|
3456
|
-
constructor(message) {
|
|
3457
|
-
super(message);
|
|
3458
|
-
}
|
|
3459
|
-
}
|
|
3460
|
-
|
|
3461
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
3462
|
-
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
|
|
3463
|
-
function assert(expr, msg = "") {
|
|
3464
|
-
if (!expr) {
|
|
3465
|
-
throw new AssertionError(msg);
|
|
3466
|
-
}
|
|
3467
|
-
}
|
|
3468
|
-
|
|
3469
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
3470
|
-
// This module is browser compatible.
|
|
3471
|
-
/**
|
|
3472
|
-
* Formats the given date to IMF date time format. (Reference:
|
|
3473
|
-
* https://tools.ietf.org/html/rfc7231#section-7.1.1.1).
|
|
3474
|
-
* IMF is the time format to use when generating times in HTTP
|
|
3475
|
-
* headers. The time being formatted must be in UTC for Format to
|
|
3476
|
-
* generate the correct format.
|
|
3477
|
-
*
|
|
3478
|
-
* @example
|
|
3479
|
-
* ```ts
|
|
3480
|
-
* import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/to_imf.ts";
|
|
3481
|
-
*
|
|
3482
|
-
* toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT"
|
|
3483
|
-
* ```
|
|
3484
|
-
* @param date Date to parse
|
|
3485
|
-
* @return IMF date formatted string
|
|
3486
|
-
*/
|
|
3487
|
-
function toIMF(date) {
|
|
3488
|
-
function dtPad(v, lPad = 2) {
|
|
3489
|
-
return v.padStart(lPad, "0");
|
|
3490
|
-
}
|
|
3491
|
-
const d = dtPad(date.getUTCDate().toString());
|
|
3492
|
-
const h = dtPad(date.getUTCHours().toString());
|
|
3493
|
-
const min = dtPad(date.getUTCMinutes().toString());
|
|
3494
|
-
const s = dtPad(date.getUTCSeconds().toString());
|
|
3495
|
-
const y = date.getUTCFullYear();
|
|
3496
|
-
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
3497
|
-
const months = [
|
|
3498
|
-
"Jan",
|
|
3499
|
-
"Feb",
|
|
3500
|
-
"Mar",
|
|
3501
|
-
"Apr",
|
|
3502
|
-
"May",
|
|
3503
|
-
"Jun",
|
|
3504
|
-
"Jul",
|
|
3505
|
-
"Aug",
|
|
3506
|
-
"Sep",
|
|
3507
|
-
"Oct",
|
|
3508
|
-
"Nov",
|
|
3509
|
-
"Dec",
|
|
3510
|
-
];
|
|
3511
|
-
return `${days[date.getUTCDay()]}, ${d} ${months[date.getUTCMonth()]} ${y} ${h}:${min}:${s} GMT`;
|
|
3512
|
-
}
|
|
3513
|
-
|
|
3514
|
-
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
3515
|
-
const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;
|
|
3516
|
-
function toString(cookie) {
|
|
3517
|
-
if (!cookie.name) {
|
|
3518
|
-
return "";
|
|
3519
|
-
}
|
|
3520
|
-
const out = [];
|
|
3521
|
-
validateName(cookie.name);
|
|
3522
|
-
validateValue(cookie.name, cookie.value);
|
|
3523
|
-
out.push(`${cookie.name}=${cookie.value}`);
|
|
3524
|
-
// Fallback for invalid Set-Cookie
|
|
3525
|
-
// ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
|
|
3526
|
-
if (cookie.name.startsWith("__Secure")) {
|
|
3527
|
-
cookie.secure = true;
|
|
3528
|
-
}
|
|
3529
|
-
if (cookie.name.startsWith("__Host")) {
|
|
3530
|
-
cookie.path = "/";
|
|
3531
|
-
cookie.secure = true;
|
|
3532
|
-
delete cookie.domain;
|
|
3533
|
-
}
|
|
3534
|
-
if (cookie.secure) {
|
|
3535
|
-
out.push("Secure");
|
|
3536
|
-
}
|
|
3537
|
-
if (cookie.httpOnly) {
|
|
3538
|
-
out.push("HttpOnly");
|
|
3539
|
-
}
|
|
3540
|
-
if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) {
|
|
3541
|
-
assert(cookie.maxAge >= 0, "Max-Age must be an integer superior or equal to 0");
|
|
3542
|
-
out.push(`Max-Age=${cookie.maxAge}`);
|
|
3543
|
-
}
|
|
3544
|
-
if (cookie.domain) {
|
|
3545
|
-
validateDomain(cookie.domain);
|
|
3546
|
-
out.push(`Domain=${cookie.domain}`);
|
|
3547
|
-
}
|
|
3548
|
-
if (cookie.sameSite) {
|
|
3549
|
-
out.push(`SameSite=${cookie.sameSite}`);
|
|
3550
|
-
}
|
|
3551
|
-
if (cookie.path) {
|
|
3552
|
-
validatePath(cookie.path);
|
|
3553
|
-
out.push(`Path=${cookie.path}`);
|
|
3554
|
-
}
|
|
3555
|
-
if (cookie.expires) {
|
|
3556
|
-
const { expires } = cookie;
|
|
3557
|
-
const dateString = toIMF(typeof expires === "number" ? new Date(expires) : expires);
|
|
3558
|
-
out.push(`Expires=${dateString}`);
|
|
3559
|
-
}
|
|
3560
|
-
if (cookie.unparsed) {
|
|
3561
|
-
out.push(cookie.unparsed.join("; "));
|
|
3562
|
-
}
|
|
3563
|
-
return out.join("; ");
|
|
3564
|
-
}
|
|
3565
|
-
/**
|
|
3566
|
-
* Validate Cookie Name.
|
|
3567
|
-
* @param name Cookie name.
|
|
3568
|
-
*/
|
|
3569
|
-
function validateName(name) {
|
|
3570
|
-
if (name && !FIELD_CONTENT_REGEXP.test(name)) {
|
|
3571
|
-
throw new TypeError(`Invalid cookie name: "${name}".`);
|
|
3572
|
-
}
|
|
3573
|
-
}
|
|
3574
|
-
/**
|
|
3575
|
-
* Validate Path Value.
|
|
3576
|
-
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1.2.4}.
|
|
3577
|
-
* @param path Path value.
|
|
3578
|
-
*/
|
|
3579
|
-
function validatePath(path) {
|
|
3580
|
-
if (path == null) {
|
|
3581
|
-
return;
|
|
3582
|
-
}
|
|
3583
|
-
for (let i = 0; i < path.length; i++) {
|
|
3584
|
-
const c = path.charAt(i);
|
|
3585
|
-
if (c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7E) || c == ";") {
|
|
3586
|
-
throw new Error(path + ": Invalid cookie path char '" + c + "'");
|
|
3587
|
-
}
|
|
3588
|
-
}
|
|
3589
|
-
}
|
|
3590
|
-
/**
|
|
3591
|
-
* Validate Cookie Value.
|
|
3592
|
-
* See {@link https://tools.ietf.org/html/rfc6265#section-4.1}.
|
|
3593
|
-
* @param value Cookie value.
|
|
3594
|
-
*/
|
|
3595
|
-
function validateValue(name, value) {
|
|
3596
|
-
if (value == null || name == null)
|
|
3597
|
-
return;
|
|
3598
|
-
for (let i = 0; i < value.length; i++) {
|
|
3599
|
-
const c = value.charAt(i);
|
|
3600
|
-
if (c < String.fromCharCode(0x21) || c == String.fromCharCode(0x22) ||
|
|
3601
|
-
c == String.fromCharCode(0x2c) || c == String.fromCharCode(0x3b) ||
|
|
3602
|
-
c == String.fromCharCode(0x5c) || c == String.fromCharCode(0x7f)) {
|
|
3603
|
-
throw new Error("RFC2616 cookie '" + name + "' cannot contain character '" + c + "'");
|
|
3604
|
-
}
|
|
3605
|
-
if (c > String.fromCharCode(0x80)) {
|
|
3606
|
-
throw new Error("RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" +
|
|
3607
|
-
c.charCodeAt(0).toString(16));
|
|
3608
|
-
}
|
|
3609
|
-
}
|
|
3610
|
-
}
|
|
3611
|
-
/**
|
|
3612
|
-
* Validate Cookie Domain.
|
|
3613
|
-
* See {@link https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3}.
|
|
3614
|
-
* @param domain Cookie domain.
|
|
3615
|
-
*/
|
|
3616
|
-
function validateDomain(domain) {
|
|
3617
|
-
if (domain == null) {
|
|
3618
|
-
return;
|
|
3619
|
-
}
|
|
3620
|
-
const char1 = domain.charAt(0);
|
|
3621
|
-
const charN = domain.charAt(domain.length - 1);
|
|
3622
|
-
if (char1 == "-" || charN == "." || charN == "-") {
|
|
3623
|
-
throw new Error("Invalid first/last char in cookie domain: " + domain);
|
|
3624
|
-
}
|
|
3625
|
-
}
|
|
3626
|
-
/**
|
|
3627
|
-
* Set the cookie header properly in the headers
|
|
3628
|
-
*
|
|
3629
|
-
* @example
|
|
3630
|
-
* ```ts
|
|
3631
|
-
* import {
|
|
3632
|
-
* Cookie,
|
|
3633
|
-
* setCookie,
|
|
3634
|
-
* } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
|
|
3635
|
-
*
|
|
3636
|
-
* const headers = new Headers();
|
|
3637
|
-
* const cookie: Cookie = { name: "Space", value: "Cat" };
|
|
3638
|
-
* setCookie(headers, cookie);
|
|
3639
|
-
*
|
|
3640
|
-
* const cookieHeader = headers.get("set-cookie");
|
|
3641
|
-
* console.log(cookieHeader); // Space=Cat
|
|
3642
|
-
* ```
|
|
3643
|
-
*
|
|
3644
|
-
* @param headers The headers instance to set the cookie to
|
|
3645
|
-
* @param cookie Cookie to set
|
|
3646
|
-
*/
|
|
3647
|
-
function setCookie(headers, cookie) {
|
|
3648
|
-
// Parsing cookie headers to make consistent set-cookie header
|
|
3649
|
-
// ref: https://tools.ietf.org/html/rfc6265#section-4.1.1
|
|
3650
|
-
const v = toString(cookie);
|
|
3651
|
-
if (v) {
|
|
3652
|
-
headers.append("Set-Cookie", v);
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
|
|
3656
3706
|
const code$5 = "createSession";
|
|
3657
3707
|
async function handler$5(plugin, ctx, options) {
|
|
3658
3708
|
const { server, input, routerContext } = ctx;
|
|
@@ -3868,7 +3918,7 @@ var pluginRoutes = [
|
|
|
3868
3918
|
/**
|
|
3869
3919
|
* Auth manager plugin
|
|
3870
3920
|
*/
|
|
3871
|
-
class
|
|
3921
|
+
class AuthPlugin {
|
|
3872
3922
|
get code() {
|
|
3873
3923
|
return "authManager";
|
|
3874
3924
|
}
|
|
@@ -3914,6 +3964,26 @@ class AuthManager {
|
|
|
3914
3964
|
}
|
|
3915
3965
|
async onApplicationReady(server, applicationConfig) {
|
|
3916
3966
|
}
|
|
3967
|
+
async onPrepareRouteContext(server, routeContext) {
|
|
3968
|
+
const request = routeContext.request;
|
|
3969
|
+
let token;
|
|
3970
|
+
const headers = request.headers;
|
|
3971
|
+
// No Authorization header
|
|
3972
|
+
if (headers.has("Authorization")) {
|
|
3973
|
+
// Authorization header has no Bearer or no token
|
|
3974
|
+
const authHeader = headers.get("Authorization");
|
|
3975
|
+
if (!authHeader.startsWith("Bearer ") || authHeader.length <= 7) {
|
|
3976
|
+
throw new Error('AUTHORIZATION_HEADER_INVALID');
|
|
3977
|
+
}
|
|
3978
|
+
token = authHeader.slice(7);
|
|
3979
|
+
}
|
|
3980
|
+
else {
|
|
3981
|
+
token = request.cookies[server.config.sessionCookieName];
|
|
3982
|
+
}
|
|
3983
|
+
const tokenPayload = verifyJwt(token, server.config.jwtKey);
|
|
3984
|
+
routeContext.state.userId = tokenPayload.aud;
|
|
3985
|
+
routeContext.state.userLogin = tokenPayload.act;
|
|
3986
|
+
}
|
|
3917
3987
|
}
|
|
3918
3988
|
|
|
3919
3989
|
async function readFile(path) {
|
|
@@ -4076,7 +4146,7 @@ class FileManager {
|
|
|
4076
4146
|
|
|
4077
4147
|
fixBigIntJSONSerialize();
|
|
4078
4148
|
|
|
4079
|
-
exports.AuthPlugin =
|
|
4149
|
+
exports.AuthPlugin = AuthPlugin;
|
|
4080
4150
|
exports.DataManagePlugin = DataManager;
|
|
4081
4151
|
exports.FileManagePlugin = FileManager;
|
|
4082
4152
|
exports.GlobalRequest = GlobalRequest;
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { RpdApplicationConfig } from "~/types";
|
|
5
5
|
import { IRpdServer, RapidPlugin, RpdConfigurationItemOptions, RpdServerPluginConfigurableTargetOptions, RpdServerPluginExtendingAbilities } from "~/core/server";
|
|
6
|
-
|
|
6
|
+
import { RouteContext } from "~/core/routeContext";
|
|
7
|
+
declare class AuthPlugin implements RapidPlugin {
|
|
7
8
|
get code(): string;
|
|
8
9
|
get description(): string;
|
|
9
10
|
get extendingAbilities(): RpdServerPluginExtendingAbilities[];
|
|
@@ -21,5 +22,6 @@ declare class AuthManager implements RapidPlugin {
|
|
|
21
22
|
configureRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any>;
|
|
22
23
|
onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any>;
|
|
23
24
|
onApplicationReady(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any>;
|
|
25
|
+
onPrepareRouteContext(server: IRpdServer, routeContext: RouteContext): Promise<void>;
|
|
24
26
|
}
|
|
25
|
-
export default
|
|
27
|
+
export default AuthPlugin;
|
|
@@ -5,7 +5,7 @@ declare const _default: ({
|
|
|
5
5
|
type: "RESTful";
|
|
6
6
|
method: "GET";
|
|
7
7
|
endpoint: string;
|
|
8
|
-
|
|
8
|
+
actions: {
|
|
9
9
|
code: string;
|
|
10
10
|
}[];
|
|
11
11
|
} | {
|
|
@@ -15,7 +15,7 @@ declare const _default: ({
|
|
|
15
15
|
type: "RESTful";
|
|
16
16
|
method: "POST";
|
|
17
17
|
endpoint: string;
|
|
18
|
-
|
|
18
|
+
actions: {
|
|
19
19
|
code: string;
|
|
20
20
|
}[];
|
|
21
21
|
})[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Secret } from "jsonwebtoken";
|
|
1
|
+
import { Secret, JwtPayload } from "jsonwebtoken";
|
|
2
2
|
export declare function createJwt(payload: Record<string, any>, secret: Secret): string;
|
|
3
|
-
export declare function verifyJwt(token: string, secret: Secret):
|
|
3
|
+
export declare function verifyJwt(token: string, secret: Secret): JwtPayload;
|
|
4
4
|
export declare function decodeJwt(token: string): import("jsonwebtoken").Jwt;
|
|
5
5
|
export declare function generateJwtSecretKey(): Promise<string>;
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RpdApplicationConfig } from "~/types";
|
|
2
2
|
import { IRpdServer, RapidPlugin } from "./server";
|
|
3
|
+
import { RouteContext } from "./routeContext";
|
|
3
4
|
|
|
4
5
|
class PluginManager {
|
|
5
6
|
#server: IRpdServer;
|
|
@@ -139,6 +140,18 @@ class PluginManager {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
}
|
|
143
|
+
|
|
144
|
+
/** 在接收到HTTP请求,准备路由上下文时调用。 */
|
|
145
|
+
async onPrepareRouteContext(
|
|
146
|
+
server: IRpdServer,
|
|
147
|
+
routeContext: RouteContext,
|
|
148
|
+
) {
|
|
149
|
+
for (const plugin of this.#plugins) {
|
|
150
|
+
if (plugin.onPrepareRouteContext) {
|
|
151
|
+
await plugin.onPrepareRouteContext(server, routeContext);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
export default PluginManager;
|
package/src/core/request.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import qs from "qs";
|
|
2
2
|
import { parseFormDataBody } from "./http/formDataParser";
|
|
3
|
+
import { getCookies } from "~/deno-std/http/cookie";
|
|
3
4
|
|
|
4
5
|
export const GlobalRequest = global.Request;
|
|
5
6
|
|
|
@@ -12,15 +13,16 @@ export class RapidRequest {
|
|
|
12
13
|
#raw: Request;
|
|
13
14
|
#bodyParsed: boolean;
|
|
14
15
|
#body: RapidRequestBody;
|
|
16
|
+
#headers: Headers;
|
|
17
|
+
#parsedCookies: Record<string, string>;
|
|
15
18
|
method: string;
|
|
16
19
|
url: URL;
|
|
17
|
-
headers: Headers;
|
|
18
20
|
|
|
19
21
|
constructor(req: Request) {
|
|
20
22
|
this.#raw = req;
|
|
21
23
|
this.method = req.method;
|
|
22
24
|
this.url = new URL(req.url);
|
|
23
|
-
this
|
|
25
|
+
this.#headers = req.headers;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
async parseBody(): Promise<void> {
|
|
@@ -32,7 +34,7 @@ export class RapidRequest {
|
|
|
32
34
|
const requestMethod = this.method;
|
|
33
35
|
if (requestMethod === "POST" || requestMethod === "PUT" || requestMethod === "PATCH") {
|
|
34
36
|
const req = this.#raw;
|
|
35
|
-
const contentType = this
|
|
37
|
+
const contentType = this.#headers.get("Content-Type");
|
|
36
38
|
if (contentType.includes("json")) {
|
|
37
39
|
this.#body = {
|
|
38
40
|
type: "json",
|
|
@@ -60,6 +62,17 @@ export class RapidRequest {
|
|
|
60
62
|
return this.#raw;
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
get headers(): Headers {
|
|
66
|
+
return this.#headers;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get cookies(): Record<string, string> {
|
|
70
|
+
if (!this.#parsedCookies) {
|
|
71
|
+
this.#parsedCookies = getCookies(this.#headers);
|
|
72
|
+
}
|
|
73
|
+
return this.#parsedCookies;
|
|
74
|
+
}
|
|
75
|
+
|
|
63
76
|
get body(): RapidRequestBody {
|
|
64
77
|
if (!this.#bodyParsed) {
|
|
65
78
|
throw new Error("Request body not parsed, you should call 'parseBody()' method before getting the body.")
|
package/src/core/server.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GetDataAccessorOptions, GetModelOptions, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RapidServerConfig, RpdApplicationConfig, RpdDataModel, RpdServerEventTypes } from "~/types";
|
|
2
2
|
import { IPluginActionHandler, ActionHandler } from "./actionHandler";
|
|
3
|
-
import { Next } from "./routeContext";
|
|
3
|
+
import { Next, RouteContext } from "./routeContext";
|
|
4
4
|
|
|
5
5
|
export interface IRpdServer {
|
|
6
6
|
config: RapidServerConfig;
|
|
@@ -100,28 +100,29 @@ export interface RapidPlugin {
|
|
|
100
100
|
/** 插件的全局配置项 */
|
|
101
101
|
get configurations(): RpdConfigurationItemOptions[];
|
|
102
102
|
/** 初始化插件时调用。插件可以在此时进行一些内部对象的初始化工作。 */
|
|
103
|
-
initPlugin(server: IRpdServer)
|
|
103
|
+
initPlugin?: (server: IRpdServer) => Promise<any>;
|
|
104
104
|
/** 注册中间件 */
|
|
105
|
-
registerMiddlewares(server: IRpdServer)
|
|
105
|
+
registerMiddlewares?: (server: IRpdServer) => Promise<any>;
|
|
106
106
|
/** 注册接口动作处理程序 */
|
|
107
|
-
registerActionHandlers(server: IRpdServer)
|
|
107
|
+
registerActionHandlers?: (server: IRpdServer) => Promise<any>;
|
|
108
108
|
/** 注册事件处理程序 */
|
|
109
|
-
registerEventHandlers(server: IRpdServer)
|
|
109
|
+
registerEventHandlers?: (server: IRpdServer) => Promise<any>;
|
|
110
110
|
/** 注册消息处理程序 */
|
|
111
|
-
registerMessageHandlers(server: IRpdServer)
|
|
111
|
+
registerMessageHandlers?: (server: IRpdServer) => Promise<any>;
|
|
112
112
|
/** 注册任务处理程序 */
|
|
113
|
-
registerTaskProcessors(server: IRpdServer)
|
|
113
|
+
registerTaskProcessors?: (server: IRpdServer) => Promise<any>;
|
|
114
114
|
/** 在加载应用前调用。 */
|
|
115
|
-
onLoadingApplication(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
115
|
+
onLoadingApplication?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
116
116
|
/** 配置数据集合 */
|
|
117
|
-
configureModels(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
117
|
+
configureModels?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
118
118
|
/** 配置模型属性 */
|
|
119
|
-
configureModelProperties(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
119
|
+
configureModelProperties?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
120
120
|
/** 配置路由 */
|
|
121
|
-
configureRoutes(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
121
|
+
configureRoutes?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
122
122
|
/** 在应用配置加载完成后调用。此时插件可以进行一些数据的初始化工作。 */
|
|
123
|
-
onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
123
|
+
onApplicationLoaded?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
124
124
|
/** 在应用准备完成后调用。此时服务器已经可以处理网络请求,可以对外广播消息。 */
|
|
125
|
-
onApplicationReady(server: IRpdServer, applicationConfig: RpdApplicationConfig)
|
|
125
|
+
onApplicationReady?: (server: IRpdServer, applicationConfig: RpdApplicationConfig) => Promise<any>;
|
|
126
|
+
/** 在接收到HTTP请求,准备路由上下文时调用。 */
|
|
127
|
+
onPrepareRouteContext?: (server: IRpdServer, routeContext: RouteContext) => Promise<any>;
|
|
126
128
|
}
|
|
127
|
-
|
package/src/index.ts
CHANGED
|
@@ -14,9 +14,9 @@ export * from "./utilities/jwtUtility";
|
|
|
14
14
|
|
|
15
15
|
export * as bootstrapApplicationConfig from "./bootstrapApplicationConfig";
|
|
16
16
|
|
|
17
|
-
export { default as MetaManagePlugin } from "./plugins/metaManage/
|
|
18
|
-
export { default as DataManagePlugin } from "./plugins/dataManage/
|
|
19
|
-
export { default as RouteManagePlugin } from "./plugins/routeManage/
|
|
20
|
-
export { default as WebhooksPlugin } from "./plugins/webhooks/
|
|
21
|
-
export { default as AuthPlugin } from "./plugins/auth/
|
|
22
|
-
export { default as FileManagePlugin } from "./plugins/fileManage/
|
|
17
|
+
export { default as MetaManagePlugin } from "./plugins/metaManage/MetaManagePlugin";
|
|
18
|
+
export { default as DataManagePlugin } from "./plugins/dataManage/DataManagePlugin";
|
|
19
|
+
export { default as RouteManagePlugin } from "./plugins/routeManage/RouteManagePlugin";
|
|
20
|
+
export { default as WebhooksPlugin } from "./plugins/webhooks/WebhooksPlugin";
|
|
21
|
+
export { default as AuthPlugin } from "./plugins/auth/AuthPlugin";
|
|
22
|
+
export { default as FileManagePlugin } from "./plugins/fileManage/FileManagePlugin";
|
|
@@ -11,9 +11,11 @@ import { IRpdServer, RapidPlugin, RpdConfigurationItemOptions, RpdServerPluginCo
|
|
|
11
11
|
import pluginActionHandlers from "./actionHandlers";
|
|
12
12
|
import pluginModels from "./models";
|
|
13
13
|
import pluginRoutes from "./routes";
|
|
14
|
+
import { RouteContext } from "~/core/routeContext";
|
|
15
|
+
import { verifyJwt } from "~/utilities/jwtUtility";
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
class
|
|
18
|
+
class AuthPlugin implements RapidPlugin {
|
|
17
19
|
get code(): string {
|
|
18
20
|
return "authManager";
|
|
19
21
|
}
|
|
@@ -75,6 +77,29 @@ class AuthManager implements RapidPlugin {
|
|
|
75
77
|
|
|
76
78
|
async onApplicationReady(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<any> {
|
|
77
79
|
}
|
|
80
|
+
|
|
81
|
+
async onPrepareRouteContext(server: IRpdServer, routeContext: RouteContext) {
|
|
82
|
+
const request = routeContext.request;
|
|
83
|
+
let token: string;
|
|
84
|
+
|
|
85
|
+
const headers = request.headers;
|
|
86
|
+
// No Authorization header
|
|
87
|
+
if (headers.has("Authorization")) {
|
|
88
|
+
// Authorization header has no Bearer or no token
|
|
89
|
+
const authHeader = headers.get("Authorization")!;
|
|
90
|
+
if (!authHeader.startsWith("Bearer ") || authHeader.length <= 7) {
|
|
91
|
+
throw new Error('AUTHORIZATION_HEADER_INVALID');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
token = authHeader.slice(7);
|
|
95
|
+
} else {
|
|
96
|
+
token = request.cookies[server.config.sessionCookieName];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const tokenPayload = verifyJwt(token, server.config.jwtKey);
|
|
100
|
+
routeContext.state.userId = tokenPayload.aud as string;
|
|
101
|
+
routeContext.state.userLogin = tokenPayload.act as string;
|
|
102
|
+
}
|
|
78
103
|
}
|
|
79
104
|
|
|
80
|
-
export default
|
|
105
|
+
export default AuthPlugin;
|
package/src/server.ts
CHANGED
|
@@ -230,6 +230,7 @@ export class RapidServer implements IRpdServer {
|
|
|
230
230
|
const rapidRequest = new RapidRequest(request);
|
|
231
231
|
await rapidRequest.parseBody();
|
|
232
232
|
const routeContext = new RouteContext(rapidRequest);
|
|
233
|
+
|
|
233
234
|
await this.#buildedRoutes(routeContext, next);
|
|
234
235
|
return routeContext.response.getResponse();
|
|
235
236
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sign, verify, decode, Secret } from "jsonwebtoken";
|
|
1
|
+
import { sign, verify, decode, Secret, JwtPayload } from "jsonwebtoken";
|
|
2
2
|
import { encode as base64Encode } from "~/deno-std/encoding/base64";
|
|
3
3
|
import crypto from "crypto";
|
|
4
4
|
|
|
@@ -8,10 +8,10 @@ export function createJwt(payload: Record<string, any>, secret: Secret) {
|
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function verifyJwt(token: string, secret: Secret) {
|
|
11
|
+
export function verifyJwt(token: string, secret: Secret): JwtPayload {
|
|
12
12
|
return verify(token, secret, {
|
|
13
13
|
algorithms: ['HS512'],
|
|
14
|
-
});
|
|
14
|
+
}) as JwtPayload;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function decodeJwt(token: string) {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|