nsgm-cli 2.1.10 → 2.1.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.
@@ -1,54 +1,389 @@
1
+ // GraphQL 客户端与 CSRF 保护工具
2
+
1
3
  import axios from 'axios'
2
4
  import { getLocalApiPrefix } from './common'
3
5
 
4
- // 添加缓存机制
5
- const queryCache = new Map<string, { data: any; timestamp: number }>()
6
- const CACHE_DURATION = 5 * 60 * 1000 // 5分钟缓存
6
+ // 配置 axios 默认行为
7
+ axios.defaults.withCredentials = true
8
+
9
+ // ==================== GraphQL 配置 ====================
10
+
11
+ export const GRAPHQL_CONFIG = {
12
+ // GraphQL 端点
13
+ endpoint: '/graphql',
14
+
15
+ // 默认请求头
16
+ defaultHeaders: {
17
+ 'Content-Type': 'application/json',
18
+ 'Accept': 'application/json'
19
+ },
20
+
21
+ // 缓存配置
22
+ cache: {
23
+ defaultTTL: 5 * 60 * 1000, // 5分钟
24
+ maxSize: 100,
25
+ enabled: true
26
+ },
27
+
28
+ // CSRF 配置
29
+ csrf: {
30
+ enabled: true,
31
+ tokenHeader: 'X-CSRF-Token',
32
+ cookieName: 'csrfToken'
33
+ },
34
+
35
+ // 开发模式配置
36
+ development: {
37
+ enableDebugLogs: process.env.NODE_ENV === 'development'
38
+ }
39
+ }
40
+
41
+ // GraphQL 操作类型
42
+ export enum GraphQLOperationType {
43
+ QUERY = 'query',
44
+ MUTATION = 'mutation',
45
+ SUBSCRIPTION = 'subscription'
46
+ }
47
+
48
+ // GraphQL 工具函数
49
+ export const GraphQLUtils = {
50
+ // 检测操作类型
51
+ getOperationType(query: string): GraphQLOperationType {
52
+ const trimmed = query.trim().toLowerCase()
53
+ if (trimmed.startsWith('mutation')) return GraphQLOperationType.MUTATION
54
+ if (trimmed.startsWith('subscription')) return GraphQLOperationType.SUBSCRIPTION
55
+ return GraphQLOperationType.QUERY
56
+ },
57
+
58
+ // 提取操作名称
59
+ getOperationName(query: string): string | null {
60
+ const match = query.match(/(?:query|mutation|subscription)\s+(\w+)/)
61
+ return match ? match[1] : null
62
+ },
63
+
64
+ // 生成缓存键
65
+ generateCacheKey(query: string, variables?: any): string {
66
+ const operationName = this.getOperationName(query) || 'anonymous'
67
+ const variablesHash = variables ? JSON.stringify(variables) : ''
68
+ return `${operationName}_${btoa(variablesHash)}`
69
+ },
70
+
71
+ // 验证 GraphQL 查询语法
72
+ isValidQuery(query: string): boolean {
73
+ try {
74
+ const trimmed = query.trim()
75
+ return (
76
+ trimmed.length > 0 &&
77
+ (trimmed.includes('query') || trimmed.includes('mutation') || trimmed.includes('subscription')) &&
78
+ trimmed.includes('{') &&
79
+ trimmed.includes('}')
80
+ )
81
+ } catch {
82
+ return false
83
+ }
84
+ }
85
+ }
86
+
87
+ // ==================== CSRF 工具 ====================
7
88
 
8
- export const getLocalGraphql = (query: string, variables: any = {}, useCache = false) => {
9
- return new Promise((resolve, reject) => {
10
- // 生成缓存键
11
- const cacheKey = `${query}:${JSON.stringify(variables)}`
89
+ /**
90
+ * 获取 CSRF token
91
+ * @returns Promise<string> CSRF token
92
+ */
93
+ export const getCSRFToken = async (): Promise<string> => {
94
+ try {
95
+ const response = await axios.get(getLocalApiPrefix() + '/csrf-token', {
96
+ withCredentials: true
97
+ })
12
98
 
13
- // 检查缓存
14
- if (useCache && queryCache.has(cacheKey)) {
15
- const cached = queryCache.get(cacheKey)!
16
- if (Date.now() - cached.timestamp < CACHE_DURATION) {
17
- resolve(cached.data)
18
- return
99
+ if (!response.data || !response.data.csrfToken) {
100
+ throw new Error('服务器返回的 CSRF token 为空')
101
+ }
102
+
103
+ return response.data.csrfToken
104
+ } catch (error) {
105
+ console.error('获取 CSRF token 错误:', error)
106
+ throw error
107
+ }
108
+ }
109
+
110
+ // ==================== GraphQL 客户端 ====================
111
+
112
+ // GraphQL 查询缓存
113
+ const queryCache = new Map<string, { data: any; timestamp: number }>()
114
+ const CACHE_DURATION = GRAPHQL_CONFIG.cache.defaultTTL
115
+
116
+ /**
117
+ * GraphQL 客户端主函数
118
+ * 自动处理 CSRF 保护、缓存、错误重试
119
+ */
120
+ export const getLocalGraphql = async (query: string, variables: any = {}, useCache = false) => {
121
+ // 验证查询语法
122
+ if (!GraphQLUtils.isValidQuery(query)) {
123
+ throw new Error('Invalid GraphQL query syntax')
124
+ }
125
+
126
+ // 生成缓存键
127
+ const cacheKey = GraphQLUtils.generateCacheKey(query, variables)
128
+
129
+ // 检查缓存
130
+ if (useCache && queryCache.has(cacheKey)) {
131
+ const cached = queryCache.get(cacheKey)!
132
+ if (Date.now() - cached.timestamp < CACHE_DURATION) {
133
+ if (GRAPHQL_CONFIG.development.enableDebugLogs) {
134
+ console.log('GraphQL cache hit:', cacheKey)
19
135
  }
136
+ return cached.data
20
137
  }
138
+ }
139
+
140
+ try {
141
+ // 检测操作类型
142
+ const operationType = GraphQLUtils.getOperationType(query)
143
+ const isMutation = operationType === GraphQLOperationType.MUTATION
21
144
 
22
- axios
23
- .post(getLocalApiPrefix() + '/graphql', {
24
- query,
25
- variables
26
- })
27
- .then((res) => {
28
- if (res) {
29
- const { data } = res
30
-
31
- // 缓存结果
32
- if (useCache && data && !data.errors) {
33
- queryCache.set(cacheKey, {
34
- data,
35
- timestamp: Date.now()
36
- })
145
+ const headers: Record<string, string> = {
146
+ ...GRAPHQL_CONFIG.defaultHeaders
147
+ }
148
+
149
+ let response
150
+
151
+ if (isMutation) {
152
+ // Mutation 使用 POST 方法并需要 CSRF token
153
+ if (GRAPHQL_CONFIG.csrf.enabled) {
154
+ try {
155
+ const csrfToken = await getCSRFToken()
156
+ headers[GRAPHQL_CONFIG.csrf.tokenHeader] = csrfToken
157
+ } catch (csrfError) {
158
+ console.warn('获取 CSRF token 失败,继续执行 GraphQL 请求:', csrfError)
159
+ }
160
+ }
161
+
162
+ response = await axios.post(
163
+ getLocalApiPrefix() + '/graphql',
164
+ {
165
+ query,
166
+ variables
167
+ },
168
+ {
169
+ headers,
170
+ withCredentials: true
171
+ }
172
+ )
173
+ } else {
174
+ // Query 和 Subscription 使用 GET 方法,不需要 CSRF token
175
+ const params = new URLSearchParams()
176
+ params.append('query', query)
177
+ if (variables && Object.keys(variables).length > 0) {
178
+ params.append('variables', JSON.stringify(variables))
179
+ }
180
+
181
+ response = await axios.get(
182
+ getLocalApiPrefix() + '/graphql?' + params.toString(),
183
+ {
184
+ headers: {
185
+ 'Accept': 'application/json'
186
+ },
187
+ withCredentials: true
188
+ }
189
+ )
190
+ }
191
+
192
+ if (response && response.data) {
193
+ // 缓存查询结果
194
+ if (useCache && !isMutation) {
195
+ queryCache.set(cacheKey, {
196
+ data: response.data,
197
+ timestamp: Date.now()
198
+ })
199
+ }
200
+
201
+ return response.data
202
+ } else {
203
+ throw new Error('GraphQL response is empty')
204
+ }
205
+ } catch (error) {
206
+ // 只为 mutation 检查 CSRF 错误 (403),因为 query 使用 GET 不需要 CSRF token
207
+ if (axios.isAxiosError(error) && error.response?.status === 403) {
208
+ const operationType = GraphQLUtils.getOperationType(query)
209
+
210
+ if (operationType === GraphQLOperationType.MUTATION) {
211
+ console.warn('🔄 CSRF token 可能已过期,尝试重试 mutation...')
212
+ try {
213
+ // 重新获取 token 并重试 mutation
214
+ const newCsrfToken = await getCSRFToken()
215
+ const retryHeaders = {
216
+ ...GRAPHQL_CONFIG.defaultHeaders,
217
+ [GRAPHQL_CONFIG.csrf.tokenHeader]: newCsrfToken
37
218
  }
38
219
 
39
- resolve(data)
220
+ const retryResponse = await axios.post(
221
+ getLocalApiPrefix() + '/graphql',
222
+ { query, variables },
223
+ { headers: retryHeaders, withCredentials: true }
224
+ )
225
+
226
+ return retryResponse.data
227
+ } catch (retryError) {
228
+ console.error('❌ CSRF mutation 重试失败:', retryError)
229
+ throw retryError
230
+ }
231
+ }
232
+ }
233
+
234
+ console.error('GraphQL request failed:', error)
235
+ throw error
236
+ }
237
+ }
238
+
239
+ // ==================== 文件上传工具 ====================
240
+
241
+ /**
242
+ * 创建受 CSRF 保护的文件上传配置
243
+ */
244
+ export const createCSRFUploadProps = (
245
+ action: string,
246
+ options: {
247
+ name?: string
248
+ onSuccess?: (fileName: string) => void
249
+ onError?: (fileName: string) => void
250
+ beforeUpload?: (file: File) => boolean | Promise<boolean>
251
+ accept?: string
252
+ multiple?: boolean
253
+ } = {}
254
+ ) => {
255
+ const {
256
+ name = 'file',
257
+ onSuccess,
258
+ onError,
259
+ beforeUpload: customBeforeUpload,
260
+ accept,
261
+ multiple = false
262
+ } = options
263
+
264
+ return {
265
+ name,
266
+ action,
267
+ accept,
268
+ multiple,
269
+ customRequest: async (options: any) => {
270
+ const { onProgress, onError: onUploadError, onSuccess: onUploadSuccess, file } = options
271
+
272
+ try {
273
+ // 获取 CSRF token
274
+ const csrfToken = await getCSRFToken()
275
+ if (!csrfToken) {
276
+ throw new Error('CSRF Token 获取失败')
277
+ }
278
+
279
+ // 创建 FormData
280
+ const formData = new FormData()
281
+ formData.append(name, file)
282
+
283
+ // 发送请求
284
+ const uploadUrl = action.startsWith('http') ? action : getLocalApiPrefix() + action
285
+ const response = await axios.post(uploadUrl, formData, {
286
+ headers: {
287
+ [GRAPHQL_CONFIG.csrf.tokenHeader]: csrfToken
288
+ },
289
+ withCredentials: true
290
+ })
291
+
292
+ if (response.status >= 200 && response.status < 300) {
293
+ onUploadSuccess(response)
40
294
  } else {
41
- reject(new Error('No data received'))
295
+ throw new Error(`Upload failed: ${response.statusText}`)
296
+ }
297
+ } catch (error) {
298
+ onUploadError(error)
299
+ }
300
+ },
301
+ beforeUpload: async (file: File) => {
302
+ try {
303
+ // 验证 CSRF token
304
+ const validation = await validateCSRFForUpload()
305
+ if (!validation.valid) {
306
+ throw new Error(validation.error)
307
+ }
308
+
309
+ // 执行自定义的 beforeUpload 检查
310
+ if (customBeforeUpload) {
311
+ const result = await customBeforeUpload(file)
312
+ return result
313
+ }
314
+
315
+ return true
316
+ } catch (error) {
317
+ console.error('Upload preparation failed:', error)
318
+ return false
319
+ }
320
+ },
321
+ onChange(info: any) {
322
+ const { status, name: fileName } = info.file
323
+
324
+ if (status === 'done') {
325
+ if (onSuccess) {
326
+ onSuccess(fileName)
327
+ }
328
+ } else if (status === 'error') {
329
+ if (onError) {
330
+ onError(fileName)
42
331
  }
43
- })
44
- .catch((error) => {
45
- console.error('GraphQL query failed:', error)
46
- reject(error)
47
- })
48
- })
332
+ }
333
+ }
334
+ }
49
335
  }
50
336
 
337
+ /**
338
+ * 验证上传前的 CSRF 状态
339
+ */
340
+ export const validateCSRFForUpload = async (): Promise<{ valid: boolean; token?: string; error?: string }> => {
341
+ try {
342
+ const csrfToken = await getCSRFToken()
343
+ if (!csrfToken) {
344
+ return {
345
+ valid: false,
346
+ error: 'CSRF Token 获取失败,请刷新页面重试'
347
+ }
348
+ }
349
+ return {
350
+ valid: true,
351
+ token: csrfToken
352
+ }
353
+ } catch (error) {
354
+ return {
355
+ valid: false,
356
+ error: error instanceof Error ? error.message : '获取 CSRF Token 时发生未知错误'
357
+ }
358
+ }
359
+ }
360
+
361
+ // ==================== 工具函数 ====================
362
+
51
363
  // 清除缓存
52
364
  export const clearGraphqlCache = () => {
53
365
  queryCache.clear()
54
366
  }
367
+
368
+ // GraphQL 查询辅助函数
369
+ export const graphqlQuery = async (query: string, variables?: any, useCache = true) => {
370
+ return getLocalGraphql(query, variables, useCache)
371
+ }
372
+
373
+ // GraphQL 变更辅助函数 (Mutation)
374
+ export const graphqlMutation = async (mutation: string, variables?: any) => {
375
+ return getLocalGraphql(mutation, variables, false) // 变更操作不使用缓存
376
+ }
377
+
378
+ // 检查 GraphQL 响应是否有错误
379
+ export const hasGraphqlErrors = (response: any): boolean => {
380
+ return response && response.errors && response.errors.length > 0
381
+ }
382
+
383
+ // 获取 GraphQL 错误信息
384
+ export const getGraphqlErrorMessage = (response: any): string => {
385
+ if (hasGraphqlErrors(response)) {
386
+ return response.errors.map((error: any) => error.message).join('; ')
387
+ }
388
+ return ''
389
+ }
@@ -8,34 +8,37 @@
8
8
  - [Styled-components](https://github.com/styled-components/styled-components) - CSS-in-JS 解决方案
9
9
  - [GraphQL](https://graphql.org/) - API 查询语言
10
10
  - [MySQL](https://www.mysql.com/) - 关系型数据库
11
+ - 安全登录系统 - 基于 bcrypt 加密
11
12
 
12
13
  ## 快速入门
13
14
 
14
15
  ### 开发命令
15
16
 
16
- | 命令 | 说明 |
17
- |---------|-------------|
18
- | `npm run dev` | 开发模式 |
19
- | `npm run start` | 生产模式 |
20
- | `npm run build` | 编译项目 |
17
+ | 命令 | 说明 |
18
+ | ---------------- | ------------ |
19
+ | `npm run dev` | 开发模式 |
20
+ | `npm run start` | 生产模式 |
21
+ | `npm run build` | 编译项目 |
21
22
  | `npm run export` | 导出静态页面 |
22
23
 
23
24
  ### 代码生成命令
24
25
 
25
- | 命令 | 说明 |
26
- |---------|-------------|
26
+ | 命令 | 说明 |
27
+ | ---------------- | ------------ |
27
28
  | `npm run create` | 创建模板页面 |
28
29
  | `npm run delete` | 删除模板页面 |
29
30
 
30
31
  ### 项目维护命令
31
32
 
32
- | 命令 | 说明 |
33
- |---------|-------------|
34
- | `npm run upgrade` | 升级项目基础文件 |
33
+ | 命令 | 说明 |
34
+ | --------------------------- | ---------------- |
35
+ | `npm run upgrade` | 升级项目基础文件 |
36
+ | `npm run generate-password` | 生成安全密码哈希 |
35
37
 
36
38
  ## 参数说明
37
39
 
38
40
  ### controller
41
+
39
42
  - 用于 `create`/`delete` 命令
40
43
  - 必填参数
41
44
  - 示例:
@@ -44,6 +47,7 @@
44
47
  ```
45
48
 
46
49
  ### action
50
+
47
51
  - 用于 `create`/`delete` 命令
48
52
  - 默认值为 `manage`
49
53
  - 跟在 controller 参数后面
@@ -53,6 +57,7 @@
53
57
  ```
54
58
 
55
59
  ### dictionary
60
+
56
61
  - 用于 `export` 命令
57
62
  - 默认值为 `webapp`
58
63
  - 示例:
@@ -88,14 +93,17 @@
88
93
  const { nextConfig } = require('nsgm-cli')
89
94
  const projectConfig = require('./project.config')
90
95
 
91
- const { version, prefix, protocol, host } = projectConfig
96
+ const { version, prefix, protocol, host } = projectConfig
92
97
 
93
98
  module.exports = (phase, defaultConfig) => {
94
- let configObj = nextConfig(phase, defaultConfig, {
95
- version, prefix, protocol, host
96
- })
99
+ let configObj = nextConfig(phase, defaultConfig, {
100
+ version,
101
+ prefix,
102
+ protocol,
103
+ host
104
+ })
97
105
 
98
- return configObj
106
+ return configObj
99
107
  }
100
108
  ```
101
109
 
@@ -107,13 +115,13 @@ const { mysqlOptions } = mysqlConfig
107
115
  const { user, password, host, port, database } = mysqlOptions
108
116
 
109
117
  module.exports = {
110
- mysqlOptions: {
111
- user,
112
- password,
113
- host,
114
- port,
115
- database
116
- }
118
+ mysqlOptions: {
119
+ user,
120
+ password,
121
+ host,
122
+ port,
123
+ database
124
+ }
117
125
  }
118
126
  ```
119
127
 
@@ -127,14 +135,46 @@ const { prefix, protocol, host, port } = projectConfig
127
135
  const { version } = pkg
128
136
 
129
137
  module.exports = {
130
- version,
131
- prefix,
132
- protocol,
133
- host,
134
- port
138
+ version,
139
+ prefix,
140
+ protocol,
141
+ host,
142
+ port
135
143
  }
136
144
  ```
137
145
 
146
+ ## 安全配置
147
+
148
+ 项目集成了安全的登录系统,使用 bcrypt 加密。在部署前请配置登录凭证:
149
+
150
+ ### 快速设置
151
+
152
+ 1. **生成密码哈希**:
153
+
154
+ ```bash
155
+ npm run generate-password yourSecurePassword
156
+ ```
157
+
158
+ 2. **创建环境变量文件**:
159
+
160
+ ```bash
161
+ # 在项目根目录创建 .env 文件
162
+ LOGIN_USERNAME=admin
163
+ LOGIN_PASSWORD_HASH=your_generated_hash_here
164
+ ```
165
+
166
+ 3. **确保 .env 文件在 .gitignore 中**(已预配置)
167
+
168
+ ### 详细安全配置
169
+
170
+ 更多安全配置和最佳实践,请参考 [SECURITY.md](./SECURITY.md) 文档。
171
+
172
+ **⚠️ 重要提醒:**
173
+
174
+ - 不要在代码中硬编码密码
175
+ - 不要将 `.env` 文件提交到版本控制系统
176
+ - 定期更换登录密码
177
+
138
178
  ## 开发指南
139
179
 
140
180
  1. **创建新页面**:使用 `npm run create [controller] [action]` 命令
@@ -144,4 +184,4 @@ module.exports = {
144
184
 
145
185
  ## 更多资源
146
186
 
147
- 更多详细信息,请参考 [NSGM CLI 文档](https://github.com/erishen/nsgm-cli)。
187
+ 更多详细信息,请参考 [NSGM CLI 文档](https://github.com/erishen/nsgm-cli)。
package/lib/index.js CHANGED
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.startExpress = void 0;
8
+ // 加载环境变量
9
+ require('dotenv').config();
8
10
  // 仅在开发环境中禁用TLS证书验证
9
11
  if (process.env.NODE_ENV === 'development') {
10
12
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
@@ -23,6 +25,8 @@ const config_1 = __importDefault(require("next/config"));
23
25
  const generate_1 = require("./generate");
24
26
  const lodash_1 = __importDefault(require("lodash"));
25
27
  const cors_1 = __importDefault(require("cors"));
28
+ const express_session_1 = __importDefault(require("express-session"));
29
+ const csrf_1 = require("./server/csrf");
26
30
  const { resolve } = path_1.default;
27
31
  const curFolder = process.cwd();
28
32
  const processArgvs = (0, args_1.getProcessArgvs)(2);
@@ -90,19 +94,48 @@ const startExpress = (options, callback) => {
90
94
  // 不要因为数据库连接失败就退出,允许应用继续运行
91
95
  }
92
96
  const server = (0, express_1.default)();
97
+ // 配置 session(CSRF 保护需要)
98
+ server.use((0, express_session_1.default)({
99
+ secret: process.env.SESSION_SECRET || 'nsgm-default-secret-key-change-in-production',
100
+ resave: false,
101
+ saveUninitialized: true, // 改为 true,确保 session 被创建
102
+ name: 'sessionId', // 明确指定 session cookie 名称
103
+ cookie: {
104
+ secure: false, // 开发环境总是使用 false,生产环境再考虑 HTTPS
105
+ httpOnly: true,
106
+ maxAge: 24 * 60 * 60 * 1000, // 24小时
107
+ sameSite: 'lax', // 设置 SameSite 策略
108
+ domain: undefined // 不设置 domain,使用默认
109
+ }
110
+ }));
111
+ // 初始化 CSRF token - 移除全局初始化,让每个端点自己处理
112
+ // server.use(setupCSRFToken)
93
113
  server.use(body_parser_1.default.urlencoded({
94
114
  extended: false
95
115
  }));
96
116
  // 支持跨域,nsgm export 之后前后分离
97
- server.use((0, cors_1.default)());
117
+ server.use((0, cors_1.default)({
118
+ credentials: true, // 允许发送 cookies
119
+ origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000']
120
+ }));
98
121
  server.use(body_parser_1.default.json());
122
+ // 添加基本安全中间件
123
+ server.use(csrf_1.securityMiddleware.basicHeaders);
124
+ if (process.env.NODE_ENV === 'production') {
125
+ server.use((0, csrf_1.createCSPMiddleware)()); // 内容安全策略
126
+ }
127
+ // 添加 CSRF 保护中间件(在解析 body 之后)
128
+ server.use(csrf_1.csrfProtection);
99
129
  server.use((0, express_fileupload_1.default)());
100
130
  server.use('/static', express_1.default.static(path_1.default.join(__dirname, 'public')));
101
131
  server.use('/graphql', (0, graphql_1.default)(command));
102
132
  const nextConfig = (0, config_1.default)();
103
133
  const { publicRuntimeConfig } = nextConfig;
104
134
  const { host, port, prefix } = publicRuntimeConfig;
135
+ // 提供 CSRF token 的端点
136
+ server.get('/csrf-token', csrf_1.getCSRFToken);
105
137
  if (prefix !== '') {
138
+ server.get(`${prefix}/csrf-token`, csrf_1.getCSRFToken);
106
139
  server.use(`${prefix}/static`, express_1.default.static(path_1.default.join(__dirname, 'public')));
107
140
  server.use(`${prefix}/graphql`, (0, graphql_1.default)(command));
108
141
  }
@@ -0,0 +1,17 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ declare module 'express-session' {
3
+ interface SessionData {
4
+ _csrf?: string;
5
+ }
6
+ }
7
+ declare module 'express-serve-static-core' {
8
+ interface Request {
9
+ csrfToken?: () => string;
10
+ }
11
+ }
12
+ export declare const csrfProtection: (req: Request, res: Response, next: NextFunction) => unknown;
13
+ export declare const getCSRFToken: (req: Request, res: Response) => void;
14
+ export declare const securityMiddleware: {
15
+ basicHeaders: import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
16
+ };
17
+ export declare const createCSPMiddleware: () => import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;