Files
dashcaddy/dashcaddy-api/constants.js
Sami e615f24627 Add Docker hygiene, deployment manifests, and daily log digest
Prevents Docker disk bloat by adding log rotation (10MB max, 3 files)
to all container creation and update paths, auto-pruning dangling
images after deploy/remove/update, and a daily maintenance module
that cleans build cache and warns on disk thresholds.

Saves a deployment manifest in services.json at deploy time so users
can restore all their apps after a Docker purge. Adds restore-all
and restore-single endpoints that recreate containers, Caddy config,
and DNS records from the saved manifests.

Adds an hourly log collector and daily digest generator that
summarizes errors, warnings, and events across all services into
a single human-readable report with guidance on where to investigate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:41:40 -07:00

163 lines
6.5 KiB
JavaScript

// DashCaddy Shared Constants
// Centralizes configuration values used across the application.
// Edit values here instead of hunting through server.js.
// ── App Identity ──────────────────────────────────────────────
const APP = {
NAME: 'DashCaddy',
VERSION: '1.1',
PORT: 3001,
USER_AGENTS: {
PROBE: 'DashCaddy-Probe/1.0',
API: 'DashCaddy/1.0',
HEALTH: 'DashCaddy-HealthCheck/1.0',
},
DEVICE_IDS: {
SSO: 'dashcaddy-sso', // Backend auth gate (never invalidates browser token)
BROWSER: 'dashcaddy-browser', // Browser-side localStorage token
},
};
// ── Default Ports for Media/Arr Apps ──────────────────────────
const APP_PORTS = {
plex: 32400,
radarr: 7878,
sonarr: 8989,
seerr: 5055,
lidarr: 8686,
prowlarr: 9696,
};
// Arr service discovery config (used by /api/arr/* endpoints)
const ARR_SERVICES = {
plex: { names: ['plex'], port: APP_PORTS.plex, configPath: 'Plex' },
radarr: { names: ['radarr'], port: APP_PORTS.radarr, configPath: 'radarr' },
sonarr: { names: ['sonarr'], port: APP_PORTS.sonarr, configPath: 'sonarr' },
seerr: { names: ['seerr', 'requests'], port: APP_PORTS.seerr, configPath: 'seerr' },
lidarr: { names: ['lidarr'], port: APP_PORTS.lidarr, configPath: 'lidarr' },
prowlarr: { names: ['prowlarr'], port: APP_PORTS.prowlarr, configPath: 'prowlarr' },
};
// ── Timeouts (ms) ─────────────────────────────────────────────
const TIMEOUTS = {
HTTP_DEFAULT: 5000, // Standard fetch/http timeout
HTTP_LONG: 10000, // DNS ops, downloads, login requests
DEPLOY_SETTLE: 3000, // Wait after container start before health check
CADDY_PRE_RELOAD: 2000, // Pause before Caddy reload
RETRY_SHORT: 1000, // Short retry delay
RETRY_MEDIUM: 2000, // Medium retry delay
SHUTDOWN_GRACE: 5000, // Graceful shutdown window
SHUTDOWN_ERROR: 1000, // Error shutdown window
PORT_CHECK: 2000, // TCP port availability check
};
// ── Retry Configuration ───────────────────────────────────────
const RETRIES = {
CADDY_RELOAD: 3, // Max attempts to reload Caddy
};
// ── Session / Cache Expiry (ms) ───────────────────────────────
const SESSION_TTL = {
IP_SESSION: 30 * 60 * 1000, // 30 min — router IP-based sessions
COOKIE_SESSION: 30 * 60 * 1000, // 30 min — cookie-based login sessions
TOKEN_SESSION: 60 * 60 * 1000, // 60 min — JWT/access token sessions (Jellyfin, Plex, etc.)
FAILED_LOGIN: 5 * 60 * 1000, // 5 min — cooldown after failed login
DNS_TOKEN: 6 * 60 * 60 * 1000, // 6 hrs — DNS auto-refresh interval
};
// ── Rate Limiting ─────────────────────────────────────────────
const RATE_LIMITS = {
GENERAL: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
},
STRICT: {
windowMs: 15 * 60 * 1000,
max: 20,
},
TOTP: {
windowMs: 15 * 60 * 1000,
max: 10,
},
};
// ── Caddy ─────────────────────────────────────────────────────
const CADDY = {
CONTENT_TYPE: 'text/caddyfile',
DEFAULT_DNS_PORT: '5380',
DEFAULT_TTL: 300,
TTL_MIN: 60,
TTL_MAX: 86400,
};
// ── Validation Patterns ─────────────────────────────────────────
const REGEX = {
SUBDOMAIN: /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i,
DOMAIN: /^[a-z0-9]([a-z0-9.-]{0,251}[a-z0-9])?$/i,
};
// ── DNS ─────────────────────────────────────────────────────────
const DNS_RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'NS', 'SRV', 'PTR', 'SOA'];
// ── Docker ──────────────────────────────────────────────────────
const DOCKER = {
CONTAINER_PREFIX: 'sami-',
TIMEOUT: 30000, // 30s — timeout for docker pull/create operations
LOG_CONFIG: {
Type: 'json-file',
Config: { 'max-size': '10m', 'max-file': '3' } // 30MB max per container
},
MAINTENANCE: {
INTERVAL: 24 * 60 * 60 * 1000, // 24 hours
DISK_WARN_GB: 20, // Warn when Docker uses more than 20GB
},
DIGEST: {
COLLECT_INTERVAL: 60 * 60 * 1000, // Hourly log collection
DIGEST_HOUR: 0, // Generate daily digest at midnight
MAX_HOURLY_ENTRIES: 24, // Keep 24 hours of hourly summaries
MAX_DIGEST_FILES: 30, // Keep 30 days of daily digests
LOG_TAIL: 500, // Lines to fetch per container per hour
},
};
// ── Emby/Jellyfin Auth Header Builder ─────────────────────────
function buildMediaAuth(deviceId) {
return `MediaBrowser Client="${APP.NAME}", Device="${APP.NAME}", DeviceId="${deviceId}", Version="${APP.VERSION}"`;
}
// ── Plex Auth Headers ─────────────────────────────────────────
const PLEX = {
AUTH_URL: 'https://plex.tv/users/sign_in.json',
};
// ── Tailscale API ────────────────────────────────────────────
const TAILSCALE = {
API_BASE: 'https://api.tailscale.com/api/v2',
OAUTH_TOKEN_URL: 'https://api.tailscale.com/api/v2/oauth/token',
};
// ── Error Log ─────────────────────────────────────────────────
const LIMITS = {
ERROR_LOG_SIZE: 5 * 1024 * 1024, // 5 MB
BODY_DEFAULT: '1mb',
BODY_UPLOAD: '10mb',
};
module.exports = {
APP,
TAILSCALE,
APP_PORTS,
ARR_SERVICES,
TIMEOUTS,
RETRIES,
SESSION_TTL,
RATE_LIMITS,
CADDY,
PLEX,
LIMITS,
REGEX,
DNS_RECORD_TYPES,
DOCKER,
buildMediaAuth,
};