tambah webhook

This commit is contained in:
Saufi
2026-05-22 21:57:33 +08:00
parent f2f0a2405f
commit c5b5e6069d
3 changed files with 213 additions and 0 deletions

View File

@@ -63,3 +63,7 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}" VITE_APP_NAME="${APP_NAME}"
# Gitea Webhook
WEBHOOK_SECRET=tukar_kepada_secret_yang_kuat
WEBHOOK_BRANCH=master

132
public/webhook.php Normal file
View File

@@ -0,0 +1,132 @@
<?php
/**
* Gitea Webhook Handler
*
* URL: https://your-domain.com/webhook.php
* Gitea Content-Type: application/json
* Gitea Secret: nilai WEBHOOK_SECRET dalam .env
* Gitea Events: Push Events
*/
define('APP_ROOT', dirname(__DIR__));
define('LOG_FILE', APP_ROOT . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'webhook.log');
function log_msg(string $message): void
{
$line = '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;
file_put_contents(LOG_FILE, $line, FILE_APPEND | LOCK_EX);
}
function respond(int $code, string $message): never
{
http_response_code($code);
header('Content-Type: application/json');
echo json_encode(['status' => $code, 'message' => $message]);
exit;
}
function read_env(string $key, string $default = ''): string
{
static $env = null;
if ($env === null) {
$env = [];
$file = APP_ROOT . DIRECTORY_SEPARATOR . '.env';
if (file_exists($file)) {
foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
$line = trim($line);
if ($line === '' || str_starts_with($line, '#')) {
continue;
}
if (str_contains($line, '=')) {
[$k, $v] = explode('=', $line, 2);
$env[trim($k)] = trim($v, " \t\"'");
}
}
}
}
return $env[$key] ?? (getenv($key) ?: $default);
}
// ── Hanya terima POST ────────────────────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
respond(405, 'Method Not Allowed');
}
$payload = file_get_contents('php://input');
if (empty($payload)) {
respond(400, 'Empty payload');
}
// ── Sahkan signature Gitea ───────────────────────────────────────────────────
$secret = read_env('WEBHOOK_SECRET');
if (!empty($secret)) {
$signature = $_SERVER['HTTP_X_GITEA_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
log_msg('WARN: Signature tidak sah dari IP ' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
respond(403, 'Invalid signature');
}
}
// ── Parse payload ────────────────────────────────────────────────────────────
$data = json_decode($payload, true);
if (json_last_error() !== JSON_ERROR_NONE) {
respond(400, 'Invalid JSON payload');
}
$event = $_SERVER['HTTP_X_GITEA_EVENT'] ?? 'unknown';
$ref = $data['ref'] ?? '';
$branch = basename($ref);
$pusher = $data['pusher']['login'] ?? 'unknown';
$commitId = substr($data['after'] ?? '', 0, 8);
log_msg("Event: $event | Branch: $branch | Pusher: $pusher | Commit: $commitId");
// ── Hanya deploy pada push ke master/main ───────────────────────────────────
$targetBranch = read_env('WEBHOOK_BRANCH', 'master');
if ($event !== 'push' || $branch !== $targetBranch) {
respond(200, "Skipped — event=$event branch=$branch (target=$targetBranch)");
}
// ── Jalankan deployment script ───────────────────────────────────────────────
$deployScript = APP_ROOT . '\\scripts\\deploy.ps1';
$deployLog = APP_ROOT . '\\storage\\logs\\deploy-' . date('Ymd-His') . '.log';
if (!file_exists($deployScript)) {
log_msg('ERROR: Deploy script tidak dijumpai: ' . $deployScript);
respond(500, 'Deploy script not found');
}
$command = sprintf(
'powershell.exe -NonInteractive -ExecutionPolicy Bypass -File "%s" > "%s" 2>&1',
$deployScript,
$deployLog
);
log_msg("Menjalankan: $command");
$descriptors = [
0 => ['pipe', 'r'],
1 => ['file', $deployLog, 'a'],
2 => ['file', $deployLog, 'a'],
];
$process = proc_open($command, $descriptors, $pipes, APP_ROOT);
if (!is_resource($process)) {
log_msg('ERROR: Gagal memulakan proses deployment');
respond(500, 'Failed to start deployment process');
}
fclose($pipes[0]);
$exitCode = proc_close($process);
log_msg("Deployment selesai — exit code: $exitCode | log: $deployLog");
if ($exitCode === 0) {
respond(200, "Deployment berjaya — commit $commitId");
} else {
respond(500, "Deployment gagal — exit code $exitCode, semak $deployLog");
}

77
scripts/deploy.ps1 Normal file
View File

@@ -0,0 +1,77 @@
# =============================================================================
# Deploy Script — myEventPostMortem (Laravel)
# Windows Server 2019
#
# Dipanggil oleh public/webhook.php apabila Gitea push event diterima.
# Pastikan user yang menjalankan IIS/PHP ada akses ke git, composer, npm, php.
# =============================================================================
$AppRoot = Split-Path -Parent $PSScriptRoot
$ErrorActionPreference = "Stop"
function Write-Log {
param([string]$Message)
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "[$ts] $Message"
}
function Invoke-Step {
param([string]$Label, [scriptblock]$Action)
Write-Log ">>> $Label"
& $Action
if ($LASTEXITCODE -ne 0) {
throw "$Label gagal dengan exit code $LASTEXITCODE"
}
}
# ── Masuk ke direktori aplikasi ──────────────────────────────────────────────
Set-Location $AppRoot
Write-Log "=== Deployment Bermula ==="
Write-Log "App Root : $AppRoot"
Write-Log "User : $env:USERNAME"
Write-Log "Masa : $(Get-Date)"
# ── Git pull ─────────────────────────────────────────────────────────────────
Invoke-Step "Git pull" {
git pull origin master
}
# ── Composer install ─────────────────────────────────────────────────────────
Invoke-Step "Composer install" {
composer install --no-dev --optimize-autoloader --no-interaction --no-progress
}
# ── NPM install & build ──────────────────────────────────────────────────────
Invoke-Step "NPM install" {
npm ci --prefer-offline
}
Invoke-Step "NPM build" {
npm run build
}
# ── Laravel: cache config & migrate ─────────────────────────────────────────
Invoke-Step "Artisan down (maintenance mode)" {
php artisan down --retry=10
}
try {
Invoke-Step "Database migrate" {
php artisan migrate --force
}
Invoke-Step "Optimize Laravel" {
php artisan optimize
}
Invoke-Step "Queue restart" {
php artisan queue:restart
}
}
finally {
# Pastikan aplikasi dibuka semula walaupun ada ralat
Write-Log ">>> Artisan up"
php artisan up
}
Write-Log "=== Deployment Berjaya ==="