Healthchecks and fixes for QR API
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/push/deploy unknown status

This commit is contained in:
2026-02-07 14:45:02 -03:00
parent 771d0ccf27
commit ace33435fb
9 changed files with 164 additions and 19 deletions

View File

@@ -34,6 +34,11 @@ services:
volumes:
- /mnt/config/docker/kutt/redis:/data
command: redis-server --appendonly yes
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
kutt:
image: kutt/kutt:latest
@@ -46,7 +51,17 @@ services:
kutt_db:
condition: service_healthy
kutt_redis:
condition: service_started
condition: service_healthy
healthcheck:
test:
[
'CMD-SHELL',
"node -e \"require('http').get('http://127.0.0.1:3000/', (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on('error', () => process.exit(1))\"",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
environment:
ADMIN_EMAILS: ${ADMIN_EMAILS:?Set ADMIN_EMAILS}
DEFAULT_DOMAIN: mifi.me
@@ -85,7 +100,8 @@ services:
- 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt'
- 'traefik.http.routers.kutt-mifi.service=kutt-short'
- 'traefik.http.services.kutt-short.loadbalancer.server.port=3000'
- 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout'
# Backend timeout: use transport from file provider (see traefik-kutt-timeout.example.yml).
- 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file'
qr_api:
image: ${REGISTRY:-git.mifi.dev}/mifi-holdings/shorty-qr-api:${IMAGE_TAG:-latest}
@@ -103,6 +119,18 @@ services:
KUTT_API_KEY: ${KUTT_API_KEY:-}
KUTT_BASE_URL: http://kutt:3000
SHORT_DOMAIN: https://mifi.me
healthcheck:
test:
[
'CMD',
'node',
'-e',
"require('http').get('http://127.0.0.1:8080/health', (r) => { r.resume(); process.exit(r.statusCode === 200 ? 0 : 1); }).on('error', () => process.exit(1))",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
qr_web:
image: ${REGISTRY:-git.mifi.dev}/mifi-holdings/shorty-qr-web:${IMAGE_TAG:-latest}
@@ -112,9 +140,20 @@ services:
- marina-net
- backend
depends_on:
- qr_api
qr_api:
condition: service_healthy
environment:
QR_API_URL: http://qr_api:8080
healthcheck:
test:
[
'CMD-SHELL',
"node -e \"require('http').get('http://127.0.0.1:3000/', (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on('error', () => process.exit(1))\"",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
labels:
- 'traefik.enable=true'
- 'docker.network=marina-net'

View File

@@ -28,6 +28,11 @@ services:
volumes:
- /mnt/config/docker/kutt/redis:/data
command: redis-server --appendonly yes
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
kutt:
image: kutt/kutt:latest
@@ -39,7 +44,17 @@ services:
kutt_db:
condition: service_healthy
kutt_redis:
condition: service_started
condition: service_healthy
healthcheck:
test:
[
'CMD-SHELL',
"node -e \"require('http').get('http://127.0.0.1:3000/', (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on('error', () => process.exit(1))\"",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
environment:
DB_CLIENT: pg
DB_HOST: kutt_db
@@ -85,6 +100,18 @@ services:
KUTT_API_KEY: ${KUTT_API_KEY:-}
KUTT_BASE_URL: http://kutt:3000
SHORT_DOMAIN: https://mifi.me
healthcheck:
test:
[
'CMD',
'node',
'-e',
"require('http').get('http://127.0.0.1:8080/health', (r) => { r.resume(); process.exit(r.statusCode === 200 ? 0 : 1); }).on('error', () => process.exit(1))",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
qr_web:
build:
@@ -95,9 +122,20 @@ services:
- marina-net
- backend
depends_on:
- qr_api
qr_api:
condition: service_healthy
environment:
QR_API_URL: http://qr_api:8080
healthcheck:
test:
[
'CMD-SHELL',
"node -e \"require('http').get('http://127.0.0.1:3000/', (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on('error', () => process.exit(1))\"",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
labels:
- 'traefik.enable=true'
- 'docker.network=marina-net'

View File

@@ -1,4 +1,5 @@
FROM node:20-alpine AS builder
# Use Debian-based image so better-sqlite3 prebuilds (glibc) work; Alpine/musl has no prebuilds.
FROM node:20-bookworm-slim AS builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY package.json pnpm-lock.yaml* ./
@@ -6,7 +7,7 @@ RUN pnpm install
COPY . .
RUN pnpm run build
FROM node:20-alpine
FROM node:20-bookworm-slim
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
ENV NODE_ENV=production

View File

@@ -18,7 +18,13 @@ export async function GET(
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}
@@ -43,7 +49,13 @@ export async function PUT(
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}
@@ -65,6 +77,12 @@ export async function DELETE(
{ status: res.status },
);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}

View File

@@ -12,7 +12,13 @@ export async function GET() {
}
return Response.json(Array.isArray(data) ? data : data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}
@@ -33,6 +39,12 @@ export async function POST(request: Request) {
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}

View File

@@ -24,7 +24,13 @@ export async function GET(
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}
@@ -55,7 +61,13 @@ export async function PUT(
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}
@@ -77,6 +89,12 @@ export async function DELETE(
{ status: res.status },
);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}

View File

@@ -21,7 +21,13 @@ export async function GET() {
}
return Response.json(Array.isArray(data) ? rewriteLogoUrl(data) : data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}
@@ -42,6 +48,12 @@ export async function POST(request: Request) {
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}

View File

@@ -17,6 +17,12 @@ export async function POST(request: Request) {
}
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 502 });
return Response.json(
{
error: 'QR API unreachable',
detail: e instanceof Error ? e.message : String(e),
},
{ status: 502 },
);
}
}

View File

@@ -5,7 +5,8 @@
# Place this file in the same directory as your other dynamic config (e.g. /etc/traefik/conf.d/)
# so the file provider picks it up.
#
# Then uncomment the serversTransport label on the kutt service in docker-compose.portainer.yml.
# In docker-compose.portainer.yml set the kutt service label to use this transport with @file:
# traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file
#
# responseHeaderTimeout: time to wait for backend response headers (0 = no timeout).
# 300s helps avoid 504 Gateway Timeout when Kutt or OIDC is slow.