Repository navigation
Expand file tree
/
Copy pathserver.js
More file actions
110 lines (99 loc) · 4.26 KB
/
Copy pathserver.js
File metadata and controls
110 lines (99 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
'use strict';
// IDL Service Design Studio —— 本地服务
// 功能:1) 提供静态页面 public/ 2) 持久化 IDL 项目 /api/project
// 仅绑定 127.0.0.1。IDL 的编译(OpenAPI / protobuf)在浏览器端完成,服务端只做保存。
const http = require('http');
const fs = require('fs');
const path = require('path');
const { URL } = require('url');
const ROOT = __dirname;
const PUBLIC = path.join(ROOT, 'public');
const HOST = process.env.HOST || '127.0.0.1'; // 容器内设 0.0.0.0 供端口映射;本地默认仅本机
const PORT = process.env.PORT ? Number(process.env.PORT) : 8010;
const PROJECT_FILE = process.env.PROJECT_FILE || path.join(ROOT, 'project.json'); // 容器内指向卷 /app/data
const PKG = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')); // 唯一版本源
function readProject() {
if (!fs.existsSync(PROJECT_FILE)) return null;
try { return JSON.parse(fs.readFileSync(PROJECT_FILE, 'utf8')); }
catch (e) { console.error('[project] 读取 project.json 失败:', e.message); return null; }
}
function writeProject(data) {
fs.writeFileSync(PROJECT_FILE, JSON.stringify(data, null, 2), 'utf8');
}
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.ico': 'image/x-icon',
'.txt': 'text/plain; charset=utf-8',
'.md': 'text/markdown; charset=utf-8',
};
function readBody(req) {
return new Promise((resolve, reject) => {
let data = '';
req.on('data', (c) => {
data += c;
if (data.length > 20 * 1024 * 1024) { req.destroy(); reject(new Error('body 过大')); }
});
req.on('end', () => resolve(data));
req.on('error', reject);
});
}
function sendJSON(res, status, obj) {
const body = JSON.stringify(obj);
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(body) });
res.end(body);
}
function start() {
console.log('IDL Service Design Studio v' + (PKG.version || '0.0.0') + '(IDL -> OpenAPI / protobuf)');
console.log(' 打开 : http://' + HOST + ':' + PORT);
const server = http.createServer(async (req, res) => {
try {
const urlObj = new URL(req.url, 'http://' + HOST + ':' + PORT);
const pathname = urlObj.pathname;
if (pathname === '/api/project' && req.method === 'GET') {
return sendJSON(res, 200, { ok: true, project: readProject() });
}
if (pathname === '/api/project' && req.method === 'POST') {
let p = {};
try { p = JSON.parse((await readBody(req)) || '{}'); }
catch (e) { return sendJSON(res, 400, { error: 'bad_request', message: '请求体不是有效 JSON' }); }
writeProject(p.project);
return sendJSON(res, 200, { ok: true });
}
if (pathname === '/api/health') {
return sendJSON(res, 200, { ok: true });
}
if (pathname === '/api/version') {
return sendJSON(res, 200, { version: PKG.version || '0.0.0' });
}
// 浏览器版 js-yaml 已 vendor 进 public/vendor/,走下方静态文件分支(无需运行时 npm install)
let filePath = pathname === '/' ? '/index.html' : pathname;
filePath = path.normalize(filePath).replace(/^(\.\.[/\\])+/, '');
const full = path.join(PUBLIC, filePath);
if (!full.startsWith(PUBLIC)) { res.writeHead(403); return res.end('forbidden'); }
if (!fs.existsSync(full) || !fs.statSync(full).isFile()) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end('not found');
}
const ext = path.extname(full).toLowerCase();
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream', 'Cache-Control': 'no-cache' });
fs.createReadStream(full).pipe(res);
} catch (e) {
if (!res.headersSent) res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('server error: ' + e.message);
}
});
server.on('error', (e) => {
if (e.code === 'EADDRINUSE') {
console.error('端口 ' + PORT + ' 已被占用,请用 PORT=8011 npm start 换端口');
process.exit(1);
}
throw e;
});
server.listen(PORT, HOST);
}
start();