@lark-apaas/fullstack-nestjs-core 0.1.0-alpha.10 → 0.1.0-alpha.11
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/index.cjs +63 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +63 -72
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.cjs
CHANGED
|
@@ -5,11 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
9
|
-
var __typeError = (msg) => {
|
|
10
|
-
throw TypeError(msg);
|
|
11
|
-
};
|
|
12
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
14
9
|
var __export = (target, all) => {
|
|
15
10
|
for (var name in all)
|
|
@@ -32,44 +27,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
27
|
mod
|
|
33
28
|
));
|
|
34
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
|
-
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
36
|
-
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
37
|
-
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
38
|
-
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
39
|
-
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
40
|
-
var __runInitializers = (array, flags, self, value) => {
|
|
41
|
-
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
42
|
-
return value;
|
|
43
|
-
};
|
|
44
|
-
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
45
|
-
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
46
|
-
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
47
|
-
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
48
|
-
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
49
|
-
return __privateGet(this, extra);
|
|
50
|
-
}, set [name](x) {
|
|
51
|
-
return __privateSet(this, extra, x);
|
|
52
|
-
} }, name));
|
|
53
|
-
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
54
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
55
|
-
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
56
|
-
if (k) {
|
|
57
|
-
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
58
|
-
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
59
|
-
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
60
|
-
}
|
|
61
|
-
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
62
|
-
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
63
|
-
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
64
|
-
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
65
|
-
}
|
|
66
|
-
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
67
|
-
};
|
|
68
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
69
|
-
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
70
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
71
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
72
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
73
30
|
|
|
74
31
|
// src/index.ts
|
|
75
32
|
var index_exports = {};
|
|
@@ -93,6 +50,7 @@ function normalizeBasePath(rawBasePath) {
|
|
|
93
50
|
const normalizedBasePath = rawBasePath.startsWith("/") ? rawBasePath : `/${rawBasePath}`;
|
|
94
51
|
return normalizedBasePath.endsWith("/") ? normalizedBasePath.slice(0, -1) : normalizedBasePath;
|
|
95
52
|
}
|
|
53
|
+
__name(normalizeBasePath, "normalizeBasePath");
|
|
96
54
|
function resolveOptsWithDefaultValue(options) {
|
|
97
55
|
const basePath = normalizeBasePath(options.basePath || "/");
|
|
98
56
|
const docsPath = normalizeBasePath(options.docsPath || `api/docs`);
|
|
@@ -112,26 +70,35 @@ function resolveOptsWithDefaultValue(options) {
|
|
|
112
70
|
}
|
|
113
71
|
};
|
|
114
72
|
}
|
|
73
|
+
__name(resolveOptsWithDefaultValue, "resolveOptsWithDefaultValue");
|
|
115
74
|
function ensureDirAndWrite(filePath, content) {
|
|
116
75
|
const dir = (0, import_node_path.dirname)(filePath);
|
|
117
|
-
(0, import_node_fs.mkdirSync)(dir, {
|
|
76
|
+
(0, import_node_fs.mkdirSync)(dir, {
|
|
77
|
+
recursive: true
|
|
78
|
+
});
|
|
118
79
|
(0, import_node_fs.writeFileSync)(filePath, content);
|
|
119
80
|
}
|
|
81
|
+
__name(ensureDirAndWrite, "ensureDirAndWrite");
|
|
120
82
|
|
|
121
83
|
// src/modules/devtool/index.ts
|
|
122
84
|
var DevToolsModule = class {
|
|
85
|
+
static {
|
|
86
|
+
__name(this, "DevToolsModule");
|
|
87
|
+
}
|
|
123
88
|
static async mount(app, opts = {}) {
|
|
124
89
|
const options = resolveOptsWithDefaultValue(opts);
|
|
125
90
|
const baseDirname = process.cwd();
|
|
126
91
|
const builder = new import_swagger.DocumentBuilder().setTitle(options.swaggerOptions.title).setVersion(options.swaggerOptions.version);
|
|
127
92
|
const document = import_swagger.SwaggerModule.createDocument(app, builder.build(), {
|
|
128
|
-
operationIdFactory: (_c, m) => m
|
|
93
|
+
operationIdFactory: /* @__PURE__ */ __name((_c, m) => m, "operationIdFactory")
|
|
129
94
|
});
|
|
130
95
|
if (options.needSetupServer) {
|
|
131
96
|
import_swagger.SwaggerModule.setup(options.docsPath, app, document, {
|
|
132
97
|
customSiteTitle: options.swaggerOptions.customSiteTitle,
|
|
133
98
|
customCss: options.swaggerOptions.customCss,
|
|
134
|
-
swaggerOptions: {
|
|
99
|
+
swaggerOptions: {
|
|
100
|
+
persistAuthorization: true
|
|
101
|
+
}
|
|
135
102
|
});
|
|
136
103
|
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
137
104
|
}
|
|
@@ -139,7 +106,9 @@ var DevToolsModule = class {
|
|
|
139
106
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
140
107
|
if (options.needGenerateClientSdk) {
|
|
141
108
|
const clientSdkOutPath = (0, import_node_path2.resolve)(baseDirname, options.clientSdkOut);
|
|
142
|
-
(0, import_node_fs2.mkdirSync)(clientSdkOutPath, {
|
|
109
|
+
(0, import_node_fs2.mkdirSync)(clientSdkOutPath, {
|
|
110
|
+
recursive: true
|
|
111
|
+
});
|
|
143
112
|
const { generate } = await import("openapi-typescript-codegen");
|
|
144
113
|
await generate({
|
|
145
114
|
input: openapiPath,
|
|
@@ -166,21 +135,29 @@ function resolveCsrfTokenOptions(options) {
|
|
|
166
135
|
cookiePath: options.cookiePath ?? "/"
|
|
167
136
|
};
|
|
168
137
|
}
|
|
138
|
+
__name(resolveCsrfTokenOptions, "resolveCsrfTokenOptions");
|
|
169
139
|
function genToken() {
|
|
170
140
|
const ts = Math.floor(Date.now() / 1e3);
|
|
171
|
-
const randInt64 = BigInt(
|
|
172
|
-
"0x" + import_crypto.default.randomBytes(8).toString("hex")
|
|
173
|
-
).toString();
|
|
141
|
+
const randInt64 = BigInt("0x" + import_crypto.default.randomBytes(8).toString("hex")).toString();
|
|
174
142
|
const s = `${randInt64}.${ts}`;
|
|
175
143
|
const sha1 = import_crypto.default.createHash("sha1");
|
|
176
144
|
sha1.update(s);
|
|
177
145
|
return `${sha1.digest("hex")}-${ts}`;
|
|
178
146
|
}
|
|
147
|
+
__name(genToken, "genToken");
|
|
179
148
|
|
|
180
149
|
// src/middlewares/csrf_token/index.ts
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
150
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
151
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
152
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
153
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
154
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
155
|
+
}
|
|
156
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
157
|
+
var CsrfTokenMiddleware = class _CsrfTokenMiddleware {
|
|
158
|
+
static {
|
|
159
|
+
__name(this, "CsrfTokenMiddleware");
|
|
160
|
+
}
|
|
184
161
|
static options;
|
|
185
162
|
static configure(opts) {
|
|
186
163
|
this.options = resolveCsrfTokenOptions(opts);
|
|
@@ -201,16 +178,14 @@ var _CsrfTokenMiddleware = class _CsrfTokenMiddleware {
|
|
|
201
178
|
secure: true,
|
|
202
179
|
sameSite: "none",
|
|
203
180
|
partitioned: true
|
|
204
|
-
// 默认开启 Partitioned Cookie
|
|
205
181
|
});
|
|
206
182
|
next();
|
|
207
183
|
}
|
|
208
184
|
}
|
|
209
185
|
};
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
var CsrfTokenMiddleware = _CsrfTokenMiddleware;
|
|
186
|
+
CsrfTokenMiddleware = _ts_decorate([
|
|
187
|
+
(0, import_common.Injectable)()
|
|
188
|
+
], CsrfTokenMiddleware);
|
|
214
189
|
|
|
215
190
|
// src/middlewares/csrf/index.ts
|
|
216
191
|
var import_common2 = require("@nestjs/common");
|
|
@@ -223,14 +198,24 @@ function resolveCsrfOptions(options) {
|
|
|
223
198
|
cookieKey: options.cookieKey ?? "suda-csrf-token"
|
|
224
199
|
};
|
|
225
200
|
}
|
|
201
|
+
__name(resolveCsrfOptions, "resolveCsrfOptions");
|
|
226
202
|
function sendForbidden(res, message) {
|
|
227
203
|
res.status(403).send(`Forbidden\uFF0C${message}`);
|
|
228
204
|
}
|
|
205
|
+
__name(sendForbidden, "sendForbidden");
|
|
229
206
|
|
|
230
207
|
// src/middlewares/csrf/index.ts
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
208
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
209
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
210
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
211
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
212
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
213
|
+
}
|
|
214
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
215
|
+
var CsrfMiddleware = class _CsrfMiddleware {
|
|
216
|
+
static {
|
|
217
|
+
__name(this, "CsrfMiddleware");
|
|
218
|
+
}
|
|
234
219
|
static options;
|
|
235
220
|
static configure(opts) {
|
|
236
221
|
this.options = resolveCsrfOptions(opts);
|
|
@@ -254,10 +239,9 @@ var _CsrfMiddleware = class _CsrfMiddleware {
|
|
|
254
239
|
next();
|
|
255
240
|
}
|
|
256
241
|
};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
var CsrfMiddleware = _CsrfMiddleware;
|
|
242
|
+
CsrfMiddleware = _ts_decorate2([
|
|
243
|
+
(0, import_common2.Injectable)()
|
|
244
|
+
], CsrfMiddleware);
|
|
261
245
|
|
|
262
246
|
// src/middlewares/user-context/index.ts
|
|
263
247
|
var import_common3 = require("@nestjs/common");
|
|
@@ -279,11 +263,20 @@ function getWebUserFromHeader(req) {
|
|
|
279
263
|
}
|
|
280
264
|
return null;
|
|
281
265
|
}
|
|
266
|
+
__name(getWebUserFromHeader, "getWebUserFromHeader");
|
|
282
267
|
|
|
283
268
|
// src/middlewares/user-context/index.ts
|
|
284
|
-
|
|
285
|
-
|
|
269
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
270
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
271
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
272
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
273
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
274
|
+
}
|
|
275
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
286
276
|
var UserContextMiddleware = class {
|
|
277
|
+
static {
|
|
278
|
+
__name(this, "UserContextMiddleware");
|
|
279
|
+
}
|
|
287
280
|
use(req, _res, next) {
|
|
288
281
|
const webUser = getWebUserFromHeader(req);
|
|
289
282
|
req.userContext = {
|
|
@@ -294,9 +287,9 @@ var UserContextMiddleware = class {
|
|
|
294
287
|
next();
|
|
295
288
|
}
|
|
296
289
|
};
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
290
|
+
UserContextMiddleware = _ts_decorate3([
|
|
291
|
+
(0, import_common3.Injectable)()
|
|
292
|
+
], UserContextMiddleware);
|
|
300
293
|
// Annotate the CommonJS export names for ESM import in node:
|
|
301
294
|
0 && (module.exports = {
|
|
302
295
|
CsrfMiddleware,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/modules/devtool/index.ts","../src/modules/devtool/helper.ts","../src/middlewares/csrf_token/index.ts","../src/middlewares/csrf_token/helper.ts","../src/middlewares/csrf/index.ts","../src/middlewares/csrf/helper.ts","../src/middlewares/user-context/index.ts","../src/middlewares/user-context/helper.ts"],"sourcesContent":["import './types/express.d';\n\nexport { DevToolsModule } from './modules/devtool';\n\n// Middlewares\nexport { CsrfTokenMiddleware } from './middlewares/csrf_token';\nexport { CsrfMiddleware } from './middlewares/csrf';\nexport { UserContextMiddleware } from './middlewares/user-context';\n","import type { INestApplication } from '@nestjs/common';\n\nimport { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';\nimport { mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { resolveOptsWithDefaultValue, ensureDirAndWrite } from './helper';\nimport { DevToolsOptions } from './type';\n\nexport class DevToolsModule {\n static async mount(app: INestApplication, opts: DevToolsOptions = {}) {\n const options = resolveOptsWithDefaultValue(opts);\n const baseDirname = process.cwd(); // 跟随命令的根目录\n\n // 1) 生成 Swagger 文档\n const builder = new DocumentBuilder()\n .setTitle(options.swaggerOptions.title)\n .setVersion(options.swaggerOptions.version);\n const document = SwaggerModule.createDocument(app, builder.build(), {\n operationIdFactory: (_c, m) => m,\n });\n // 1.1) 挂载 Swagger UI(可关)\n if(options.needSetupServer){\n SwaggerModule.setup(options.docsPath, app, document, {\n customSiteTitle: options.swaggerOptions.customSiteTitle,\n customCss: options.swaggerOptions.customCss,\n swaggerOptions: { persistAuthorization: true },\n });\n console.log(`[OpenAPI] Swagger UI 已挂载至 ${options.docsPath}`);\n }\n\n // 2) 导出 openapi.json\n const openapiPath = resolve(baseDirname, options.openapiOut);\n ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));\n\n // 3) 生成 axios SDK(可关)\n if (options.needGenerateClientSdk) {\n const clientSdkOutPath = resolve(baseDirname, options.clientSdkOut);\n mkdirSync(clientSdkOutPath, { recursive: true });\n const { generate } = await import('openapi-typescript-codegen');\n await generate({\n input: openapiPath,\n output: clientSdkOutPath,\n httpClient: 'axios',\n useOptions: false,\n exportServices: true,\n });\n console.log('[OpenAPI] 导出 openapi.json 并生成 axios SDK ✅');\n }\n }\n}\n","import { dirname } from 'node:path';\nimport { writeFileSync, mkdirSync } from 'node:fs';\n\nimport { DevToolsOptions } from './type';\n/**\n * 标准化基础路径,确保以 '/' 开头且以 '/' 结尾\n *\n * @param rawBasePath 原始的基础路径,可能以 '/' 开头或结尾\n * @returns 标准化后的基础路径,确保以 '/' 开头且不以 '/' 结尾\n */\nexport function normalizeBasePath(rawBasePath: string): string {\n const normalizedBasePath = rawBasePath.startsWith('/')\n ? rawBasePath\n : `/${rawBasePath}`;\n return normalizedBasePath.endsWith('/')\n ? normalizedBasePath.slice(0, -1)\n : normalizedBasePath;\n}\n\ntype ResolvedDevToolsOptions = Required<\n Omit<DevToolsOptions, 'swaggerOptions'>\n> & {\n swaggerOptions: Required<NonNullable<DevToolsOptions['swaggerOptions']>>;\n};\n\nexport function resolveOptsWithDefaultValue(\n options: DevToolsOptions,\n): ResolvedDevToolsOptions {\n const basePath = normalizeBasePath(options.basePath || '/');\n const docsPath = normalizeBasePath(options.docsPath || `api/docs`);\n return {\n ...options,\n needSetupServer: options.needSetupServer ?? false,\n basePath,\n docsPath: `${basePath}${docsPath}`,\n openapiOut: options.openapiOut || './client/src/api/gen/openapi.json',\n clientSdkOut: options.clientSdkOut || './client/src/api/gen',\n needGenerateClientSdk: options.needGenerateClientSdk ?? true,\n swaggerOptions: {\n title: options.swaggerOptions?.title ?? 'NestJS Fullstack API',\n version: options.swaggerOptions?.version ?? '1.0.0',\n customSiteTitle:\n options.swaggerOptions?.customSiteTitle ?? 'API Documentation',\n customCss:\n options.swaggerOptions?.customCss ??\n '.swagger-ui .topbar { display: none }',\n },\n };\n}\n\nexport function ensureDirAndWrite(filePath: string, content: string) {\n // 1. 拿到文件的上级目录\n const dir = dirname(filePath);\n\n // 2. 确保目录存在,不存在就递归创建\n mkdirSync(dir, { recursive: true });\n\n // 3. 写文件\n writeFileSync(filePath, content);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\nimport { resolveCsrfTokenOptions, genToken } from './helper';\n\n@Injectable()\nexport class CsrfTokenMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfTokenOptions;\n\n public static configure(opts: CsrfTokenOptions) {\n this.options = resolveCsrfTokenOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { cookieKey, cookieMaxAge, cookiePath } = CsrfTokenMiddleware.options;\n const originToken = req.cookies[cookieKey.toLowerCase()];\n // 如果存在 Cookie,则直接消费,无需生成\n if (originToken) {\n req.csrfToken = originToken;\n next();\n } else {\n // 如果不存在 token,则生成新的 csrfToken,并 setCookie\n const token = genToken();\n req.csrfToken = token;\n res.cookie(cookieKey, token, {\n maxAge: cookieMaxAge,\n path: cookiePath,\n httpOnly: true,\n secure: true,\n sameSite: 'none',\n partitioned: true, // 默认开启 Partitioned Cookie\n });\n next();\n }\n }\n}\n","import crypto from 'crypto';\nimport { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\n\nexport function resolveCsrfTokenOptions(\n options: CsrfTokenOptions,\n): ResolvedCsrfTokenOptions {\n return {\n ...options,\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n cookieMaxAge: options.cookieMaxAge ?? 1000 *60 * 60 * 24 * 30,\n cookiePath: options.cookiePath ?? '/',\n };\n}\n\n// 生成 CsrfToken\nexport function genToken() {\n // 时间戳(秒级,和 Go 一致)\n const ts = Math.floor(Date.now() / 1000);\n\n // 随机 int64 模拟:生成 8 字节随机数并转成十进制字符串\n const randInt64 = BigInt(\n '0x' + crypto.randomBytes(8).toString('hex'),\n ).toString();\n\n // 拼接 \"<rand>.<timestamp>\"\n const s = `${randInt64}.${ts}`;\n\n // 计算 sha1\n const sha1 = crypto.createHash('sha1');\n sha1.update(s);\n\n // 返回 \"<sha1Hex>-<timestamp>\"\n return `${sha1.digest('hex')}-${ts}`;\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfOptions, ResolvedCsrfOptions } from './type';\nimport { resolveCsrfOptions, sendForbidden } from './helper';\n\n@Injectable()\nexport class CsrfMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfOptions;\n\n public static configure(opts: CsrfOptions) {\n this.options = resolveCsrfOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { headerKey, cookieKey } = CsrfMiddleware.options;\n const cookieCsrfToken = req.cookies[cookieKey.toLowerCase()];\n if (!cookieCsrfToken) {\n sendForbidden(res, 'csrf token not found in cookie.');\n return;\n }\n const headerCsrfToken = req.headers[headerKey.toLowerCase()];\n if (!headerCsrfToken) {\n sendForbidden(res, 'csrf token not found in header.');\n return;\n }\n if (cookieCsrfToken !== headerCsrfToken) {\n sendForbidden(res, 'csrf token not match.');\n return;\n }\n next();\n }\n}\n","import type { Response } from 'express';\n\nimport { CsrfOptions, ResolvedCsrfOptions } from './type';\n\nexport function resolveCsrfOptions(options: CsrfOptions): ResolvedCsrfOptions {\n return {\n ...options,\n headerKey: options.headerKey ?? 'x-suda-csrf-token',\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n };\n}\n\nexport function sendForbidden(res: Response, message: string) {\n res.status(403).send(`Forbidden,${message}`);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\nimport { getWebUserFromHeader } from './helper';\n\n@Injectable()\nexport class UserContextMiddleware implements NestMiddleware {\n\n public use(req: Request, _res: Response, next: NextFunction) {\n const webUser = getWebUserFromHeader(req);\n req.userContext = {\n userId: webUser?.user_id,\n tenantId: webUser?.tenant_id,\n appId: webUser?.app_id ?? '',\n }\n next();\n }\n}\n","import type { Request } from 'express';\n\nconst sudaWebUserHeaderKey = 'x-larkgw-suda-webuser';\n\ninterface SudaWebUser {\n user_id: string;\n tenant_id: number;\n app_id: string;\n}\n\nexport function getWebUserFromHeader(req: Request): SudaWebUser | null {\n const sudaWebUserContent = req.headers[sudaWebUserHeaderKey] as\n | string\n | undefined;\n if (!sudaWebUserContent) {\n return null;\n }\n try {\n const sudaWebUserJsonStr = decodeURIComponent(sudaWebUserContent);\n const sudaWebUserJson = JSON.parse(sudaWebUserJsonStr) as SudaWebUser;\n return sudaWebUserJson;\n } catch (err) {\n console.error('parse suda webuser from header failed, err=%o', err);\n return null;\n }\n return null;\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","crypto","import_common","_init","import_common","_init"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/modules/devtool/index.ts","../src/modules/devtool/helper.ts","../src/middlewares/csrf_token/index.ts","../src/middlewares/csrf_token/helper.ts","../src/middlewares/csrf/index.ts","../src/middlewares/csrf/helper.ts","../src/middlewares/user-context/index.ts","../src/middlewares/user-context/helper.ts"],"sourcesContent":["import './types/express.d';\n\nexport { DevToolsModule } from './modules/devtool';\n\n// Middlewares\nexport { CsrfTokenMiddleware } from './middlewares/csrf_token';\nexport { CsrfMiddleware } from './middlewares/csrf';\nexport { UserContextMiddleware } from './middlewares/user-context';\n","import type { INestApplication } from '@nestjs/common';\n\nimport { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';\nimport { mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { resolveOptsWithDefaultValue, ensureDirAndWrite } from './helper';\nimport { DevToolsOptions } from './type';\n\nexport class DevToolsModule {\n static async mount(app: INestApplication, opts: DevToolsOptions = {}) {\n const options = resolveOptsWithDefaultValue(opts);\n const baseDirname = process.cwd(); // 跟随命令的根目录\n\n // 1) 生成 Swagger 文档\n const builder = new DocumentBuilder()\n .setTitle(options.swaggerOptions.title)\n .setVersion(options.swaggerOptions.version);\n const document = SwaggerModule.createDocument(app, builder.build(), {\n operationIdFactory: (_c, m) => m,\n });\n // 1.1) 挂载 Swagger UI(可关)\n if(options.needSetupServer){\n SwaggerModule.setup(options.docsPath, app, document, {\n customSiteTitle: options.swaggerOptions.customSiteTitle,\n customCss: options.swaggerOptions.customCss,\n swaggerOptions: { persistAuthorization: true },\n });\n console.log(`[OpenAPI] Swagger UI 已挂载至 ${options.docsPath}`);\n }\n\n // 2) 导出 openapi.json\n const openapiPath = resolve(baseDirname, options.openapiOut);\n ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));\n\n // 3) 生成 axios SDK(可关)\n if (options.needGenerateClientSdk) {\n const clientSdkOutPath = resolve(baseDirname, options.clientSdkOut);\n mkdirSync(clientSdkOutPath, { recursive: true });\n const { generate } = await import('openapi-typescript-codegen');\n await generate({\n input: openapiPath,\n output: clientSdkOutPath,\n httpClient: 'axios',\n useOptions: false,\n exportServices: true,\n });\n console.log('[OpenAPI] 导出 openapi.json 并生成 axios SDK ✅');\n }\n }\n}\n","import { dirname } from 'node:path';\nimport { writeFileSync, mkdirSync } from 'node:fs';\n\nimport { DevToolsOptions } from './type';\n/**\n * 标准化基础路径,确保以 '/' 开头且以 '/' 结尾\n *\n * @param rawBasePath 原始的基础路径,可能以 '/' 开头或结尾\n * @returns 标准化后的基础路径,确保以 '/' 开头且不以 '/' 结尾\n */\nexport function normalizeBasePath(rawBasePath: string): string {\n const normalizedBasePath = rawBasePath.startsWith('/')\n ? rawBasePath\n : `/${rawBasePath}`;\n return normalizedBasePath.endsWith('/')\n ? normalizedBasePath.slice(0, -1)\n : normalizedBasePath;\n}\n\ntype ResolvedDevToolsOptions = Required<\n Omit<DevToolsOptions, 'swaggerOptions'>\n> & {\n swaggerOptions: Required<NonNullable<DevToolsOptions['swaggerOptions']>>;\n};\n\nexport function resolveOptsWithDefaultValue(\n options: DevToolsOptions,\n): ResolvedDevToolsOptions {\n const basePath = normalizeBasePath(options.basePath || '/');\n const docsPath = normalizeBasePath(options.docsPath || `api/docs`);\n return {\n ...options,\n needSetupServer: options.needSetupServer ?? false,\n basePath,\n docsPath: `${basePath}${docsPath}`,\n openapiOut: options.openapiOut || './client/src/api/gen/openapi.json',\n clientSdkOut: options.clientSdkOut || './client/src/api/gen',\n needGenerateClientSdk: options.needGenerateClientSdk ?? true,\n swaggerOptions: {\n title: options.swaggerOptions?.title ?? 'NestJS Fullstack API',\n version: options.swaggerOptions?.version ?? '1.0.0',\n customSiteTitle:\n options.swaggerOptions?.customSiteTitle ?? 'API Documentation',\n customCss:\n options.swaggerOptions?.customCss ??\n '.swagger-ui .topbar { display: none }',\n },\n };\n}\n\nexport function ensureDirAndWrite(filePath: string, content: string) {\n // 1. 拿到文件的上级目录\n const dir = dirname(filePath);\n\n // 2. 确保目录存在,不存在就递归创建\n mkdirSync(dir, { recursive: true });\n\n // 3. 写文件\n writeFileSync(filePath, content);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\nimport { resolveCsrfTokenOptions, genToken } from './helper';\n\n@Injectable()\nexport class CsrfTokenMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfTokenOptions;\n\n public static configure(opts: CsrfTokenOptions) {\n this.options = resolveCsrfTokenOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { cookieKey, cookieMaxAge, cookiePath } = CsrfTokenMiddleware.options;\n const originToken = req.cookies[cookieKey.toLowerCase()];\n // 如果存在 Cookie,则直接消费,无需生成\n if (originToken) {\n req.csrfToken = originToken;\n next();\n } else {\n // 如果不存在 token,则生成新的 csrfToken,并 setCookie\n const token = genToken();\n req.csrfToken = token;\n res.cookie(cookieKey, token, {\n maxAge: cookieMaxAge,\n path: cookiePath,\n httpOnly: true,\n secure: true,\n sameSite: 'none',\n partitioned: true, // 默认开启 Partitioned Cookie\n });\n next();\n }\n }\n}\n","import crypto from 'crypto';\nimport { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\n\nexport function resolveCsrfTokenOptions(\n options: CsrfTokenOptions,\n): ResolvedCsrfTokenOptions {\n return {\n ...options,\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n cookieMaxAge: options.cookieMaxAge ?? 1000 *60 * 60 * 24 * 30,\n cookiePath: options.cookiePath ?? '/',\n };\n}\n\n// 生成 CsrfToken\nexport function genToken() {\n // 时间戳(秒级,和 Go 一致)\n const ts = Math.floor(Date.now() / 1000);\n\n // 随机 int64 模拟:生成 8 字节随机数并转成十进制字符串\n const randInt64 = BigInt(\n '0x' + crypto.randomBytes(8).toString('hex'),\n ).toString();\n\n // 拼接 \"<rand>.<timestamp>\"\n const s = `${randInt64}.${ts}`;\n\n // 计算 sha1\n const sha1 = crypto.createHash('sha1');\n sha1.update(s);\n\n // 返回 \"<sha1Hex>-<timestamp>\"\n return `${sha1.digest('hex')}-${ts}`;\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfOptions, ResolvedCsrfOptions } from './type';\nimport { resolveCsrfOptions, sendForbidden } from './helper';\n\n@Injectable()\nexport class CsrfMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfOptions;\n\n public static configure(opts: CsrfOptions) {\n this.options = resolveCsrfOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { headerKey, cookieKey } = CsrfMiddleware.options;\n const cookieCsrfToken = req.cookies[cookieKey.toLowerCase()];\n if (!cookieCsrfToken) {\n sendForbidden(res, 'csrf token not found in cookie.');\n return;\n }\n const headerCsrfToken = req.headers[headerKey.toLowerCase()];\n if (!headerCsrfToken) {\n sendForbidden(res, 'csrf token not found in header.');\n return;\n }\n if (cookieCsrfToken !== headerCsrfToken) {\n sendForbidden(res, 'csrf token not match.');\n return;\n }\n next();\n }\n}\n","import type { Response } from 'express';\n\nimport { CsrfOptions, ResolvedCsrfOptions } from './type';\n\nexport function resolveCsrfOptions(options: CsrfOptions): ResolvedCsrfOptions {\n return {\n ...options,\n headerKey: options.headerKey ?? 'x-suda-csrf-token',\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n };\n}\n\nexport function sendForbidden(res: Response, message: string) {\n res.status(403).send(`Forbidden,${message}`);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\nimport { getWebUserFromHeader } from './helper';\n\n@Injectable()\nexport class UserContextMiddleware implements NestMiddleware {\n\n public use(req: Request, _res: Response, next: NextFunction) {\n const webUser = getWebUserFromHeader(req);\n req.userContext = {\n userId: webUser?.user_id,\n tenantId: webUser?.tenant_id,\n appId: webUser?.app_id ?? '',\n }\n next();\n }\n}\n","import type { Request } from 'express';\n\nconst sudaWebUserHeaderKey = 'x-larkgw-suda-webuser';\n\ninterface SudaWebUser {\n user_id: string;\n tenant_id: number;\n app_id: string;\n}\n\nexport function getWebUserFromHeader(req: Request): SudaWebUser | null {\n const sudaWebUserContent = req.headers[sudaWebUserHeaderKey] as\n | string\n | undefined;\n if (!sudaWebUserContent) {\n return null;\n }\n try {\n const sudaWebUserJsonStr = decodeURIComponent(sudaWebUserContent);\n const sudaWebUserJson = JSON.parse(sudaWebUserJsonStr) as SudaWebUser;\n return sudaWebUserJson;\n } catch (err) {\n console.error('parse suda webuser from header failed, err=%o', err);\n return null;\n }\n return null;\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","normalizeBasePath","rawBasePath","normalizedBasePath","startsWith","endsWith","slice","resolveOptsWithDefaultValue","options","basePath","docsPath","needSetupServer","openapiOut","clientSdkOut","needGenerateClientSdk","swaggerOptions","title","version","customSiteTitle","customCss","ensureDirAndWrite","filePath","content","dir","dirname","mkdirSync","recursive","writeFileSync","DevToolsModule","mount","app","opts","options","resolveOptsWithDefaultValue","baseDirname","process","cwd","builder","DocumentBuilder","setTitle","swaggerOptions","title","setVersion","version","document","SwaggerModule","createDocument","build","operationIdFactory","_c","m","needSetupServer","setup","docsPath","customSiteTitle","customCss","persistAuthorization","console","log","openapiPath","resolve","openapiOut","ensureDirAndWrite","JSON","stringify","needGenerateClientSdk","clientSdkOutPath","clientSdkOut","mkdirSync","recursive","generate","input","output","httpClient","useOptions","exportServices","resolveCsrfTokenOptions","options","cookieKey","cookieMaxAge","cookiePath","genToken","ts","Math","floor","Date","now","randInt64","BigInt","crypto","randomBytes","toString","s","sha1","createHash","update","digest","CsrfTokenMiddleware","options","configure","opts","resolveCsrfTokenOptions","use","req","res","next","cookieKey","cookieMaxAge","cookiePath","originToken","cookies","toLowerCase","csrfToken","token","genToken","cookie","maxAge","path","httpOnly","secure","sameSite","partitioned","import_common","resolveCsrfOptions","options","headerKey","cookieKey","sendForbidden","res","message","status","send","CsrfMiddleware","options","configure","opts","resolveCsrfOptions","use","req","res","next","headerKey","cookieKey","cookieCsrfToken","cookies","toLowerCase","sendForbidden","headerCsrfToken","headers","import_common","sudaWebUserHeaderKey","getWebUserFromHeader","req","sudaWebUserContent","headers","sudaWebUserJsonStr","decodeURIComponent","sudaWebUserJson","JSON","parse","err","console","error","UserContextMiddleware","use","req","_res","next","webUser","getWebUserFromHeader","userContext","userId","user_id","tenantId","tenant_id","appId","app_id"]}
|
package/dist/index.js
CHANGED
|
@@ -1,50 +1,5 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
5
|
-
var __typeError = (msg) => {
|
|
6
|
-
throw TypeError(msg);
|
|
7
|
-
};
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
-
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
11
|
-
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
12
|
-
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
13
|
-
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
14
|
-
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
15
|
-
var __runInitializers = (array, flags, self, value) => {
|
|
16
|
-
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
17
|
-
return value;
|
|
18
|
-
};
|
|
19
|
-
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
20
|
-
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
21
|
-
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
22
|
-
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
23
|
-
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
24
|
-
return __privateGet(this, extra);
|
|
25
|
-
}, set [name](x) {
|
|
26
|
-
return __privateSet(this, extra, x);
|
|
27
|
-
} }, name));
|
|
28
|
-
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
29
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
30
|
-
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
31
|
-
if (k) {
|
|
32
|
-
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
33
|
-
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
34
|
-
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
35
|
-
}
|
|
36
|
-
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
37
|
-
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
38
|
-
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
39
|
-
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
40
|
-
}
|
|
41
|
-
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
42
|
-
};
|
|
43
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
44
|
-
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
45
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
46
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
47
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
48
3
|
|
|
49
4
|
// src/modules/devtool/index.ts
|
|
50
5
|
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
|
@@ -58,6 +13,7 @@ function normalizeBasePath(rawBasePath) {
|
|
|
58
13
|
const normalizedBasePath = rawBasePath.startsWith("/") ? rawBasePath : `/${rawBasePath}`;
|
|
59
14
|
return normalizedBasePath.endsWith("/") ? normalizedBasePath.slice(0, -1) : normalizedBasePath;
|
|
60
15
|
}
|
|
16
|
+
__name(normalizeBasePath, "normalizeBasePath");
|
|
61
17
|
function resolveOptsWithDefaultValue(options) {
|
|
62
18
|
const basePath = normalizeBasePath(options.basePath || "/");
|
|
63
19
|
const docsPath = normalizeBasePath(options.docsPath || `api/docs`);
|
|
@@ -77,26 +33,35 @@ function resolveOptsWithDefaultValue(options) {
|
|
|
77
33
|
}
|
|
78
34
|
};
|
|
79
35
|
}
|
|
36
|
+
__name(resolveOptsWithDefaultValue, "resolveOptsWithDefaultValue");
|
|
80
37
|
function ensureDirAndWrite(filePath, content) {
|
|
81
38
|
const dir = dirname(filePath);
|
|
82
|
-
mkdirSync(dir, {
|
|
39
|
+
mkdirSync(dir, {
|
|
40
|
+
recursive: true
|
|
41
|
+
});
|
|
83
42
|
writeFileSync(filePath, content);
|
|
84
43
|
}
|
|
44
|
+
__name(ensureDirAndWrite, "ensureDirAndWrite");
|
|
85
45
|
|
|
86
46
|
// src/modules/devtool/index.ts
|
|
87
47
|
var DevToolsModule = class {
|
|
48
|
+
static {
|
|
49
|
+
__name(this, "DevToolsModule");
|
|
50
|
+
}
|
|
88
51
|
static async mount(app, opts = {}) {
|
|
89
52
|
const options = resolveOptsWithDefaultValue(opts);
|
|
90
53
|
const baseDirname = process.cwd();
|
|
91
54
|
const builder = new DocumentBuilder().setTitle(options.swaggerOptions.title).setVersion(options.swaggerOptions.version);
|
|
92
55
|
const document = SwaggerModule.createDocument(app, builder.build(), {
|
|
93
|
-
operationIdFactory: (_c, m) => m
|
|
56
|
+
operationIdFactory: /* @__PURE__ */ __name((_c, m) => m, "operationIdFactory")
|
|
94
57
|
});
|
|
95
58
|
if (options.needSetupServer) {
|
|
96
59
|
SwaggerModule.setup(options.docsPath, app, document, {
|
|
97
60
|
customSiteTitle: options.swaggerOptions.customSiteTitle,
|
|
98
61
|
customCss: options.swaggerOptions.customCss,
|
|
99
|
-
swaggerOptions: {
|
|
62
|
+
swaggerOptions: {
|
|
63
|
+
persistAuthorization: true
|
|
64
|
+
}
|
|
100
65
|
});
|
|
101
66
|
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
102
67
|
}
|
|
@@ -104,7 +69,9 @@ var DevToolsModule = class {
|
|
|
104
69
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
105
70
|
if (options.needGenerateClientSdk) {
|
|
106
71
|
const clientSdkOutPath = resolve(baseDirname, options.clientSdkOut);
|
|
107
|
-
mkdirSync2(clientSdkOutPath, {
|
|
72
|
+
mkdirSync2(clientSdkOutPath, {
|
|
73
|
+
recursive: true
|
|
74
|
+
});
|
|
108
75
|
const { generate } = await import("openapi-typescript-codegen");
|
|
109
76
|
await generate({
|
|
110
77
|
input: openapiPath,
|
|
@@ -131,21 +98,29 @@ function resolveCsrfTokenOptions(options) {
|
|
|
131
98
|
cookiePath: options.cookiePath ?? "/"
|
|
132
99
|
};
|
|
133
100
|
}
|
|
101
|
+
__name(resolveCsrfTokenOptions, "resolveCsrfTokenOptions");
|
|
134
102
|
function genToken() {
|
|
135
103
|
const ts = Math.floor(Date.now() / 1e3);
|
|
136
|
-
const randInt64 = BigInt(
|
|
137
|
-
"0x" + crypto.randomBytes(8).toString("hex")
|
|
138
|
-
).toString();
|
|
104
|
+
const randInt64 = BigInt("0x" + crypto.randomBytes(8).toString("hex")).toString();
|
|
139
105
|
const s = `${randInt64}.${ts}`;
|
|
140
106
|
const sha1 = crypto.createHash("sha1");
|
|
141
107
|
sha1.update(s);
|
|
142
108
|
return `${sha1.digest("hex")}-${ts}`;
|
|
143
109
|
}
|
|
110
|
+
__name(genToken, "genToken");
|
|
144
111
|
|
|
145
112
|
// src/middlewares/csrf_token/index.ts
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
113
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
114
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
115
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
116
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
117
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
118
|
+
}
|
|
119
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
120
|
+
var CsrfTokenMiddleware = class _CsrfTokenMiddleware {
|
|
121
|
+
static {
|
|
122
|
+
__name(this, "CsrfTokenMiddleware");
|
|
123
|
+
}
|
|
149
124
|
static options;
|
|
150
125
|
static configure(opts) {
|
|
151
126
|
this.options = resolveCsrfTokenOptions(opts);
|
|
@@ -166,16 +141,14 @@ var _CsrfTokenMiddleware = class _CsrfTokenMiddleware {
|
|
|
166
141
|
secure: true,
|
|
167
142
|
sameSite: "none",
|
|
168
143
|
partitioned: true
|
|
169
|
-
// 默认开启 Partitioned Cookie
|
|
170
144
|
});
|
|
171
145
|
next();
|
|
172
146
|
}
|
|
173
147
|
}
|
|
174
148
|
};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
var CsrfTokenMiddleware = _CsrfTokenMiddleware;
|
|
149
|
+
CsrfTokenMiddleware = _ts_decorate([
|
|
150
|
+
Injectable()
|
|
151
|
+
], CsrfTokenMiddleware);
|
|
179
152
|
|
|
180
153
|
// src/middlewares/csrf/index.ts
|
|
181
154
|
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
@@ -188,14 +161,24 @@ function resolveCsrfOptions(options) {
|
|
|
188
161
|
cookieKey: options.cookieKey ?? "suda-csrf-token"
|
|
189
162
|
};
|
|
190
163
|
}
|
|
164
|
+
__name(resolveCsrfOptions, "resolveCsrfOptions");
|
|
191
165
|
function sendForbidden(res, message) {
|
|
192
166
|
res.status(403).send(`Forbidden\uFF0C${message}`);
|
|
193
167
|
}
|
|
168
|
+
__name(sendForbidden, "sendForbidden");
|
|
194
169
|
|
|
195
170
|
// src/middlewares/csrf/index.ts
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
171
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
172
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
173
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
174
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
175
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
176
|
+
}
|
|
177
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
178
|
+
var CsrfMiddleware = class _CsrfMiddleware {
|
|
179
|
+
static {
|
|
180
|
+
__name(this, "CsrfMiddleware");
|
|
181
|
+
}
|
|
199
182
|
static options;
|
|
200
183
|
static configure(opts) {
|
|
201
184
|
this.options = resolveCsrfOptions(opts);
|
|
@@ -219,10 +202,9 @@ var _CsrfMiddleware = class _CsrfMiddleware {
|
|
|
219
202
|
next();
|
|
220
203
|
}
|
|
221
204
|
};
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
var CsrfMiddleware = _CsrfMiddleware;
|
|
205
|
+
CsrfMiddleware = _ts_decorate2([
|
|
206
|
+
Injectable2()
|
|
207
|
+
], CsrfMiddleware);
|
|
226
208
|
|
|
227
209
|
// src/middlewares/user-context/index.ts
|
|
228
210
|
import { Injectable as Injectable3 } from "@nestjs/common";
|
|
@@ -244,11 +226,20 @@ function getWebUserFromHeader(req) {
|
|
|
244
226
|
}
|
|
245
227
|
return null;
|
|
246
228
|
}
|
|
229
|
+
__name(getWebUserFromHeader, "getWebUserFromHeader");
|
|
247
230
|
|
|
248
231
|
// src/middlewares/user-context/index.ts
|
|
249
|
-
|
|
250
|
-
|
|
232
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
233
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
234
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
235
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
236
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
237
|
+
}
|
|
238
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
251
239
|
var UserContextMiddleware = class {
|
|
240
|
+
static {
|
|
241
|
+
__name(this, "UserContextMiddleware");
|
|
242
|
+
}
|
|
252
243
|
use(req, _res, next) {
|
|
253
244
|
const webUser = getWebUserFromHeader(req);
|
|
254
245
|
req.userContext = {
|
|
@@ -259,9 +250,9 @@ var UserContextMiddleware = class {
|
|
|
259
250
|
next();
|
|
260
251
|
}
|
|
261
252
|
};
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
253
|
+
UserContextMiddleware = _ts_decorate3([
|
|
254
|
+
Injectable3()
|
|
255
|
+
], UserContextMiddleware);
|
|
265
256
|
export {
|
|
266
257
|
CsrfMiddleware,
|
|
267
258
|
CsrfTokenMiddleware,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/modules/devtool/index.ts","../src/modules/devtool/helper.ts","../src/middlewares/csrf_token/index.ts","../src/middlewares/csrf_token/helper.ts","../src/middlewares/csrf/index.ts","../src/middlewares/csrf/helper.ts","../src/middlewares/user-context/index.ts","../src/middlewares/user-context/helper.ts"],"sourcesContent":["import type { INestApplication } from '@nestjs/common';\n\nimport { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';\nimport { mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { resolveOptsWithDefaultValue, ensureDirAndWrite } from './helper';\nimport { DevToolsOptions } from './type';\n\nexport class DevToolsModule {\n static async mount(app: INestApplication, opts: DevToolsOptions = {}) {\n const options = resolveOptsWithDefaultValue(opts);\n const baseDirname = process.cwd(); // 跟随命令的根目录\n\n // 1) 生成 Swagger 文档\n const builder = new DocumentBuilder()\n .setTitle(options.swaggerOptions.title)\n .setVersion(options.swaggerOptions.version);\n const document = SwaggerModule.createDocument(app, builder.build(), {\n operationIdFactory: (_c, m) => m,\n });\n // 1.1) 挂载 Swagger UI(可关)\n if(options.needSetupServer){\n SwaggerModule.setup(options.docsPath, app, document, {\n customSiteTitle: options.swaggerOptions.customSiteTitle,\n customCss: options.swaggerOptions.customCss,\n swaggerOptions: { persistAuthorization: true },\n });\n console.log(`[OpenAPI] Swagger UI 已挂载至 ${options.docsPath}`);\n }\n\n // 2) 导出 openapi.json\n const openapiPath = resolve(baseDirname, options.openapiOut);\n ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));\n\n // 3) 生成 axios SDK(可关)\n if (options.needGenerateClientSdk) {\n const clientSdkOutPath = resolve(baseDirname, options.clientSdkOut);\n mkdirSync(clientSdkOutPath, { recursive: true });\n const { generate } = await import('openapi-typescript-codegen');\n await generate({\n input: openapiPath,\n output: clientSdkOutPath,\n httpClient: 'axios',\n useOptions: false,\n exportServices: true,\n });\n console.log('[OpenAPI] 导出 openapi.json 并生成 axios SDK ✅');\n }\n }\n}\n","import { dirname } from 'node:path';\nimport { writeFileSync, mkdirSync } from 'node:fs';\n\nimport { DevToolsOptions } from './type';\n/**\n * 标准化基础路径,确保以 '/' 开头且以 '/' 结尾\n *\n * @param rawBasePath 原始的基础路径,可能以 '/' 开头或结尾\n * @returns 标准化后的基础路径,确保以 '/' 开头且不以 '/' 结尾\n */\nexport function normalizeBasePath(rawBasePath: string): string {\n const normalizedBasePath = rawBasePath.startsWith('/')\n ? rawBasePath\n : `/${rawBasePath}`;\n return normalizedBasePath.endsWith('/')\n ? normalizedBasePath.slice(0, -1)\n : normalizedBasePath;\n}\n\ntype ResolvedDevToolsOptions = Required<\n Omit<DevToolsOptions, 'swaggerOptions'>\n> & {\n swaggerOptions: Required<NonNullable<DevToolsOptions['swaggerOptions']>>;\n};\n\nexport function resolveOptsWithDefaultValue(\n options: DevToolsOptions,\n): ResolvedDevToolsOptions {\n const basePath = normalizeBasePath(options.basePath || '/');\n const docsPath = normalizeBasePath(options.docsPath || `api/docs`);\n return {\n ...options,\n needSetupServer: options.needSetupServer ?? false,\n basePath,\n docsPath: `${basePath}${docsPath}`,\n openapiOut: options.openapiOut || './client/src/api/gen/openapi.json',\n clientSdkOut: options.clientSdkOut || './client/src/api/gen',\n needGenerateClientSdk: options.needGenerateClientSdk ?? true,\n swaggerOptions: {\n title: options.swaggerOptions?.title ?? 'NestJS Fullstack API',\n version: options.swaggerOptions?.version ?? '1.0.0',\n customSiteTitle:\n options.swaggerOptions?.customSiteTitle ?? 'API Documentation',\n customCss:\n options.swaggerOptions?.customCss ??\n '.swagger-ui .topbar { display: none }',\n },\n };\n}\n\nexport function ensureDirAndWrite(filePath: string, content: string) {\n // 1. 拿到文件的上级目录\n const dir = dirname(filePath);\n\n // 2. 确保目录存在,不存在就递归创建\n mkdirSync(dir, { recursive: true });\n\n // 3. 写文件\n writeFileSync(filePath, content);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\nimport { resolveCsrfTokenOptions, genToken } from './helper';\n\n@Injectable()\nexport class CsrfTokenMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfTokenOptions;\n\n public static configure(opts: CsrfTokenOptions) {\n this.options = resolveCsrfTokenOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { cookieKey, cookieMaxAge, cookiePath } = CsrfTokenMiddleware.options;\n const originToken = req.cookies[cookieKey.toLowerCase()];\n // 如果存在 Cookie,则直接消费,无需生成\n if (originToken) {\n req.csrfToken = originToken;\n next();\n } else {\n // 如果不存在 token,则生成新的 csrfToken,并 setCookie\n const token = genToken();\n req.csrfToken = token;\n res.cookie(cookieKey, token, {\n maxAge: cookieMaxAge,\n path: cookiePath,\n httpOnly: true,\n secure: true,\n sameSite: 'none',\n partitioned: true, // 默认开启 Partitioned Cookie\n });\n next();\n }\n }\n}\n","import crypto from 'crypto';\nimport { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\n\nexport function resolveCsrfTokenOptions(\n options: CsrfTokenOptions,\n): ResolvedCsrfTokenOptions {\n return {\n ...options,\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n cookieMaxAge: options.cookieMaxAge ?? 1000 *60 * 60 * 24 * 30,\n cookiePath: options.cookiePath ?? '/',\n };\n}\n\n// 生成 CsrfToken\nexport function genToken() {\n // 时间戳(秒级,和 Go 一致)\n const ts = Math.floor(Date.now() / 1000);\n\n // 随机 int64 模拟:生成 8 字节随机数并转成十进制字符串\n const randInt64 = BigInt(\n '0x' + crypto.randomBytes(8).toString('hex'),\n ).toString();\n\n // 拼接 \"<rand>.<timestamp>\"\n const s = `${randInt64}.${ts}`;\n\n // 计算 sha1\n const sha1 = crypto.createHash('sha1');\n sha1.update(s);\n\n // 返回 \"<sha1Hex>-<timestamp>\"\n return `${sha1.digest('hex')}-${ts}`;\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfOptions, ResolvedCsrfOptions } from './type';\nimport { resolveCsrfOptions, sendForbidden } from './helper';\n\n@Injectable()\nexport class CsrfMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfOptions;\n\n public static configure(opts: CsrfOptions) {\n this.options = resolveCsrfOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { headerKey, cookieKey } = CsrfMiddleware.options;\n const cookieCsrfToken = req.cookies[cookieKey.toLowerCase()];\n if (!cookieCsrfToken) {\n sendForbidden(res, 'csrf token not found in cookie.');\n return;\n }\n const headerCsrfToken = req.headers[headerKey.toLowerCase()];\n if (!headerCsrfToken) {\n sendForbidden(res, 'csrf token not found in header.');\n return;\n }\n if (cookieCsrfToken !== headerCsrfToken) {\n sendForbidden(res, 'csrf token not match.');\n return;\n }\n next();\n }\n}\n","import type { Response } from 'express';\n\nimport { CsrfOptions, ResolvedCsrfOptions } from './type';\n\nexport function resolveCsrfOptions(options: CsrfOptions): ResolvedCsrfOptions {\n return {\n ...options,\n headerKey: options.headerKey ?? 'x-suda-csrf-token',\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n };\n}\n\nexport function sendForbidden(res: Response, message: string) {\n res.status(403).send(`Forbidden,${message}`);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\nimport { getWebUserFromHeader } from './helper';\n\n@Injectable()\nexport class UserContextMiddleware implements NestMiddleware {\n\n public use(req: Request, _res: Response, next: NextFunction) {\n const webUser = getWebUserFromHeader(req);\n req.userContext = {\n userId: webUser?.user_id,\n tenantId: webUser?.tenant_id,\n appId: webUser?.app_id ?? '',\n }\n next();\n }\n}\n","import type { Request } from 'express';\n\nconst sudaWebUserHeaderKey = 'x-larkgw-suda-webuser';\n\ninterface SudaWebUser {\n user_id: string;\n tenant_id: number;\n app_id: string;\n}\n\nexport function getWebUserFromHeader(req: Request): SudaWebUser | null {\n const sudaWebUserContent = req.headers[sudaWebUserHeaderKey] as\n | string\n | undefined;\n if (!sudaWebUserContent) {\n return null;\n }\n try {\n const sudaWebUserJsonStr = decodeURIComponent(sudaWebUserContent);\n const sudaWebUserJson = JSON.parse(sudaWebUserJsonStr) as SudaWebUser;\n return sudaWebUserJson;\n } catch (err) {\n console.error('parse suda webuser from header failed, err=%o', err);\n return null;\n }\n return null;\n}\n"],"mappings":"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","names":["mkdirSync","mkdirSync","Injectable","_init","Injectable","Injectable","_init","Injectable"]}
|
|
1
|
+
{"version":3,"sources":["../src/modules/devtool/index.ts","../src/modules/devtool/helper.ts","../src/middlewares/csrf_token/index.ts","../src/middlewares/csrf_token/helper.ts","../src/middlewares/csrf/index.ts","../src/middlewares/csrf/helper.ts","../src/middlewares/user-context/index.ts","../src/middlewares/user-context/helper.ts"],"sourcesContent":["import type { INestApplication } from '@nestjs/common';\n\nimport { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';\nimport { mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { resolveOptsWithDefaultValue, ensureDirAndWrite } from './helper';\nimport { DevToolsOptions } from './type';\n\nexport class DevToolsModule {\n static async mount(app: INestApplication, opts: DevToolsOptions = {}) {\n const options = resolveOptsWithDefaultValue(opts);\n const baseDirname = process.cwd(); // 跟随命令的根目录\n\n // 1) 生成 Swagger 文档\n const builder = new DocumentBuilder()\n .setTitle(options.swaggerOptions.title)\n .setVersion(options.swaggerOptions.version);\n const document = SwaggerModule.createDocument(app, builder.build(), {\n operationIdFactory: (_c, m) => m,\n });\n // 1.1) 挂载 Swagger UI(可关)\n if(options.needSetupServer){\n SwaggerModule.setup(options.docsPath, app, document, {\n customSiteTitle: options.swaggerOptions.customSiteTitle,\n customCss: options.swaggerOptions.customCss,\n swaggerOptions: { persistAuthorization: true },\n });\n console.log(`[OpenAPI] Swagger UI 已挂载至 ${options.docsPath}`);\n }\n\n // 2) 导出 openapi.json\n const openapiPath = resolve(baseDirname, options.openapiOut);\n ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));\n\n // 3) 生成 axios SDK(可关)\n if (options.needGenerateClientSdk) {\n const clientSdkOutPath = resolve(baseDirname, options.clientSdkOut);\n mkdirSync(clientSdkOutPath, { recursive: true });\n const { generate } = await import('openapi-typescript-codegen');\n await generate({\n input: openapiPath,\n output: clientSdkOutPath,\n httpClient: 'axios',\n useOptions: false,\n exportServices: true,\n });\n console.log('[OpenAPI] 导出 openapi.json 并生成 axios SDK ✅');\n }\n }\n}\n","import { dirname } from 'node:path';\nimport { writeFileSync, mkdirSync } from 'node:fs';\n\nimport { DevToolsOptions } from './type';\n/**\n * 标准化基础路径,确保以 '/' 开头且以 '/' 结尾\n *\n * @param rawBasePath 原始的基础路径,可能以 '/' 开头或结尾\n * @returns 标准化后的基础路径,确保以 '/' 开头且不以 '/' 结尾\n */\nexport function normalizeBasePath(rawBasePath: string): string {\n const normalizedBasePath = rawBasePath.startsWith('/')\n ? rawBasePath\n : `/${rawBasePath}`;\n return normalizedBasePath.endsWith('/')\n ? normalizedBasePath.slice(0, -1)\n : normalizedBasePath;\n}\n\ntype ResolvedDevToolsOptions = Required<\n Omit<DevToolsOptions, 'swaggerOptions'>\n> & {\n swaggerOptions: Required<NonNullable<DevToolsOptions['swaggerOptions']>>;\n};\n\nexport function resolveOptsWithDefaultValue(\n options: DevToolsOptions,\n): ResolvedDevToolsOptions {\n const basePath = normalizeBasePath(options.basePath || '/');\n const docsPath = normalizeBasePath(options.docsPath || `api/docs`);\n return {\n ...options,\n needSetupServer: options.needSetupServer ?? false,\n basePath,\n docsPath: `${basePath}${docsPath}`,\n openapiOut: options.openapiOut || './client/src/api/gen/openapi.json',\n clientSdkOut: options.clientSdkOut || './client/src/api/gen',\n needGenerateClientSdk: options.needGenerateClientSdk ?? true,\n swaggerOptions: {\n title: options.swaggerOptions?.title ?? 'NestJS Fullstack API',\n version: options.swaggerOptions?.version ?? '1.0.0',\n customSiteTitle:\n options.swaggerOptions?.customSiteTitle ?? 'API Documentation',\n customCss:\n options.swaggerOptions?.customCss ??\n '.swagger-ui .topbar { display: none }',\n },\n };\n}\n\nexport function ensureDirAndWrite(filePath: string, content: string) {\n // 1. 拿到文件的上级目录\n const dir = dirname(filePath);\n\n // 2. 确保目录存在,不存在就递归创建\n mkdirSync(dir, { recursive: true });\n\n // 3. 写文件\n writeFileSync(filePath, content);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\nimport { resolveCsrfTokenOptions, genToken } from './helper';\n\n@Injectable()\nexport class CsrfTokenMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfTokenOptions;\n\n public static configure(opts: CsrfTokenOptions) {\n this.options = resolveCsrfTokenOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { cookieKey, cookieMaxAge, cookiePath } = CsrfTokenMiddleware.options;\n const originToken = req.cookies[cookieKey.toLowerCase()];\n // 如果存在 Cookie,则直接消费,无需生成\n if (originToken) {\n req.csrfToken = originToken;\n next();\n } else {\n // 如果不存在 token,则生成新的 csrfToken,并 setCookie\n const token = genToken();\n req.csrfToken = token;\n res.cookie(cookieKey, token, {\n maxAge: cookieMaxAge,\n path: cookiePath,\n httpOnly: true,\n secure: true,\n sameSite: 'none',\n partitioned: true, // 默认开启 Partitioned Cookie\n });\n next();\n }\n }\n}\n","import crypto from 'crypto';\nimport { CsrfTokenOptions, ResolvedCsrfTokenOptions } from './type';\n\nexport function resolveCsrfTokenOptions(\n options: CsrfTokenOptions,\n): ResolvedCsrfTokenOptions {\n return {\n ...options,\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n cookieMaxAge: options.cookieMaxAge ?? 1000 *60 * 60 * 24 * 30,\n cookiePath: options.cookiePath ?? '/',\n };\n}\n\n// 生成 CsrfToken\nexport function genToken() {\n // 时间戳(秒级,和 Go 一致)\n const ts = Math.floor(Date.now() / 1000);\n\n // 随机 int64 模拟:生成 8 字节随机数并转成十进制字符串\n const randInt64 = BigInt(\n '0x' + crypto.randomBytes(8).toString('hex'),\n ).toString();\n\n // 拼接 \"<rand>.<timestamp>\"\n const s = `${randInt64}.${ts}`;\n\n // 计算 sha1\n const sha1 = crypto.createHash('sha1');\n sha1.update(s);\n\n // 返回 \"<sha1Hex>-<timestamp>\"\n return `${sha1.digest('hex')}-${ts}`;\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\n\nimport type { CsrfOptions, ResolvedCsrfOptions } from './type';\nimport { resolveCsrfOptions, sendForbidden } from './helper';\n\n@Injectable()\nexport class CsrfMiddleware implements NestMiddleware {\n private static options: ResolvedCsrfOptions;\n\n public static configure(opts: CsrfOptions) {\n this.options = resolveCsrfOptions(opts);\n }\n\n public use(req: Request, res: Response, next: NextFunction) {\n const { headerKey, cookieKey } = CsrfMiddleware.options;\n const cookieCsrfToken = req.cookies[cookieKey.toLowerCase()];\n if (!cookieCsrfToken) {\n sendForbidden(res, 'csrf token not found in cookie.');\n return;\n }\n const headerCsrfToken = req.headers[headerKey.toLowerCase()];\n if (!headerCsrfToken) {\n sendForbidden(res, 'csrf token not found in header.');\n return;\n }\n if (cookieCsrfToken !== headerCsrfToken) {\n sendForbidden(res, 'csrf token not match.');\n return;\n }\n next();\n }\n}\n","import type { Response } from 'express';\n\nimport { CsrfOptions, ResolvedCsrfOptions } from './type';\n\nexport function resolveCsrfOptions(options: CsrfOptions): ResolvedCsrfOptions {\n return {\n ...options,\n headerKey: options.headerKey ?? 'x-suda-csrf-token',\n cookieKey: options.cookieKey ?? 'suda-csrf-token',\n };\n}\n\nexport function sendForbidden(res: Response, message: string) {\n res.status(403).send(`Forbidden,${message}`);\n}\n","import { Injectable, NestMiddleware } from '@nestjs/common';\nimport type { Request, Response, NextFunction } from 'express';\nimport { getWebUserFromHeader } from './helper';\n\n@Injectable()\nexport class UserContextMiddleware implements NestMiddleware {\n\n public use(req: Request, _res: Response, next: NextFunction) {\n const webUser = getWebUserFromHeader(req);\n req.userContext = {\n userId: webUser?.user_id,\n tenantId: webUser?.tenant_id,\n appId: webUser?.app_id ?? '',\n }\n next();\n }\n}\n","import type { Request } from 'express';\n\nconst sudaWebUserHeaderKey = 'x-larkgw-suda-webuser';\n\ninterface SudaWebUser {\n user_id: string;\n tenant_id: number;\n app_id: string;\n}\n\nexport function getWebUserFromHeader(req: Request): SudaWebUser | null {\n const sudaWebUserContent = req.headers[sudaWebUserHeaderKey] as\n | string\n | undefined;\n if (!sudaWebUserContent) {\n return null;\n }\n try {\n const sudaWebUserJsonStr = decodeURIComponent(sudaWebUserContent);\n const sudaWebUserJson = JSON.parse(sudaWebUserJsonStr) as SudaWebUser;\n return sudaWebUserJson;\n } catch (err) {\n console.error('parse suda webuser from header failed, err=%o', err);\n return null;\n }\n return null;\n}\n"],"mappings":"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","names":["SwaggerModule","DocumentBuilder","mkdirSync","resolve","dirname","writeFileSync","mkdirSync","normalizeBasePath","rawBasePath","normalizedBasePath","startsWith","endsWith","slice","resolveOptsWithDefaultValue","options","basePath","docsPath","needSetupServer","openapiOut","clientSdkOut","needGenerateClientSdk","swaggerOptions","title","version","customSiteTitle","customCss","ensureDirAndWrite","filePath","content","dir","dirname","mkdirSync","recursive","writeFileSync","DevToolsModule","mount","app","opts","options","resolveOptsWithDefaultValue","baseDirname","process","cwd","builder","DocumentBuilder","setTitle","swaggerOptions","title","setVersion","version","document","SwaggerModule","createDocument","build","operationIdFactory","_c","m","needSetupServer","setup","docsPath","customSiteTitle","customCss","persistAuthorization","console","log","openapiPath","resolve","openapiOut","ensureDirAndWrite","JSON","stringify","needGenerateClientSdk","clientSdkOutPath","clientSdkOut","mkdirSync","recursive","generate","input","output","httpClient","useOptions","exportServices","Injectable","crypto","resolveCsrfTokenOptions","options","cookieKey","cookieMaxAge","cookiePath","genToken","ts","Math","floor","Date","now","randInt64","BigInt","crypto","randomBytes","toString","s","sha1","createHash","update","digest","CsrfTokenMiddleware","options","configure","opts","resolveCsrfTokenOptions","use","req","res","next","cookieKey","cookieMaxAge","cookiePath","originToken","cookies","toLowerCase","csrfToken","token","genToken","cookie","maxAge","path","httpOnly","secure","sameSite","partitioned","Injectable","resolveCsrfOptions","options","headerKey","cookieKey","sendForbidden","res","message","status","send","CsrfMiddleware","options","configure","opts","resolveCsrfOptions","use","req","res","next","headerKey","cookieKey","cookieCsrfToken","cookies","toLowerCase","sendForbidden","headerCsrfToken","headers","Injectable","sudaWebUserHeaderKey","getWebUserFromHeader","req","sudaWebUserContent","headers","sudaWebUserJsonStr","decodeURIComponent","sudaWebUserJson","JSON","parse","err","console","error","UserContextMiddleware","use","req","_res","next","webUser","getWebUserFromHeader","userContext","userId","user_id","tenantId","tenant_id","appId","app_id"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-nestjs-core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.11",
|
|
4
4
|
"description": "FullStack Nestjs Core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,6 +28,15 @@
|
|
|
28
28
|
"require": "./dist/index.cjs"
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev": "tsup --watch",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"lint": "echo 'ESLint skipped for TypeScript files'",
|
|
38
|
+
"lint:fix": "eslint src --ext .ts --fix"
|
|
39
|
+
},
|
|
31
40
|
"dependencies": {
|
|
32
41
|
"openapi-typescript-codegen": "^0.29.0"
|
|
33
42
|
},
|
|
@@ -44,14 +53,5 @@
|
|
|
44
53
|
"@nestjs/common": "^10.4.20",
|
|
45
54
|
"@nestjs/platform-express": "^10.4.20",
|
|
46
55
|
"@nestjs/swagger": "^7.4.2"
|
|
47
|
-
},
|
|
48
|
-
"scripts": {
|
|
49
|
-
"build": "tsup",
|
|
50
|
-
"dev": "tsup --watch",
|
|
51
|
-
"typecheck": "tsc --noEmit",
|
|
52
|
-
"test": "vitest run",
|
|
53
|
-
"test:watch": "vitest",
|
|
54
|
-
"lint": "echo 'ESLint skipped for TypeScript files'",
|
|
55
|
-
"lint:fix": "eslint src --ext .ts --fix"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|