befly 2.3.3 → 3.0.0
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/apis/health/info.ts +64 -0
- package/apis/tool/tokenCheck.ts +51 -0
- package/bin/befly.ts +202 -0
- package/checks/conflict.ts +408 -0
- package/checks/{table.js → table.ts} +139 -61
- package/config/env.ts +218 -0
- package/config/reserved.ts +96 -0
- package/main.ts +101 -0
- package/package.json +44 -8
- package/plugins/{db.js → db.ts} +24 -11
- package/plugins/logger.ts +28 -0
- package/plugins/redis.ts +51 -0
- package/plugins/tool.ts +34 -0
- package/scripts/syncDb/apply.ts +171 -0
- package/scripts/syncDb/constants.ts +70 -0
- package/scripts/syncDb/ddl.ts +182 -0
- package/scripts/syncDb/helpers.ts +172 -0
- package/scripts/syncDb/index.ts +215 -0
- package/scripts/syncDb/schema.ts +199 -0
- package/scripts/syncDb/sqlite.ts +50 -0
- package/scripts/syncDb/state.ts +104 -0
- package/scripts/syncDb/table.ts +204 -0
- package/scripts/syncDb/tableCreate.ts +142 -0
- package/scripts/syncDb/tests/constants.test.ts +104 -0
- package/scripts/syncDb/tests/ddl.test.ts +134 -0
- package/scripts/syncDb/tests/helpers.test.ts +70 -0
- package/scripts/syncDb/types.ts +92 -0
- package/scripts/syncDb/version.ts +73 -0
- package/scripts/syncDb.ts +9 -0
- package/scripts/{syncDev.js → syncDev.ts} +41 -25
- package/system.ts +149 -0
- package/tables/_common.json +21 -0
- package/tables/admin.json +10 -0
- package/tsconfig.json +58 -0
- package/types/api.d.ts +246 -0
- package/types/befly.d.ts +234 -0
- package/types/common.d.ts +215 -0
- package/types/context.ts +167 -0
- package/types/crypto.d.ts +23 -0
- package/types/database.d.ts +278 -0
- package/types/index.d.ts +16 -0
- package/types/index.ts +459 -0
- package/types/jwt.d.ts +99 -0
- package/types/logger.d.ts +43 -0
- package/types/plugin.d.ts +109 -0
- package/types/redis.d.ts +44 -0
- package/types/tool.d.ts +67 -0
- package/types/validator.d.ts +45 -0
- package/utils/addonHelper.ts +60 -0
- package/utils/api.ts +23 -0
- package/utils/{colors.js → colors.ts} +79 -21
- package/utils/crypto.ts +308 -0
- package/utils/datetime.ts +51 -0
- package/utils/dbHelper.ts +142 -0
- package/utils/errorHandler.ts +68 -0
- package/utils/index.ts +46 -0
- package/utils/jwt.ts +493 -0
- package/utils/logger.ts +284 -0
- package/utils/objectHelper.ts +68 -0
- package/utils/pluginHelper.ts +62 -0
- package/utils/redisHelper.ts +338 -0
- package/utils/response.ts +38 -0
- package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
- package/utils/sqlHelper.ts +447 -0
- package/utils/tableHelper.ts +167 -0
- package/utils/tool.ts +230 -0
- package/utils/typeHelper.ts +101 -0
- package/utils/validate.ts +451 -0
- package/utils/{xml.js → xml.ts} +100 -74
- package/.npmrc +0 -3
- package/.prettierignore +0 -2
- package/.prettierrc +0 -11
- package/apis/health/info.js +0 -49
- package/apis/tool/tokenCheck.js +0 -29
- package/bin/befly.js +0 -109
- package/config/env.js +0 -64
- package/main.js +0 -579
- package/plugins/logger.js +0 -14
- package/plugins/redis.js +0 -32
- package/plugins/tool.js +0 -8
- package/scripts/syncDb.js +0 -752
- package/system.js +0 -118
- package/tables/common.json +0 -16
- package/tables/tool.json +0 -6
- package/utils/api.js +0 -27
- package/utils/crypto.js +0 -260
- package/utils/index.js +0 -334
- package/utils/jwt.js +0 -387
- package/utils/logger.js +0 -143
- package/utils/redisHelper.js +0 -74
- package/utils/sqlManager.js +0 -471
- package/utils/tool.js +0 -31
- package/utils/validate.js +0 -226
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 健康检查接口 - TypeScript 版本
|
|
3
|
+
* 检查服务器、Redis、数据库等状态
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Env } from '../../config/env.js';
|
|
7
|
+
import { Api } from '../../utils/api.js';
|
|
8
|
+
import { Yes } from '../../utils/index.js';
|
|
9
|
+
import type { BeflyContext } from '../../types/befly.js';
|
|
10
|
+
import type { HealthInfo } from '../../types/api.js';
|
|
11
|
+
|
|
12
|
+
export default Api('健康检查', {
|
|
13
|
+
fields: {},
|
|
14
|
+
required: [],
|
|
15
|
+
handler: async (befly: BeflyContext, ctx: any) => {
|
|
16
|
+
const info: HealthInfo = {
|
|
17
|
+
status: 'ok',
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
uptime: process.uptime(),
|
|
20
|
+
memory: process.memoryUsage(),
|
|
21
|
+
runtime: 'Bun',
|
|
22
|
+
version: Bun.version,
|
|
23
|
+
platform: process.platform,
|
|
24
|
+
arch: process.arch
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// 检查 Redis 连接状态
|
|
28
|
+
if (Env.REDIS_ENABLE === 1) {
|
|
29
|
+
if (befly.redis) {
|
|
30
|
+
try {
|
|
31
|
+
await befly.redis.getRedisClient().ping();
|
|
32
|
+
info.redis = '已连接';
|
|
33
|
+
} catch (error: any) {
|
|
34
|
+
info.redis = '未连接';
|
|
35
|
+
info.redisError = error.message;
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
info.redis = '未开启';
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
info.redis = '禁用';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 检查数据库连接状态
|
|
45
|
+
if (Env.DB_ENABLE === 1) {
|
|
46
|
+
if (befly.db) {
|
|
47
|
+
try {
|
|
48
|
+
// 执行简单查询测试连接
|
|
49
|
+
await befly.db.query('SELECT 1');
|
|
50
|
+
info.database = '已连接';
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
info.database = '未连接';
|
|
53
|
+
info.databaseError = error.message;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
info.database = '未开启';
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
info.database = '禁用';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return Yes('健康检查成功', info);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 令牌检测接口 - TypeScript 版本
|
|
3
|
+
* 验证 JWT 令牌是否有效
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Env } from '../../config/env.js';
|
|
7
|
+
import { Api } from '../../utils/api.js';
|
|
8
|
+
import { Yes, No } from '../../utils/index.js';
|
|
9
|
+
import { Jwt } from '../../utils/jwt.js';
|
|
10
|
+
import type { BeflyContext } from '../../types/befly.js';
|
|
11
|
+
import type { JwtPayload } from '../../utils/jwt.js';
|
|
12
|
+
import type { TokenCheckData } from '../../types/api.js';
|
|
13
|
+
|
|
14
|
+
export default Api('令牌检测', {
|
|
15
|
+
fields: {},
|
|
16
|
+
required: [],
|
|
17
|
+
handler: async (befly: BeflyContext, ctx: any) => {
|
|
18
|
+
// 从 Authorization 头获取 token
|
|
19
|
+
const authHeader = ctx.req?.headers?.get('authorization') || '';
|
|
20
|
+
const token = authHeader.split(' ')[1] || '';
|
|
21
|
+
|
|
22
|
+
if (!token) {
|
|
23
|
+
return No('令牌不能为空');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 验证令牌
|
|
28
|
+
const jwtData = await Jwt.verify(token);
|
|
29
|
+
|
|
30
|
+
// 计算剩余有效期
|
|
31
|
+
const expiresIn = jwtData.exp ? jwtData.exp - Math.floor(Date.now() / 1000) : undefined;
|
|
32
|
+
|
|
33
|
+
const data: TokenCheckData = {
|
|
34
|
+
valid: true,
|
|
35
|
+
payload: jwtData,
|
|
36
|
+
expiresIn
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return Yes('令牌有效', data);
|
|
40
|
+
} catch (error: any) {
|
|
41
|
+
// 针对预期的令牌错误,返回友好提示(非致命错误)
|
|
42
|
+
if (error.message.includes('expired')) {
|
|
43
|
+
return No('令牌已过期', { expired: true });
|
|
44
|
+
} else if (error.message.includes('invalid')) {
|
|
45
|
+
return No('令牌无效', { invalid: true });
|
|
46
|
+
}
|
|
47
|
+
// 其他未知错误向上抛出,由路由层统一处理
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
package/bin/befly.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun run
|
|
2
|
+
/**
|
|
3
|
+
* Befly CLI - TypeScript 版本
|
|
4
|
+
* 列出并执行 core/scripts 与 tpl/scripts 下的脚本
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { Glob } from 'bun';
|
|
9
|
+
import { __dirscript as coreScriptsDir, getProjectDir } from '../system.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 脚本项接口
|
|
13
|
+
*/
|
|
14
|
+
interface ScriptItem {
|
|
15
|
+
/** 脚本名称 */
|
|
16
|
+
name: string;
|
|
17
|
+
/** 脚本来源 (core 或 tpl) */
|
|
18
|
+
source: 'core' | 'tpl';
|
|
19
|
+
/** 是否与另一来源的脚本重名 */
|
|
20
|
+
duplicate: boolean;
|
|
21
|
+
/** 脚本完整路径 */
|
|
22
|
+
path: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 命令行参数接口
|
|
27
|
+
*/
|
|
28
|
+
interface CliArgs {
|
|
29
|
+
/** 是否为预演模式(只输出计划不执行) */
|
|
30
|
+
DRY_RUN: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 解析目录(来自 system.js)
|
|
34
|
+
// 核心脚本目录:core/scripts
|
|
35
|
+
// 用户项目(如 tpl)的脚本目录:始终基于当前工作目录
|
|
36
|
+
const tplScriptsDir = getProjectDir('scripts');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 安全地列出目录下的所有 .js/.ts 脚本文件
|
|
40
|
+
* @param dir - 目录路径
|
|
41
|
+
* @returns 脚本名称数组(不含扩展名)
|
|
42
|
+
*/
|
|
43
|
+
function safeList(dir: string): string[] {
|
|
44
|
+
try {
|
|
45
|
+
// 使用 Bun.Glob 查找当前目录下的所有 .js 和 .ts 文件(不递归)
|
|
46
|
+
const glob = new Glob('*.{js,ts}');
|
|
47
|
+
const files = Array.from(
|
|
48
|
+
glob.scanSync({
|
|
49
|
+
cwd: dir,
|
|
50
|
+
absolute: false,
|
|
51
|
+
onlyFiles: true,
|
|
52
|
+
dot: false
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
// 移除扩展名并排序
|
|
56
|
+
return files
|
|
57
|
+
.map((f) => {
|
|
58
|
+
const basename = path.basename(f);
|
|
59
|
+
return basename.replace(/\.(js|ts)$/, '');
|
|
60
|
+
})
|
|
61
|
+
.sort();
|
|
62
|
+
} catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 构建所有可用脚本的列表
|
|
69
|
+
* @returns 脚本项数组
|
|
70
|
+
*/
|
|
71
|
+
function buildScriptItems(): ScriptItem[] {
|
|
72
|
+
const coreList = safeList(coreScriptsDir);
|
|
73
|
+
const tplList = safeList(tplScriptsDir);
|
|
74
|
+
const coreSet = new Set(coreList);
|
|
75
|
+
|
|
76
|
+
const items: ScriptItem[] = [];
|
|
77
|
+
|
|
78
|
+
// 添加核心脚本
|
|
79
|
+
for (const name of coreList) {
|
|
80
|
+
items.push({
|
|
81
|
+
name: name,
|
|
82
|
+
source: 'core',
|
|
83
|
+
duplicate: tplList.includes(name),
|
|
84
|
+
path: path.resolve(coreScriptsDir, `${name}.js`) // 优先 .js
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 添加用户脚本
|
|
89
|
+
for (const name of tplList) {
|
|
90
|
+
items.push({
|
|
91
|
+
name: name,
|
|
92
|
+
source: 'tpl',
|
|
93
|
+
duplicate: coreSet.has(name),
|
|
94
|
+
path: path.resolve(tplScriptsDir, `${name}.js`) // 优先 .js
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 排序:名称字典序,core 在前
|
|
99
|
+
items.sort((a, b) => {
|
|
100
|
+
if (a.name === b.name) {
|
|
101
|
+
return a.source === b.source ? 0 : a.source === 'core' ? -1 : 1;
|
|
102
|
+
}
|
|
103
|
+
return a.name.localeCompare(b.name);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return items;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 打印所有可用的脚本列表
|
|
111
|
+
*/
|
|
112
|
+
function printAllScripts(): void {
|
|
113
|
+
const items = buildScriptItems();
|
|
114
|
+
if (items.length === 0) {
|
|
115
|
+
console.log(' • <无>');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
for (const it of items) {
|
|
119
|
+
if (it.source === 'tpl' && it.duplicate) {
|
|
120
|
+
console.log(` • ${it.name}(重复)`);
|
|
121
|
+
} else {
|
|
122
|
+
console.log(` • ${it.name}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 解析脚本名称到完整路径
|
|
129
|
+
* @param name - 脚本名称(可带或不带 .js/.ts 扩展名)
|
|
130
|
+
* @returns 脚本完整路径,未找到返回 null
|
|
131
|
+
*/
|
|
132
|
+
async function resolveScriptPath(name: string): Promise<string | null> {
|
|
133
|
+
// 移除扩展名
|
|
134
|
+
const base = name.replace(/\.(js|ts)$/, '');
|
|
135
|
+
|
|
136
|
+
// 检查 .ts 文件(优先)
|
|
137
|
+
const coreTsPath = path.resolve(coreScriptsDir, `${base}.ts`);
|
|
138
|
+
const tplTsPath = path.resolve(tplScriptsDir, `${base}.ts`);
|
|
139
|
+
if (await Bun.file(coreTsPath).exists()) return coreTsPath;
|
|
140
|
+
if (await Bun.file(tplTsPath).exists()) return tplTsPath;
|
|
141
|
+
|
|
142
|
+
// 回退到 .js 文件
|
|
143
|
+
const coreJsPath = path.resolve(coreScriptsDir, `${base}.js`);
|
|
144
|
+
const tplJsPath = path.resolve(tplScriptsDir, `${base}.js`);
|
|
145
|
+
if (await Bun.file(coreJsPath).exists()) return coreJsPath;
|
|
146
|
+
if (await Bun.file(tplJsPath).exists()) return tplJsPath;
|
|
147
|
+
|
|
148
|
+
// 回退到列表匹配(防止极端路径或大小写差异)
|
|
149
|
+
const items = buildScriptItems();
|
|
150
|
+
const hit = items.find((it) => it.name.toLowerCase() === base.toLowerCase() && it.source === 'core') || items.find((it) => it.name.toLowerCase() === base.toLowerCase());
|
|
151
|
+
|
|
152
|
+
return hit ? hit.path : null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 在指定路径运行脚本
|
|
157
|
+
* @param targetPath - 脚本完整路径
|
|
158
|
+
* @param label - 脚本标签(用于日志)
|
|
159
|
+
* @param args - 传递给脚本的参数
|
|
160
|
+
* @returns 脚本退出码
|
|
161
|
+
*/
|
|
162
|
+
async function runScriptAtPath(targetPath: string, label: string, args: string[] = []): Promise<number> {
|
|
163
|
+
const bunExe = process.execPath || 'bun';
|
|
164
|
+
const child = Bun.spawn({
|
|
165
|
+
cmd: [bunExe, targetPath, ...args],
|
|
166
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
167
|
+
cwd: process.cwd(),
|
|
168
|
+
env: { ...process.env, LOG_TO_CONSOLE: '1' }
|
|
169
|
+
});
|
|
170
|
+
const code = await child.exited;
|
|
171
|
+
return code ?? 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* CLI 主函数
|
|
176
|
+
*/
|
|
177
|
+
async function main(): Promise<void> {
|
|
178
|
+
const [, , cmd, ...args] = process.argv;
|
|
179
|
+
|
|
180
|
+
// 无参数:打印所有脚本
|
|
181
|
+
if (!cmd) {
|
|
182
|
+
printAllScripts();
|
|
183
|
+
process.exit(0);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 按名称执行(将剩余参数透传给脚本)
|
|
187
|
+
const target = await resolveScriptPath(cmd);
|
|
188
|
+
if (!target) {
|
|
189
|
+
console.error(`未找到脚本: ${cmd}`);
|
|
190
|
+
printAllScripts();
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const code = await runScriptAtPath(target, cmd, args);
|
|
195
|
+
process.exit(code ?? 0);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 启动 CLI
|
|
199
|
+
main().catch((e: Error) => {
|
|
200
|
+
console.error('Befly CLI 执行失败:', e);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|