@gogoqiu/tencent-http-server 0.0.3

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.
Files changed (84) hide show
  1. package/bin/gogoqiu-node-http-service +3 -0
  2. package/dist/build.d.ts +3 -0
  3. package/dist/build.d.ts.map +1 -0
  4. package/dist/build.js +3 -0
  5. package/dist/build.js.map +1 -0
  6. package/dist/routes/index.d.ts +4 -0
  7. package/dist/routes/index.d.ts.map +1 -0
  8. package/dist/routes/index.js +106 -0
  9. package/dist/routes/index.js.map +1 -0
  10. package/dist/server.d.ts +3 -0
  11. package/dist/server.d.ts.map +1 -0
  12. package/dist/server.js +318 -0
  13. package/dist/server.js.map +1 -0
  14. package/dist/serverCmd.d.ts +8 -0
  15. package/dist/serverCmd.d.ts.map +1 -0
  16. package/dist/serverCmd.js +44 -0
  17. package/dist/serverCmd.js.map +1 -0
  18. package/dist/shell/install.d.ts +5 -0
  19. package/dist/shell/install.d.ts.map +1 -0
  20. package/dist/shell/install.js +221 -0
  21. package/dist/shell/install.js.map +1 -0
  22. package/dist/shell/postinst.d.ts +2 -0
  23. package/dist/shell/postinst.d.ts.map +1 -0
  24. package/dist/shell/postinst.js +4 -0
  25. package/dist/shell/postinst.js.map +1 -0
  26. package/dist/shell.d.ts +2 -0
  27. package/dist/shell.d.ts.map +1 -0
  28. package/dist/shell.js +249 -0
  29. package/dist/shell.js.map +1 -0
  30. package/package.json +82 -0
  31. package/public/captures1//346/265/213/350/257/225/347/224/250captures +0 -0
  32. package/public/chat/offer.html +796 -0
  33. package/public/chat/options.html +58 -0
  34. package/public/chat/server-info.html +32 -0
  35. package/public/chat2.html +272 -0
  36. package/public/chat3.html +246 -0
  37. package/public/chat4.html +302 -0
  38. package/public/chat5.html +41 -0
  39. package/public/formdata.html +41 -0
  40. package/public/hls-player.htm +41 -0
  41. package/public/img/back.svg +1 -0
  42. package/public/img/offline.svg +1 -0
  43. package/public/img/online.svg +1 -0
  44. package/public/ip-record.html +28 -0
  45. package/public/js/encrypt.js +36 -0
  46. package/public/js/socket.io.min.js +7 -0
  47. package/public/js/socket.io.min.js.map +1 -0
  48. package/public/myhost/hostReg.html +35 -0
  49. package/public/mylog-chat.htm +260 -0
  50. package/public/mylog3.html +245 -0
  51. package/public/navbar.css +17 -0
  52. package/public/readme.txt +3 -0
  53. package/public/scroll.htm +139 -0
  54. package/public/ssh-client.html +0 -0
  55. package/public/upload-file.html +226 -0
  56. package/public/upload.html +23 -0
  57. package/public/uploads1/files-1757866537383-447469495.jpg +0 -0
  58. package/public/uploads1/files-1757867389485-764531720.jpg +0 -0
  59. package/public/uploads1/files-1757867518311-278635302.jpg +0 -0
  60. package/public/uploads1/files-1757867629687-688924576.jpg +0 -0
  61. package/public/uploads1/files-1757868630683-52261917.jpg +0 -0
  62. package/public/uploads1/files-1757869187061-619427683.jpg +0 -0
  63. package/public/uploads1/small_files-1757869187061-619427683.jpg +0 -0
  64. package/public/uploads1//346/265/213/350/257/225/347/224/250upload +0 -0
  65. package/public/utils.html +57 -0
  66. package/public/utils.js +161 -0
  67. package/public/webrtc/rtc-client.html +238 -0
  68. package/public/webrtc/rtc-file-transfer-client.html +238 -0
  69. package/public/webrtc/rtc-file-transfer-server.html +453 -0
  70. package/public/webrtc/rtc-server.html +453 -0
  71. package/public/webrtc/video-client-input.html +264 -0
  72. package/public/webrtc/video-server-input.html +312 -0
  73. package/public/webrtc/webrtc-chat-with-files.html +581 -0
  74. package/public/webrtc/webrtc-chat.html +367 -0
  75. package/public/webrtc/webrtc-file-offer.html +88 -0
  76. package/public/webrtc/webrtc1.html +186 -0
  77. package/readme.txt +71 -0
  78. package/views/chat.ejs +53 -0
  79. package/views/index.ejs +125 -0
  80. package/views/special-message.ejs +8 -0
  81. package/views/ssh.ejs +142 -0
  82. package/views/utils.ejs +0 -0
  83. package/views/webrtc/client.ejs +203 -0
  84. package/views/webrtc/server.ejs +365 -0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import '../dist/shell.js'
@@ -0,0 +1,3 @@
1
+ export declare const buildTime = "2026/3/28 21:32:55";
2
+ export declare const version = "0.0.2";
3
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,uBAAuB,CAAC;AAC9C,eAAO,MAAM,OAAO,UAAU,CAAA"}
package/dist/build.js ADDED
@@ -0,0 +1,3 @@
1
+ export const buildTime = "2026/3/28 21:32:55";
2
+ export const version = "0.0.2";
3
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,oBAAoB,CAAC;AAC9C,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA","sourcesContent":["export const buildTime = \"2026/3/28 21:32:55\";\nexport const version = \"0.0.2\""]}
@@ -0,0 +1,4 @@
1
+ import { Router } from "express";
2
+ declare function getRouter(debug: boolean): Router;
3
+ export default getRouter;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAK1C,iBAAS,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA+GzC;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,106 @@
1
+ import express from "express";
2
+ import multer from "multer";
3
+ import { buildTime } from "../build.js";
4
+ //import { upload } from "../server";
5
+ function getRouter(debug) {
6
+ const router = express.Router();
7
+ /* GET home page. */
8
+ /*
9
+ 引用路径
10
+ */
11
+ router.get('/', (req, res, next) => {
12
+ res.render('index', { title: 'Express' });
13
+ });
14
+ router.get('/server', (req, res, next) => {
15
+ //res.render('test', { title: 'Express' });
16
+ res.write("server");
17
+ });
18
+ /*
19
+ use socket.io to chat
20
+ */
21
+ router.get('/client', (req, res, next) => {
22
+ //res.render('test', { title: 'Express' });
23
+ res.write("client");
24
+ });
25
+ router.get('/chat', (req, res, next) => {
26
+ res.render('chat', { title: 'Express' });
27
+ //res.render('index', { title: 'Express' });
28
+ });
29
+ router.get('/version', (req, res, next) => {
30
+ res.json({ 'buildTime': buildTime });
31
+ });
32
+ const upload = multer();
33
+ // 解析 FormData 数据(.none() 表示只处理非文件字段)
34
+ //app.post('/formdata', upload.none(), (req, res, next) => {
35
+ /*
36
+ "MulterError: Unexpected field"
37
+ upload.none()
38
+ upload.any()
39
+ upload.single("user")
40
+
41
+ 处理多个文件(同名字段):upload.array('fieldname', 数量限制)
42
+ 处理多个不同字段:upload.fields([{ name: 'field1' }, { name: 'field2' }])
43
+ 允许所有字段(不限制):upload.any()(谨慎使用,有安全风险)
44
+ */
45
+ //router.post('/formdata', upload.any(), (req, res, next) => {
46
+ router.post('/formdata', upload.fields([{ name: 'name' }, { name: 'file' }, { name: 'field2' }]), (req, res, next) => {
47
+ // 解析后的数据会放在 req.body 中
48
+ /*
49
+ FormData 数据: [Object: null prototype] { name: 'gogoqiu' }
50
+ 上传的文件: undefined
51
+ */
52
+ console.log('FormData 数据:', req.body);
53
+ // 访问具体字段
54
+ //console.log('key1 的值:', req.body.key1); // 输出: value1
55
+ console.log('上传的文件:', req.file); // 上传的文件信息
56
+ /*
57
+ [
58
+ {
59
+ fieldname: 'file',
60
+ originalname: 'doPrint.json',
61
+ encoding: '7bit',
62
+ mimetype: 'application/json',
63
+ buffer: <Buffer 7b 22 73 75 63 63 65 73 73 22 3a 74 72 75 65 2c 22 72 65 73 75 6c 74 22 3a 5b 5d 7d>,
64
+ size: 28
65
+ }
66
+ ] gogoqiu
67
+ */
68
+ console.log(req.files, req.body.name);
69
+ res.send('FormData 解析成功');
70
+ });
71
+ // 处理单个文件上传(字段名为 file)
72
+ //app.post('/upload', upload.single('file'), (req, res) => {
73
+ router.post('/upload', upload.any(), (req, res) => {
74
+ console.log('表单字段:', req.body); // 普通字段
75
+ console.log('上传的文件:', req.file); // 上传的文件信息
76
+ console.log(req);
77
+ res.send('文件上传成功');
78
+ });
79
+ /*
80
+ 每次启动时产生密钥
81
+ 每天密钥都记录下来
82
+ 把密钥用zip
83
+ 每条chat后面需要指定密钥组序号。
84
+ */
85
+ router.post('/public-key', upload.any(), (req, res) => {
86
+ });
87
+ router.post('/reflect', upload.any(), (req, res) => {
88
+ // 测试反馈所有req.body
89
+ // 普通表单字段 → req.body
90
+ // 上传的文件 → req.files
91
+ res.send(req.body);
92
+ });
93
+ // add Responsive node
94
+ router.get('/reflect', (req, res) => {
95
+ // 测试反馈所有req.query
96
+ /*
97
+ router.get('/reflect/:id', (req, res) => {
98
+ const { id } = req.params; // 通过 req.params 获取路径参数
99
+ 处理 GET 请求时,99% 的场景下使用 req.query(查询参数)或 req.params(路径参数)
100
+ */
101
+ res.send(req.query);
102
+ });
103
+ return router;
104
+ }
105
+ export default getRouter;
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Router } from \"express\";\nimport multer from \"multer\";\nimport { buildTime } from \"../build.js\";\n//import { upload } from \"../server\";\n\nfunction getRouter(debug: boolean): Router {\n const router: Router = express.Router();\n\n /* GET home page. */\n /*\n 引用路径\n */\n router.get('/', (req, res, next) => {\n res.render('index', { title: 'Express' });\n });\n\n router.get('/server', (req, res, next) => {\n //res.render('test', { title: 'Express' });\n res.write(\"server\")\n });\n\n /*\n use socket.io to chat\n */\n router.get('/client', (req, res, next) => {\n //res.render('test', { title: 'Express' });\n res.write(\"client\")\n });\n\n router.get('/chat', (req, res, next) => {\n res.render('chat', { title: 'Express' });\n //res.render('index', { title: 'Express' });\n });\n\n router.get('/version', (req, res, next) => {\n res.json({ 'buildTime': buildTime });\n });\n\n const upload = multer();\n\n // 解析 FormData 数据(.none() 表示只处理非文件字段)\n //app.post('/formdata', upload.none(), (req, res, next) => {\n /*\n \"MulterError: Unexpected field\"\n upload.none()\n upload.any()\n upload.single(\"user\")\n \n 处理多个文件(同名字段):upload.array('fieldname', 数量限制)\n 处理多个不同字段:upload.fields([{ name: 'field1' }, { name: 'field2' }])\n 允许所有字段(不限制):upload.any()(谨慎使用,有安全风险)\n */\n //router.post('/formdata', upload.any(), (req, res, next) => {\n router.post('/formdata', upload.fields([{ name: 'name' }, { name: 'file' }, { name: 'field2' }]), (req, res, next) => {\n // 解析后的数据会放在 req.body 中\n /*\n FormData 数据: [Object: null prototype] { name: 'gogoqiu' }\n 上传的文件: undefined\n */\n console.log('FormData 数据:', req.body);\n // 访问具体字段\n //console.log('key1 的值:', req.body.key1); // 输出: value1\n console.log('上传的文件:', req.file); // 上传的文件信息\n /*\n [\n {\n fieldname: 'file',\n originalname: 'doPrint.json',\n encoding: '7bit',\n mimetype: 'application/json',\n buffer: <Buffer 7b 22 73 75 63 63 65 73 73 22 3a 74 72 75 65 2c 22 72 65 73 75 6c 74 22 3a 5b 5d 7d>,\n size: 28\n }\n ] gogoqiu\n */\n console.log(req.files, req.body.name)\n res.send('FormData 解析成功');\n });\n\n // 处理单个文件上传(字段名为 file)\n //app.post('/upload', upload.single('file'), (req, res) => {\n router.post('/upload', upload.any(), (req, res) => {\n console.log('表单字段:', req.body); // 普通字段\n console.log('上传的文件:', req.file); // 上传的文件信息\n console.log(req)\n res.send('文件上传成功');\n });\n\n /*\n 每次启动时产生密钥\n 每天密钥都记录下来\n 把密钥用zip\n 每条chat后面需要指定密钥组序号。\n */\n router.post('/public-key', upload.any(), (req, res) => {\n })\n\n router.post('/reflect', upload.any(), (req, res) => {\n // 测试反馈所有req.body\n // 普通表单字段 → req.body\n // 上传的文件 → req.files\n res.send(req.body);\n })\n\n // add Responsive node\n router.get('/reflect', (req, res) => {\n // 测试反馈所有req.query\n /*\n router.get('/reflect/:id', (req, res) => {\n const { id } = req.params; // 通过 req.params 获取路径参数\n 处理 GET 请求时,99% 的场景下使用 req.query(查询参数)或 req.params(路径参数)\n */\n res.send(req.query);\n })\n\n return router;\n}\n\nexport default getRouter;"]}
@@ -0,0 +1,3 @@
1
+ import type { CmdOption } from "./serverCmd.js";
2
+ export declare function initServer(options: CmdOption, root: string, info: any): void;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAqGhD,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,QAyOrE"}
package/dist/server.js ADDED
@@ -0,0 +1,318 @@
1
+ import express from "express";
2
+ import https from "https";
3
+ import getIndexRouter from "./routes/index.js";
4
+ import path from "path";
5
+ import morgan from 'morgan';
6
+ import cookieParser from 'cookie-parser';
7
+ import multer from 'multer';
8
+ import { HostIP, } from "@gogoqiu/ts-base";
9
+ import { createStream } from "rotating-file-stream";
10
+ import os from "os";
11
+ import fs from "fs";
12
+ import { getConnectionPool, Port, MysqlUser, Password, getServiceName } from "@gogoqiu/node-base";
13
+ import { logError, logInfo, logWarn, outputInfo } from "@gogoqiu/node-base";
14
+ import cors from "cors";
15
+ const hostname = os.hostname();
16
+ function createRotatingStream(filename) {
17
+ return createStream(filename, {
18
+ interval: '1d', // 每天轮转一次
19
+ path: os.tmpdir(),
20
+ size: '10M', // 当文件大小达到10MB时轮转
21
+ maxSize: '100M', // 所有日志文件总大小不超过100MB
22
+ maxFiles: 10, // 最多保留10个日志文件
23
+ compress: 'gzip' // 压缩旧日志
24
+ });
25
+ }
26
+ /*
27
+ 如果在root/service下运行, os.homedir()也是root, 那就不是gogoqiu
28
+ captures/
29
+ 策略:
30
+ install:
31
+ public=>dist/public //fs.symlinkSync( path.join( root, "public"), path.join(root, "dist", "public") )
32
+ views=>dist/views
33
+ server init:
34
+ 注入到npm安装包的public下
35
+ 原来是在public下建立目录
36
+ fs.mkdirSync( "public/captures" )
37
+ fs.mkdirSync( "public/uploads" )
38
+ =>
39
+ 变更为先在/root下建立目录${serviceName}, 然后注入到npm安装包的public下
40
+ path.join( getProjectRoot(), "public", "captures" )
41
+ fs.symlinkSync( path.join( os.homedir(), serviceName, "captures" ) )
42
+ fs.symlinkSync( path.join( os.homedir(), serviceName, "upload" ) )
43
+ */
44
+ function createFs(root, userDir, subdirs, serviceName) {
45
+ outputInfo("--createFs--");
46
+ if (!fs.existsSync(userDir)) {
47
+ logError("用户目录必须已经存在", userDir);
48
+ process.exit(-1);
49
+ }
50
+ // base
51
+ const base = path.join(userDir, serviceName);
52
+ if (fs.existsSync(base)) {
53
+ logWarn("用户目录已存在, 沿用", base);
54
+ }
55
+ else {
56
+ logInfo("创建目录", base);
57
+ fs.mkdirSync(base, { recursive: true });
58
+ }
59
+ for (const subdir of subdirs) {
60
+ // 获得用户目录下的目录路径
61
+ const baseSubdir = path.join(base, subdir);
62
+ // 先建立base子目录
63
+ if (fs.existsSync(baseSubdir)) {
64
+ logWarn("用户目录已存在, 沿用", baseSubdir);
65
+ }
66
+ else {
67
+ logInfo("创建目录", baseSubdir);
68
+ fs.mkdirSync(baseSubdir, { recursive: true });
69
+ }
70
+ // 每次启动都要创建软链接?
71
+ // 如果是一个软链接,且指向当前userDir,就沿用,不是就报错退出
72
+ const p = path.join(root, "public", subdir);
73
+ if (fs.existsSync(p)) {
74
+ const isSymbolicLink = fs.lstatSync(p).isSymbolicLink();
75
+ const isSameDir = fs.readlinkSync(p) === baseSubdir;
76
+ if (isSymbolicLink && isSameDir) {
77
+ //logError( `public/${subdir}已存在? 无法执行建立软链接`, p )
78
+ //process.exit(1)
79
+ logWarn("软链接已存在, 且指向当前用户目录, 沿用");
80
+ }
81
+ else {
82
+ //
83
+ logError(`public/${subdir}已存在? 不是软链接或者是软链接但不指向当前用户目录`);
84
+ }
85
+ }
86
+ else {
87
+ logInfo("构建软链接", baseSubdir, p);
88
+ fs.symlinkSync(baseSubdir, p, os.platform() == "win32" ? "junction" : null);
89
+ //logError( `public/${subdir}已存在? 无法执行建立软链接`, p )
90
+ }
91
+ }
92
+ }
93
+ function _setupCaptures(root, subdirs) {
94
+ if (!fs.existsSync(path.join(root, 'public'))) {
95
+ logError("public目录不存在, 中止", "是否setup?");
96
+ process.exit(1);
97
+ }
98
+ for (const subdir of subdirs) {
99
+ const p = path.join(root, "public", subdir);
100
+ if (!fs.existsSync(p))
101
+ fs.mkdirSync(p);
102
+ else if (!fs.lstatSync(p).isDirectory()) {
103
+ logError("capturesDir is not a directory");
104
+ process.exit(1);
105
+ }
106
+ }
107
+ // <input type=file/>formdata.append("file", file),上传文件,给出前端js代码和后端nodejs代码
108
+ }
109
+ export function initServer(options, root, info) {
110
+ const serviceName = getServiceName(info);
111
+ const app = express();
112
+ /*
113
+ express必须解析formdata必须使用multer吗
114
+ */
115
+ const storage = multer.memoryStorage(); // 或者使用磁盘存储
116
+ const upload = multer({ storage: storage });
117
+ //console.log( upload )
118
+ //const upload = multer({ dest: 'uploads/' })
119
+ // 允许跨域请求
120
+ app.use(cors());
121
+ var accessLogStream = createStream(`${info.name}-access.log`, {
122
+ interval: '1d', // rotate daily
123
+ path: os.tmpdir()
124
+ });
125
+ // setup the logger
126
+ app.use(morgan('combined', { stream: accessLogStream }));
127
+ /*
128
+ 'dev' 是 morgan 提供的一种预定义日志格式,以简洁的方式显示关键信息,格式为:
129
+ GET /api 200 1.234 ms - 150
130
+ 分别对应:请求方法、请求路径、响应状态码、响应时间、响应体大小。
131
+ */
132
+ // app.use(morgan('dev'));
133
+ /*
134
+ bodyParser.json() 用于解析 JSON 格式 的请求体(Content-Type: application/json)。
135
+ bodyParser.urlencoded(...) 用于解析 表单 URL 编码 格式的请求体(Content-Type: application/x-www-form-urlencoded),通常对应普通表单提交(非文件上传)。
136
+ */
137
+ // 解析 JSON 格式的请求体
138
+ /*
139
+ 客户端:
140
+ const data = {
141
+ name: "张三",
142
+ age: 25,
143
+ hobbies: ["编程", "运动"]
144
+ };
145
+
146
+ // 发送 POST 请求
147
+ fetch("/api/submit", {
148
+ method: "POST", // 或其他方法(PUT、PATCH 等)
149
+ headers: {
150
+ "Content-Type": "application/json", // 关键:指定 JSON 类型
151
+ // 可选:添加其他头信息(如 Token)
152
+ // "Authorization": "Bearer your-token"
153
+ },
154
+ body: JSON.stringify(data) // 关键:将对象转为 JSON 字符串
155
+ })
156
+ 服务器端:
157
+ req.body
158
+ */
159
+ app.use(express.json());
160
+ /*
161
+ 客户端:
162
+ 当后端使用 app.use(express.urlencoded({ extended: true })) 时,
163
+ 前端需要发送 Content-Type: application/x-www-form-urlencoded 格式的请求(这是 HTML 表单默认的提交格式)。
164
+ 这种格式的请求体是键值对字符串(如 name=张三&age=20),而非 JSON 或 FormData。
165
+ const data = {
166
+ name: "张三",
167
+ age: 20,
168
+ hobbies: ["编程", "运动"] // 支持数组(会被编码为 hobbies=编程&hobbies=运动)
169
+ };
170
+
171
+ // 转换为 URLSearchParams 格式
172
+ const formData = new URLSearchParams();
173
+ ...
174
+ // 发送 POST 请求
175
+ fetch("/api/submit", {
176
+ method: "POST",
177
+ headers: {
178
+ "Content-Type": "application/x-www-form-urlencoded" // 必须指定该类型
179
+ },
180
+ body: formData // 直接传入 URLSearchParams 实例
181
+ })
182
+ 服务器端:
183
+
184
+ */
185
+ app.use(express.urlencoded({ extended: true }));
186
+ app.use(cookieParser());
187
+ /*
188
+ 为了减少实际需要打包/拷贝的包大小,使用rollup -c编译
189
+ .node文件的处理
190
+ format: 'es'
191
+ morgan组件,总是报错,引用__dirname,无法运行
192
+ format: 'cjs'
193
+ server.ts,引用的import.meta.url/__dirname
194
+ package.json必须去掉"type":"module", 报错require
195
+ format: 'cjs',"type":"module",无法运行
196
+
197
+ 基本:
198
+ rollup.js
199
+ format: 'es'
200
+ package.json
201
+ "type":"module"
202
+ 能用就行
203
+ require("ssh2")报错
204
+ */
205
+ logInfo("脚本的当前目录", root);
206
+ const capturesDir = path.join(root, 'public', 'captures');
207
+ const uploadsDir = path.join(root, 'public', 'uploads');
208
+ // 如果需要
209
+ createFs(root, options.userDir, ['captures', 'uploads'], serviceName);
210
+ /*
211
+ 编译出来dist/pages也要作为public的一部分,可以被前端访问
212
+ express.static
213
+ */
214
+ app.use(express.static(path.join(root, 'dist', 'pages')));
215
+ // 通过 /pages 访问 pages 目录的资源
216
+ //app.use('/pages', express.static('pages'));
217
+ const resources = path.join(root, 'public');
218
+ app.use(express.static(resources));
219
+ // app.use('/static', express.static('public'));
220
+ app.use('/av', (req, res, next) => {
221
+ // 设置跨域相关响应头
222
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
223
+ res.setHeader('Access-Control-Allow-Headers', '*');
224
+ res.setHeader('Access-Control-Allow-Origin', '*');
225
+ next(); // 继续处理静态文件请求
226
+ });
227
+ app.use('/hosts', (req, res, next) => {
228
+ // 设置跨域相关响应头
229
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
230
+ res.setHeader('Access-Control-Allow-Headers', '*');
231
+ res.setHeader('Access-Control-Allow-Origin', '*');
232
+ next(); // 继续处理静态文件请求
233
+ });
234
+ // http://localhost:8081/socket.io/?EIO=4&transport=polling&t=nqa5bo29
235
+ // Access-Control-Allow-Origin
236
+ app.use('/socket.io', (req, res, next) => {
237
+ // 设置跨域相关响应头
238
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
239
+ res.setHeader('Access-Control-Allow-Headers', '*');
240
+ res.setHeader('Access-Control-Allow-Origin', '*');
241
+ next(); // 继续处理静态文件请求
242
+ });
243
+ let db = "media";
244
+ if (options.debug)
245
+ db = "mysql";
246
+ /*
247
+ 更新为可选服务器
248
+ */
249
+ const pool = getConnectionPool(HostIP.aliyun, Port.aliyun, MysqlUser.aliyun, Password.aliyun, db, 50);
250
+ app.use('/', getIndexRouter(options.debug));
251
+ app.set('views', path.join(root, 'views'));
252
+ app.set('view engine', 'ejs');
253
+ /*
254
+ "如何捕捉“MulterError: Unexpected field”错误",并不反馈到客户端
255
+ */
256
+ // 全局错误处理中间件(必须放在所有路由之后)
257
+ app.use((err, req, res, next) => {
258
+ // 判断是否为 Multer 错误
259
+ if (err instanceof multer.MulterError) {
260
+ // 处理 "Unexpected field" 错误
261
+ if (err.code === 'LIMIT_UNEXPECTED_FILE') {
262
+ return res.status(400).json({
263
+ error: '上传了未预期的字段',
264
+ details: `意外的字段名: ${err.field}` // err.field 会返回错误的字段名
265
+ });
266
+ }
267
+ // 其他 Multer 错误(如文件大小超限等)
268
+ return res.status(400).json({
269
+ error: '文件上传错误',
270
+ details: err.message
271
+ });
272
+ }
273
+ logError(err);
274
+ // 其他类型错误
275
+ res.status(500).json({ error: '服务器内部错误,查看日志' });
276
+ });
277
+ // 强制跳转到 HTTPS
278
+ app.use((req, res, next) => {
279
+ if (!req.secure) {
280
+ return res.redirect('https://' + req.headers.host + req.url);
281
+ }
282
+ next();
283
+ });
284
+ // 启动 HTTP 服务器(用于重定向),转到https
285
+ /*
286
+ http.createServer(app).listen(options.port, () => {
287
+ logInfo(`Server running on port ${options.port}`);
288
+ });
289
+ */
290
+ /*
291
+ app from express
292
+ server(http) from app
293
+ ws-io from ioServer( server )
294
+ */
295
+ const server = https.createServer(// 1. 加载证书和私钥
296
+ {
297
+ key: fs.readFileSync("/etc/letsencrypt/live/gogoqiu.dpdns.org/privkey.pem"),
298
+ cert: fs.readFileSync("/etc/letsencrypt/live/gogoqiu.dpdns.org/fullchain.pem")
299
+ }, app);
300
+ server.listen(options.port, () => {
301
+ logInfo(`Server running on port ${options.port}`);
302
+ });
303
+ server.on("close", () => {
304
+ //能否捕捉ctrl+c中断程序时的事件
305
+ console.log("server", "close");
306
+ });
307
+ // 捕捉 Ctrl+C 信号
308
+ process.on('SIGINT', () => {
309
+ console.log('\n收到 Ctrl+C 信号,准备退出程序...');
310
+ // 在这里执行退出前的清理操作(如关闭数据库连接、保存数据等)
311
+ console.log('执行清理工作...');
312
+ pool.end(() => {
313
+ // 完成清理后,手动退出进程
314
+ process.exit(0); // 0 表示正常退出,非 0 表示异常退出
315
+ });
316
+ });
317
+ }
318
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import express from \"express\";\nimport http from \"http\";\nimport https from \"https\";\nimport { DisconnectReason, Server, Socket } from \"socket.io\";\nimport getIndexRouter from \"./routes/index.js\";\nimport { } from \"./routes/index.js\"\nimport path, { dirname } from \"path\";\nimport morgan from 'morgan';\nimport cookieParser from 'cookie-parser'\nimport bodyParser from 'body-parser'\nimport { fileURLToPath } from \"url\";\nimport { ClientChannel } from \"ssh2\";\nimport multer from 'multer'\nimport { HostIP, Hostname, } from \"@gogoqiu/ts-base\";\nimport { Client } from 'ssh2';\nimport { RotatingFileStream, createStream } from \"rotating-file-stream\"\nimport os from \"os\"\nimport fs from \"fs\"\nimport log4js from \"log4js\";\nimport debug from \"debug\";\nimport { getAvRouter } from \"@gogoqiu/repo-http-server\"\nimport {\n getConnectionPool, Port, MysqlUser,\n Password, getServiceName\n} from \"@gogoqiu/node-base\";\nimport {\n getPackageInfoFromScriptPath, getProjectRoot, getVersion, getPackageInfo,\n initLog, logError, logInfo, logWarn,\n outputInfo, outputWarn, outputErr, runCmd, RunResult\n} from \"@gogoqiu/node-base\"\nimport { fstat } from \"fs\";\nimport cors from \"cors\"\nimport type { CmdOption } from \"./serverCmd.js\";\n\nconst hostname = os.hostname()\n\nfunction createRotatingStream(filename: string) {\n return createStream(filename, {\n interval: '1d', // 每天轮转一次\n path: os.tmpdir(),\n size: '10M', // 当文件大小达到10MB时轮转\n maxSize: '100M', // 所有日志文件总大小不超过100MB\n maxFiles: 10, // 最多保留10个日志文件\n compress: 'gzip' // 压缩旧日志\n });\n}\n\n/*\n如果在root/service下运行, os.homedir()也是root, 那就不是gogoqiu\ncaptures/\n策略:\n install:\n public=>dist/public //fs.symlinkSync( path.join( root, \"public\"), path.join(root, \"dist\", \"public\") )\n views=>dist/views\n server init:\n 注入到npm安装包的public下\n 原来是在public下建立目录\n fs.mkdirSync( \"public/captures\" )\n fs.mkdirSync( \"public/uploads\" )\n =>\n 变更为先在/root下建立目录${serviceName}, 然后注入到npm安装包的public下\n path.join( getProjectRoot(), \"public\", \"captures\" ) \n fs.symlinkSync( path.join( os.homedir(), serviceName, \"captures\" ) )\n fs.symlinkSync( path.join( os.homedir(), serviceName, \"upload\" ) )\n*/\nfunction createFs(root: string, userDir: string,\n subdirs: string[], serviceName: string) {\n\n outputInfo(\"--createFs--\")\n\n if (!fs.existsSync(userDir)) {\n logError(\"用户目录必须已经存在\", userDir)\n process.exit(-1)\n }\n // base\n const base = path.join(userDir, serviceName);\n if (fs.existsSync(base)) {\n logWarn(\"用户目录已存在, 沿用\", base)\n } else {\n logInfo(\"创建目录\", base)\n fs.mkdirSync(base, { recursive: true })\n }\n\n for (const subdir of subdirs) {\n // 获得用户目录下的目录路径\n const baseSubdir = path.join(base, subdir)\n // 先建立base子目录\n if (fs.existsSync(baseSubdir)) {\n logWarn(\"用户目录已存在, 沿用\", baseSubdir)\n } else {\n logInfo(\"创建目录\", baseSubdir)\n fs.mkdirSync(baseSubdir, { recursive: true })\n }\n // 每次启动都要创建软链接?\n // 如果是一个软链接,且指向当前userDir,就沿用,不是就报错退出\n const p = path.join(root, \"public\", subdir)\n if (fs.existsSync(p)) {\n const isSymbolicLink = fs.lstatSync(p).isSymbolicLink()\n const isSameDir = fs.readlinkSync(p) === baseSubdir\n if (isSymbolicLink && isSameDir) {\n //logError( `public/${subdir}已存在? 无法执行建立软链接`, p )\n //process.exit(1)\n logWarn(\"软链接已存在, 且指向当前用户目录, 沿用\")\n } else {\n //\n logError(`public/${subdir}已存在? 不是软链接或者是软链接但不指向当前用户目录`)\n }\n } else {\n logInfo(\"构建软链接\", baseSubdir, p)\n fs.symlinkSync(baseSubdir, p, os.platform() == \"win32\" ? \"junction\" : null)\n //logError( `public/${subdir}已存在? 无法执行建立软链接`, p )\n }\n\n }\n}\n\nfunction _setupCaptures(root: string, subdirs: string[]) {\n if (!fs.existsSync(path.join(root, 'public'))) {\n logError(\"public目录不存在, 中止\", \"是否setup?\")\n process.exit(1)\n }\n for (const subdir of subdirs) {\n const p = path.join(root, \"public\", subdir)\n if (!fs.existsSync(p))\n fs.mkdirSync(p)\n else if (!fs.lstatSync(p).isDirectory()) {\n logError(\"capturesDir is not a directory\")\n process.exit(1)\n }\n }\n // <input type=file/>formdata.append(\"file\", file),上传文件,给出前端js代码和后端nodejs代码\n}\n\nexport function initServer(options: CmdOption, root: string, info: any) {\n const serviceName = getServiceName(info)\n const app = express();\n /*\n express必须解析formdata必须使用multer吗\n */\n const storage = multer.memoryStorage(); // 或者使用磁盘存储\n const upload = multer({ storage: storage });\n //console.log( upload )\n //const upload = multer({ dest: 'uploads/' })\n\n // 允许跨域请求\n app.use(cors());\n\n var accessLogStream = createStream(`${info.name}-access.log`, {\n interval: '1d', // rotate daily\n path: os.tmpdir()\n })\n\n // setup the logger\n app.use(morgan('combined', { stream: accessLogStream }))\n\n\n /*\n 'dev' 是 morgan 提供的一种预定义日志格式,以简洁的方式显示关键信息,格式为:\n GET /api 200 1.234 ms - 150\n 分别对应:请求方法、请求路径、响应状态码、响应时间、响应体大小。\n */\n // app.use(morgan('dev'));\n\n /*\n bodyParser.json() 用于解析 JSON 格式 的请求体(Content-Type: application/json)。\n bodyParser.urlencoded(...) 用于解析 表单 URL 编码 格式的请求体(Content-Type: application/x-www-form-urlencoded),通常对应普通表单提交(非文件上传)。\n */\n // 解析 JSON 格式的请求体\n /*\n 客户端:\n const data = {\n name: \"张三\",\n age: 25,\n hobbies: [\"编程\", \"运动\"]\n };\n\n // 发送 POST 请求\n fetch(\"/api/submit\", {\n method: \"POST\", // 或其他方法(PUT、PATCH 等)\n headers: {\n \"Content-Type\": \"application/json\", // 关键:指定 JSON 类型\n // 可选:添加其他头信息(如 Token)\n // \"Authorization\": \"Bearer your-token\"\n },\n body: JSON.stringify(data) // 关键:将对象转为 JSON 字符串\n })\n 服务器端:\n req.body\n */\n app.use(express.json());\n /*\n 客户端:\n 当后端使用 app.use(express.urlencoded({ extended: true })) 时,\n 前端需要发送 Content-Type: application/x-www-form-urlencoded 格式的请求(这是 HTML 表单默认的提交格式)。\n 这种格式的请求体是键值对字符串(如 name=张三&age=20),而非 JSON 或 FormData。\n const data = {\n name: \"张三\",\n age: 20,\n hobbies: [\"编程\", \"运动\"] // 支持数组(会被编码为 hobbies=编程&hobbies=运动)\n };\n\n // 转换为 URLSearchParams 格式\n const formData = new URLSearchParams();\n ...\n // 发送 POST 请求\n fetch(\"/api/submit\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\" // 必须指定该类型\n },\n body: formData // 直接传入 URLSearchParams 实例\n })\n 服务器端:\n\n */\n app.use(express.urlencoded({ extended: true }));\n\n app.use(cookieParser());\n\n /*\n 为了减少实际需要打包/拷贝的包大小,使用rollup -c编译\n .node文件的处理\n format: 'es'\n morgan组件,总是报错,引用__dirname,无法运行\n format: 'cjs'\n server.ts,引用的import.meta.url/__dirname\n package.json必须去掉\"type\":\"module\", 报错require\n format: 'cjs',\"type\":\"module\",无法运行\n\n 基本:\n rollup.js\n format: 'es'\n package.json\n \"type\":\"module\"\n 能用就行\n require(\"ssh2\")报错\n */\n logInfo(\"脚本的当前目录\", root)\n const capturesDir = path.join(root, 'public', 'captures')\n const uploadsDir = path.join(root, 'public', 'uploads')\n // 如果需要\n createFs(root, options.userDir, ['captures', 'uploads'], serviceName)\n\n /*\n 编译出来dist/pages也要作为public的一部分,可以被前端访问\n express.static\n */\n app.use(express.static(path.join(root, 'dist', 'pages')));\n // 通过 /pages 访问 pages 目录的资源\n //app.use('/pages', express.static('pages'));\n const resources = path.join(root, 'public')\n app.use(express.static(resources));\n // app.use('/static', express.static('public'));\n\n app.use('/av', (req, res, next) => {\n // 设置跨域相关响应头\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Allow-Headers', '*');\n res.setHeader('Access-Control-Allow-Origin', '*');\n next(); // 继续处理静态文件请求\n });\n\n app.use('/hosts', (req, res, next) => {\n // 设置跨域相关响应头\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Allow-Headers', '*');\n res.setHeader('Access-Control-Allow-Origin', '*');\n next(); // 继续处理静态文件请求\n });\n\n // http://localhost:8081/socket.io/?EIO=4&transport=polling&t=nqa5bo29\n // Access-Control-Allow-Origin\n app.use('/socket.io', (req, res, next) => {\n // 设置跨域相关响应头\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Allow-Headers', '*');\n res.setHeader('Access-Control-Allow-Origin', '*');\n next(); // 继续处理静态文件请求\n });\n\n let db = \"media\"\n if (options.debug)\n db = \"mysql\"\n /*\n 更新为可选服务器\n */\n const pool = getConnectionPool(HostIP.aliyun, Port.aliyun,\n MysqlUser.aliyun, Password.aliyun, db, 50);\n\n app.use('/', getIndexRouter(options.debug));\n\n app.set('views', path.join(root, 'views'));\n app.set('view engine', 'ejs');\n\n /*\n \"如何捕捉“MulterError: Unexpected field”错误\",并不反馈到客户端\n */\n // 全局错误处理中间件(必须放在所有路由之后)\n app.use((err, req, res, next) => {\n // 判断是否为 Multer 错误\n if (err instanceof multer.MulterError) {\n // 处理 \"Unexpected field\" 错误\n if (err.code === 'LIMIT_UNEXPECTED_FILE') {\n return res.status(400).json({\n error: '上传了未预期的字段',\n details: `意外的字段名: ${err.field}` // err.field 会返回错误的字段名\n });\n }\n // 其他 Multer 错误(如文件大小超限等)\n return res.status(400).json({\n error: '文件上传错误',\n details: err.message\n });\n }\n\n logError(err)\n // 其他类型错误\n res.status(500).json({ error: '服务器内部错误,查看日志' });\n });\n\n // 强制跳转到 HTTPS\n app.use((req, res, next) => {\n if (!req.secure) {\n return res.redirect('https://' + req.headers.host + req.url);\n }\n next();\n });\n // 启动 HTTP 服务器(用于重定向),转到https \n /* \n http.createServer(app).listen(options.port, () => {\n logInfo(`Server running on port ${options.port}`);\n });\n */\n\n /*\n app from express\n server(http) from app\n ws-io from ioServer( server )\n */\n const server = https.createServer(// 1. 加载证书和私钥\n {\n key: fs.readFileSync(\"/etc/letsencrypt/live/gogoqiu.dpdns.org/privkey.pem\"),\n cert: fs.readFileSync(\"/etc/letsencrypt/live/gogoqiu.dpdns.org/fullchain.pem\")\n }, app);\n\n server.listen(options.port, () => {\n logInfo(`Server running on port ${options.port}`);\n });\n\n server.on(\"close\", () => {\n //能否捕捉ctrl+c中断程序时的事件\n console.log(\"server\", \"close\")\n })\n\n // 捕捉 Ctrl+C 信号\n process.on('SIGINT', () => {\n console.log('\\n收到 Ctrl+C 信号,准备退出程序...');\n\n // 在这里执行退出前的清理操作(如关闭数据库连接、保存数据等)\n console.log('执行清理工作...');\n\n pool.end(() => {\n // 完成清理后,手动退出进程\n process.exit(0); // 0 表示正常退出,非 0 表示异常退出\n })\n });\n}\n\n\n"]}
@@ -0,0 +1,8 @@
1
+ export interface CmdOption {
2
+ port: number;
3
+ debug: boolean;
4
+ userDir: string;
5
+ mysqlAddr: string;
6
+ mysqlPort: number;
7
+ }
8
+ //# sourceMappingURL=serverCmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverCmd.d.ts","sourceRoot":"","sources":["../src/serverCmd.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB"}
@@ -0,0 +1,44 @@
1
+ import { program } from "commander";
2
+ import { initServer } from "./server.js";
3
+ import { getProjectRoot, getPackageInfo, initLog, logError } from "@gogoqiu/node-base";
4
+ import { buildTime } from "./build.js";
5
+ import os from "os";
6
+ function main() {
7
+ /*
8
+ /tmp/@gogoqiu/aliyun-http-server-info.log
9
+ */
10
+ const root = getProjectRoot();
11
+ const info = getPackageInfo(root);
12
+ const v = info.version;
13
+ initLog(info);
14
+ /*
15
+ 投射信息
16
+ hashinfo
17
+ */
18
+ // 参考:
19
+ // code /mnt/Data/init/git_spsp/nodejs2/vite-vscode1/src/chat/src/chat.ts
20
+ program
21
+ .name('string-util')
22
+ .description('CLI to host-info')
23
+ .version(`${v}/${buildTime}`)
24
+ //.option('-f, --fix', 'fix the /etc/hosts', false)
25
+ .option('--port <number>', "listen port", "80")
26
+ //.option('--mode <string>', "run mode", false)
27
+ //.option('--mysqlIp <string>', "mysql ip", "localhost")
28
+ //.option('--mysqlPort <number>', "mysql port", "3306")
29
+ .option('--user-dir <string>', "用户空间(captures, uploads...)", os.homedir())
30
+ .option('--debug', "debug mode", false);
31
+ program.parse();
32
+ const cmdOptions = program.opts();
33
+ console.log(cmdOptions);
34
+ initServer(cmdOptions, root, info);
35
+ }
36
+ // 保证exception都会被记录
37
+ try {
38
+ logError("测试error日志");
39
+ main();
40
+ }
41
+ catch (err) {
42
+ logError("异常", err);
43
+ }
44
+ //# sourceMappingURL=serverCmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverCmd.js","sourceRoot":"","sources":["../src/serverCmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,OAAO,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACyB,cAAc,EAAE,cAAc,EAC5D,OAAO,EACP,QAAQ,EACT,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,MAAM,IAAI,CAAA;AAUnB,SAAS,IAAI;IACX;;MAEE;IACF,MAAM,IAAI,GAAG,cAAc,EAAE,CAAA;IAC7B,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;IAEtB,OAAO,CAAC,IAAI,CAAC,CAAA;IACb;;;MAGE;IACF,MAAM;IACN,yEAAyE;IACzE,OAAO;SACJ,IAAI,CAAC,aAAa,CAAC;SACnB,WAAW,CAAC,kBAAkB,CAAC;SAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;QAC7B,mDAAmD;SAClD,MAAM,CAAC,iBAAiB,EAAE,aAAa,EAAE,IAAI,CAAC;QAC/C,iDAAiD;QACjD,0DAA0D;QAC1D,uDAAuD;SACtD,MAAM,CAAC,qBAAqB,EAAE,4BAA4B,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;SACzE,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,CAAC,CAAA;IAEzC,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,MAAM,UAAU,GAAc,OAAO,CAAC,IAAI,EAAE,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACvB,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,mBAAmB;AACnB,IAAI,CAAC;IACH,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtB,IAAI,EAAE,CAAA;AACR,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AACrB,CAAC","sourcesContent":["import { Command, program } from \"commander\"\nimport { initServer } from \"./server.js\";\nimport {\n getPackageInfoFromScriptPath, getProjectRoot, getPackageInfo,\n initLog, getServiceName,\n logError\n} from \"@gogoqiu/node-base\";\nimport { buildTime } from \"./build.js\";\nimport os from \"os\"\n\nexport interface CmdOption {\n port: number\n debug: boolean\n userDir: string\n mysqlAddr: string\n mysqlPort: number\n}\n\nfunction main() {\n /*\n /tmp/@gogoqiu/aliyun-http-server-info.log\n */\n const root = getProjectRoot()\n const info = getPackageInfo(root);\n const v = info.version\n\n initLog(info)\n /*\n 投射信息\n hashinfo\n */\n // 参考:\n // code /mnt/Data/init/git_spsp/nodejs2/vite-vscode1/src/chat/src/chat.ts\n program\n .name('string-util')\n .description('CLI to host-info')\n .version(`${v}/${buildTime}`)\n //.option('-f, --fix', 'fix the /etc/hosts', false)\n .option('--port <number>', \"listen port\", \"80\")\n //.option('--mode <string>', \"run mode\", false) \n //.option('--mysqlIp <string>', \"mysql ip\", \"localhost\") \n //.option('--mysqlPort <number>', \"mysql port\", \"3306\")\n .option('--user-dir <string>', \"用户空间(captures, uploads...)\", os.homedir())\n .option('--debug', \"debug mode\", false)\n\n program.parse();\n const cmdOptions: CmdOption = program.opts();\n console.log(cmdOptions)\n initServer(cmdOptions, root, info);\n}\n\n// 保证exception都会被记录\ntry {\n logError(\"测试error日志\");\n main()\n} catch (err) {\n logError(\"异常\", err)\n}"]}
@@ -0,0 +1,5 @@
1
+ export declare function startService(serviceName: string): Promise<void>;
2
+ export declare function stopService(serviceName: string): Promise<void>;
3
+ export declare function restartService(serviceName: string): Promise<void>;
4
+ export declare function setup(root: string, serviceName: string, isFirst: boolean): Promise<void>;
5
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/shell/install.ts"],"names":[],"mappings":"AA0LA,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,iBAErD;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,iBAEpD;AAED,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,iBAEvD;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,iBAkC9E"}