feat(deployments): docker tabanlı proje yönetim ve otomatik deploy sistemi ekle
Docker Compose projeleri için tam kapsamlı yönetim paneli ve otomatik deployment altyapısı eklendi. Sistem özellikleri: - Belirtilen root dizin altındaki docker-compose dosyası içeren projeleri tarama - Git repo bağlantısı ile branch yönetimi ve klonlama/pull işlemleri - Docker compose up/down komutları ile otomatik deploy - Gitea webhook entegrasyonu ile commit bazlı tetikleme - Deploy geçmişi, log kayıtları ve durum takibi (running/success/failed) - Deploy metrikleri ve dashboard görselleştirmesi - Webhook token ve secret yönetimi ile güvenlik - Proje favicon servisi Teknik değişiklikler: - Backend: deploymentProject, deploymentRun ve settings modelleri eklendi - Backend: deploymentService ile git ve docker işlemleri otomatize edildi - Backend: webhook doğrulaması için signature kontrolü eklendi - Docker: docker-cli ve docker-compose bağımlılıkları eklendi - Frontend: deployments ve settings sayfaları eklendi - Frontend: dashboard'a deploy metrikleri ve aktivite akışı eklendi - API: /api/deployments ve /api/settings yolları eklendi
This commit is contained in:
66
backend/src/routes/webhooks.ts
Normal file
66
backend/src/routes/webhooks.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Router, Request } from "express";
|
||||
import crypto from "crypto";
|
||||
import { deploymentService } from "../services/deploymentService.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
type RawBodyRequest = Request & { rawBody?: Buffer };
|
||||
|
||||
function getHeaderValue(value: string | string[] | undefined) {
|
||||
if (!value) return "";
|
||||
return Array.isArray(value) ? value[0] : value;
|
||||
}
|
||||
|
||||
function verifySignature(rawBody: Buffer, secret: string, signature: string) {
|
||||
const cleaned = signature.startsWith("sha256=") ? signature.slice(7) : signature;
|
||||
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
|
||||
if (cleaned.length !== expected.length) return false;
|
||||
return crypto.timingSafeEqual(Buffer.from(cleaned), Buffer.from(expected));
|
||||
}
|
||||
|
||||
router.post("/api/deployments/webhook/:token", async (req, res) => {
|
||||
const { token } = req.params;
|
||||
const settings = await deploymentService.ensureSettings();
|
||||
|
||||
const authHeader = getHeaderValue(req.headers.authorization);
|
||||
if (!authHeader) {
|
||||
return res.status(401).json({ message: "Yetkisiz" });
|
||||
}
|
||||
const providedToken = authHeader.startsWith("Bearer ")
|
||||
? authHeader.slice("Bearer ".length)
|
||||
: authHeader;
|
||||
if (providedToken !== settings.webhookToken) {
|
||||
return res.status(401).json({ message: "Yetkisiz" });
|
||||
}
|
||||
|
||||
const signatureHeader =
|
||||
getHeaderValue(req.headers["x-gitea-signature"]) ||
|
||||
getHeaderValue(req.headers["x-gitea-signature-256"]);
|
||||
const rawBody = (req as RawBodyRequest).rawBody;
|
||||
if (!rawBody || !signatureHeader) {
|
||||
return res.status(401).json({ message: "Imza eksik" });
|
||||
}
|
||||
if (!verifySignature(rawBody, settings.webhookSecret, signatureHeader)) {
|
||||
return res.status(401).json({ message: "Imza dogrulanamadi" });
|
||||
}
|
||||
|
||||
const payload = req.body as { ref?: string; head_commit?: { message?: string }; commits?: Array<{ message?: string }> };
|
||||
const ref = payload?.ref || "";
|
||||
const branch = ref.startsWith("refs/heads/") ? ref.replace("refs/heads/", "") : ref;
|
||||
const commitMessage =
|
||||
payload?.head_commit?.message || payload?.commits?.[payload.commits.length - 1]?.message;
|
||||
|
||||
const project = await deploymentService.findByWebhookToken(token);
|
||||
if (!project) return res.status(404).json({ message: "Deployment bulunamadi" });
|
||||
|
||||
if (branch && branch !== project.branch) {
|
||||
return res.json({ ignored: true });
|
||||
}
|
||||
|
||||
deploymentService
|
||||
.runDeployment(project._id.toString(), commitMessage ? { message: commitMessage } : undefined)
|
||||
.catch(() => undefined);
|
||||
return res.json({ queued: true });
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user