Different tables used different date formats: Servers & Tasks used a
fixed "YYYY-MM-DD HH:mm:ss" (24h), while Audit Log, DNS, Integrations
(Tailscale/Semaphore), and Users called plain .toLocaleString() with
no options, which renders using the browser's own locale — different
per browser/OS, and inconsistent with the other pages' fixed format.
- New Settings -> Date & Time page: pick date order (YYYY-MM-DD,
DD/MM/YYYY, MM/DD/YYYY) and 12h vs 24h clock, with a live preview.
- utils/date.ts's formatDateTime() now reads these settings instead of
being hardcoded to sv-SE/24h; the setting is fetched once at app
startup (alongside /api/me) via a new non-secret GET
/api/settings/display (any signed-in user, same rationale as the
badge-color endpoints) and applied immediately on save too, without
needing a page reload.
- Switched every remaining raw new Date(...).toLocaleString() call
(Audit Log, DNS zone sync time, Tailscale/Semaphore last-seen,
Users' last login) over to the shared formatter, so every table now
renders dates identically.
Verified the formatter's date-order x 12h logic against all six
combinations plus the midnight/noon 12h edge cases, and the settings
endpoints end-to-end (defaults, partial updates, validation
rejection, audit logging) against the real dev server.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Settings → DNS Badges only let you customize DNS provider badge
colors. Expand it into a general Badges page with a second section
for the six integration types (Tailscale, Proxmox, Synology,
Semaphore, Gitea, Dockhand), applied to the type badge in the Manage
integrations table.
- New integrationColors key on AppSettings, stored/merged the same way
as providerColors via the existing settingsStore.
- New GET /api/settings/integration-colors — non-secret, any signed-in
user, mirroring /provider-colors — so the badge color can be read
without needing admin access to the full settings payload.
- Renamed DnsBadgeSettings.tsx -> BadgeSettings.tsx (route
/settings/dns-badges -> /settings/badges, sub-nav label "DNS
Badges" -> "Badges") with both color sections saved together.
Verified end-to-end against the real dev server: PUT persists
integration colors independently of provider colors, the public
integration-colors endpoint reflects updates immediately, and the
change is audit-logged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Proxmox start/stop/restart routes already existed (used by the
Integrations page's guest table) but weren't reachable from a server's
own detail page, even when that server was linked to a Proxmox guest.
Reuses the existing POST /api/integrations/:id/proxmox/nodes/:node/
:type/:vmid/{start,stop,restart} routes and the operator+ role gate
already enforced there — no server-side changes needed. Buttons only
render for hardware.source === "proxmox", mirroring the Integrations
page's running/stopped button-set logic, with the same confirm-before-
stop prompt for the non-reversible action.
Verified end-to-end against the real dev server with a mock Proxmox
HTTPS server: linked a server to a mock guest, confirmed the detail
endpoint's live status, then triggered restart/stop/start and
confirmed the mock actually received each action and every call was
audit-logged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Manage integrations only supported add/toggle/delete — fixing an
expired API token meant deleting and recreating the whole integration.
- New GET /api/integrations/:id/config returns only the non-secret
config fields (never the decrypted secret) so an edit form can
pre-fill URL/tailnet/etc. fields.
- New POST /api/integrations/:id/test merges the stored, decrypted
config with any freshly-typed overrides and pings the real adapter —
lets "Test connection" work during an edit without ever sending the
current secret back to the browser.
- New IntegrationEditForm component: secret fields render blank with a
"leave blank to keep the current value" placeholder; submitting only
sends the fields that were actually filled in, so a name/URL edit
can't accidentally wipe a secret and a secret rotation can't touch
anything else. Reuses the existing PATCH /:id route, which already
merged partial config updates correctly.
Verified end-to-end against the real dev server: confirmed via direct
DB decryption that a non-secret-only edit leaves the stored secret
byte-for-byte unchanged, and that a secret-only edit rotates it without
touching other config; the test route was confirmed to make a real
network call (got a genuine "API token invalid" from Tailscale's API
against a fake key).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Servers & Tasks only tracked scheduled tasks — there was no overview of
the servers themselves and no way to see CPU/RAM/disk/IP info. Add a
clickable server overview (visible to every role, not just admins) that
opens a per-server detail page.
- servers table gains an agent-reported hardware/network snapshot
(IPs, CPU model/cores/load, memory, disks) and an optional link to a
Proxmox VM/LXC (integration + node + guest type + vmid).
- Proxmox adapter gains getGuestDetail(): live cores/memory/disk from
/config, live cpu/mem/uptime from /status/current, and IPs (LXC net
config directly, QEMU via a best-effort guest-agent call that degrades
gracefully when the agent isn't installed).
- New GET /api/servers/:id/detail combines whichever hardware source
applies (live Proxmox vs. last agent report) with a DNS reverse-lookup
against the DNS module's own record cache, so matching hostnames show
up next to each IP. New PATCH /api/servers/:id manages the Proxmox
link.
- agent/linux/report-tasks.sh now also collects and reports IPs,
CPU/memory/disk info on every check-in (load-average-based CPU number,
not instantaneous, to keep the agent a cheap oneshot).
- Extracted the per-server task table into a shared ServerTaskTable
component so the all-servers view and the new detail page render
tasks identically.
Verified server-side end-to-end against the real dev server (agent
report -> detail endpoint -> DNS match) and the Proxmox adapter against
a mock HTTPS server covering LXC/QEMU config parsing and the
guest-agent-unavailable fallback.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Settings page was a "coming soon" placeholder. Port Sloth Manager's
settings feature set: Gotify/ntfy/SMTP/webhook notification channels
(each with its own test-send button), per-event toggles (DNS record
added/updated/deleted, a daily secret-expiry digest with configurable
time/timezone), and per-provider DNS badge color customization.
Settings persist in the existing `settings` key/value table via a new
settingsStore service; a notify service fans a message out to every
enabled channel. DNS record add/update/delete now fire notifications,
and a node-schedule job re-arms itself whenever the notification
settings change. Removed the now-superseded GOTIFY_URL/GOTIFY_TOKEN
env vars in favor of in-app configuration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>