a control panel for my server

feat: auto deploy

dunkirk.sh 9c3d4087 21f7f5e9

verified
Changed files
+77
.github
workflows
src
+73
.github/workflows/deploy.yaml
···
··· 1 + name: Deploy Control 2 + 3 + on: 4 + push: 5 + branches: 6 + - main 7 + workflow_dispatch: 8 + 9 + jobs: 10 + deploy: 11 + runs-on: ubuntu-latest 12 + steps: 13 + - uses: actions/checkout@v3 14 + 15 + - name: Setup Tailscale 16 + uses: tailscale/github-action@v3 17 + with: 18 + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} 19 + oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} 20 + tags: tag:ci 21 + use-cache: "true" 22 + 23 + - name: Configure SSH 24 + run: | 25 + mkdir -p ~/.ssh 26 + echo "StrictHostKeyChecking no" >> ~/.ssh/config 27 + 28 + - name: Deploy to server 29 + run: | 30 + ssh control@terebithia << 'EOF' 31 + cd /var/lib/control/app 32 + git fetch --all 33 + git reset --hard origin/main 34 + bun install 35 + sudo /run/current-system/sw/bin/systemctl restart control.service 36 + EOF 37 + 38 + - name: Health check 39 + run: | 40 + HEALTH_URL="https://control.dunkirk.sh/health" 41 + MAX_RETRIES=6 42 + RETRY_DELAY=5 43 + 44 + for i in $(seq 1 $MAX_RETRIES); do 45 + echo "Health check attempt $i/$MAX_RETRIES..." 46 + 47 + RESPONSE=$(curl -s -w "\n%{http_code}" "$HEALTH_URL" || echo "000") 48 + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) 49 + BODY=$(echo "$RESPONSE" | head -n-1) 50 + 51 + if [ "$HTTP_CODE" = "200" ]; then 52 + # Validate response contains "status":"ok" 53 + if echo "$BODY" | grep -q '"status":"ok"'; then 54 + echo "✅ Service is healthy (HTTP $HTTP_CODE)" 55 + echo "Response: $BODY" 56 + exit 0 57 + else 58 + echo "❌ Health check returned 200 but invalid body" 59 + echo "Response: $BODY" 60 + fi 61 + else 62 + echo "❌ Health check failed with HTTP $HTTP_CODE" 63 + echo "Response: $BODY" 64 + fi 65 + 66 + if [ $i -lt $MAX_RETRIES ]; then 67 + echo "Retrying in ${RETRY_DELAY}s..." 68 + sleep $RETRY_DELAY 69 + fi 70 + done 71 + 72 + echo "❌ Health check failed after $MAX_RETRIES attempts" 73 + exit 1
+4
src/index.ts
··· 39 }); 40 }); 41 42 // Kill-check endpoint for Caddy to call before proxying protected routes 43 // Returns 200 to allow, 503 to block 44 // No auth required - this is called by Caddy internally
··· 39 }); 40 }); 41 42 + app.get("/health", (c) => { 43 + return c.json({ status: "ok" }); 44 + }); 45 + 46 // Kill-check endpoint for Caddy to call before proxying protected routes 47 // Returns 200 to allow, 503 to block 48 // No auth required - this is called by Caddy internally