Files
tiny-forge/web/.svelte-kit/output/server/chunks/index2.js
T
alexei.dolgolyov 670948f113 fix: address code review findings for DNS management
- CRITICAL: Change DNS zones endpoint from GET to POST to avoid
  leaking API token in URL query parameters
- HIGH: Add sync.RWMutex to protect dnsProvider field in Server,
  Deployer, and proxy Manager against concurrent read/write races
- HIGH: Capture old DNS provider reference synchronously before
  launching background cleanup goroutine
- HIGH: Use getDNS()/getDNSProviderLocked() accessors instead of
  direct field reads in all DNS operations
2026-04-02 14:54:15 +03:00

173 lines
44 KiB
JavaScript

import { w as writable, d as derived } from "./index3.js";
const app$1 = { "name": "Docker Watcher", "version": "v0.1" };
const health$1 = { "connected": "connected", "disconnected": "disconnected", "rawError": "Technical details", "retryNow": "Retry now" };
const nav$1 = { "dashboard": "Dashboard", "projects": "Projects", "deploy": "Deploy", "proxies": "Proxies", "events": "Events", "settings": "Settings", "logout": "Log out" };
const dashboard$1 = { "title": "Dashboard", "quickDeploy": "Quick Deploy", "totalProjects": "Total Projects", "runningInstances": "Running Instances", "failedInstances": "Failed Instances", "projects": "Projects", "retry": "Retry", "noProjects": "No projects yet.", "addFirst": "Add your first project", "loadFailed": "Failed to load dashboard", "staleContainers": "Stale Containers" };
const projects$1 = { "title": "Projects", "addProject": "Add Project", "cancel": "Cancel", "newProject": "New Project", "name": "Name", "image": "Image", "port": "Port", "registry": "Registry", "created": "Created", "view": "View", "noProjects": "No projects configured yet.", "getStarted": 'Click "Add Project" to get started.', "createProject": "Create Project", "creating": "Creating...", "healthcheck": "Healthcheck Path", "nameRequired": "Name and image are required.", "loadFailed": "Failed to load projects", "createFailed": "Failed to create project", "browseImages": "Browse Images", "selectImage": "Select an image", "noImages": "No images found", "loadingImages": "Loading images...", "imageLoadFailed": "Failed to load images" };
const projectDetail$1 = { "deleteProject": "Delete Project", "envVars": "Environment Variables", "volumes": "Volume Mounts", "stages": "Stages", "noStages": "No stages configured for this project.", "pattern": "Pattern", "autoDeploy": "auto-deploy", "requiresConfirm": "requires confirm", "instances": "instances", "deployNewVersion": "Deploy new version", "selectTag": "Select tag to deploy", "loadingTags": "Loading tags...", "chooseTag": "Choose a tag...", "enterTag": "Enter image tag (e.g., dev-abc123)", "deploy": "Deploy", "deploying": "Deploying...", "recentDeploys": "Recent Deploys", "noDeployHistory": "No deploy history for this project.", "tag": "Tag", "status": "Status", "started": "Started", "finished": "Finished", "error": "Error", "noInstancesRunning": "No instances running", "deleteConfirmTitle": "Delete Project", "deleteConfirmMessage": "This will permanently delete the project '{name}' and all its stages, instances, and deploy history. This cannot be undone.", "loadFailed": "Failed to load project", "deleteFailed": "Failed to delete project", "deployFailed": "Deploy failed" };
const envEditor$1 = { "title": "Environment Variables", "description": "Manage per-stage environment variable overrides. Stage-level values override project-level defaults.", "stage": "Stage", "projectDefaults": "Project-Level Defaults", "stageOverrides": "Stage Overrides", "key": "Key", "value": "Value", "secret": "Secret", "source": "Source", "actions": "Actions", "overridden": "overridden", "inherited": "inherited", "overridesProject": "overrides project", "stageOnly": "stage only", "edit": "Edit", "change": "Change", "delete": "Delete", "save": "Save", "add": "Add", "adding": "Adding...", "noStages": "No stages configured. Add stages to the project first.", "loadFailed": "Failed to load project", "envAdded": "Environment variable added", "envUpdated": "Environment variable updated", "envDeleted": "Environment variable deleted", "addFailed": "Failed to add env var", "updateFailed": "Failed to update env var", "deleteFailed": "Failed to delete env var", "loadEnvFailed": "Failed to load env vars" };
const volumeEditor$1 = { "title": "Volume Mounts", "description": "Configure volume mounts for containers. Choose a scope to control how volumes are shared between deploys.", "sourceHost": "Source (Host)", "targetContainer": "Target (Container)", "scope": "Scope", "nameColumn": "Name", "namePlaceholder": "e.g. shared-db", "requiresName": "requires name", "noHostPath": "no host path", "tmpfs": "tmpfs (in-memory)", "actions": "Actions", "edit": "Edit", "delete": "Delete", "save": "Save", "add": "Add", "adding": "Adding...", "noVolumes": "No volumes configured yet. Add one above.", "volumeAdded": "Volume added", "volumeUpdated": "Volume updated", "volumeDeleted": "Volume deleted", "loadFailed": "Failed to load volumes", "addFailed": "Failed to add volume", "updateFailed": "Failed to update volume", "deleteFailed": "Failed to delete volume" };
const volumeBrowser$1 = { "title": "Volume Browser", "loadFailed": "Failed to load directory", "empty": "This directory is empty.", "name": "Name", "size": "Size", "modified": "Modified", "downloadAll": "Download volume as ZIP", "downloadFolder": "Download folder as ZIP", "upload": "Upload files", "uploaded": "Uploaded", "files": "file(s)", "uploadFailed": "Failed to upload files", "browse": "Browse", "download": "Download" };
const quickDeploy$1 = { "title": "Quick Deploy", "description": "Deploy a container image with zero configuration. Paste an image URL, review the defaults, and deploy.", "step1": "1. Enter Image URL", "imageUrl": "Image URL", "imageUrlHelp": "Full image URL including tag (e.g., git.example.com/user/app:dev-abc123)", "inspect": "Inspect", "inspecting": "Inspecting...", "step2": "2. Review Configuration", "reviewDesc": "These defaults were detected from the image. Adjust as needed before deploying.", "projectName": "Project Name", "port": "Port", "portHelp": "Container port to expose (1-65535)", "healthCheckPath": "Health Check Path", "healthCheckHelp": "Optional HTTP path for health verification", "stage": "Stage", "development": "Development", "release": "Release", "production": "Production", "stageHelp": "Deployment stage for this image", "subdomainOverride": "Subdomain Override", "subdomainHelp": "Leave empty to use the default subdomain pattern", "envVars": "Environment Variables", "envVarsHelp": "One per line, KEY=VALUE format", "step3": "3. Deploy", "deployDesc": "A new project will be created and the container will be deployed immediately.", "deployBtn": "Deploy", "inspectedSuccess": "Image inspected successfully", "deployedSuccess": "Deployed {name} successfully!", "inspectFailed": "Failed to inspect image", "deployFailed": "Deployment failed", "browseImages": "Browse", "selectImage": "Select an image from a registry", "noImages": "No images found", "loadingImages": "Loading...", "imageLoadFailed": "Failed to load images" };
const settings$1 = { "title": "Settings", "general": "General", "registries": "Registries", "credentials": "Credentials", "authentication": "Authentication", "appearance": "Appearance", "staleThreshold": "Stale threshold (days)", "staleThresholdHelp": "Containers inactive for longer than this will be flagged as stale." };
const settingsGeneral$1 = { "title": "General Settings", "globalConfig": "Global Configuration", "domain": "Domain", "domainHelp": "Base domain for subdomain routing", "serverIp": "Server IP", "serverIpHelp": "Public IP address of the server", "dockerNetwork": "Docker Network", "dockerNetworkHelp": "Docker network for deployed containers", "subdomainPattern": "Subdomain Pattern", "subdomainPatternHelp": "Pattern for auto-generated subdomains", "pollingInterval": "Polling Interval (seconds)", "pollingIntervalHelp": "How often to check registries for new tags (10-86400)", "notificationUrl": "Notification URL", "notificationUrlHelp": "Webhook URL for deploy notifications", "saveSettings": "Save Settings", "saving": "Saving...", "saved": "Settings saved successfully", "saveFailed": "Failed to save settings", "loadFailed": "Failed to load settings", "webhookUrl": "Webhook URL", "webhookDesc": "This secret URL receives image push notifications from your CI pipeline.", "noWebhookUrl": "No webhook URL configured", "copy": "Copy", "copied": "Webhook URL copied to clipboard", "regenerateUrl": "Regenerate URL", "regenerating": "Regenerating...", "regenerated": "Webhook URL regenerated", "regenerateFailed": "Failed to regenerate webhook URL", "regenerateWarning": "Warning: regenerating will invalidate the current URL. Update your CI pipelines.", "sslCertificate": "SSL Certificate", "sslCertificateHelp": "Wildcard certificate from NPM for auto-SSL on proxy hosts", "selectCertificate": "Select Certificate", "noCertificate": "None (no SSL)", "clearCertificate": "Clear", "loadingCertificates": "Loading certificates...", "noCertificatesFound": "No wildcard certificates found in NPM" };
const settingsRegistries$1 = { "title": "Container Registries", "description": "Manage your container registries for image detection.", "addRegistry": "Add Registry", "editRegistry": "Edit Registry", "addNewRegistry": "Add New Registry", "name": "Name", "nameHelp": "A friendly name for this registry", "url": "URL", "urlHelp": "Registry base URL", "type": "Type", "typeHelp": "Registry type for API compatibility", "token": "Token", "tokenHelpNew": "API token for authentication", "tokenHelpEdit": "Leave empty to keep the existing token", "owner": "Owner", "ownerHelp": "Package owners, comma-separated (e.g., alexei,my-org)", "save": "Save", "saving": "Saving...", "update": "Update", "test": "Test", "testing": "Testing...", "edit": "Edit", "delete": "Delete", "noRegistries": "No registries configured yet.", "addFirst": "Add your first registry", "registryUpdated": "Registry updated", "registryAdded": "Registry added", "registryDeleted": 'Registry "{name}" deleted', "testSuccess": 'Connection to "{name}" successful', "saveFailed": "Failed to save registry", "deleteFailed": "Failed to delete registry", "testFailed": "Connection test failed", "loadFailed": "Failed to load registries", "deleteConfirm": 'Delete registry "{name}"? This cannot be undone.' };
const settingsCredentials$1 = { "title": "Credentials", "description": "Manage credentials for Nginx Proxy Manager and registry tokens. All values are encrypted at rest.", "npm": "Nginx Proxy Manager", "npmDesc": "Credentials for managing proxy hosts via NPM API", "configured": "Configured", "npmUrl": "NPM URL", "npmUrlHelp": "Nginx Proxy Manager API URL", "email": "Email", "emailHelp": "NPM admin email", "password": "Password", "passwordHelpNew": "NPM admin password (will be encrypted)", "passwordHelpEdit": "Enter the new password to replace the existing one", "changeCredentials": "Change Credentials", "save": "Save", "saving": "Saving...", "saved": "NPM credentials saved", "saveFailed": "Failed to save NPM credentials", "loadFailed": "Failed to load credentials", "registryTokens": "Registry Tokens", "registryTokensDesc": "Registry authentication tokens are managed per-registry in the", "registriesLink": "Registries", "registryTokensSuffix": "section. Each registry stores its token encrypted in the database." };
const settingsAuth$1 = { "title": "Authentication Settings", "description": "Configure authentication mode and manage users.", "authMode": "Authentication Mode", "local": "Local (username/password)", "oidc": "OIDC (SSO)", "oidcConfig": "OIDC Provider Configuration", "issuerUrl": "Issuer URL", "clientId": "Client ID", "clientSecret": "Client Secret", "redirectUrl": "Redirect URL", "saveSettings": "Save Settings", "saving": "Saving...", "saved": "Settings saved", "saveFailed": "Failed to save", "loadFailed": "Failed to load settings", "localUsers": "Local Users", "username": "Username", "email": "Email", "role": "Role", "created": "Created", "noUsers": "No users found.", "addUser": "Add User", "viewer": "Viewer", "admin": "Admin", "userCreated": "User created", "userDeleted": "User deleted", "createFailed": "Failed to create user", "deleteFailed": "Failed to delete user", "deleteConfirm": "Are you sure you want to delete this user?", "usernameRequired": "Username and password are required", "password": "Password" };
const login$1 = { "title": "Docker Watcher", "subtitle": "Sign in to your account", "username": "Username", "password": "Password", "signIn": "Sign in", "signingIn": "Signing in...", "or": "or", "ssoButton": "Sign in with SSO (OIDC)", "loginFailed": "Login failed", "networkError": "Network error" };
const proxies$1 = { "title": "Proxies", "create": "Create Proxy", "noProxies": "No proxies configured yet.", "noProxiesDesc": "Create a standalone proxy or deploy a project to see proxies here.", "standalone": "Standalone Proxies", "managed": "Managed", "lastChecked": "Last checked", "health": { "healthy": "Healthy", "unhealthy": "Unhealthy", "unknown": "Unknown" }, "filter": { "search": "Search proxies...", "health": "Health", "type": "Type", "all": "All", "clear": "Clear filters" }, "form": { "title": "Create Proxy", "editTitle": "Edit Proxy", "destination": "Destination URL / IP", "port": "Port", "domain": "Domain", "domainHelp": "The public domain for this proxy.", "validate": "Validate", "validating": "Validating...", "create": "Create Proxy", "save": "Save Changes", "cancel": "Cancel", "delete": "Delete", "deleteConfirm": "Delete this proxy? This cannot be undone." }, "validation": { "title": "Destination Validation", "syntax": "URL syntax", "dns": "DNS resolution", "tcp": "TCP connection", "http": "HTTP response", "checking": "Checking...", "skipped": "Skipped" } };
const common$1 = { "cancel": "Cancel", "confirm": "Confirm", "delete": "Delete", "edit": "Edit", "save": "Save", "retry": "Retry", "loading": "Loading...", "noData": "No data", "project": "Project", "back": "Back", "actions": "Actions", "stop": "Stop", "start": "Start", "restart": "Restart", "remove": "Remove", "instance": "instance", "instances": "instances" };
const instance$1 = { "stopConfirm": "This will stop the running container. The instance can be started again later.", "restartConfirm": "This will restart the container, causing brief downtime.", "removeConfirm": "This will permanently remove the container and its proxy configuration. This cannot be undone.", "actionFailed": "Action failed" };
const empty$1 = { "noProjects": "No projects yet", "noProjectsDesc": "Get started by creating your first project or use Quick Deploy.", "createProject": "Create Project", "noInstances": "No instances", "noInstancesDesc": "Deploy a new version to see instances here.", "noDeploys": "No deploy history", "noDeploysDesc": "Deploy history will appear here after your first deployment.", "noRegistries": "No registries", "noRegistriesDesc": "Add a container registry to enable image detection.", "noVolumes": "No volumes", "noVolumesDesc": "Configure volume mounts for persistent data.", "noUsers": "No users", "noUsersDesc": "Add local users to manage access." };
const validation$1 = { "required": "{field} is required", "invalidUrl": "Invalid URL format", "invalidDomain": "Invalid domain format", "invalidIp": "Invalid IP format", "invalidEmail": "Invalid email format", "invalidPort": "Port must be between 1 and 65535", "invalidPollingInterval": "Polling interval must be between 10 and 86400 seconds", "invalidProjectName": "Only lowercase letters, numbers, and hyphens allowed", "requiredWhenUpdating": "{field} is required when updating credentials", "requiredForNew": "{field} is required for new registries" };
const confirm$1 = { "stopInstance": "Stop Instance", "startInstance": "Start Instance", "restartInstance": "Restart Instance", "removeInstance": "Remove Instance" };
const theme$1 = { "light": "Light", "dark": "Dark", "system": "System" };
const entityPicker$1 = { "search": "Search...", "noResults": "No results found" };
const stale$1 = { "title": "Stale Containers", "noStale": "No stale containers", "noStaleDesc": "All containers are healthy and running.", "cleanup": "Clean up", "cleanupAll": "Clean up all", "confirmCleanup": "This will stop and remove the container. Continue?", "confirmBulkCleanup": "This will stop and remove all stale containers. Continue?", "daysStale": "days stale", "lastAlive": "Last alive", "count": "Stale", "cleanedUp": "Container cleaned up", "bulkCleanedUp": "{count} containers cleaned up", "cleanupFailed": "Cleanup failed", "loadFailed": "Failed to load stale containers" };
const events$1 = { "title": "Event Log", "noEvents": "No events found", "noEventsDesc": "Events will appear here as they occur.", "loadMore": "Load more", "newEvents": "new events", "filter": { "severity": "Severity", "source": "Source", "dateRange": "Date range", "search": "Search events...", "lastHour": "Last hour", "last24h": "Last 24 hours", "last7d": "Last 7 days", "allTime": "All time", "clear": "Clear filters" }, "severity": { "info": "Info", "warn": "Warning", "error": "Error" }, "source": { "deploy": "Deploy", "container": "Container", "proxy": "Proxy", "system": "System" }, "metadata": "Details" };
const stats$1 = { "cpu": "CPU", "mem": "MEM", "unavailable": "Stats unavailable" };
const systemHealth$1 = { "title": "System Health", "containers": "Containers", "proxies": "Proxies", "recentErrors": "Recent Errors" };
const language$1 = { "en": "English", "ru": "Russian" };
const en = {
app: app$1,
health: health$1,
nav: nav$1,
dashboard: dashboard$1,
projects: projects$1,
projectDetail: projectDetail$1,
envEditor: envEditor$1,
volumeEditor: volumeEditor$1,
volumeBrowser: volumeBrowser$1,
quickDeploy: quickDeploy$1,
settings: settings$1,
settingsGeneral: settingsGeneral$1,
settingsRegistries: settingsRegistries$1,
settingsCredentials: settingsCredentials$1,
settingsAuth: settingsAuth$1,
login: login$1,
proxies: proxies$1,
common: common$1,
instance: instance$1,
empty: empty$1,
validation: validation$1,
confirm: confirm$1,
theme: theme$1,
entityPicker: entityPicker$1,
stale: stale$1,
events: events$1,
stats: stats$1,
systemHealth: systemHealth$1,
language: language$1
};
const app = { "name": "Docker Watcher", "version": "v0.1" };
const health = { "connected": "подключён", "disconnected": "отключён", "rawError": "Технические детали", "retryNow": "Проверить сейчас" };
const nav = { "dashboard": "Панель", "projects": "Проекты", "deploy": "Деплой", "proxies": "Прокси", "events": "События", "settings": "Настройки", "logout": "Выйти" };
const dashboard = { "title": "Панель управления", "quickDeploy": "Быстрый деплой", "totalProjects": "Всего проектов", "runningInstances": "Запущенных экземпляров", "failedInstances": "Сбойных экземпляров", "projects": "Проекты", "retry": "Повторить", "noProjects": "Проектов пока нет.", "addFirst": "Добавьте первый проект", "loadFailed": "Не удалось загрузить панель", "staleContainers": "Устаревшие контейнеры" };
const projects = { "title": "Проекты", "addProject": "Добавить проект", "cancel": "Отмена", "newProject": "Новый проект", "name": "Название", "image": "Образ", "port": "Порт", "registry": "Реестр", "created": "Создан", "view": "Открыть", "noProjects": "Проекты ещё не настроены.", "getStarted": "Нажмите «Добавить проект» для начала.", "createProject": "Создать проект", "creating": "Создание...", "healthcheck": "Путь проверки здоровья", "nameRequired": "Название и образ обязательны.", "loadFailed": "Не удалось загрузить проекты", "createFailed": "Не удалось создать проект", "browseImages": "Обзор образов", "selectImage": "Выберите образ", "noImages": "Образы не найдены", "loadingImages": "Загрузка образов...", "imageLoadFailed": "Не удалось загрузить образы" };
const projectDetail = { "deleteProject": "Удалить проект", "envVars": "Переменные окружения", "volumes": "Тома", "stages": "Стадии", "noStages": "Для этого проекта не настроены стадии.", "pattern": "Шаблон", "autoDeploy": "авто-деплой", "requiresConfirm": "нужно подтверждение", "instances": "экземпляров", "deployNewVersion": "Развернуть новую версию", "selectTag": "Выберите тег для деплоя", "loadingTags": "Загрузка тегов...", "chooseTag": "Выберите тег...", "enterTag": "Введите тег образа (напр., dev-abc123)", "deploy": "Развернуть", "deploying": "Развёртывание...", "recentDeploys": "Последние деплои", "noDeployHistory": "Нет истории деплоев для этого проекта.", "tag": "Тег", "status": "Статус", "started": "Начат", "finished": "Завершён", "error": "Ошибка", "noInstancesRunning": "Нет запущенных экземпляров", "deleteConfirmTitle": "Удалить проект", "deleteConfirmMessage": "Это безвозвратно удалит проект '{name}' и все его стадии, экземпляры и историю деплоев.", "loadFailed": "Не удалось загрузить проект", "deleteFailed": "Не удалось удалить проект", "deployFailed": "Деплой не удался" };
const envEditor = { "title": "Переменные окружения", "description": "Управление переопределениями переменных окружения на уровне стадий. Значения стадий переопределяют значения проекта.", "stage": "Стадия", "projectDefaults": "Значения проекта по умолчанию", "stageOverrides": "Переопределения стадии", "key": "Ключ", "value": "Значение", "secret": "Секрет", "source": "Источник", "actions": "Действия", "overridden": "переопределено", "inherited": "наследуется", "overridesProject": "переопределяет проект", "stageOnly": "только стадия", "edit": "Изменить", "change": "Изменить", "delete": "Удалить", "save": "Сохранить", "add": "Добавить", "adding": "Добавление...", "noStages": "Стадии не настроены. Сначала добавьте стадии к проекту.", "loadFailed": "Не удалось загрузить проект", "envAdded": "Переменная окружения добавлена", "envUpdated": "Переменная окружения обновлена", "envDeleted": "Переменная окружения удалена", "addFailed": "Не удалось добавить переменную", "updateFailed": "Не удалось обновить переменную", "deleteFailed": "Не удалось удалить переменную", "loadEnvFailed": "Не удалось загрузить переменные" };
const volumeEditor = { "title": "Тома", "description": "Настройка монтирования томов для контейнеров. Выберите область видимости для управления общим доступом между развёртываниями.", "sourceHost": "Источник (хост)", "targetContainer": "Цель (контейнер)", "scope": "Область", "nameColumn": "Имя", "namePlaceholder": "напр. shared-db", "requiresName": "требуется имя", "noHostPath": "нет пути на хосте", "tmpfs": "tmpfs (в памяти)", "actions": "Действия", "edit": "Изменить", "delete": "Удалить", "save": "Сохранить", "add": "Добавить", "adding": "Добавление...", "noVolumes": "Тома ещё не настроены. Добавьте один выше.", "volumeAdded": "Том добавлен", "volumeUpdated": "Том обновлён", "volumeDeleted": "Том удалён", "loadFailed": "Не удалось загрузить тома", "addFailed": "Не удалось добавить том", "updateFailed": "Не удалось обновить том", "deleteFailed": "Не удалось удалить том" };
const volumeBrowser = { "title": "Обзор тома", "loadFailed": "Не удалось загрузить каталог", "empty": "Этот каталог пуст.", "name": "Имя", "size": "Размер", "modified": "Изменён", "downloadAll": "Скачать том как ZIP", "downloadFolder": "Скачать папку как ZIP", "upload": "Загрузить файлы", "uploaded": "Загружено", "files": "файл(ов)", "uploadFailed": "Не удалось загрузить файлы", "browse": "Обзор", "download": "Скачать" };
const quickDeploy = { "title": "Быстрый деплой", "description": "Разверните образ контейнера без настройки. Вставьте URL образа, проверьте параметры и разверните.", "step1": "1. Введите URL образа", "imageUrl": "URL образа", "imageUrlHelp": "Полный URL образа с тегом (напр., git.example.com/user/app:dev-abc123)", "inspect": "Проверить", "inspecting": "Проверка...", "step2": "2. Проверка конфигурации", "reviewDesc": "Эти параметры были обнаружены из образа. Измените при необходимости перед деплоем.", "projectName": "Имя проекта", "port": "Порт", "portHelp": "Порт контейнера (1-65535)", "healthCheckPath": "Путь проверки здоровья", "healthCheckHelp": "Необязательный HTTP-путь для проверки работоспособности", "stage": "Стадия", "development": "Разработка", "release": "Релиз", "production": "Продакшн", "stageHelp": "Стадия развёртывания для этого образа", "subdomainOverride": "Переопределение поддомена", "subdomainHelp": "Оставьте пустым для использования шаблона по умолчанию", "envVars": "Переменные окружения", "envVarsHelp": "По одной на строку, формат KEY=VALUE", "step3": "3. Развёртывание", "deployDesc": "Будет создан новый проект и контейнер будет развёрнут немедленно.", "deployBtn": "Развернуть", "inspectedSuccess": "Образ успешно проверен", "deployedSuccess": "{name} успешно развёрнут!", "inspectFailed": "Не удалось проверить образ", "deployFailed": "Развёртывание не удалось", "browseImages": "Обзор", "selectImage": "Выберите образ из реестра", "noImages": "Образы не найдены", "loadingImages": "Загрузка...", "imageLoadFailed": "Не удалось загрузить образы" };
const settings = { "title": "Настройки", "general": "Общие", "registries": "Реестры", "credentials": "Учётные данные", "authentication": "Аутентификация", "appearance": "Внешний вид", "staleThreshold": "Порог устаревания (дни)", "staleThresholdHelp": "Контейнеры, неактивные дольше этого срока, будут помечены как устаревшие." };
const settingsGeneral = { "title": "Общие настройки", "globalConfig": "Глобальная конфигурация", "domain": "Домен", "domainHelp": "Базовый домен для маршрутизации поддоменов", "serverIp": "IP сервера", "serverIpHelp": "Публичный IP-адрес сервера", "dockerNetwork": "Docker-сеть", "dockerNetworkHelp": "Docker-сеть для развёрнутых контейнеров", "subdomainPattern": "Шаблон поддомена", "subdomainPatternHelp": "Шаблон для автоматически генерируемых поддоменов", "pollingInterval": "Интервал опроса (секунды)", "pollingIntervalHelp": "Как часто проверять реестры на новые теги (10-86400)", "notificationUrl": "URL уведомлений", "notificationUrlHelp": "URL вебхука для уведомлений о деплоях", "saveSettings": "Сохранить настройки", "saving": "Сохранение...", "saved": "Настройки успешно сохранены", "saveFailed": "Не удалось сохранить настройки", "loadFailed": "Не удалось загрузить настройки", "webhookUrl": "URL вебхука", "webhookDesc": "Этот секретный URL получает уведомления о push-событиях из вашего CI-пайплайна.", "noWebhookUrl": "URL вебхука не настроен", "copy": "Копировать", "copied": "URL вебхука скопирован в буфер обмена", "regenerateUrl": "Перегенерировать URL", "regenerating": "Перегенерация...", "regenerated": "URL вебхука перегенерирован", "regenerateFailed": "Не удалось перегенерировать URL вебхука", "regenerateWarning": "Внимание: перегенерация сделает текущий URL недействительным. Обновите ваши CI-пайплайны.", "sslCertificate": "SSL-сертификат", "sslCertificateHelp": "Wildcard-сертификат из NPM для автоматического SSL на прокси-хостах", "selectCertificate": "Выбрать сертификат", "noCertificate": "Нет (без SSL)", "clearCertificate": "Очистить", "loadingCertificates": "Загрузка сертификатов...", "noCertificatesFound": "Wildcard-сертификаты в NPM не найдены" };
const settingsRegistries = { "title": "Реестры контейнеров", "description": "Управление реестрами контейнеров для обнаружения образов.", "addRegistry": "Добавить реестр", "editRegistry": "Редактировать реестр", "addNewRegistry": "Добавить новый реестр", "name": "Название", "nameHelp": "Понятное название для этого реестра", "url": "URL", "urlHelp": "Базовый URL реестра", "type": "Тип", "typeHelp": "Тип реестра для совместимости API", "token": "Токен", "tokenHelpNew": "API-токен для аутентификации", "tokenHelpEdit": "Оставьте пустым, чтобы сохранить текущий токен", "owner": "Владелец", "ownerHelp": "Владельцы пакетов через запятую (напр., alexei,my-org)", "save": "Сохранить", "saving": "Сохранение...", "update": "Обновить", "test": "Тест", "testing": "Тестирование...", "edit": "Изменить", "delete": "Удалить", "noRegistries": "Реестры ещё не настроены.", "addFirst": "Добавьте первый реестр", "registryUpdated": "Реестр обновлён", "registryAdded": "Реестр добавлен", "registryDeleted": "Реестр «{name}» удалён", "testSuccess": "Подключение к «{name}» успешно", "saveFailed": "Не удалось сохранить реестр", "deleteFailed": "Не удалось удалить реестр", "testFailed": "Тест подключения не удался", "loadFailed": "Не удалось загрузить реестры", "deleteConfirm": "Удалить реестр «{name}»? Это действие необратимо." };
const settingsCredentials = { "title": "Учётные данные", "description": "Управление учётными данными для Nginx Proxy Manager и токенами реестров. Все значения зашифрованы.", "npm": "Nginx Proxy Manager", "npmDesc": "Учётные данные для управления прокси-хостами через NPM API", "configured": "Настроено", "npmUrl": "URL NPM", "npmUrlHelp": "URL API Nginx Proxy Manager", "email": "Email", "emailHelp": "Email администратора NPM", "password": "Пароль", "passwordHelpNew": "Пароль администратора NPM (будет зашифрован)", "passwordHelpEdit": "Введите новый пароль для замены текущего", "changeCredentials": "Изменить учётные данные", "save": "Сохранить", "saving": "Сохранение...", "saved": "Учётные данные NPM сохранены", "saveFailed": "Не удалось сохранить учётные данные NPM", "loadFailed": "Не удалось загрузить учётные данные", "registryTokens": "Токены реестров", "registryTokensDesc": "Токены аутентификации реестров управляются для каждого реестра в разделе", "registriesLink": "Реестры", "registryTokensSuffix": ". Каждый реестр хранит свой токен в зашифрованном виде." };
const settingsAuth = { "title": "Настройки аутентификации", "description": "Настройка режима аутентификации и управление пользователями.", "authMode": "Режим аутентификации", "local": "Локальный (логин/пароль)", "oidc": "OIDC (SSO)", "oidcConfig": "Конфигурация OIDC-провайдера", "issuerUrl": "URL издателя", "clientId": "ID клиента", "clientSecret": "Секрет клиента", "redirectUrl": "URL перенаправления", "saveSettings": "Сохранить настройки", "saving": "Сохранение...", "saved": "Настройки сохранены", "saveFailed": "Не удалось сохранить", "loadFailed": "Не удалось загрузить настройки", "localUsers": "Локальные пользователи", "username": "Имя пользователя", "email": "Email", "role": "Роль", "created": "Создан", "noUsers": "Пользователи не найдены.", "addUser": "Добавить пользователя", "viewer": "Наблюдатель", "admin": "Администратор", "userCreated": "Пользователь создан", "userDeleted": "Пользователь удалён", "createFailed": "Не удалось создать пользователя", "deleteFailed": "Не удалось удалить пользователя", "deleteConfirm": "Вы уверены, что хотите удалить этого пользователя?", "usernameRequired": "Имя пользователя и пароль обязательны", "password": "Пароль" };
const login = { "title": "Docker Watcher", "subtitle": "Войдите в свой аккаунт", "username": "Имя пользователя", "password": "Пароль", "signIn": "Войти", "signingIn": "Вход...", "or": "или", "ssoButton": "Войти через SSO (OIDC)", "loginFailed": "Ошибка входа", "networkError": "Ошибка сети" };
const proxies = { "title": "Прокси", "create": "Создать прокси", "noProxies": "Прокси ещё не настроены.", "noProxiesDesc": "Создайте автономный прокси или разверните проект, чтобы увидеть прокси здесь.", "standalone": "Автономные прокси", "managed": "Управляемые", "lastChecked": "Последняя проверка", "health": { "healthy": "Работает", "unhealthy": "Недоступен", "unknown": "Неизвестно" }, "filter": { "search": "Поиск прокси...", "health": "Здоровье", "type": "Тип", "all": "Все", "clear": "Сбросить фильтры" }, "form": { "title": "Создать прокси", "editTitle": "Редактировать прокси", "destination": "URL / IP назначения", "port": "Порт", "domain": "Домен", "domainHelp": "Публичный домен для этого прокси.", "validate": "Проверить", "validating": "Проверка...", "create": "Создать прокси", "save": "Сохранить изменения", "cancel": "Отмена", "delete": "Удалить", "deleteConfirm": "Удалить этот прокси? Это действие необратимо." }, "validation": { "title": "Проверка назначения", "syntax": "Синтаксис URL", "dns": "DNS разрешение", "tcp": "TCP подключение", "http": "HTTP ответ", "checking": "Проверка...", "skipped": "Пропущено" } };
const common = { "cancel": "Отмена", "confirm": "Подтвердить", "delete": "Удалить", "edit": "Изменить", "save": "Сохранить", "retry": "Повторить", "loading": "Загрузка...", "noData": "Нет данных", "project": "Проект", "back": "Назад", "actions": "Действия", "stop": "Остановить", "start": "Запустить", "restart": "Перезапустить", "remove": "Удалить", "instance": "экземпляр", "instances": "экземпляров" };
const instance = { "stopConfirm": "Контейнер будет остановлен. Экземпляр можно будет запустить снова позже.", "restartConfirm": "Контейнер будет перезапущен с кратковременным простоем.", "removeConfirm": "Контейнер и его прокси-конфигурация будут безвозвратно удалены.", "actionFailed": "Действие не удалось" };
const empty = { "noProjects": "Проектов пока нет", "noProjectsDesc": "Начните с создания первого проекта или используйте быстрый деплой.", "createProject": "Создать проект", "noInstances": "Нет экземпляров", "noInstancesDesc": "Разверните новую версию, чтобы увидеть экземпляры здесь.", "noDeploys": "Нет истории деплоев", "noDeploysDesc": "История деплоев появится здесь после первого развёртывания.", "noRegistries": "Нет реестров", "noRegistriesDesc": "Добавьте реестр контейнеров для обнаружения образов.", "noVolumes": "Нет томов", "noVolumesDesc": "Настройте монтирование томов для постоянных данных.", "noUsers": "Нет пользователей", "noUsersDesc": "Добавьте локальных пользователей для управления доступом." };
const validation = { "required": "Поле {field} обязательно", "invalidUrl": "Неверный формат URL", "invalidDomain": "Неверный формат домена", "invalidIp": "Неверный формат IP", "invalidEmail": "Неверный формат email", "invalidPort": "Порт должен быть от 1 до 65535", "invalidPollingInterval": "Интервал опроса должен быть от 10 до 86400 секунд", "invalidProjectName": "Допускаются только строчные буквы, цифры и дефисы", "requiredWhenUpdating": "Поле {field} обязательно при обновлении учётных данных", "requiredForNew": "Поле {field} обязательно для новых реестров" };
const confirm = { "stopInstance": "Остановить экземпляр", "startInstance": "Запустить экземпляр", "restartInstance": "Перезапустить экземпляр", "removeInstance": "Удалить экземпляр" };
const theme = { "light": "Светлая", "dark": "Тёмная", "system": "Системная" };
const entityPicker = { "search": "Поиск...", "noResults": "Ничего не найдено" };
const stale = { "title": "Устаревшие контейнеры", "noStale": "Нет устаревших контейнеров", "noStaleDesc": "Все контейнеры исправны и работают.", "cleanup": "Очистить", "cleanupAll": "Очистить все", "confirmCleanup": "Это остановит и удалит контейнер. Продолжить?", "confirmBulkCleanup": "Это остановит и удалит все устаревшие контейнеры. Продолжить?", "daysStale": "дней устарел", "lastAlive": "Последний раз жив", "count": "Устаревшие", "cleanedUp": "Контейнер очищен", "bulkCleanedUp": "{count} контейнеров очищено", "cleanupFailed": "Не удалось очистить", "loadFailed": "Не удалось загрузить устаревшие контейнеры" };
const events = { "title": "Журнал событий", "noEvents": "Событий не найдено", "noEventsDesc": "События будут отображаться здесь по мере их возникновения.", "loadMore": "Загрузить ещё", "newEvents": "новых событий", "filter": { "severity": "Уровень", "source": "Источник", "dateRange": "Период", "search": "Поиск событий...", "lastHour": "Последний час", "last24h": "Последние 24 часа", "last7d": "Последние 7 дней", "allTime": "За всё время", "clear": "Сбросить фильтры" }, "severity": { "info": "Инфо", "warn": "Предупреждение", "error": "Ошибка" }, "source": { "deploy": "Развёртывание", "container": "Контейнер", "proxy": "Прокси", "system": "Система" }, "metadata": "Подробности" };
const stats = { "cpu": "ЦП", "mem": "ОЗУ", "unavailable": "Статистика недоступна" };
const systemHealth = { "title": "Состояние системы", "containers": "Контейнеры", "proxies": "Прокси", "recentErrors": "Недавние ошибки" };
const language = { "en": "Английский", "ru": "Русский" };
const ru = {
app,
health,
nav,
dashboard,
projects,
projectDetail,
envEditor,
volumeEditor,
volumeBrowser,
quickDeploy,
settings,
settingsGeneral,
settingsRegistries,
settingsCredentials,
settingsAuth,
login,
proxies,
common,
instance,
empty,
validation,
confirm,
theme,
entityPicker,
stale,
events,
stats,
systemHealth,
language
};
const LOCALE_KEY = "dw_locale";
const translations = { en, ru };
function getInitialLocale() {
if (typeof localStorage !== "undefined") {
const stored = localStorage.getItem(LOCALE_KEY);
if (stored === "en" || stored === "ru") return stored;
}
if (typeof navigator !== "undefined") {
const lang = navigator.language.toLowerCase();
if (lang.startsWith("ru")) return "ru";
}
return "en";
}
const locale = writable(getInitialLocale());
locale.subscribe((value) => {
if (typeof localStorage !== "undefined") {
localStorage.setItem(LOCALE_KEY, value);
}
});
function getNestedValue(obj, path) {
const parts = path.split(".");
let current = obj;
for (const part of parts) {
if (current === null || current === void 0 || typeof current !== "object") {
return path;
}
current = current[part];
}
return typeof current === "string" ? current : path;
}
const t = derived(locale, ($locale) => {
const dict = translations[$locale] ?? translations.en;
return (key, params) => {
let result = getNestedValue(dict, key);
if (result === key) {
result = getNestedValue(translations.en, key);
}
if (params) {
for (const [paramKey, paramValue] of Object.entries(params)) {
result = result.replace(new RegExp(`\\{${paramKey}\\}`, "g"), paramValue);
}
}
return result;
};
});
const availableLocales = ["en", "ru"];
export {
availableLocales as a,
locale as l,
t
};