## Changes
- Add 59 missing keys to every non-en locale (DNS monitor strings,
status-page themes, monitor status labels, network error messages,
email sender display name)
- Catalan: add 12 bulk-monitor selection keys that were introduced after
ca.json was first added
- Fill 188 empty string values across ar, cs, fi, fr, ja, pt-BR, tr,
uk and vi
- Remove 10 orphan keys from es.json that were not referenced anywhere
in the client code
## Benefits
- All 16 locales now report 100% structural coverage against en.json
- Status page theme picker, DNS monitor type, and bulk monitor actions
are fully translated
- No more empty strings rendered as blank labels in production UI
- es.json no longer carries dead keys diverging from the reference
## Notes
- Placeholders ({{count}}, {{value}}, {{name}}) verified intact in
every locale
- Brand terms (DNS, A, AAAA, CNAME, MX, NS, TXT, 8.8.8.8) intentionally
kept untranslated
- Build, prettier format-check and JSON validity all pass
Maintenance windows could disappear seconds after creation when the API received
a payload with end dates already in the past. The repository auto-set
expiry = end for one-time windows (repeat = 0), and Mongo's TTL index on the
expiry field then deleted the row on its next sweep. This was particularly
visible with automation tools sending payloads in non-UTC timezones.
## Changes
### Validator hardening
- Add .strict() to create + edit body schemas so unknown keys return 400
instead of being silently dropped.
- Refine end > start on create.
- Refine end > now for one-time create payloads (repeat = 0).
- Refine end > start on edit when both are provided.
- Remove the accepted expiry field from both schemas.
### Stop the auto-delete behaviour
- Repository.create no longer copies end into expiry.
- Schema's expiry field + TTL index removed.
- Migration 0007 drops the live TTL index and unsets stale expiry values on
existing rows so legacy data is no longer at risk.
### Client
- Form submission no longer includes expiry.
Status code 5000 is Checkmate's internal sentinel for "no HTTP response
received" (DNS failure, connection refused, timeout, TLS error, etc.).
Showing the raw number to admins is confusing — the value isn't a real
HTTP code and means nothing without context.
This change replaces "5000" with "Down" across all check/incident
surfaces, and adds a hover tooltip that explains the underlying cause
based on the stored error message. Real HTTP codes (2xx/3xx/4xx/5xx)
are unchanged.
## Changes
- Add client/src/Utils/statusCode.ts with formatStatusCode,
getStatusCodeTooltip, getStatusCodeValueType, extractErrorCode, and a
message-to-subtype parser (timeout / dns / refused / tls / reset).
- Wire into 6 render sites: Checks table, Uptime details Checks table,
GeoChecks table + map popup, Incidents table, Incident details dialog.
- Add common.statusCode.* i18n keys in en.json with subtype-specific
descriptions. Tooltip appends the raw error code (ECONNREFUSED,
ETIMEDOUT, CERT_HAS_EXPIRED, etc.) when present in the message.
## Tooltip examples
- "queryA ECONNREFUSED www.example.com" →
"The server refused the connection (port closed or service not listening). (ECONNREFUSED)"
- "connect ETIMEDOUT 1.2.3.4:443" →
"The server didn't respond in time (request timed out). (ETIMEDOUT)"
## Notes
- No DB or server changes; statusCode field stays 5000 in storage.
- Other locales fall back to English keys until translated.
Addresses PR feedback:
- Replaces forbidden any type in catch block with unknown
- Adds type discrimination using axios.isAxiosError and instanceof Error
- Narrows error type for logger.error compatibility
Fixes#3602