feat: add GlitchTip test page and PUBLIC_UMAMI_SCRIPT_URL to ui env
Some checks failed
CI / Test backend (pull_request) Successful in 19s
CI / Check ui (pull_request) Successful in 40s
CI / Docker / caddy (pull_request) Failing after 43s
CI / Docker / ui (pull_request) Successful in 1m17s
CI / Docker / backend (pull_request) Successful in 1m58s
CI / Docker / runner (pull_request) Successful in 2m12s

This commit is contained in:
Admin
2026-03-24 15:06:01 +05:00
parent 5fea8f67d0
commit 8a0f5b6cde
4 changed files with 72 additions and 3 deletions

View File

@@ -160,6 +160,7 @@ services:
LOG_LEVEL: "${LOG_LEVEL}" LOG_LEVEL: "${LOG_LEVEL}"
KOKORO_URL: "${KOKORO_URL}" KOKORO_URL: "${KOKORO_URL}"
KOKORO_VOICE: "${KOKORO_VOICE}" KOKORO_VOICE: "${KOKORO_VOICE}"
GLITCHTIP_DSN: "${GLITCHTIP_DSN}"
healthcheck: healthcheck:
test: ["CMD", "/healthcheck", "http://localhost:8080/health"] test: ["CMD", "/healthcheck", "http://localhost:8080/health"]
interval: 15s interval: 15s
@@ -167,7 +168,11 @@ services:
retries: 3 retries: 3
# ─── Runner (background task worker) ───────────────────────────────────────── # ─── Runner (background task worker) ─────────────────────────────────────────
# profiles: [runner] prevents accidental restart on `docker compose up -d`.
# The homelab runner (192.168.0.109) is the sole worker in production.
# To start explicitly: doppler run -- docker compose --profile runner up -d runner
runner: runner:
profiles: [runner]
image: kalekber/libnovel-runner:${GIT_TAG:-latest} image: kalekber/libnovel-runner:${GIT_TAG:-latest}
build: build:
context: ./backend context: ./backend
@@ -211,6 +216,7 @@ services:
# Kokoro-FastAPI TTS endpoint # Kokoro-FastAPI TTS endpoint
KOKORO_URL: "${KOKORO_URL}" KOKORO_URL: "${KOKORO_URL}"
KOKORO_VOICE: "${KOKORO_VOICE}" KOKORO_VOICE: "${KOKORO_VOICE}"
GLITCHTIP_DSN: "${GLITCHTIP_DSN}"
healthcheck: healthcheck:
# The runner writes /tmp/runner.alive on every poll. # The runner writes /tmp/runner.alive on every poll.
# 120s = 2× the default 30s poll interval with generous headroom. # 120s = 2× the default 30s poll interval with generous headroom.
@@ -256,6 +262,11 @@ services:
PUBLIC_MINIO_PUBLIC_URL: "${MINIO_PUBLIC_ENDPOINT}" PUBLIC_MINIO_PUBLIC_URL: "${MINIO_PUBLIC_ENDPOINT}"
# Valkey # Valkey
VALKEY_ADDR: "valkey:6379" VALKEY_ADDR: "valkey:6379"
# Umami analytics
PUBLIC_UMAMI_WEBSITE_ID: "${PUBLIC_UMAMI_WEBSITE_ID}"
PUBLIC_UMAMI_SCRIPT_URL: "${PUBLIC_UMAMI_SCRIPT_URL}"
# GlitchTip client + server-side error tracking
PUBLIC_GLITCHTIP_DSN: "${PUBLIC_GLITCHTIP_DSN}"
healthcheck: healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"] test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"]
interval: 15s interval: 15s
@@ -556,7 +567,7 @@ services:
GOTIFY_DEFAULTUSER_PASS: "${GOTIFY_ADMIN_PASS}" GOTIFY_DEFAULTUSER_PASS: "${GOTIFY_ADMIN_PASS}"
GOTIFY_SERVER_PORT: "80" GOTIFY_SERVER_PORT: "80"
healthcheck: healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:80/health"] test: ["CMD", "curl", "-sf", "http://localhost:80/health"]
interval: 15s interval: 15s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View File

@@ -171,10 +171,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>libnovel</title> <title>libnovel</title>
<!-- Umami analytics — no-op when PUBLIC_UMAMI_WEBSITE_ID is unset --> <!-- Umami analytics — no-op when PUBLIC_UMAMI_WEBSITE_ID is unset -->
{#if env.PUBLIC_UMAMI_WEBSITE_ID} {#if env.PUBLIC_UMAMI_WEBSITE_ID && env.PUBLIC_UMAMI_SCRIPT_URL}
<script <script
defer defer
src="https://analytics.libnovel.cc/script.js" src={env.PUBLIC_UMAMI_SCRIPT_URL}
data-website-id={env.PUBLIC_UMAMI_WEBSITE_ID} data-website-id={env.PUBLIC_UMAMI_WEBSITE_ID}
></script> ></script>
{/if} {/if}

View File

@@ -0,0 +1,10 @@
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ url }) => {
if (url.searchParams.get('server_error') === '1') {
// This unhandled throw is caught by handleError in hooks.server.ts,
// which forwards it to GlitchTip via Sentry.captureException.
throw new Error('GlitchTip server-side test error — ' + new Date().toISOString());
}
return {};
};

View File

@@ -0,0 +1,48 @@
<script lang="ts">
import * as Sentry from '@sentry/sveltekit';
import { Button } from '$lib/components/ui/button';
function throwClientError() {
throw new Error('GlitchTip client-side test error — ' + new Date().toISOString());
}
function captureManual() {
Sentry.captureException(
new Error('GlitchTip manual capture test — ' + new Date().toISOString())
);
alert('Manually captured exception sent to GlitchTip.');
}
</script>
<div class="max-w-lg mx-auto py-16 flex flex-col gap-6">
<h1 class="text-2xl font-bold text-zinc-100">GlitchTip Test Page</h1>
<p class="text-zinc-400 text-sm">
Use the buttons below to fire test errors. Check
<a
href="https://errors.libnovel.cc"
target="_blank"
rel="noopener noreferrer"
class="text-amber-400 hover:text-amber-300 underline">errors.libnovel.cc</a
> to confirm they arrive.
</p>
<div class="flex flex-col gap-3">
<!-- Triggers an unhandled JS exception → caught by handleError in hooks.client.ts -->
<Button variant="destructive" onclick={throwClientError}>
Throw unhandled client-side error
</Button>
<!-- Sends an exception directly via Sentry.captureException -->
<Button variant="outline" onclick={captureManual}>
Manually capture exception (Sentry.captureException)
</Button>
<!-- SSR load error — visit this URL to trigger a server-side throw -->
<a
href="/glitchtip-test?server_error=1"
class="text-sm text-zinc-400 hover:text-amber-400 underline text-center"
>
Trigger server-side load error (navigate here with ?server_error=1)
</a>
</div>
</div>