| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- "use strict";
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- var desc = Object.getOwnPropertyDescriptor(m, k);
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
- desc = { enumerable: true, get: function() { return m[k]; } };
- }
- Object.defineProperty(o, k2, desc);
- }) : (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
- }));
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
- Object.defineProperty(o, "default", { enumerable: true, value: v });
- }) : function(o, v) {
- o["default"] = v;
- });
- var __importStar = (this && this.__importStar) || (function () {
- var ownKeys = function(o) {
- ownKeys = Object.getOwnPropertyNames || function (o) {
- var ar = [];
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
- return ar;
- };
- return ownKeys(o);
- };
- return function (mod) {
- if (mod && mod.__esModule) return mod;
- var result = {};
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
- __setModuleDefault(result, mod);
- return result;
- };
- })();
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.listDeployLogs = exports.getDeployLogs = exports.deploy = void 0;
- const child_process_1 = require("child_process");
- const fs = __importStar(require("fs"));
- const path = __importStar(require("path"));
- const crypto = __importStar(require("crypto"));
- const loggerService_1 = require("../services/loggerService");
- const DEPLOY_TOKEN = process.env.DEPLOY_TOKEN || 'mqtt-webhook-secure-2024-abc123xyz';
- const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || '';
- const DEPLOY_LOG_DIR = path.join(__dirname, '../../logs');
- const DEPLOY_BASE_DIR = process.env.DEPLOY_BASE_DIR || '/home/yangfei/mqtt';
- if (!fs.existsSync(DEPLOY_LOG_DIR)) {
- fs.mkdirSync(DEPLOY_LOG_DIR, { recursive: true });
- }
- function generateDeployId() {
- return `deploy-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
- }
- function getBeijingTime() {
- const now = new Date();
- const beijingTime = new Date(now.getTime() + (8 * 60 * 60 * 1000));
- return beijingTime;
- }
- function formatBeijingTime(date) {
- const beijingTime = new Date(date.getTime() + (8 * 60 * 60 * 1000));
- return beijingTime.toISOString().replace('T', ' ').replace('Z', '').substring(0, 19);
- }
- async function executeDeployCommand(project, deployId) {
- return new Promise((resolve, reject) => {
- const logFile = path.join(DEPLOY_LOG_DIR, `${deployId}.log`);
- const logStream = fs.createWriteStream(logFile, { flags: 'a' });
- const startTime = getBeijingTime();
- console.log(`[${formatBeijingTime(startTime)}] 开始执行部署: ${project}, 日志文件: ${logFile}`);
- logStream.write(`[${formatBeijingTime(startTime)}] 开始执行部署: ${project}\n`);
- logStream.write(`部署基础目录: ${DEPLOY_BASE_DIR}\n`);
- logStream.write(`DEBUG模式: 已启用\n\n`);
- const scriptPath = '/home/yangfei/mqtt/mqtt-dashboard-backend/deploy.sh';
- const command = `bash ${scriptPath} ${project}`;
- const child = (0, child_process_1.spawn)('/bin/bash', ['-c', command], {
- env: {
- ...process.env,
- PATH: process.env.PATH
- }
- });
- let streamEnded = false;
- child.stdout.on('data', (data) => {
- if (!streamEnded) {
- const output = data.toString();
- console.log(`[${formatBeijingTime(new Date())}] ${output.trim()}`);
- logStream.write(output);
- }
- });
- child.stderr.on('data', (data) => {
- if (!streamEnded) {
- const error = data.toString();
- console.error(`[${formatBeijingTime(new Date())}] ${error.trim()}`);
- logStream.write(`ERROR: ${error}`);
- }
- });
- child.on('close', (code) => {
- if (!streamEnded) {
- const message = `\n部署完成,退出码: ${code}\n`;
- console.log(`[${formatBeijingTime(new Date())}] ${message.trim()}`);
- logStream.write(message);
- logStream.end();
- streamEnded = true;
- }
- if (code === 0) {
- resolve();
- }
- else {
- reject(new Error(`部署失败,退出码: ${code}`));
- }
- });
- child.on('error', (error) => {
- if (!streamEnded) {
- const message = `部署错误: ${error.message}\n`;
- console.error(`[${formatBeijingTime(new Date())}] ${message.trim()}`);
- logStream.write(message);
- logStream.end();
- streamEnded = true;
- }
- reject(error);
- });
- });
- }
- const deploy = async (req, res) => {
- try {
- let project;
- let source = 'manual';
- const isWebhook = req.headers['x-gogs-event'] === 'push' ||
- req.headers['x-github-event'] === 'push';
- if (isWebhook) {
- source = 'webhook';
- const webhookData = req.body;
- console.log(`[${formatBeijingTime(new Date())}] 收到 Webhook 请求`);
- console.log(`[${formatBeijingTime(new Date())}] WEBHOOK_SECRET 是否设置: ${!!WEBHOOK_SECRET}`);
- console.log(`[${formatBeijingTime(new Date())}] x-gogs-signature: ${req.headers['x-gogs-signature']}`);
- console.log(`[${formatBeijingTime(new Date())}] x-hub-signature-256: ${req.headers['x-hub-signature-256']}`);
- if (WEBHOOK_SECRET) {
- const signature = req.headers['x-gogs-signature'] ||
- req.headers['x-hub-signature-256'];
- console.log(`[${formatBeijingTime(new Date())}] 检测到的签名: ${signature}`);
- console.log(`[${formatBeijingTime(new Date())}] 请求体长度: ${JSON.stringify(req.body).length}`);
- if (!signature) {
- console.log(`[${formatBeijingTime(new Date())}] 签名验证失败: 未收到签名`);
- console.log(`[${formatBeijingTime(new Date())}] 临时禁用签名验证,继续部署`);
- }
- else {
- const isValid = verifyWebhookSignature(JSON.stringify(req.body), signature, WEBHOOK_SECRET);
- console.log(`[${formatBeijingTime(new Date())}] 签名验证结果: ${isValid}`);
- if (!isValid) {
- console.log(`[${formatBeijingTime(new Date())}] 签名验证失败: 签名不匹配`);
- console.log(`[${formatBeijingTime(new Date())}] 临时禁用签名验证,继续部署`);
- }
- }
- }
- if (webhookData.ref !== 'refs/heads/master') {
- res.json({
- success: true,
- message: '非 master 分支,跳过部署'
- });
- return;
- }
- project = determineProjectFromCommits(webhookData.commits);
- console.log(`[${formatBeijingTime(new Date())}] 收到 Webhook 部署请求: ${project}`);
- }
- else {
- const { token, timestamp } = req.body;
- project = req.body.project;
- if (!token || token !== DEPLOY_TOKEN) {
- res.status(401).json({
- success: false,
- message: '无效的部署令牌'
- });
- return;
- }
- if (!project || !['frontend', 'backend', 'all'].includes(project)) {
- res.status(400).json({
- success: false,
- message: '无效的项目参数'
- });
- return;
- }
- console.log(`[${formatBeijingTime(new Date())}] 收到手动部署请求: ${project}`);
- }
- const deployId = generateDeployId();
- console.log(`[${formatBeijingTime(new Date())}] 部署来源: ${source}, 项目: ${project}, ID: ${deployId}`);
- loggerService_1.LoggerService.info('部署任务开始', {
- source: 'deploy',
- module: 'deploy',
- details: JSON.stringify({
- deployId,
- project,
- source,
- ip: req.ip,
- userAgent: req.get('user-agent')
- })
- }).catch(err => {
- console.error('部署开始日志写入失败:', err);
- });
- res.json({
- success: true,
- deployId,
- message: '部署已启动',
- logUrl: `/api/deploy/logs/${deployId}`
- });
- executeDeployCommand(project, deployId)
- .then(() => {
- console.log(`[${formatBeijingTime(new Date())}] 部署成功: ${deployId}`);
- loggerService_1.LoggerService.info('部署任务完成', {
- source: 'deploy',
- module: 'deploy',
- details: JSON.stringify({
- deployId,
- project,
- source,
- status: 'success'
- })
- }).catch(err => {
- console.error('部署成功日志写入失败:', err);
- });
- })
- .catch((error) => {
- console.error(`[${formatBeijingTime(new Date())}] 部署失败: ${deployId}`, error);
- loggerService_1.LoggerService.error('部署任务失败', {
- source: 'deploy',
- module: 'deploy',
- details: JSON.stringify({
- deployId,
- project,
- source,
- status: 'failed',
- error: error instanceof Error ? error.message : '未知错误'
- })
- }).catch(err => {
- console.error('部署失败日志写入失败:', err);
- });
- });
- }
- catch (error) {
- console.error('部署接口错误:', error);
- loggerService_1.LoggerService.error('部署接口错误', {
- source: 'deploy',
- module: 'deploy',
- details: JSON.stringify({
- error: error instanceof Error ? error.message : '未知错误',
- ip: req.ip,
- userAgent: req.get('user-agent')
- })
- }).catch(err => {
- console.error('部署接口错误日志写入失败:', err);
- });
- res.status(500).json({
- success: false,
- message: error instanceof Error ? error.message : '部署失败'
- });
- }
- };
- exports.deploy = deploy;
- function verifyWebhookSignature(payload, signature, secret) {
- console.log(`[${new Date().toISOString()}] 签名验证详情:`);
- console.log(`[${new Date().toISOString()}] Secret 长度: ${secret.length}`);
- console.log(`[${new Date().toISOString()}] Secret 内容: [${secret}]`);
- console.log(`[${new Date().toISOString()}] Payload 长度: ${payload.length}`);
- console.log(`[${new Date().toISOString()}] Payload 前100字符: ${payload.substring(0, 100)}`);
- console.log(`[${new Date().toISOString()}] 收到的签名: ${signature}`);
- const expectedSignature = crypto.createHmac('sha256', secret).update(payload).digest('hex');
- console.log(`[${new Date().toISOString()}] 计算的签名: ${expectedSignature}`);
- const receivedSignature = signature.startsWith('sha256=')
- ? signature.substring(7)
- : signature;
- console.log(`[${new Date().toISOString()}] 处理后的签名: ${receivedSignature}`);
- console.log(`[${new Date().toISOString()}] 签名是否匹配: ${receivedSignature === expectedSignature}`);
- return crypto.timingSafeEqual(Buffer.from(receivedSignature), Buffer.from(expectedSignature));
- }
- function determineProjectFromCommits(commits) {
- let hasFrontend = false;
- let hasBackend = false;
- for (const commit of commits) {
- const files = [
- ...(commit.added || []),
- ...(commit.removed || []),
- ...(commit.modified || [])
- ];
- for (const file of files) {
- if (file.startsWith('mqtt-dashboard-frontend/')) {
- hasFrontend = true;
- }
- else if (file.startsWith('mqtt-dashboard-backend/')) {
- hasBackend = true;
- }
- }
- }
- if (hasFrontend && hasBackend)
- return 'all';
- if (hasFrontend)
- return 'frontend';
- if (hasBackend)
- return 'backend';
- return 'all';
- }
- const getDeployLogs = async (req, res) => {
- try {
- const { deployId } = req.params;
- const logFile = path.join(DEPLOY_LOG_DIR, `${deployId}.log`);
- if (!fs.existsSync(logFile)) {
- res.status(404).json({
- success: false,
- message: '日志文件不存在'
- });
- return;
- }
- const logs = fs.readFileSync(logFile, 'utf-8');
- loggerService_1.LoggerService.info('部署日志查询成功', {
- source: 'deploy',
- module: 'get_deploy_logs',
- details: JSON.stringify({
- deployId,
- logSize: logs.length,
- ip: req.ip
- })
- }).catch(err => {
- console.error('部署日志查询成功日志写入失败:', err);
- });
- res.json({
- success: true,
- logs
- });
- }
- catch (error) {
- console.error('获取日志错误:', error);
- loggerService_1.LoggerService.error('部署日志查询失败', {
- source: 'deploy',
- module: 'get_deploy_logs',
- details: JSON.stringify({
- deployId: req.params.deployId,
- error: error instanceof Error ? error.message : '未知错误',
- ip: req.ip
- })
- }).catch(err => {
- console.error('部署日志查询失败日志写入失败:', err);
- });
- res.status(500).json({
- success: false,
- message: error instanceof Error ? error.message : '获取日志失败'
- });
- }
- };
- exports.getDeployLogs = getDeployLogs;
- const listDeployLogs = async (req, res) => {
- try {
- const files = fs.readdirSync(DEPLOY_LOG_DIR)
- .filter(file => file.endsWith('.log'))
- .map(file => {
- const filePath = path.join(DEPLOY_LOG_DIR, file);
- const stats = fs.statSync(filePath);
- return {
- deployId: file.replace('.log', ''),
- size: stats.size,
- created: stats.birthtime,
- modified: stats.mtime
- };
- })
- .sort((a, b) => b.created.getTime() - a.created.getTime());
- loggerService_1.LoggerService.info('部署日志列表查询成功', {
- source: 'deploy',
- module: 'list_deploy_logs',
- details: JSON.stringify({
- logCount: files.length,
- ip: req.ip
- })
- }).catch(err => {
- console.error('部署日志列表查询成功日志写入失败:', err);
- });
- res.json({
- success: true,
- logs: files
- });
- }
- catch (error) {
- console.error('列出日志错误:', error);
- loggerService_1.LoggerService.error('部署日志列表查询失败', {
- source: 'deploy',
- module: 'list_deploy_logs',
- details: JSON.stringify({
- error: error instanceof Error ? error.message : '未知错误',
- ip: req.ip
- })
- }).catch(err => {
- console.error('部署日志列表查询失败日志写入失败:', err);
- });
- res.status(500).json({
- success: false,
- message: error instanceof Error ? error.message : '列出日志失败'
- });
- }
- };
- exports.listDeployLogs = listDeployLogs;
- //# sourceMappingURL=deployController.js.map
|