nsgm-cli 2.0.25 → 2.1.1
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/babel.config.js +18 -0
- package/client/components/Button.tsx +8 -0
- package/client/components/__tests__/Button.test.tsx +15 -0
- package/client/redux/store.ts +13 -11
- package/client/utils/fetch.ts +35 -6
- package/eslint.config.js +87 -0
- package/generation/eslintrc.js +16 -0
- package/generation/server/rest.js +2 -2
- package/generation/server/utils/common.js +4 -2
- package/generation/tsconfig.json +2 -1
- package/jest.config.js +29 -0
- package/lib/args.js +8 -9
- package/lib/generate.js +313 -420
- package/lib/index.d.ts +1 -1
- package/lib/index.js +106 -98
- package/lib/server/db.d.ts +6 -1
- package/lib/server/db.js +143 -61
- package/lib/server/graphql.js +147 -84
- package/lib/server/plugins/date.js +10 -10
- package/lib/server/utils/graphql-cache.d.ts +9 -0
- package/lib/server/utils/graphql-cache.js +69 -0
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/next-env.d.ts +1 -1
- package/next.config.js +67 -0
- package/package.json +41 -17
- package/pages/template/manage.tsx +3 -3
- package/scripts/performance-check.sh +29 -0
- package/server/modules/template/resolver.js +211 -208
- package/server/rest.js +2 -2
- package/server/utils/common.js +4 -2
- package/server/utils/db-pool-manager.js +64 -0
- package/server/utils/performance.js +70 -0
package/lib/server/graphql.js
CHANGED
|
@@ -1,112 +1,175 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
4
|
};
|
|
16
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
6
|
+
const express_graphql_1 = require("express-graphql");
|
|
7
|
+
const graphql_1 = require("graphql");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const date_1 = __importDefault(require("./plugins/date"));
|
|
12
|
+
// 缓存已生成的 schema 和 resolvers
|
|
13
|
+
let cachedSchema = null;
|
|
14
|
+
let cachedResolvers = null;
|
|
15
|
+
const defaultPath = (0, path_1.resolve)(__dirname, './modules/');
|
|
16
|
+
const typeDefFileName = 'schema.js';
|
|
17
|
+
const resolverFileName = 'resolver.js';
|
|
18
|
+
const curFolder = process.cwd();
|
|
19
|
+
const outModulesPath = (0, path_1.resolve)(`${curFolder}/server/modules/`);
|
|
20
|
+
const handleOutPlugins = () => {
|
|
21
|
+
let result = {};
|
|
22
|
+
const outPluginsPath = (0, path_1.resolve)(`${curFolder}/server/plugins/`);
|
|
23
|
+
if (!fs_1.default.existsSync(outPluginsPath)) {
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const list = fs_1.default.readdirSync(outPluginsPath);
|
|
28
|
+
list.forEach((item) => {
|
|
29
|
+
const resolverPath = (0, path_1.resolve)(`${outPluginsPath}/${item}`);
|
|
30
|
+
if (!fs_1.default.existsSync(resolverPath))
|
|
31
|
+
return;
|
|
32
|
+
const stat = fs_1.default.statSync(resolverPath);
|
|
33
|
+
const isFile = stat.isFile();
|
|
34
|
+
if (isFile && (item.endsWith('.js') || item.endsWith('.ts'))) {
|
|
35
|
+
const pluginModule = require(resolverPath);
|
|
36
|
+
result = {
|
|
37
|
+
...result,
|
|
38
|
+
...(pluginModule.default || pluginModule)
|
|
39
|
+
};
|
|
39
40
|
}
|
|
40
41
|
});
|
|
41
42
|
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.warn('⚠️ Error loading plugins:', error);
|
|
45
|
+
}
|
|
42
46
|
return result;
|
|
43
47
|
};
|
|
44
48
|
function generateTypeDefsAndResolvers() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
// 如果已有缓存且在生产环境,直接返回缓存
|
|
50
|
+
if (process.env.NODE_ENV === 'production' && cachedSchema && cachedResolvers) {
|
|
51
|
+
return {
|
|
52
|
+
schemaStr: cachedSchema,
|
|
53
|
+
resolvers: cachedResolvers
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const querySchemes = ['_: Boolean'];
|
|
57
|
+
const mutationSchemes = ['_: Boolean'];
|
|
58
|
+
const subscriptionSchemes = ['_: Boolean'];
|
|
59
|
+
const typeSchemes = [];
|
|
60
|
+
const resolvers = {
|
|
61
|
+
...date_1.default,
|
|
62
|
+
...handleOutPlugins()
|
|
63
|
+
};
|
|
64
|
+
const scalars = lodash_1.default.keys(resolvers);
|
|
65
|
+
let scalarStr = '';
|
|
66
|
+
lodash_1.default.each(scalars, (item) => {
|
|
67
|
+
scalarStr += `scalar ${item}\n `;
|
|
60
68
|
});
|
|
61
69
|
// console.log('resolvers', resolvers, _.keys(resolvers), scalarStr)
|
|
62
|
-
|
|
63
|
-
if (path
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
list.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const _generateAllComponentRecursive = (path = defaultPath) => {
|
|
71
|
+
if (!fs_1.default.existsSync(path))
|
|
72
|
+
return;
|
|
73
|
+
try {
|
|
74
|
+
const list = fs_1.default.readdirSync(path);
|
|
75
|
+
list.forEach((item) => {
|
|
76
|
+
const resolverPath = `${path}/${item}`;
|
|
77
|
+
if (!fs_1.default.existsSync(resolverPath))
|
|
78
|
+
return;
|
|
79
|
+
const stat = fs_1.default.statSync(resolverPath);
|
|
80
|
+
const isDir = stat.isDirectory();
|
|
81
|
+
const isFile = stat.isFile();
|
|
71
82
|
if (isDir) {
|
|
72
83
|
_generateAllComponentRecursive(resolverPath);
|
|
73
84
|
}
|
|
74
85
|
else if (isFile && item === typeDefFileName) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
try {
|
|
87
|
+
let schemaObj = require(resolverPath);
|
|
88
|
+
if (schemaObj.default !== undefined)
|
|
89
|
+
schemaObj = schemaObj.default;
|
|
90
|
+
const { query, mutation, subscription, type } = schemaObj;
|
|
91
|
+
if (query && query !== '')
|
|
92
|
+
querySchemes.push(query);
|
|
93
|
+
if (mutation && mutation !== '')
|
|
94
|
+
mutationSchemes.push(mutation);
|
|
95
|
+
if (subscription && subscription !== '')
|
|
96
|
+
subscriptionSchemes.push(subscription);
|
|
97
|
+
if (type && type !== '')
|
|
98
|
+
typeSchemes.push(type);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.warn(`⚠️ Error loading schema from ${resolverPath}:`, error);
|
|
102
|
+
}
|
|
88
103
|
}
|
|
89
104
|
else if (isFile && item === resolverFileName) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
try {
|
|
106
|
+
let resolversObj = require(resolverPath);
|
|
107
|
+
if (resolversObj.default !== undefined)
|
|
108
|
+
resolversObj = resolversObj.default;
|
|
109
|
+
Object.keys(resolversObj).forEach((k) => {
|
|
110
|
+
if (!resolvers[k])
|
|
111
|
+
resolvers[k] = {};
|
|
112
|
+
resolvers[k] = resolversObj[k];
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.warn(`⚠️ Error loading resolver from ${resolverPath}:`, error);
|
|
117
|
+
}
|
|
99
118
|
}
|
|
100
119
|
});
|
|
101
120
|
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.warn(`⚠️ Error reading directory ${path}:`, error);
|
|
123
|
+
}
|
|
102
124
|
};
|
|
103
125
|
_generateAllComponentRecursive();
|
|
104
126
|
_generateAllComponentRecursive(outModulesPath);
|
|
105
|
-
|
|
127
|
+
const schemaStr = `
|
|
128
|
+
${scalarStr}
|
|
129
|
+
|
|
130
|
+
type Query {
|
|
131
|
+
${querySchemes.join('\n')}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
type Mutation {
|
|
135
|
+
${mutationSchemes.join('\n')}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type Subscription {
|
|
139
|
+
${subscriptionSchemes.join('\n')}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
${typeSchemes.join('\n')}
|
|
143
|
+
`;
|
|
144
|
+
// 在生产环境中缓存结果
|
|
145
|
+
if (process.env.NODE_ENV === 'production') {
|
|
146
|
+
cachedSchema = schemaStr;
|
|
147
|
+
cachedResolvers = resolvers;
|
|
148
|
+
}
|
|
149
|
+
return { querySchemes, mutationSchemes, subscriptionSchemes, typeSchemes, resolvers, scalarStr, schemaStr };
|
|
106
150
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
151
|
+
const generateResult = generateTypeDefsAndResolvers();
|
|
152
|
+
const { querySchemes: querySchemesV, mutationSchemes: mutationSchemesV, subscriptionSchemes: subscriptionSchemesV, typeSchemes: typeSchemesV, resolvers: resolversV, scalarStr: scalarStrV, schemaStr: generatedSchemaStr } = generateResult;
|
|
153
|
+
// 使用生成的 schema 或构建新的 schema
|
|
154
|
+
const schemaStr = generatedSchemaStr ||
|
|
155
|
+
`
|
|
156
|
+
${scalarStrV}
|
|
157
|
+
|
|
158
|
+
type Query {
|
|
159
|
+
${querySchemesV?.join('\n') || '_: Boolean'}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
type Mutation {
|
|
163
|
+
${mutationSchemesV?.join('\n') || '_: Boolean'}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type Subscription {
|
|
167
|
+
${subscriptionSchemesV?.join('\n') || '_: Boolean'}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
${typeSchemesV?.join('\n') || ''}
|
|
171
|
+
`;
|
|
172
|
+
exports.default = (command) => {
|
|
110
173
|
if (command === 'dev') {
|
|
111
174
|
console.log('schemaStr', schemaStr);
|
|
112
175
|
console.log('resolvers', resolversV);
|
|
@@ -116,4 +179,4 @@ exports.default = (function (command) {
|
|
|
116
179
|
rootValue: resolversV,
|
|
117
180
|
graphiql: true
|
|
118
181
|
});
|
|
119
|
-
}
|
|
182
|
+
};
|
|
@@ -3,32 +3,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
7
|
+
const language_1 = require("graphql/language");
|
|
8
|
+
const graphql_1 = require("graphql");
|
|
9
|
+
const customScalarDate = new graphql_1.GraphQLScalarType({
|
|
10
10
|
name: 'Date',
|
|
11
11
|
description: 'Date custom scalar type',
|
|
12
|
-
parseValue:
|
|
13
|
-
|
|
12
|
+
parseValue: (value) => {
|
|
13
|
+
const date = (0, dayjs_1.default)(value);
|
|
14
14
|
if (!date.isValid()) {
|
|
15
15
|
throw new Error('Invalid date format');
|
|
16
16
|
}
|
|
17
17
|
return date.valueOf(); // Ensure this returns a number
|
|
18
18
|
},
|
|
19
|
-
serialize:
|
|
20
|
-
|
|
19
|
+
serialize: (value) => {
|
|
20
|
+
const date = (0, dayjs_1.default)(value);
|
|
21
21
|
if (!date.isValid()) {
|
|
22
22
|
throw new Error('Invalid date format');
|
|
23
23
|
}
|
|
24
24
|
return date.format('YYYY-MM-DD HH:mm:ss:SSS'); // Ensure this returns a string
|
|
25
25
|
},
|
|
26
|
-
parseLiteral:
|
|
26
|
+
parseLiteral: (ast) => {
|
|
27
27
|
if (ast.kind === language_1.Kind.INT) {
|
|
28
28
|
return parseInt(ast.value, 10);
|
|
29
29
|
}
|
|
30
30
|
else if (ast.kind === language_1.Kind.STRING) {
|
|
31
|
-
|
|
31
|
+
const date = (0, dayjs_1.default)(ast.value);
|
|
32
32
|
if (!date.isValid()) {
|
|
33
33
|
throw new Error('Invalid date format');
|
|
34
34
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCacheStats = exports.clearGraphQLCache = exports.graphqlCacheMiddleware = void 0;
|
|
4
|
+
// GraphQL 查询缓存中间件
|
|
5
|
+
const queryCache = new Map();
|
|
6
|
+
const CACHE_TTL = 5 * 60 * 1000; // 5分钟
|
|
7
|
+
const graphqlCacheMiddleware = (req, res, next) => {
|
|
8
|
+
const { query, variables } = req.body || {};
|
|
9
|
+
// 只缓存查询操作,不缓存变更操作
|
|
10
|
+
if (!query || query.trim().startsWith('mutation')) {
|
|
11
|
+
return next();
|
|
12
|
+
}
|
|
13
|
+
const cacheKey = JSON.stringify({ query, variables });
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
// 检查缓存
|
|
16
|
+
const cached = queryCache.get(cacheKey);
|
|
17
|
+
if (cached && now - cached.timestamp < CACHE_TTL) {
|
|
18
|
+
console.log('🚀 GraphQL cache hit for:', `${query.substring(0, 50)}...`);
|
|
19
|
+
return res.json(cached.data);
|
|
20
|
+
}
|
|
21
|
+
// 拦截响应以存储缓存
|
|
22
|
+
const originalSend = res.send;
|
|
23
|
+
res.send = function (body) {
|
|
24
|
+
try {
|
|
25
|
+
const data = typeof body === 'string' ? JSON.parse(body) : body;
|
|
26
|
+
// 只缓存成功的查询结果
|
|
27
|
+
if (!data.errors) {
|
|
28
|
+
queryCache.set(cacheKey, {
|
|
29
|
+
data,
|
|
30
|
+
timestamp: now
|
|
31
|
+
});
|
|
32
|
+
// 清理过期缓存
|
|
33
|
+
cleanExpiredCache();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.warn('⚠️ GraphQL cache error:', error);
|
|
38
|
+
}
|
|
39
|
+
originalSend.call(this, body);
|
|
40
|
+
};
|
|
41
|
+
next();
|
|
42
|
+
};
|
|
43
|
+
exports.graphqlCacheMiddleware = graphqlCacheMiddleware;
|
|
44
|
+
// 清理过期缓存
|
|
45
|
+
function cleanExpiredCache() {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
for (const [key, entry] of queryCache.entries()) {
|
|
48
|
+
if (now - entry.timestamp > CACHE_TTL) {
|
|
49
|
+
queryCache.delete(key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// 清空缓存的函数
|
|
54
|
+
const clearGraphQLCache = () => {
|
|
55
|
+
queryCache.clear();
|
|
56
|
+
console.log('🧹 GraphQL cache cleared');
|
|
57
|
+
};
|
|
58
|
+
exports.clearGraphQLCache = clearGraphQLCache;
|
|
59
|
+
// 获取缓存统计
|
|
60
|
+
const getCacheStats = () => {
|
|
61
|
+
return {
|
|
62
|
+
size: queryCache.size,
|
|
63
|
+
entries: Array.from(queryCache.keys()).map((key) => ({
|
|
64
|
+
query: `${key.substring(0, 100)}...`,
|
|
65
|
+
age: Date.now() - queryCache.get(key).timestamp
|
|
66
|
+
}))
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
exports.getCacheStats = getCacheStats;
|