Skip to content

Releases: hxx0611/Domain-Monitor

v0.8.9 — Documentation & Operations Closeout

Choose a tag to compare

@hxx0611 hxx0611 released this 20 Aug 12:00

v0.8.9 — Documentation & Operations Closeout

Phase 13A audit report archive

  • Full security/reliability audit report (docs/PHASE13A-AUDIT-REPORT.md) with Post-Audit Status appendix

Phase 13B — Domain/DNS action test coverage

  • 36 tests added; total test suite reaches 849 tests / 57 files

Phase 13C — Production backup

  • SQLite online backup API (better-sqlite3, consistent snapshot, read-only source)
  • NFS persistent backup directory (mode 600, survives container rebuild)
  • Daily QwenPaw cron domain-monitor-daily-backup (0 13 * * * Asia/Shanghai, silent)
  • 7-day retention (auto-prune), failure → Telegram alert (timestamp/exit/error only, no secrets)
  • Restore drill PASS

Phase 13D — SQLite/NFS migration preflight (blocked)

  • Current NFSv3 + nolock mount is NOT approved for hosting the production SQLite database
  • Production SQLite remains on local /tmp overlay
  • Production backups stored on NFS persistent storage

Phase 13E — Audit archive & documentation closeout

  • Operations, disaster-recovery, handover, database, testing, and architecture documentation synchronized
  • SQLite/NFS restriction documented
  • PostgreSQL or a local persistent volume remains a future architecture option (not implemented)

Important

  • Production SQLite remains on local /tmp overlay (/tmp/domain-monitor/data/domain-monitor.db)
  • Production backups are stored on NFS persistent storage
  • Current NFSv3 + nolock mount is NOT approved for hosting the production SQLite database

v0.8.8 — Windows CI SQLite Test Cleanup Fix

Choose a tag to compare

@hxx0611 hxx0611 released this 20 Aug 06:48

v0.8.8 — Windows CI SQLite Test Cleanup Fix

Fixed

  • Windows CI: SQLite temp test database could not be deleted (Phase 12A). rdap-link.test.ts failed on the windows-fresh-install (Node 24) job with EPERM / Permission denied / syscall: rm when the afterAll hook tried to remove its temporary directory.
  • Root cause: @/db opens a module-level SQLite connection (new Database(databaseUrl)) that lives for the whole process. The test's dynamic import of the domain repository triggered that connection, so at cleanup time a native file handle still held test.db. Linux allows unlinking an open file and silently masked the leak; Windows refuses to delete a file held open by a process.
  • Fix: src/db/index.ts now exports a test-lifecycle helper closeDb() (never called by production code); rdap-link.test.ts closes the connection before deleting the temp directory and uses rmSync({ recursive, force, maxRetries: 5, retryDelay: 100 }) as a residual native-lock fallback.

Notes

  • Test infrastructure / cross-platform compatibility fix only. This release does not change production database behavior, does not touch migrations or schema, and does not change RDAP business logic.
  • Windows Node 24 fresh-install CI passes with no EPERM; all four CI jobs pass with 813 tests / 55 files.

v0.8.7

Choose a tag to compare

@hxx0611 hxx0611 released this 20 Aug 05:26

v0.8.7 — Phase 11J: Channel Notification Timezone\n\n### Added\n- Channel-level notification timezone for Telegram: notification_channels.config gains an optional timezone field (IANA name, e.g. Asia/Shanghai / UTC / America/New_York), defaulting to UTC. No schema/migration change — the config is a JSON text column and existing channels without the field keep rendering exactly as before (UTC).\n- buildTelegramMessage(event, hostname, language, timezone) renders the message timestamp via Node's built-in Intl.DateTimeFormat as YYYY-MM-DD HH:mm:ss (Timezone) — language-independent numeric format, correct DST handling, zero new dependencies.\n- Internal storage stays UTC: only the rendered message timestamp is converted; event.occurredAt, the worker, dedup/CAS and the DB are untouched.\n- Channel edit UI gains a Message Timezone field (datalist of common IANA zones + free-form IANA name); saveTelegramChannelAction validates it (invalid_timezone) and persists it with the config; the channels table displays it.\n- New src/lib/notifications/timezone.ts (isValidTimezone, formatNotificationTime, COMMON_NOTIFICATION_TIMEZONES) + unit tests.\n\n### Quality\n- 813 tests (55 files) — 13 new (validation/render/action/view); tsc 0, next lint 0, prettier PASS, build PASS (BUILD_ID q1VeYr6rXq2W46t7BNaOm)\n\n### Notes\n- Scope: Telegram message timestamp only. webhook / email notifications and UI time displays are NOT covered in this release (decided separately).\n- Default UTC keeps every existing channel byte-for-byte compatible — zero migration.

v0.8.6

Choose a tag to compare

@hxx0611 hxx0611 released this 20 Aug 04:15

v0.8.6 — Phase 11I: Channel-Level Notification Language

Added

  • Per-channel notification language for Telegram: notification_channels.config gains optional language (en / zh-CN), default en — no schema/migration change, existing channels unchanged
  • buildTelegramMessage(event, hostname, language) localizes template labels (app title / event / domain / status / time / event ID) and event labels; machine state values stay canonical (ok / down / server_error / client_error, HTTP status codes) to avoid ambiguity
  • Channel edit UI: Notification Language dropdown (English / 简体中文); saveTelegramChannelAction validates (invalid_language) and persists with config
  • New src/lib/notifications/i18n.ts dictionary + integrity tests (identical key sets across languages)
  • Language is independent from the UI locale (cookie-based)

Scope

  • Telegram only (webhook / email channels adopt the dictionary later)

Quality

  • 800 tests (54 files) — 15 new (parse/ignore language, zh-CN rendering incl. test-notification label, sender body language, action persistence/validation, toChannelView display); tsc 0, lint 0, prettier PASS, build PASS (BUILD_ID CVfpMJqXYG7srT4aZ4GAt)

v0.8.5

Choose a tag to compare

@hxx0611 hxx0611 released this 20 Aug 02:30

v0.8.5 — Phase 11G-C: Telegram Rejection Diagnosis

Added

  • When Telegram rejects a send (non-2xx), the sender now reads the API response body's description (e.g. Forbidden: bot was blocked by the user) and includes it in the controlled error stored on the delivery: Telegram API returned HTTP 403: Forbidden: ...
  • Sanitization: description truncated to 200 chars; bot-token-shaped substrings → [token]; URL-shaped → [url]; non-JSON/missing body → status-only message (unchanged behavior). Errors never contain the token, API base, or sensitive values

Quality

  • 785 tests (53 files) — 5 new (description inclusion, token/URL redaction, non-JSON fallback, long-description truncation); tsc 0, lint 0, prettier PASS, build PASS (BUILD_ID vaw7Vhxu43aSQAc2U_hOY)

Notes

  • Added to diagnose a production Telegram 403 (controlled test sends reached the API and were rejected with HTTP 403, but the previous sender only recorded the status code). The precise Telegram-side reason is captured on the next real test send.
  • No schema/migration change; no behavior change for successful sends

v0.8.4

Choose a tag to compare

@hxx0611 hxx0611 released this 19 Aug 10:45

v0.8.4 — Phase 11G-A: Controlled Test Notification

Added

  • Admin-triggered test notifications: sendTestNotificationAction(channelId) + "Send Test Notification" button on the Notifications channel UI (EN/ZH)
    • requireAdmin() guard; validates channel exists → enabled → type === "telegram" → encrypted token configured (hasChannelSecret, never reads the value)
    • Exactly 1 event (source="test", event_type="test_notification", dedupKey = test-notification:<channelId>:<nonce>) + exactly 1 delivery directly for the target channel — rule engine not involved, same-nonce replay absorbed by UNIQUE dedupKey
    • Reuses the existing chain: createSender("telegram") → TelegramSender → getChannelSecret (AES-256-GCM) → Telegram API → deliverDelivery state machine
    • Message explicitly labelled Test Notification; never contains tokens, ciphertext, secretRefs, DB paths or other sensitive values

Security

  • Action never returns tokens/ciphertext/secretRefs/ENCRYPTION_KEY; machine-readable error codes (unauthorized, channel_not_found, channel_disabled, unsupported_channel, secret_not_configured, no_domain, duplicate_request, send_failed)
  • Tests prove: unauthenticated → unauthorized (no channel/event access), non-Telegram → unsupported_channel, no duplicate event/delivery on replay, attempts=1, error=null on success, zero real Telegram API calls during tests (stubbed fetch + fake AAH_* fixtures)

Quality

  • 780 tests (53 files) — 17 new (unit + real-DB integration); tsc 0, next lint 0, prettier PASS, build PASS (BUILD_ID hguDKK6wigYItW0QTJQx5)

Notes

  • Deployment to production follows immediately for the Phase 11G-REAL gate (exactly one real Telegram test message through the production UI); real notification delivery remains an explicitly approved safety-gate exercise

v0.8.3 — Production Worker & Expiration Reminder

Choose a tag to compare

@hxx0611 hxx0611 released this 19 Aug 09:10

v0.8.3 — Production Worker & Expiration Reminder (2026-08-19)

Added

  • Production Worker watchdog (Phase 11D): scripts/worker-watchdog.sh — single-instance hourly scheduler (flock-guarded, self-contained, no pnpm-on-PATH dependency). Each tick runs the worker exactly once (./node_modules/.bin/tsx --conditions=react-server scripts/worker.ts --limit 50); a non-zero tick exit is logged and the loop continues; TERM/INT exit cleanly.
  • Hourly expiration reminder processing: evaluateExpirationReminders() emits one deduplicated expiration_reminder event per domain once the reminder day arrives.
  • Expiration reminder → delivery pipeline (Phase 11D): insertEventsAndGenerateDeliveries creates the event and its deliveries together; CAS claim (UPDATE … WHERE status='pending') guarantees at most one event, one delivery and one sender invocation per reminder per day.
  • Migration journal repair (Phase 11E): 0007_manual_expiration is now registered in the drizzle journal (_journal.json idx: 7, when: 1787063220000), 0007_snapshot.json generated (13 tables incl. expiration_reminders), and the production __drizzle_migrations table registered with the migration's sha256 hash. A fresh 0000 → 0007 migration was verified end-to-end; the migration itself was not re-executed on any database.

Fixed

  • Worker crash under react-server / production conditions (Phase 11D): senders/factory.ts imported senders through the @/lib/domains barrel, which pulled in next/cache and crashed under production conditions. The factory now imports from the repository module directly.
  • Expiration reminder events never produced deliveries (Phase 11D): events and deliveries are now created together via insertEventsAndGenerateDeliveries.

Changed

  • Version bumped from v0.8.2 → v0.8.3; tsx@4.23.12 added as a development dependency.
  • src/db/migrations/meta/_journal.json now has 8 entries (0000–0007); 0007_snapshot.json added.

Notes

  • The worker is enabled in production (hourly watchdog). Real notification delivery was not exercised as part of this release; the real-notification safety gate remains a separate approved exercise.
  • 763 tests / 51 files all green; tsc 0, lint 0, prettier PASS, build PASS.

v0.8.2 — Manual Expiration & Reminders

Choose a tag to compare

@hxx0611 hxx0611 released this 18 Aug 16:40

v0.8.2 — Manual Expiration & Reminders (Phase 11A)

Added

  • Manual expiration (source manual): set registration date, expiration date, registration platform (validated presets: GNAME, Alibaba Cloud, Tencent Cloud, Namecheap, Porkbun; or custom provider + HTTPS URL) from the Add/Edit domain forms.
  • Manual dates never overwritten by RDAP refreshes: Refresh only updates RDAP metadata or clears it for no-object/parent results.
  • Expiration reminders: per-domain reminder days (1–3650, presets + custom), evaluated by the delivery worker as deduplicated expiration_reminder events (source expiration, once per domain per day) through the normal rule → delivery pipeline.
  • Migration 0007: expiration_source / registration_provider / registration_provider_url columns + expiration_reminders table (additive).
  • Worker summary now reports expirationEvents.

Notes

  • Automatic delivery is not yet enabled in production (worker tsx runtime not installed there). UI + rule configuration are fully live; reminder delivery activates once the runtime dependency is installed and pnpm worker is scheduled via cron.

Quality

  • 761 tests / 50 files, tsc / lint / prettier / build all green.

v0.8.1 — RDAP Ownership & Expiration Fixes

Choose a tag to compare

@hxx0611 hxx0611 released this 18 Aug 13:17

v0.8.1 — RDAP Ownership & Expiration Fixes (bugfix / stability release)

Bugfix release over v0.8.0 (which introduced admin authentication, the notification configuration UI, the Telegram channel, and encrypted secret storage — all unchanged here).

Fixed

  • RDAP fallback ownership: registered-domain fallback no longer treats parent-domain data as child-domain ownership. A subdomain without its own RDAP object (e.g. opusai.eu.cc → 404) previously resolved to the parent (eu.cc) and the parent's expiration, registrar, nameservers and status were stored on the child row. RDAP results now carry explicit ownership detection (exact / parent), decided strictly from the RDAP object's canonical/LDH identity — never merely from "a fallback succeeded".
  • No parent data on the child: parent-derived expiration / registrar / nameservers / status are never written to the subdomain's own fields. The child is marked rdap_status = ["no-object"] and its registration fields are cleared; the UI shows Unavailable instead of inventing a parent expiration for the subdomain.
  • Expiration-date UI duplication: the detail page showed the expiration twice (Expiration heading plus an Expires: … prefix); it now renders a plain date.
  • Fallback safety net: fallback is allowed only for not-found (HTTP 404 / no domain object) or a successful response without an expiration date; network, timeout, 429, 500 and invalid responses are never masked by a parent query; a bare TLD is never queried.
  • Production data repair: production records affected by the previous fallback behavior were corrected through the normal Refresh flow.

Notes

  • No schema / migration changes. updateDomainRdap(id, data, ownership) now requires an explicit ownership argument.
  • Telegram secret storage, authentication, and the notification pipeline are unchanged.
  • Test suite: 708 tests (46 files) — tsc / lint / prettier all green.

v0.8.0

Choose a tag to compare

@hxx0611 hxx0611 released this 18 Aug 10:03

v0.8.0 — Admin Authentication, Telegram Notifications & Encrypted Secrets

Added

  • Admin authentication: one-time /setup wizard with scrypt-hashed admin password, /login / /logout, one-time recovery code for password reset (recovery rotates the session secret, invalidating all old sessions).
  • HMAC-signed admin session: signed, HttpOnly, SameSite=Lax cookies with expiry + entropy; no third-party auth dependency.
  • Protected pages and Server Actions: /, /domains/[id], /notifications and every mutating Server Action require an authenticated admin session.
  • Telegram notification channel: create/edit Telegram channels from the notification UI; bot token validated via Telegram getMe (server-side only) before saving.
  • Encrypted notification secret storage: AES-256-GCM (iv:tag:ciphertext) for the Telegram bot token; ENCRYPTION_KEY read from the environment (required in production).
  • Telegram sender secret resolution: encrypted store first, legacy TELEGRAM_BOT_TOKEN env fallback, controlled failure when neither is available (decryption failure never falls back).
  • Notification configuration UI: channel CRUD, rule CRUD, token configuration status, delivery retry.
  • Migration 0006: admin_settings and notification_secrets tables.

Security

  • Telegram bot tokens encrypted at rest; never rendered back into HTML/RSC/client bundles — only CONFIGURED/NOT CONFIGURED status exposed.
  • Unified, non-enumerating auth errors.
  • SSRF protection kept: HTTPS-only outbound with per-redirect re-validation.
  • No secret in logs: tokens, passwords, recovery codes, encryption keys never appear in logs/worker output/errors.

Changed

  • Version bumped v0.7.3 → v0.8.0.
  • Channel types now include Telegram alongside Webhook and Email.
  • Secret handling moved to encrypted storage with legacy env fallback.

Fixed

  • Notification configuration pages previously public; now admin-authenticated.
  • Documentation synchronized with the implemented feature set (README bilingual, docs, CHANGELOG).

Compatibility

  • Migration 0006 adds two tables; existing data preserved (non-destructive).
  • Production requires ENCRYPTION_KEY (32-byte / 64 hex). Existing TELEGRAM_BOT_TOKEN deployments keep working via legacy fallback.
  • Node.js >= 22 supported (22 / 24 / 26 CI-tested).