mirror of
https://github.com/khoaliber/dockhand.git
synced 2026-03-03 21:19:06 +00:00
Initial commit
This commit is contained in:
103
routes/api/git/webhook/[id]/+server.ts
Normal file
103
routes/api/git/webhook/[id]/+server.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { json, text } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getGitRepository } from '$lib/server/db';
|
||||
import { deployFromRepository } from '$lib/server/git';
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
function verifySignature(payload: string, signature: string | null, secret: string): boolean {
|
||||
if (!signature) return false;
|
||||
|
||||
// Support both GitHub and GitLab webhook signatures
|
||||
// GitHub: sha256=<hash>
|
||||
// GitLab: just the token value in X-Gitlab-Token header
|
||||
|
||||
if (signature.startsWith('sha256=')) {
|
||||
const expectedSignature = 'sha256=' + crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(payload)
|
||||
.digest('hex');
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(signature),
|
||||
Buffer.from(expectedSignature)
|
||||
);
|
||||
}
|
||||
|
||||
// GitLab uses X-Gitlab-Token which should match exactly
|
||||
return signature === secret;
|
||||
}
|
||||
|
||||
export const POST: RequestHandler = async ({ params, request }) => {
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
if (isNaN(id)) {
|
||||
return json({ error: 'Invalid repository ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
const repository = await getGitRepository(id);
|
||||
if (!repository) {
|
||||
return json({ error: 'Repository not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
if (!repository.webhookEnabled) {
|
||||
return json({ error: 'Webhook is not enabled for this repository' }, { status: 403 });
|
||||
}
|
||||
|
||||
// Verify webhook secret if set
|
||||
if (repository.webhookSecret) {
|
||||
const payload = await request.text();
|
||||
const githubSignature = request.headers.get('x-hub-signature-256');
|
||||
const gitlabToken = request.headers.get('x-gitlab-token');
|
||||
|
||||
const signature = githubSignature || gitlabToken;
|
||||
|
||||
if (!verifySignature(payload, signature, repository.webhookSecret)) {
|
||||
return json({ error: 'Invalid webhook signature' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally check which branch was pushed (for GitHub)
|
||||
// const body = await request.json();
|
||||
// if (body.ref && body.ref !== `refs/heads/${repository.branch}`) {
|
||||
// return json({ message: 'Push was not to tracked branch, skipping' });
|
||||
// }
|
||||
|
||||
// Deploy from repository
|
||||
const result = await deployFromRepository(id);
|
||||
return json(result);
|
||||
} catch (error: any) {
|
||||
console.error('Webhook error:', error);
|
||||
return json({ success: false, error: error.message }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
// Also support GET for simple polling/manual triggers
|
||||
export const GET: RequestHandler = async ({ params, url }) => {
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
if (isNaN(id)) {
|
||||
return json({ error: 'Invalid repository ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
const repository = await getGitRepository(id);
|
||||
if (!repository) {
|
||||
return json({ error: 'Repository not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
if (!repository.webhookEnabled) {
|
||||
return json({ error: 'Webhook is not enabled for this repository' }, { status: 403 });
|
||||
}
|
||||
|
||||
// Verify secret via query parameter for GET requests
|
||||
const secret = url.searchParams.get('secret');
|
||||
if (repository.webhookSecret && secret !== repository.webhookSecret) {
|
||||
return json({ error: 'Invalid webhook secret' }, { status: 401 });
|
||||
}
|
||||
|
||||
// Deploy from repository
|
||||
const result = await deployFromRepository(id);
|
||||
return json(result);
|
||||
} catch (error: any) {
|
||||
console.error('Webhook GET error:', error);
|
||||
return json({ success: false, error: error.message }, { status: 500 });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user