feat: monitors worker, auth register, scan polish

Add background DNS/SSL monitor checks, registration API, smoother SSE
with stage labels, improved CDN/WAF detection, light-theme UI polish,
and README roadmap updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-24 23:33:15 +03:00
parent 1196bc3d11
commit 010e053ca1
21 changed files with 1166 additions and 75 deletions

27
scripts/monitor-worker.ts Normal file
View File

@@ -0,0 +1,27 @@
/**
* Background monitor checks (DNS change + SSL expiry MVP).
* Run via: npm run worker:monitors
*/
import { runAllMonitorChecks } from "../src/lib/monitoring/check";
const intervalMs = Number(process.env.MONITOR_INTERVAL_MS ?? 300_000);
async function tick() {
const summary = await runAllMonitorChecks();
console.log(
`[monitor-worker] ${new Date().toISOString()} checked=${summary.checked} with_alerts=${summary.alerts}`
);
}
async function main() {
await tick();
setInterval(() => {
tick().catch((e) => console.error("[monitor-worker]", e));
}, intervalMs);
console.log(`[monitor-worker] interval ${intervalMs / 1000}s`);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

28
scripts/seed-admin.ts Normal file
View File

@@ -0,0 +1,28 @@
import bcrypt from "bcryptjs";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
const email = process.env.SEED_ADMIN_EMAIL?.toLowerCase();
const password = process.env.SEED_ADMIN_PASSWORD;
if (!email || !password) {
console.log("[seed-admin] SEED_ADMIN_EMAIL / SEED_ADMIN_PASSWORD not set — skip");
return;
}
const passwordHash = await bcrypt.hash(password, 12);
await prisma.user.upsert({
where: { email },
create: { email, passwordHash, role: "ADMIN", name: "Admin" },
update: { passwordHash, role: "ADMIN" },
});
console.log(`[seed-admin] admin ready: ${email}`);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());

42
scripts/smoke-test.sh Normal file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
BASE="${SMOKE_BASE_URL:-http://127.0.0.1:3000}"
DOMAIN="${SMOKE_DOMAIN:-example.com}"
echo "[smoke] health"
curl -fsS "${BASE}/api/health" | grep -q '"ok"' || { echo "health failed"; exit 1; }
echo "[smoke] scan POST"
SCAN_JSON="$(curl -fsS -X POST "${BASE}/api/scan" \
-H 'Content-Type: application/json' \
-d "{\"domain\":\"${DOMAIN}\"}")"
SCAN_ID="$(echo "${SCAN_JSON}" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p' | head -1)"
if [[ -z "${SCAN_ID}" ]]; then
echo "scan id missing: ${SCAN_JSON}"
exit 1
fi
echo "[smoke] wait scan ${SCAN_ID}"
for _ in $(seq 1 90); do
STATUS="$(curl -fsS "${BASE}/api/scan/${SCAN_ID}" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p' | head -1)"
if [[ "${STATUS}" == "COMPLETED" || "${STATUS}" == "FAILED" ]]; then
break
fi
sleep 1
done
if [[ "${STATUS}" != "COMPLETED" ]]; then
echo "scan did not complete: ${STATUS}"
exit 1
fi
for path in / /dashboard /analytics /monitoring /api-docs /login; do
echo "[smoke] GET ${path}"
code="$(curl -s -o /dev/null -w '%{http_code}' "${BASE}${path}")"
if [[ "${code}" != "200" ]]; then
echo "page ${path} returned ${code}"
exit 1
fi
done
echo "[smoke] OK"