@kevisual/cnb 0.0.64 → 0.0.66

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/agent/cli.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { app } from './index.ts';
2
2
  import { parse } from '@kevisual/router/src/commander.ts';
3
3
 
4
- await parse({ app: app, description: 'CNB控制台命令行工具', parse: true })
4
+ await parse({ app: app, description: 'CNB控制台命令行工具', parse: true, exitOnEnd: true })
5
5
 
6
6
  console.log('命令行工具已启动,输入 help 查看可用命令', 'end');
@@ -0,0 +1,8 @@
1
+ import { app } from '../app.ts';
2
+ import { parse } from '@kevisual/router/commander';
3
+ import '../routes/auth/index.ts';
4
+
5
+ await parse({
6
+ app: app,
7
+ exitOnEnd: false
8
+ })
@@ -0,0 +1,46 @@
1
+ import { createKeepAlive } from '../../src/workspace/keep-live.ts';
2
+ import { readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { Command, program } from 'commander';
5
+
6
+ program.addCommand(new Command('live')
7
+ .description('启动 CNB Keep Alive 服务')
8
+ .option('-j, --json <string>', 'JSON数据')
9
+ .option('-c, --config <string>', '配置文件路径 (优先级高于 JSON 参数), 默认keep.json')
10
+ .action(async (opts) => {
11
+ let config: { wss: string; cookie: string; url?: string };
12
+ let configPath = opts.config || 'keep.json'
13
+ if (configPath.startsWith('/')) {
14
+ configPath = path.resolve(configPath)
15
+ } else {
16
+ configPath = path.join(process.cwd(), configPath)
17
+ }
18
+
19
+ try {
20
+ let jsonString = opts.json;
21
+
22
+ if (!jsonString) {
23
+ jsonString = readFileSync(configPath, 'utf-8').trim();
24
+ }
25
+
26
+ config = JSON.parse(jsonString);
27
+ } catch (error) {
28
+ console.error('JSON 解析错误: 请检查输入的 JSON 格式是否正确');
29
+ process.exit(1);
30
+ }
31
+ if (!config.wss || !config.cookie) {
32
+ console.error('配置错误: 必须包含 wss 和 cookie 字段');
33
+ process.exit(1);
34
+ }
35
+
36
+ createKeepAlive({
37
+ wsUrl: config.wss,
38
+ cookie: config.cookie,
39
+ onDisconnect: (code) => {
40
+ console.log(`与 CNB 服务器断开连接,代码: ${code}`);
41
+ },
42
+ debug: true
43
+ });
44
+ }));
45
+
46
+ program.parse(process.argv);
@@ -48,7 +48,8 @@ export class CNBManager {
48
48
  constructor() {
49
49
  setInterval(() => {
50
50
  this.clearExpiredCNB()
51
- }, 1000 * 60 * 30) // 每30分钟清理一次过期的 CNB 实例
51
+ // unref 定时器,避免阻止进程退出
52
+ }, 1000 * 60 * 30).unref() // 每30分钟清理一次过期的 CNB 实例
52
53
  }
53
54
  getDefaultCNB() {
54
55
  const cnbItem = this.cnbMap.get('default')
@@ -0,0 +1,45 @@
1
+ import { app } from '../../app.ts';
2
+ /**
3
+ * 验证上下文中的 App ID 是否与指定的 App ID 匹配
4
+ * @param {any} ctx - 上下文对象,可能包含 appId 属性
5
+ * @param {string} appId - 需要验证的目标 App ID
6
+ * @returns {boolean} 如果 ctx 中包含 appId 且匹配则返回 true,否则返回 false
7
+ * @throws {Error} 如果 ctx 中包含 appId 但不匹配,则抛出 403 错误
8
+ */
9
+ const checkAppId = (ctx: any, appId: string) => {
10
+ const _appId = ctx?.app?.appId;
11
+ if (_appId) {
12
+ if (_appId !== appId) {
13
+ ctx.throw(403, 'Invalid App ID');
14
+ }
15
+ return true;
16
+ }
17
+ return false;
18
+ }
19
+
20
+ app.route({
21
+ rid: 'auth',
22
+ path: 'auth',
23
+ }).define(async (ctx) => {
24
+ // ctx.body = 'Auth Route';
25
+ if (checkAppId(ctx, app.appId)) {
26
+ ctx.state.tokenUser = {
27
+ username: 'default',
28
+ }
29
+ return;
30
+ }
31
+ }).addTo(app, { overwrite: false });
32
+
33
+ app.route({
34
+ rid: 'auth-admin',
35
+ path: 'auth-admin',
36
+ middleware: ['auth'],
37
+ }).define(async (ctx) => {
38
+ // ctx.body = 'Admin Auth Route';
39
+ if (checkAppId(ctx, app.appId)) {
40
+ ctx.state.tokenUser = {
41
+ username: 'default',
42
+ }
43
+ return;
44
+ }
45
+ }).addTo(app, { overwrite: false });
@@ -0,0 +1,43 @@
1
+ import { app, cnbManager } from '../../app.ts';
2
+ import { z } from 'zod';
3
+ import fs from 'node:fs';
4
+
5
+ app.route({
6
+ path: 'cnb-deploy',
7
+ key: 'file-list',
8
+ description: '部署页面, 程序需要在自己的客户端中执行。',
9
+ middleware: ['auth'],
10
+ metadata: {
11
+ args: {
12
+ repo: z.string().describe('仓库名称,格式为 owner/repo'),
13
+ version: z.string().optional().describe('版本号,例如1.0.0,默认为1.0.0'),
14
+ // dir: z.string().optional().describe('相对执行路径的文件夹名称,默认为 dist'),
15
+ }
16
+ }
17
+ }).define(async (ctx) => {
18
+ const cnb = await cnbManager.getContext(ctx);
19
+ const version = ctx.args.version || '1.0.0';
20
+ const dir = ctx.args.dir || 'dist';
21
+ const pwd = process.cwd();
22
+ const repo = ctx.args.repo;
23
+ const dirPath = `${pwd}/${dir}`;
24
+ const dot = ctx.args.dot || false;
25
+ if (!repo) {
26
+ ctx.throw(400, 'repo 参数必填');
27
+ }
28
+ if (!fileIsExist(dirPath)) {
29
+ ctx.throw(400, `目录 ${dir} 不存在`);
30
+ }
31
+ const res = await cnb.release.list(repo, { page: 1, page_size: 50, query: version });
32
+ ctx.body = res;
33
+
34
+ }).addTo(app);
35
+
36
+ const fileIsExist = (filepath: string) => {
37
+ try {
38
+ fs.accessSync(filepath, fs.constants.F_OK);
39
+ } catch (e) {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
@@ -0,0 +1,2 @@
1
+ import './upload.ts'
2
+ import './file-list.ts';
@@ -0,0 +1,88 @@
1
+ import { app, cnbManager } from '../../app.ts';
2
+ import { z } from 'zod';
3
+ import glob from 'fast-glob';
4
+ import fs from 'node:fs';
5
+
6
+ import { uploadReleaseFiles } from '../../../src/release/upload-release.ts'
7
+ import { DefaultIgnore } from '../../../src/upload/upload-base.ts';
8
+ app.route({
9
+ path: 'cnb-deploy',
10
+ key: 'page',
11
+ description: '部署页面,上传对应的文件的内容到指定的仓库,程序需要在自己的客户端中执行。',
12
+ middleware: ['auth-admin'],
13
+ metadata: {
14
+ args: {
15
+ repo: z.string().describe('仓库名称,格式为 owner/repo'),
16
+ version: z.string().optional().describe('版本号,例如1.0.0,默认为1.0.0'),
17
+ dir: z.string().optional().describe('相对执行路径的文件夹名称,默认为 dist'),
18
+ // filename: z.string().optional().describe('部署文件名称,默认为空,如果存在,dir 不具备作用'),
19
+ dot: z.boolean().optional().describe('是否包含点文件,默认为 false'),
20
+ cwd: z.string().optional().describe('执行命令的当前工作目录,默认为 process.cwd()')
21
+ }
22
+ }
23
+ }).define(async (ctx) => {
24
+ const cnb = await cnbManager.getContext(ctx);
25
+ const version = ctx.args.version || '1.0.0';
26
+ const dir = ctx.args.dir || 'dist';
27
+ const pwd = ctx.args.cwd || process.cwd();
28
+ const repo = ctx.args.repo;
29
+ let dirPath = `${pwd}/${dir}`;
30
+ const dot = ctx.args.dot || false;
31
+ if (!repo) {
32
+ ctx.throw(400, 'repo 参数必填');
33
+ }
34
+ let dirReg = '';
35
+ if (dirPath.includes('*') || dirPath.includes('?')) {
36
+ dirReg = dirPath;
37
+ } else {
38
+ dirReg = `${dirPath}/**/*`
39
+ }
40
+ const files = await glob(dirReg, {
41
+ cwd: pwd,
42
+ onlyFiles: true,
43
+ ignore: DefaultIgnore,
44
+ dot,
45
+ });
46
+ const res = await uploadReleaseFiles({
47
+ files: files.map(file => ({
48
+ type: 'file',
49
+ filepath: file,
50
+ name: file.replace(`${dirPath}/`, '')
51
+ })),
52
+ cnb,
53
+ repo: repo,
54
+ version
55
+ });
56
+ ctx.body = res;
57
+
58
+ }).addTo(app);
59
+
60
+ app.route({
61
+ path: 'cnb-deploy',
62
+ key: 'app',
63
+ description: '部署当前文件夹下的文件列表,根据 packages.json 进行部署,程序需要在自己的客户端中执行。basename 类似 /kevision/cnb, version类似 1.0.0,deploy.dir 类似 dist',
64
+ middleware: ['auth-admin'],
65
+ }).define(async (ctx) => {
66
+ try {
67
+ const pkgs = JSON.parse(fs.readFileSync(`${process.cwd()}/packages.json`, 'utf-8'));
68
+ let repo: string = pkgs.basename || '';
69
+ const version = pkgs.version || '1.0.0';
70
+ if (repo.startsWith('/')) {
71
+ repo = repo.slice(1);
72
+ }
73
+ const dir = pkgs.deploy?.dir || 'dist';
74
+ const res = await app.run({ path: 'cnb-deploy', key: 'page', payload: { repo, version, dir } });
75
+ ctx.forward(res);
76
+ } catch (e) {
77
+ ctx.throw(500, '部署失败,无法读取 packages.json 文件');
78
+ }
79
+ }).addTo(app);
80
+
81
+ const fileIsExist = (filepath: string) => {
82
+ try {
83
+ fs.accessSync(filepath, fs.constants.F_OK);
84
+ } catch (e) {
85
+ return false;
86
+ }
87
+ return true;
88
+ }
@@ -15,47 +15,5 @@ import './missions/index.ts';
15
15
  import './labels/index.ts';
16
16
  import './package/index.ts';
17
17
 
18
- /**
19
- * 验证上下文中的 App ID 是否与指定的 App ID 匹配
20
- * @param {any} ctx - 上下文对象,可能包含 appId 属性
21
- * @param {string} appId - 需要验证的目标 App ID
22
- * @returns {boolean} 如果 ctx 中包含 appId 且匹配则返回 true,否则返回 false
23
- * @throws {Error} 如果 ctx 中包含 appId 但不匹配,则抛出 403 错误
24
- */
25
- const checkAppId = (ctx: any, appId: string) => {
26
- const _appId = ctx?.app?.appId;
27
- if (_appId) {
28
- if (_appId !== appId) {
29
- ctx.throw(403, 'Invalid App ID');
30
- }
31
- return true;
32
- }
33
- return false;
34
- }
35
-
36
- app.route({
37
- rid: 'auth',
38
- path: 'auth',
39
- }).define(async (ctx) => {
40
- // ctx.body = 'Auth Route';
41
- if (checkAppId(ctx, app.appId)) {
42
- ctx.state.tokenUser = {
43
- username: 'default',
44
- }
45
- return;
46
- }
47
- }).addTo(app, { overwrite: false });
48
-
49
- app.route({
50
- rid: 'auth-admin',
51
- path: 'auth-admin',
52
- middleware: ['auth'],
53
- }).define(async (ctx) => {
54
- // ctx.body = 'Admin Auth Route';
55
- if (checkAppId(ctx, app.appId)) {
56
- ctx.state.tokenUser = {
57
- username: 'default',
58
- }
59
- return;
60
- }
61
- }).addTo(app, { overwrite: false });
18
+ import './deploy/index.ts';
19
+ import './auth/index.ts';
package/bin/live.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import '../dist/cli-live.js';
@@ -0,0 +1,2 @@
1
+
2
+ export { };