import express from "express"; import { exec } from "child_process"; import crypto from "crypto"; const app = express(); const PORT = 9000; const SECRET = "vTEq9OTvIFs3ZbDaszLRL/ZiAEXziemX1Wh1GIeb+DI="; // 보안을 위해 변경하세요 const PROJECT_DIR = "/var/www/music/music-admin"; // JSON 파싱 미들웨어 app.use(express.json()); // 웹훅 시크릿 검증 함수 function verifySignature(payload, signature, secret) { const expectedSignature = "sha256=" + crypto.createHmac("sha256", secret).update(payload).digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } // 웹훅 엔드포인트 app.post("/webhook", (req, res) => { const signature = req.headers["x-gitea-signature"]; const payload = JSON.stringify(req.body); console.log(`[${new Date().toISOString()}] 웹훅 수신됨`); // 시크릿 검증 (선택사항) if (signature && !verifySignature(payload, signature, SECRET)) { console.log("잘못된 시크릿"); return res.status(401).send("Unauthorized"); } // main 브랜치 push 이벤트만 처리 if (req.body.ref === "refs/heads/main") { console.log("main 브랜치 push 감지, 배포 시작..."); // 배포 스크립트 실행 exec( `cd ${PROJECT_DIR} && chmod +x deploy.sh && ./deploy.sh`, (error, stdout, stderr) => { if (error) { console.error(`배포 오류: ${error}`); return res.status(500).send("Deployment failed"); } console.log("배포 성공"); console.log(stdout); if (stderr) console.error(stderr); res.status(200).send("Deployment successful"); } ); } else { console.log("main 브랜치가 아닌 push, 무시됨"); res.status(200).send("Ignored"); } }); // 헬스체크 엔드포인트 app.get("/health", (req, res) => { res.status(200).send("Webhook server is running"); }); app.listen(PORT, () => { console.log(`웹훅 서버가 포트 ${PORT}에서 실행 중입니다.`); console.log(`웹훅 URL: https://admin.youtooplay.com/webhook`); });