Add/update/delete service calls in the Service facade were bypassing
ActionOnEditableZone, so mutations could silently target a committed or
published zone instead of deriving a new editable snapshot first.
Wraps AddServiceToZone, RemoveServiceFromZone, and UpdateZoneService
with ActionOnEditableZone so the decorator is applied consistently.
Fixes regression introduced by b2b6467575.
Decouple diff computation from executable provider closures by fetching
provider records and computing diffs locally via DNSControlDiffByRecord.
On apply, build a target record set from user-selected corrections using
BuildTargetRecords, then ask the provider for executable corrections
against that target. A published snapshot is inserted at ZoneHistory[1]
while the WIP zone at position 0 remains unchanged.
Expose service editors publicly (no auth required) at /generator for
SEO discoverability. Each page shows an interactive editor alongside
a live DNS zone record preview powered by a new POST
/service_specs/:ssid/records backend endpoint.
When display_by_groups is enabled, domains are now draggable and group
containers act as drop targets. Dropping a domain onto a different group
updates its group via the API and refreshes the domain list.
The next query parameter was silently dropped when users chose OIDC
login, always redirecting to / after authentication. Forward the
validated next value to /auth/oidc, store it in the session during
redirect, and use it for the final redirect in the callback, matching
the behaviour of password-based login.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Decode and validate the next query parameter before navigating,
ensuring it is a same-origin relative path (starts with / but not //)
to prevent attackers from redirecting users to external sites after login.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The function silently fell back to creating a new session when session.id
was falsy, which could create unintended API tokens from a partial object.
Session creation is already handled by addSession(); updateSession() now
throws early when no id is present.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously, RecordFailure/RecordSuccess were only called when a captcha
provider was configured, making brute-force tracking entirely inactive
on deployments without one.
- Always track login failures and successes regardless of captcha config
- When threshold is crossed with a captcha provider: 401 + captcha_required (existing behaviour)
- When threshold is crossed without a captcha provider: 429 + rate_limited flag
- Frontend: show a rate-limited message and disable the submit button on 429
- Add errors.rate-limited translation key to all locales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SHA-1 is cryptographically broken. Replace with SHA-256 and slice to
the first 6 bytes (12 hex chars) for a compact, human-readable token
fingerprint. 48 bits is more than sufficient to distinguish a handful
of active sessions without sacrificing readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces ProviderPicker and PickProvider reusable components so that
adding a domain from NewDomainInput or FilterDomainInput opens an
inline provider-selection modal instead of navigating away to
/domains/new/:dn.
Replace the ListGroup-based provider list with a Bootstrap Table on the
providers page. Rows are clickable to edit, the domain count links to
the domains page pre-filtered by provider, and action buttons handle
propagation correctly.
Replace the dedicated provider type selection page with a modal,
using a module-level controller pattern. The /providers/new route
now redirects to /providers?newProvider, which auto-opens the modal.
Introduces a reusable PageTitle component with a teal overline accent,
display-3 heading, optional monospace domain label, subtitle, and a
children slot for future domain health/check badges. Applied consistently
to the zone viewer, history, logs, export, import, resolver, providers,
account settings, and new-domain pages.
Introduce ServiceDetailsOffcanvas, an offcanvas panel that opens when
clicking a service card. It displays the service description, its DNS
records, and provides actions buttons.
Also remove raw DNS record from service form.
Install highlight.js and apply DNS zone file syntax highlighting on the
export page and in the RecordText component. Uses the github theme and
imports only the dns language to keep the bundle small.
Convert the zone file viewer from a modal dialog to a dedicated page at
/domains/[dn]/export, following the same pattern used for service pages.
Adds a "Copy to clipboard" button in the page title bar and adds the
common.copy-clipboard translation key to all supported locales.
Introduce a helpLinkOverride store so the Header's help button can
display context-sensitive service docs. Move the svctype-to-URL
computation into Help.svelte (service prop + $effect), removing the
duplicated helpLink functions from the service edit page and the modal
Footer. Pages now render <HelpButton {service} /> to drive the override
without showing a redundant per-page button.
Replace the Service modal component with a dedicated service page route
and a ServiceSidebar component, improving navigation by giving each
service its own URL under [subdomain]/[serviceid].
Use IntersectionObserver to track which subdomain section is currently
visible in the top 30% of the viewport, bold the matching sidebar link,
and auto-scroll the sidebar to keep it in view with scroll-margin-block
so adjacent items remain visible.
Split subdomain display to show the subdomain and domain parts
separately, making the root domain bold and dimming the domain
suffix. Add hover bold effect for text-dark links.
Refactor the domain layout by moving the zone-specific sidebar content
(subdomain list, zone actions dropdown) into a new ZoneSidebar.svelte
component, improving separation of concerns between zone and service views.
- Add network fallback for asset cache misses (prevents broken requests
on install race conditions)
- Fix query string stripping to use a clean Request instead of copying
event.request options
- Await cache.put() calls to prevent incomplete writes on SW termination
- Expand auth path exclusion to startsWith("/api/auth") to cover all
auth-related endpoints