+22
-15
.github/workflows/deploy.yaml
+22
-15
.github/workflows/deploy.yaml
···
11
11
runs-on: ubuntu-latest
12
12
steps:
13
13
- uses: actions/checkout@v3
14
-
14
+
15
15
- name: Setup Tailscale
16
16
uses: tailscale/github-action@v3
17
17
with:
···
19
19
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
20
20
tags: tag:ci
21
21
use-cache: "true"
22
-
22
+
23
23
- name: Configure SSH
24
24
run: |
25
25
mkdir -p ~/.ssh
26
26
echo "StrictHostKeyChecking no" >> ~/.ssh/config
27
-
27
+
28
28
- name: Deploy to server
29
29
run: |
30
30
ssh indiko@terebithia << 'EOF'
···
34
34
bun install
35
35
sudo /run/current-system/sw/bin/systemctl restart indiko.service
36
36
EOF
37
-
38
-
- name: Wait for service to start
39
-
run: sleep 10
40
-
37
+
41
38
- name: Health check
42
39
run: |
43
-
HEALTH_URL="https://indiko.dunkirk.sh/"
40
+
HEALTH_URL="https://indiko.dunkirk.sh/health"
44
41
MAX_RETRIES=6
45
42
RETRY_DELAY=5
46
-
43
+
47
44
for i in $(seq 1 $MAX_RETRIES); do
48
45
echo "Health check attempt $i/$MAX_RETRIES..."
49
46
50
-
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" || echo "000")
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)
51
50
52
51
if [ "$HTTP_CODE" = "200" ]; then
53
-
echo "✅ Service is healthy (HTTP $HTTP_CODE)"
54
-
exit 0
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"
55
64
fi
56
65
57
-
echo "❌ Health check failed with HTTP $HTTP_CODE"
58
-
59
66
if [ $i -lt $MAX_RETRIES ]; then
60
67
echo "Retrying in ${RETRY_DELAY}s..."
61
68
sleep $RETRY_DELAY
62
69
fi
63
70
done
64
-
71
+
65
72
echo "❌ Health check failed after $MAX_RETRIES attempts"
66
73
exit 1
+12
src/index.ts
+12
src/index.ts
···
95
95
port: env.PORT ? Number.parseInt(env.PORT, 10) : 3000,
96
96
routes: {
97
97
"/": indexHTML,
98
+
"/health": () => {
99
+
try {
100
+
// Verify database is accessible
101
+
db.query("SELECT 1").get();
102
+
return Response.json({ status: "ok", timestamp: new Date().toISOString() });
103
+
} catch {
104
+
return Response.json(
105
+
{ status: "error", error: "Database unavailable" },
106
+
{ status: 503 }
107
+
);
108
+
}
109
+
},
98
110
"/admin": adminHTML,
99
111
"/admin/invites": adminInvitesHTML,
100
112
"/admin/apps": () => Response.redirect("/admin/clients", 302),