Compare commits
5 Commits
edfa12b8e9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e146126f86 | ||
|
|
3adf06e4e1 | ||
|
|
957be073ab | ||
|
|
4174b8d59c | ||
|
|
0711a7f7ad |
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
||||
node_modules
|
||||
**/node_modules
|
||||
dist
|
||||
**/dist
|
||||
.git
|
||||
.env
|
||||
.env.example
|
||||
*.log
|
||||
data
|
||||
@@ -16,6 +16,7 @@ RUN apk add --no-cache python3 make g++
|
||||
COPY package*.json ./
|
||||
RUN npm install --omit=dev && apk del python3 make g++
|
||||
COPY --from=build /app/dist ./dist
|
||||
COPY --from=build /app/public ./public
|
||||
|
||||
# DATA_DIR (SQLite DB) is a mutable runtime volume — not baked into the
|
||||
# image, so redeploys don't clobber host overrides / manual targets.
|
||||
|
||||
20
README.md
20
README.md
@@ -92,6 +92,26 @@ GET /api/status
|
||||
|
||||
`status` is `"up"` for any response (including 4xx — the server is reachable), `"down"` for connection errors and 5xx, `"unknown"` before the first check completes.
|
||||
|
||||
```
|
||||
GET /api/latency-targets
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"refreshedAt": "2026-07-26T18:00:00Z",
|
||||
"targets": [
|
||||
{
|
||||
"key": "wisp.dev.example.com",
|
||||
"name": "Wisp",
|
||||
"url": "https://wisp.dev.example.com/",
|
||||
"source": "npm"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`/api/latency-targets` exposes the current computed check list after include-pattern filtering plus admin overrides/manual targets, making it suitable as a source of truth for downstream probe consumers.
|
||||
|
||||
### Admin API
|
||||
|
||||
All routes below `/api/admin` (except `/auth/check`) require an `X-Admin-Password` header matching `ADMIN_PASSWORD`. Returns `503` for every route if `ADMIN_PASSWORD` is unset.
|
||||
|
||||
@@ -2,6 +2,9 @@ services:
|
||||
app:
|
||||
image: ${IMAGE}
|
||||
restart: unless-stopped
|
||||
pull_policy: always
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "${PORT:-3035}:3035"
|
||||
environment:
|
||||
|
||||
4
public/favicon.svg
Normal file
4
public/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" rx="7" fill="#080808"/>
|
||||
<path d="M6 18 L11 18 L13.5 11 L18 23 L20.5 18 L26 18" fill="none" stroke="#e8c400" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
@@ -5,6 +5,7 @@ export function renderAdminPage(): string {
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>admin / status</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
|
||||
:root {
|
||||
|
||||
@@ -60,7 +60,7 @@ async function refreshNpmHosts(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function currentTargets(): CheckTarget[] {
|
||||
export function currentTargets(): CheckTarget[] {
|
||||
return computeTargets(rawHosts, getAllOverrides(), listManualTargets());
|
||||
}
|
||||
|
||||
|
||||
10
src/index.ts
10
src/index.ts
@@ -1,17 +1,25 @@
|
||||
import express from 'express';
|
||||
import { config } from './config.js';
|
||||
import { startChecker, getStore } from './checker.js';
|
||||
import { startChecker, getStore, currentTargets } from './checker.js';
|
||||
import { renderPage } from './page.js';
|
||||
import { renderAdminPage } from './adminPage.js';
|
||||
import { router as adminRouter } from './admin.js';
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use(express.static('public'));
|
||||
|
||||
app.get('/api/status', (_req, res) => {
|
||||
res.json(getStore());
|
||||
});
|
||||
|
||||
app.get('/api/latency-targets', (_req, res) => {
|
||||
res.json({
|
||||
refreshedAt: new Date().toISOString(),
|
||||
targets: currentTargets(),
|
||||
});
|
||||
});
|
||||
|
||||
app.use('/api/admin', adminRouter);
|
||||
|
||||
app.get('/admin', (_req, res) => {
|
||||
|
||||
@@ -34,6 +34,7 @@ export function renderPage(store: StatusStore): string {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="refresh" content="60">
|
||||
<title>status / goonk</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
|
||||
:root {
|
||||
|
||||
Reference in New Issue
Block a user