online-editor/index.ts

124 lines
4.2 KiB
TypeScript

import Fastify from 'fastify';
import cors from '@fastify/cors'
import fastifyStat from '@fastify/static'
const app = Fastify({ logger: false });
import path, { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { rename, writeFile } from 'fs/promises';
import crypto from 'crypto';
import { spawn } from 'child_process';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
await app.register(cors)
await app.register(fastifyStat, { root: join(__dirname, 'dist'), });
const secretKey = process.env.SECRET_KEY
const rootPath = process.env.ROOT_PATH
if (!secretKey) {
throw new Error('SECRET_KEY is not set');
}
if (!rootPath) {
throw new Error('ROOT_PATH is not set');
}
// 创建 HMAC 函数
function createHmac(message: string, secretKey: string) {
const hmac = crypto.createHmac('sha256', secretKey);
hmac.update(message);
return hmac.digest('hex');
}
// 验证 HMAC
function verifyHmac(message: string, secretKey: string, hash: string) {
const calculatedHash = createHmac(message, secretKey);
console.log(`calculatedHash: ${calculatedHash}, hash: ${hash}`);
return calculatedHash === hash;
}
app.get<{ Params: { pj: string, ['*']: string }, Querystring: { secretKey: string } }>('/file/:pj/*', async (req, res) => {
const { pj, '*': pth } = req.params;
const targetFile = path.join(pj, pth);
if (!verifyHmac(targetFile, secretKey, req.query.secretKey)) {
return res.status(401).send({
message: 'Invalid secret key'
});
}
return res.sendFile(targetFile, rootPath);
});
app.get<{ Params: { pj: string, ['*']: string }, Querystring: { secretKey: string }, Body: string }>
('/compile/:pj/*', async (req, res) => {
const { pj, '*': pth } = req.params;
const targetFile = path.join(pj, pth);
console.log(`targetFile: ${targetFile}`);
console.log(createHmac(targetFile, secretKey));
if (!verifyHmac(targetFile, secretKey, req.query.secretKey)) {
return res.status(401).send({
message: 'Invalid secret key'
});
}
const targetPjPath = path.join(rootPath, pj);
res.raw.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
});
function formatSSEData(data: any): string {
const message = typeof data === 'string' ? data : JSON.stringify(data);
// 处理多行数据:
// 1. 将整个消息按换行符分割
// 2. 每行前面加上 "data: "
// 3. 最后加上额外的换行符
return message
.split('\n')
.map(line => `data: ${line}`)
.join('\n') + '\n\n';
}
const process = spawn('npm', ['run', 'build'], { cwd: targetPjPath });
process.stdout.on('data', (data) => {
res.raw.write(`event: stdout\n${formatSSEData(data.toString())}`);
});
process.stderr.on('data', (data) => {
res.raw.write(`event: stderr\n${formatSSEData(data.toString())}`);
});
process.on('close', (code) => {
res.raw.end(`event: close\n${formatSSEData({ code })}`);
});
});
app.post<{ Params: { pj: string, ['*']: string }, Querystring: { secretKey: string }, Body: string }>
('/file/:pj/*', async (req, res) => {
const { pj, '*': pth } = req.params;
const targetFile = path.join(pj, pth);
console.log(createHmac(targetFile, secretKey));
if (!verifyHmac(targetFile, secretKey, req.query.secretKey)) {
return res.status(401).send({
message: 'Invalid secret key'
});
}
const targetPath = path.join(rootPath, targetFile);
await rename(targetPath, `${targetPath}.${Date.now()}.bak`)
await writeFile(targetPath, req.body)
console.log(`File ${targetPath} saved successfully`);
return res.send({
message: 'File uploaded successfully'
});
});
app.listen({ port: 3000, host: '0.0.0.0' })