diff --git a/Dockerfile b/Dockerfile index 48e5d2f3..1d9a4931 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,12 +7,10 @@ WORKDIR /app COPY --link package.json pnpm-lock.yaml* ./ -RUN < config/settings.yaml - NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION npm run build -EOF +SHELL ["/bin/ash", "-xeo", "pipefail", "-c"] +RUN npm run telemetry \ + && mkdir config && echo '---' > config/settings.yaml \ + && NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION npm run build # Production image, copy all the files and run next FROM docker.io/node:18-alpine AS runner @@ -50,12 +46,15 @@ ENV NODE_ENV production WORKDIR /app # Copy files from context (this allows the files to copy before the builder stage is done). -COPY --link package.json next.config.js ./ -COPY --link /public ./public +COPY --link --chown=1000:1000 package.json next.config.js ./ +COPY --link --chown=1000:1000 /public ./public/ # Copy files from builder -COPY --link --from=builder /app/.next/standalone ./ -COPY --link --from=builder /app/.next/static/ ./.next/static/ +COPY --link --from=builder --chown=1000:1000 /app/.next/standalone ./ +COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static/ +COPY --link --chmod=755 docker-entrypoint.sh /usr/local/bin/ + +RUN apk add --no-cache su-exec ENV PORT 3000 EXPOSE $PORT @@ -63,4 +62,5 @@ EXPOSE $PORT HEALTHCHECK --interval=10s --timeout=3s --start-period=20s \ CMD wget --no-verbose --tries=1 --spider --no-check-certificate http://localhost:$PORT/api/healthcheck || exit 1 +ENTRYPOINT ["docker-entrypoint.sh"] CMD ["node", "server.js"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 5603d7e5..bf443461 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -2,8 +2,22 @@ set -e +# Default to root, so old installations won't break +export PUID=${PUID:-0} +export PGID=${PGID:-0} + # This is in attempt to preserve the original behavior of the Dockerfile, # while also supporting the lscr.io /config directory [ ! -d "/app/config" ] && ln -s /config /app/config -node server.js +# Set privileges for /app but only if pid 1 user is root and we are dropping privileges. +# If container is run as an unprivileged user, it means owner already handled ownership setup on their own. +# Running chown in that case (as non-root) will cause error +[ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ] && chown -R ${PUID}:${PGID} /app + +# Drop privileges (when asked to) if root, otherwise run as current user +if [ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ]; then + su-exec ${PUID}:${PGID} "$@" +else + exec "$@" +fi diff --git a/public/locales/ar/common.json b/public/locales/ar/common.json index 1c69a469..f0a8711d 100644 --- a/public/locales/ar/common.json +++ b/public/locales/ar/common.json @@ -3,16 +3,16 @@ "missing_type": "نوع القطعة مفقود: {{type}}", "api_error": "API خطأ", "status": "الحالة", - "information": "Information", + "information": "معلومات", "url": "URL", "raw_error": "Raw Error", - "response_data": "Response Data" + "response_data": "بيانات الاستجابة" }, "weather": { "current": "الموقع الحالي", - "allow": "اضغط للسماح", + "allow": "أنقر للسماح", "updating": "جاري التحديث", - "wait": "الرجاء الانتظار" + "wait": "الرجاء الإنتظار" }, "search": { "placeholder": "بحث …" @@ -27,88 +27,88 @@ "unifi": { "users": "المستخدمون", "uptime": "مدة تشغيل النظام", - "days": "ايام", + "days": "أيام", "wan": "WAN", "lan": "LAN", "wlan": "WLAN", - "devices": "الاجهزة", - "lan_devices": "LAN اجهزة", - "wlan_devices": "WLAN احهزة", + "devices": "الأجهزة", + "lan_devices": "LAN أجهزة", + "wlan_devices": "WLAN أجهزة", "lan_users": "LAN مستخدمين", "wlan_users": "WLAN مستخدمين", - "up": "اعلي", - "down": "اسفل", - "wait": "الرجاء الانتظار" + "up": "يعمل", + "down": "لا يعمل", + "wait": "الرجاء الإنتظار" }, "wmo": { - "73-day": "Snow", - "0-day": "Sunny", - "0-night": "Clear", - "1-day": "Mainly Sunny", - "1-night": "Mainly Clear", - "2-day": "Partly Cloudy", - "2-night": "Partly Cloudy", - "3-day": "Cloudy", - "3-night": "Cloudy", - "45-day": "Foggy", - "45-night": "Foggy", - "48-day": "Foggy", - "48-night": "Foggy", - "51-day": "Light Drizzle", - "51-night": "Light Drizzle", - "53-day": "Drizzle", - "53-night": "Drizzle", - "55-day": "Heavy Drizzle", - "55-night": "Heavy Drizzle", - "56-day": "Light Freezing Drizzle", - "56-night": "Light Freezing Drizzle", - "57-day": "Freezing Drizzle", - "57-night": "Freezing Drizzle", - "61-day": "Light Rain", - "61-night": "Light Rain", - "63-day": "Rain", - "63-night": "Rain", - "65-day": "Heavy Rain", - "65-night": "Heavy Rain", - "66-day": "Freezing Rain", - "66-night": "Freezing Rain", - "67-day": "Freezing Rain", - "67-night": "Freezing Rain", - "71-day": "Light Snow", - "71-night": "Light Snow", - "73-night": "Snow", - "75-day": "Heavy Snow", - "75-night": "Heavy Snow", - "77-day": "Snow Grains", - "77-night": "Snow Grains", - "80-day": "Light Showers", - "80-night": "Light Showers", - "81-day": "Showers", - "81-night": "Showers", - "82-day": "Heavy Showers", - "82-night": "Heavy Showers", - "85-day": "Snow Showers", - "85-night": "Snow Showers", - "86-day": "Snow Showers", - "86-night": "Snow Showers", - "95-day": "Thunderstorm", - "95-night": "Thunderstorm", - "96-day": "Thunderstorm With Hail", - "96-night": "Thunderstorm With Hail", - "99-day": "Thunderstorm With Hail", - "99-night": "Thunderstorm With Hail" + "73-day": "ثلج", + "0-day": "مشمس", + "0-night": "صافي", + "1-day": "مشمس غالباً", + "1-night": "صافي غالباً", + "2-day": "غائم جزئياً", + "2-night": "غائم جزئياً", + "3-day": "غائم", + "3-night": "غائم", + "45-day": "ضبابي", + "45-night": "ضبابي", + "48-day": "ضبابي", + "48-night": "ضبابي", + "51-day": "رذاذ خفيف", + "51-night": "رذاذ خفيف", + "53-day": "رذاذ", + "53-night": "رذاذ", + "55-day": "رذاذ كثيف", + "55-night": "رذاذ كثيف", + "56-day": "رذاذ متجمد خفيف", + "56-night": "رذاذ متجمد خفيف", + "57-day": "رذاذ متجمد", + "57-night": "رذاذ متجمد", + "61-day": "مطر خفيف", + "61-night": "مطر خفيف", + "63-day": "مطر", + "63-night": "مطر", + "65-day": "مطر شديد", + "65-night": "مطر شديد", + "66-day": "مطر متجمد", + "66-night": "مطر متجمد", + "67-day": "مطر متجمد", + "67-night": "مطر متجمد", + "71-day": "ثلج خفيف", + "71-night": "ثلج خفيف", + "73-night": "ثلج", + "75-day": "ثلج شديد", + "75-night": "ثلج شديد", + "77-day": "حبيبات الثلج", + "77-night": "حبيبات الثلج", + "80-day": "أمطار خفيفة", + "80-night": "أمطار خفيفة", + "81-day": "أمطار", + "81-night": "أمطار", + "82-day": "أمطار شديدة", + "82-night": "أمطار شديدة", + "85-day": "زخات الثلوج", + "85-night": "زخات الثلوج", + "86-day": "زخات الثلوج", + "86-night": "زخات الثلوج", + "95-day": "عاصفة رعدية", + "95-night": "‬عاصفة رعدية", + "96-day": "عاصفة رعدية مع مطر", + "96-night": "عاصفة رعدية مع مطر", + "99-day": "عاصفة رعدية مع مطر", + "99-night": "عاصفة رعدية مع مطر" }, "docker": { "rx": "RX", "tx": "TX", - "mem": "الرام", + "mem": "الذاكرة", "cpu": "المعالج", "offline": "غير متصل", - "error": "Error", - "unknown": "Unknown" + "error": "خطأ", + "unknown": "مجهول" }, "emby": { - "playing": "يعمل الان", + "playing": "يعمل الآن", "transcoding": "التحويل", "bitrate": "معدل البت", "no_active": "No Active Streams" @@ -118,310 +118,323 @@ "diffsDetected": "Diffs Detected" }, "tautulli": { - "playing": "Playing", - "transcoding": "Transcoding", - "bitrate": "Bitrate", + "playing": "يشتغل", + "transcoding": "التحويل", + "bitrate": "معدل البت", "no_active": "No Active Streams" }, "nzbget": { - "rate": "Rate", - "remaining": "Remaining", - "downloaded": "Downloaded" + "rate": "معدل", + "remaining": "متبقي", + "downloaded": "مُنزل" }, "plex": { "streams": "Active Streams", - "movies": "Movies", - "tv": "TV Shows" + "movies": "أفلام", + "tv": "مسلسلات" }, "sabnzbd": { - "rate": "Rate", - "queue": "Queue", - "timeleft": "Time Left" + "rate": "معدل", + "queue": "إنتظار", + "timeleft": "الوقت المتبقي" }, "rutorrent": { - "active": "Active", - "upload": "Upload", - "download": "Download" + "active": "نشط", + "upload": "تحميل", + "download": "تنزيل" }, "transmission": { - "download": "Download", - "upload": "Upload", + "download": "تنزيل", + "upload": "تحميل", "leech": "Leech", "seed": "Seed" }, "qbittorrent": { - "download": "Download", - "upload": "Upload", + "download": "تنزيل", + "upload": "تحميل", "leech": "Leech", "seed": "Seed" }, "sonarr": { - "wanted": "Wanted", - "queued": "Queued", - "series": "Series" + "wanted": "مطلوب", + "queued": "في الإنتظار", + "series": "سلسلة" }, "radarr": { - "wanted": "Wanted", - "missing": "Missing", - "queued": "Queued", - "movies": "Movies" + "wanted": "مطلوب", + "missing": "مفقود", + "queued": "في الإنتظار", + "movies": "أفلام" }, "lidarr": { - "wanted": "Wanted", - "queued": "Queued", - "albums": "Albums" + "wanted": "مطلوب", + "queued": "في الإنتظار", + "albums": "ألبومات" }, "readarr": { - "wanted": "Wanted", - "queued": "Queued", - "books": "Books" + "wanted": "مطلوب", + "queued": "في الإنتظار", + "books": "كتب" }, "bazarr": { - "missingEpisodes": "Missing Episodes", - "missingMovies": "Missing Movies" + "missingEpisodes": "حلقات مفقودة", + "missingMovies": "أفلام مفقودة" }, "ombi": { - "pending": "Pending", - "approved": "Approved", - "available": "Available" + "pending": "معلق", + "approved": "مصدق", + "available": "متاح" }, "jellyseerr": { - "pending": "Pending", - "approved": "Approved", - "available": "Available" + "pending": "معلق", + "approved": "مصدق", + "available": "متاح" }, "overseerr": { - "pending": "Pending", - "approved": "Approved", - "available": "Available", - "processing": "Processing" + "pending": "معلق", + "approved": "مصدق", + "available": "متاح", + "processing": "معالجة" }, "pihole": { - "queries": "Queries", - "blocked": "Blocked", - "gravity": "Gravity" + "queries": "الاستعلامات", + "blocked": "محظور", + "gravity": "الجاذبية" }, "adguard": { - "queries": "Queries", - "blocked": "Blocked", - "filtered": "Filtered", - "latency": "Latency" + "queries": "الاستعلامات", + "blocked": "محظور", + "filtered": "مرشح", + "latency": "الإستجابة" }, "speedtest": { - "upload": "Upload", - "download": "Download", + "upload": "التحميل", + "download": "تنزيل", "ping": "Ping" }, "portainer": { - "running": "Running", - "stopped": "Stopped", - "total": "Total" + "running": "يعمل", + "stopped": "متوقف", + "total": "مجموع" }, "traefik": { - "routers": "Routers", - "services": "Services", - "middleware": "Middleware" + "routers": "راوتر", + "services": "خدمات", + "middleware": "الوسيطة" }, "npm": { - "enabled": "Enabled", - "disabled": "Disabled", - "total": "Total" + "enabled": "مفعل", + "disabled": "معطل", + "total": "مجموع" }, "coinmarketcap": { - "configure": "Configure one or more crypto currencies to track", - "1hour": "1 Hour", - "1day": "1 Day", - "7days": "7 Days", - "30days": "30 Days" + "configure": "قم بأنشاء عملة تشفير واحدة أو أكثر للتتبع", + "1hour": "١ ساعة", + "1day": "١ يوم", + "7days": "٧ أيام", + "30days": "٣٠ يوم" }, "gotify": { - "apps": "Applications", - "clients": "Clients", - "messages": "Messages" + "apps": "التطبيقات", + "clients": "العملاء", + "messages": "الرسائل" }, "prowlarr": { - "enableIndexers": "Indexers", + "enableIndexers": "مفهرسات", "numberOfGrabs": "Grabs", - "numberOfQueries": "Queries", + "numberOfQueries": "الاستعلامات", "numberOfFailGrabs": "Fail Grabs", - "numberOfFailQueries": "Fail Queries" + "numberOfFailQueries": "فشل الاستعلامات" }, "jackett": { "configured": "Configured", - "errored": "Errored" + "errored": "خطأ" }, "strelaysrv": { - "numActiveSessions": "Sessions", - "numConnections": "Connections", + "numActiveSessions": "الجلسات", + "numConnections": "التوصيلات", "dataRelayed": "Relayed", - "transferRate": "Rate" + "transferRate": "معدل" }, "mastodon": { - "user_count": "Users", + "user_count": "المستخدمين", "status_count": "Posts", "domain_count": "Domains" }, "authentik": { - "users": "Users", - "loginsLast24H": "Logins (24h)", - "failedLoginsLast24H": "Failed Logins (24h)" + "users": "المستخدمين", + "loginsLast24H": "تسجيلات الدخول (٢٤س)", + "failedLoginsLast24H": "فشل تسجيلات الدخول (٢٤س)" }, "proxmox": { - "mem": "MEM", - "cpu": "CPU", + "mem": "الذاكرة", + "cpu": "المعالج", "lxc": "LXC", "vms": "VMs" }, "glances": { - "cpu": "CPU", - "mem": "MEM", - "wait": "Please wait" + "cpu": "معالج", + "mem": "الذاكرة", + "wait": "الرجاء الإنتظار" }, "quicklaunch": { - "bookmark": "Bookmark", - "service": "Service" + "bookmark": "مفضلة", + "service": "خدمة" }, "homebridge": { - "available_update": "System", - "updates": "Updates", - "update_available": "Update Available", - "up_to_date": "Up to Date", + "available_update": "نظام", + "updates": "تحديثات", + "update_available": "تحديث متاح", + "up_to_date": "حتى الآن", "child_bridges": "Child Bridges", "child_bridges_status": "{{ok}}/{{total}}" }, "watchtower": { "containers_scanned": "Scanned", - "containers_updated": "Updated", - "containers_failed": "Failed" + "containers_updated": "محدث", + "containers_failed": "فشل" }, "autobrr": { - "approvedPushes": "Approved", - "rejectedPushes": "Rejected", - "filters": "Filters", - "indexers": "Indexers" + "approvedPushes": "مصدق", + "rejectedPushes": "مرفوض", + "filters": "المرشحات", + "indexers": "مفهرسات" }, "tubearchivist": { - "downloads": "Queue", - "videos": "Videos", - "channels": "Channels", - "playlists": "Playlists" + "downloads": "إنتظار", + "videos": "الفيديوهات", + "channels": "القنوات", + "playlists": "قوائم التشغيل" }, "truenas": { - "load": "System Load", - "uptime": "Uptime", - "alerts": "Alerts", + "load": "حمل النظام", + "uptime": "مدة التشغيل", + "alerts": "تنبيهات", "time": "{{value, number(style: unit; unitDisplay: long;)}}" }, "navidrome": { "nothing_streaming": "No Active Streams", - "please_wait": "Please Wait" + "please_wait": "الرجاء الإنتظار" }, "pyload": { - "speed": "Speed", - "active": "Active", - "queue": "Queue", - "total": "Total" + "speed": "السرعة", + "active": "نشط", + "queue": "إنتظار", + "total": "مجموع" }, "gluetun": { - "public_ip": "Public IP", - "region": "Region", - "country": "Country" + "public_ip": "العام IP", + "region": "منطقة", + "country": "الدولة" }, "hdhomerun": { - "channels": "Channels", + "channels": "قنوات", "hd": "HD" }, "ping": { - "error": "Error", + "error": "خطأ", "ping": "Ping" }, "scrutiny": { - "passed": "Passed", - "failed": "Failed", - "unknown": "Unknown" + "passed": "إجتاز", + "failed": "فشل", + "unknown": "مجهول" }, "paperlessngx": { - "inbox": "Inbox", - "total": "Total" + "inbox": "صندوق الوارد", + "total": "المجموع" }, "deluge": { - "download": "Download", - "upload": "Upload", + "download": "تنزيل", + "upload": "تحميل", "leech": "Leech", "seed": "Seed" }, "flood": { - "download": "Download", - "upload": "Upload", + "download": "التنزيل", + "upload": "التحميل", "leech": "Leech", "seed": "Seed" }, "tdarr": { - "queue": "Queue", - "processed": "Processed", - "errored": "Errored", - "saved": "Saved" + "queue": "إنتظار", + "processed": "معالجة", + "errored": "خطأ", + "saved": "حفظ" }, "miniflux": { - "read": "Read", - "unread": "Unread" + "read": "قراءة", + "unread": "غير مقروء" }, "nextdns": { - "wait": "Please Wait", - "no_devices": "No Device Data Received" + "wait": "الرجاء الإنتظار", + "no_devices": "لم يتم استلام بيانات الجهاز" }, "common": { "bibyterate": "{{value, rate(bits: false; binary: true)}}", "bibitrate": "{{value, rate(bits: true; binary: true)}}" }, "omada": { - "connectedAp": "Connected APs", - "activeUser": "Active devices", - "alerts": "Alerts", + "connectedAp": "المتصلة APs", + "activeUser": "الأجهزة النشطة", + "alerts": "تنبيهات", "connectedGateway": "Connected gateways", "connectedSwitches": "Connected switches" }, "downloadstation": { - "download": "Download", - "upload": "Upload", + "download": "تنزيل", + "upload": "تحميل", "leech": "Leech", "seed": "Seed" }, "mikrotik": { - "cpuLoad": "CPU Load", - "memoryUsed": "Memory Used", - "uptime": "Uptime", + "cpuLoad": "حمل المعالج", + "memoryUsed": "الذاكرة الستخدمة", + "uptime": "مدة التشغيل", "numberOfLeases": "Leases" }, "xteve": { "streams_all": "All Streams", "streams_active": "Active Streams", - "streams_xepg": "XEPG Channels" + "streams_xepg": "XEPG قنوات" }, "opnsense": { - "cpu": "CPU Load", - "memory": "Active Memory", - "wanUpload": "WAN Upload", - "wanDownload": "WAN Download" + "cpu": "حمل المعالج", + "memory": "الذاكرة النشطة", + "wanUpload": "WAN التحميل", + "wanDownload": "WAN التنزيل" }, "moonraker": { - "printer_state": "Printer State", - "print_status": "Print Status", - "print_progress": "Progress", - "layers": "Layers" + "printer_state": "حالة الطابعة", + "print_status": "حالة الطابعة", + "print_progress": "تقدم", + "layers": "طبقات" }, "medusa": { - "wanted": "Wanted", - "queued": "Queued", - "series": "Series" - }, - "octoPrint": { - "job_completion": "Completion" + "wanted": "مطلوب", + "queued": "في الإنتظار", + "series": "سلسلة" }, "octoprint": { - "printer_state": "Status", - "temp_tool": "Tool temp", - "temp_bed": "Bed temp", - "job_completion": "Completion" + "printer_state": "حالة", + "temp_tool": "أداة درجة الحرارة", + "temp_bed": "درجة حرارة السرير", + "job_completion": "إتمام" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "حالة" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/bg/common.json b/public/locales/bg/common.json index 050f7c9f..e7fc6e97 100644 --- a/public/locales/bg/common.json +++ b/public/locales/bg/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/ca/common.json b/public/locales/ca/common.json index 5540b487..31abc3d2 100644 --- a/public/locales/ca/common.json +++ b/public/locales/ca/common.json @@ -184,7 +184,7 @@ "failedLoginsLast24H": "Errors d'inici de sessió (24h)" }, "proxmox": { - "vms": "Màquines Virtuals", + "vms": "VMs", "mem": "Memòria", "cpu": "Processador", "lxc": "LXC" @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/cs/common.json b/public/locales/cs/common.json index 094c8cf0..167600a2 100644 --- a/public/locales/cs/common.json +++ b/public/locales/cs/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/da/common.json b/public/locales/da/common.json index 2e5391a7..6ce7c5a2 100644 --- a/public/locales/da/common.json +++ b/public/locales/da/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/de/common.json b/public/locales/de/common.json index 795c5767..afdf62e0 100644 --- a/public/locales/de/common.json +++ b/public/locales/de/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "cpu_usage": "CPU", + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 67914569..8378a3a9 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -431,6 +431,22 @@ "temp_bed": "Bed temp", "job_completion": "Completion" }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" + }, "diskstation": { "days": "Days", "uptime": "Uptime", diff --git a/public/locales/eo/common.json b/public/locales/eo/common.json index f0750357..70260f7a 100644 --- a/public/locales/eo/common.json +++ b/public/locales/eo/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 5cade675..f80f8f6c 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -415,13 +415,26 @@ "queued": "A la espera", "series": "Serie" }, - "octoPrint": { - "job_completion": "Conclusión" - }, "octoprint": { "temp_bed": "Bed temp", "printer_state": "Status", "temp_tool": "Tool temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "IP de origen", + "status": "Estado" + }, + "proxmoxbackupserver": { + "cpu_usage": "CPU", + "datastore_usage": "Almacén de datos", + "failed_tasks_24h": "Tareas fallidas en 24h", + "memory_usage": "Memoria" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/fi/common.json b/public/locales/fi/common.json index af8a04a9..07448c8b 100644 --- a/public/locales/fi/common.json +++ b/public/locales/fi/common.json @@ -194,7 +194,7 @@ "uptime": "System Uptime", "lan_users": "LAN Users", "wlan_users": "WLAN Users", - "wait": "Please wait", + "wait": "Odota, ole hyvä", "days": "Days", "wan": "WAN", "up": "UP", @@ -314,7 +314,7 @@ }, "navidrome": { "nothing_streaming": "No Active Streams", - "please_wait": "Please Wait" + "please_wait": "Odota, ole hyvä" }, "pyload": { "speed": "Speed", @@ -411,17 +411,30 @@ "layers": "Layers" }, "medusa": { - "wanted": "Wanted", - "queued": "Queued", - "series": "Series" - }, - "octoPrint": { - "job_completion": "Completion" + "wanted": "Haluttu", + "queued": "Jonossa", + "series": "Sarja" }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index bc76b2c2..f25bd9a0 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -415,13 +415,26 @@ "queued": "En attente", "series": "Séries" }, - "octoPrint": { - "job_completion": "Achèvement" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", - "job_completion": "Completion" + "job_completion": "Achèvement" + }, + "cloudflared": { + "origin_ip": "IP Publique", + "status": "Statut" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Tâches échouées 24h", + "cpu_usage": "CPU", + "memory_usage": "Mémoire" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/he/common.json b/public/locales/he/common.json index 21577cac..db63fedb 100644 --- a/public/locales/he/common.json +++ b/public/locales/he/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json index 1752cb98..b78a2acd 100644 --- a/public/locales/hi/common.json +++ b/public/locales/hi/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/hr/common.json b/public/locales/hr/common.json index 5d981740..7f02d8aa 100644 --- a/public/locales/hr/common.json +++ b/public/locales/hr/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/hu/common.json b/public/locales/hu/common.json index 70d00aec..19dd6384 100644 --- a/public/locales/hu/common.json +++ b/public/locales/hu/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/it/common.json b/public/locales/it/common.json index 20a9c541..f793bf06 100644 --- a/public/locales/it/common.json +++ b/public/locales/it/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index 9589e378..df32660e 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "temp_bed": "Bed temp", "printer_state": "Status", "temp_tool": "Tool temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/lv/common.json b/public/locales/lv/common.json index 7329287e..40e31fe1 100644 --- a/public/locales/lv/common.json +++ b/public/locales/lv/common.json @@ -415,13 +415,26 @@ "print_progress": "Progress", "layers": "Layers" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/ms/common.json b/public/locales/ms/common.json index 096c956e..527d77d2 100644 --- a/public/locales/ms/common.json +++ b/public/locales/ms/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/nb-NO/common.json b/public/locales/nb-NO/common.json index f3d981ca..cdd61403 100644 --- a/public/locales/nb-NO/common.json +++ b/public/locales/nb-NO/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/nl/common.json b/public/locales/nl/common.json index f632179b..53a6483a 100644 --- a/public/locales/nl/common.json +++ b/public/locales/nl/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/pl/common.json b/public/locales/pl/common.json index 8bccfdd8..44d3f316 100644 --- a/public/locales/pl/common.json +++ b/public/locales/pl/common.json @@ -415,13 +415,26 @@ "queued": "Zakolejkowane", "series": "Seria" }, - "octoPrint": { - "job_completion": "Ukończenie" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/pt-BR/common.json b/public/locales/pt-BR/common.json index e6f524f3..6b01edf7 100644 --- a/public/locales/pt-BR/common.json +++ b/public/locales/pt-BR/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json index 4be6454a..a61169f5 100644 --- a/public/locales/pt/common.json +++ b/public/locales/pt/common.json @@ -424,13 +424,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/ro/common.json b/public/locales/ro/common.json index 5b5d89a8..5e9e47d3 100644 --- a/public/locales/ro/common.json +++ b/public/locales/ro/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index 2c741aba..c52e101f 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/sr/common.json b/public/locales/sr/common.json index 7b362ecc..9c39d9c2 100644 --- a/public/locales/sr/common.json +++ b/public/locales/sr/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/sv/common.json b/public/locales/sv/common.json index e90ff86f..596e9e93 100644 --- a/public/locales/sv/common.json +++ b/public/locales/sv/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/te/common.json b/public/locales/te/common.json index 01bdbb53..1e040d98 100644 --- a/public/locales/te/common.json +++ b/public/locales/te/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/tr/common.json b/public/locales/tr/common.json index b1cfbf0e..b38ec04e 100644 --- a/public/locales/tr/common.json +++ b/public/locales/tr/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/uk/common.json b/public/locales/uk/common.json index 189b3a0d..5cf01ce0 100644 --- a/public/locales/uk/common.json +++ b/public/locales/uk/common.json @@ -415,13 +415,26 @@ "queued": "У черзі", "series": "Серії" }, - "octoPrint": { + "octoprint": { + "printer_state": "Стан", + "temp_tool": "Температура інструменту", + "temp_bed": "Температура ліжка", "job_completion": "Завершення" }, - "octoprint": { - "printer_state": "Status", - "temp_tool": "Tool temp", - "temp_bed": "Bed temp", - "job_completion": "Completion" + "cloudflared": { + "origin_ip": "Походження IP", + "status": "Стан" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/vi/common.json b/public/locales/vi/common.json index 609318fa..97a458f7 100644 --- a/public/locales/vi/common.json +++ b/public/locales/vi/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/yue/common.json b/public/locales/yue/common.json index 6ee13704..112699ee 100644 --- a/public/locales/yue/common.json +++ b/public/locales/yue/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/zh-CN/common.json b/public/locales/zh-CN/common.json index 12b72076..008d09b4 100644 --- a/public/locales/zh-CN/common.json +++ b/public/locales/zh-CN/common.json @@ -415,13 +415,26 @@ "queued": "Queued", "series": "Series" }, - "octoPrint": { - "job_completion": "Completion" - }, "octoprint": { "printer_state": "Status", "temp_tool": "Tool temp", "temp_bed": "Bed temp", "job_completion": "Completion" + }, + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/public/locales/zh-Hant/common.json b/public/locales/zh-Hant/common.json index d2b4951e..7e873813 100644 --- a/public/locales/zh-Hant/common.json +++ b/public/locales/zh-Hant/common.json @@ -415,13 +415,26 @@ "queued": "已加入佇列", "series": "影集" }, - "octoPrint": { + "octoprint": { + "printer_state": "狀態", + "temp_tool": "噴頭溫度", + "temp_bed": "平台溫度", "job_completion": "完成度" }, - "octoprint": { - "printer_state": "Status", - "temp_tool": "Tool temp", - "temp_bed": "Bed temp", - "job_completion": "Completion" + "cloudflared": { + "origin_ip": "Origin IP", + "status": "Status" + }, + "proxmoxbackupserver": { + "datastore_usage": "Datastore", + "failed_tasks_24h": "Failed Tasks 24h", + "cpu_usage": "CPU", + "memory_usage": "Memory" + }, + "immich": { + "users": "Users", + "photos": "Photos", + "videos": "Videos", + "storage": "Storage" } } diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js index 82604f8c..84e90065 100644 --- a/src/pages/api/docker/stats/[...service].js +++ b/src/pages/api/docker/stats/[...service].js @@ -1,16 +1,18 @@ import Docker from "dockerode"; import getDockerArguments from "utils/config/docker"; +import createLogger from "utils/logger"; + +const logger = createLogger("dockerStatsService"); export default async function handler(req, res) { const { service } = req.query; const [containerName, containerServer] = service; if (!containerName && !containerServer) { - res.status(400).send({ + return res.status(400).send({ error: "docker query parameters are required", }); - return; } try { @@ -23,10 +25,9 @@ export default async function handler(req, res) { // bad docker connections can result in a object? // in any case, this ensures the result is the expected array if (!Array.isArray(containers)) { - res.status(500).send({ + return res.status(500).send({ error: "query failed", }); - return; } const containerNames = containers.map((container) => container.Names[0].replace(/^\//, "")); @@ -36,10 +37,9 @@ export default async function handler(req, res) { const container = docker.getContainer(containerName); const stats = await container.stats({ stream: false }); - res.status(200).json({ + return res.status(200).json({ stats, }); - return; } // Try with a service deployed in Docker Swarm, if enabled @@ -61,19 +61,19 @@ export default async function handler(req, res) { const container = docker.getContainer(taskContainerId); const stats = await container.stats({ stream: false }); - res.status(200).json({ + return res.status(200).json({ stats, }); - return; } } - res.status(200).send({ + return res.status(200).send({ error: "not found", }); - } catch { - res.status(500).send({ - error: {message: "Unknown error"}, + } catch (e) { + logger.error(e); + return res.status(500).send({ + error: {message: e?.message ?? "Unknown error"}, }); } } diff --git a/src/pages/api/docker/status/[...service].js b/src/pages/api/docker/status/[...service].js index fa54e6f3..f232eb98 100644 --- a/src/pages/api/docker/status/[...service].js +++ b/src/pages/api/docker/status/[...service].js @@ -1,6 +1,9 @@ import Docker from "dockerode"; import getDockerArguments from "utils/config/docker"; +import createLogger from "utils/logger"; + +const logger = createLogger("dockerStatusService"); export default async function handler(req, res) { const { service } = req.query; @@ -68,9 +71,10 @@ export default async function handler(req, res) { return res.status(200).send({ error: "not found", }); - } catch { + } catch (e) { + logger.error(e); return res.status(500).send({ - error: "unknown error", + error: {message: e?.message ?? "Unknown error"}, }); } } diff --git a/src/utils/config/api-response.js b/src/utils/config/api-response.js index 854bfe62..497ba678 100644 --- a/src/utils/config/api-response.js +++ b/src/utils/config/api-response.js @@ -13,6 +13,17 @@ import { } from "utils/config/service-helpers"; import { cleanWidgetGroups, widgetsFromConfig } from "utils/config/widget-helpers"; +/** + * Compares services by weight then by name. + */ +function compareServices(service1, service2) { + const comp = service1.weight - service2.weight; + if (comp !== 0) { + return comp; + } + return service1.name.localeCompare(service2.name); +} + export async function bookmarksResponse() { checkAndCopyConfig("bookmarks.yaml"); @@ -112,7 +123,8 @@ export async function servicesResponse() { ...discoveredDockerGroup.services, ...discoveredKubernetesGroup.services, ...configuredGroup.services - ].filter((service) => service), + ].filter((service) => service) + .sort(compareServices), }; if (definedLayouts) { diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 28f4d76d..352367d2 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -33,6 +33,15 @@ export async function servicesFromConfig() { })), })); + // add default weight to services based on their position in the configuration + servicesArray.forEach((group, groupIndex) => { + group.services.forEach((service, serviceIndex) => { + if(!service.weight) { + servicesArray[groupIndex].services[serviceIndex].weight = (serviceIndex + 1) * 100; + } + }); + }); + return servicesArray; } @@ -152,6 +161,7 @@ export async function servicesFromKubernetes() { href: ingress.metadata.annotations[`${ANNOTATION_BASE}/href`] || getUrlFromIngress(ingress), name: ingress.metadata.annotations[`${ANNOTATION_BASE}/name`] || ingress.metadata.name, group: ingress.metadata.annotations[`${ANNOTATION_BASE}/group`] || "Kubernetes", + weight: ingress.metadata.annotations[`${ANNOTATION_BASE}/weight`] || '0', icon: ingress.metadata.annotations[`${ANNOTATION_BASE}/icon`] || '', description: ingress.metadata.annotations[`${ANNOTATION_BASE}/description`] || '', }; @@ -201,6 +211,17 @@ export function cleanServiceGroups(groups) { name: serviceGroup.name, services: serviceGroup.services.map((service) => { const cleanedService = { ...service }; + if (typeof service.weight === 'string') { + const weight = parseInt(service.weight, 10); + if (Number.isNaN(weight)) { + cleanedService.weight = 0; + } else { + cleanedService.weight = weight; + } + } + if (typeof cleanedService.weight !== "number") { + cleanedService.weight = 0; + } if (cleanedService.widget) { // whitelisted set of keys to pass to the frontend @@ -214,12 +235,15 @@ export function cleanServiceGroups(groups) { defaultinterval, namespace, // kubernetes widget app, - podSelector + podSelector, + wan // opnsense widget } = cleanedService.widget; + const fieldsList = typeof fields === 'string' ? JSON.parse(fields) : fields; + cleanedService.widget = { type, - fields: fields || null, + fields: fieldsList || null, service_name: service.name, service_group: serviceGroup.name, }; @@ -237,6 +261,9 @@ export function cleanServiceGroups(groups) { if (app) cleanedService.widget.app = app; if (podSelector) cleanedService.widget.podSelector = podSelector; } + if (type === "opnsense") { + if (wan) cleanedService.widget.wan = wan; + } } return cleanedService; diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 10ca2b9c..23e06524 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -34,12 +34,18 @@ export default async function credentialedProxyHandler(req, res, map) { headers.Authorization = `Bearer ${widget.key}`; } else if (widget.type === "proxmox") { headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`; + } else if (widget.type === "proxmoxbackupserver") { + delete headers["Content-Type"]; + headers.Authorization = `PBSAPIToken=${widget.username}:${widget.password}`; } else if (widget.type === "autobrr") { headers["X-API-Token"] = `${widget.key}`; } else if (widget.type === "tubearchivist") { headers.Authorization = `Token ${widget.key}`; } else if (widget.type === "miniflux") { headers["X-Auth-Token"] = `${widget.key}`; + } else if (widget.type === "cloudflared") { + headers["X-Auth-Email"] = `${widget.email}`; + headers["X-Auth-Key"] = `${widget.key}`; } else { headers["X-API-Key"] = `${widget.key}`; } diff --git a/src/widgets/cloudflared/component.jsx b/src/widgets/cloudflared/component.jsx new file mode 100644 index 00000000..2cbcd45b --- /dev/null +++ b/src/widgets/cloudflared/component.jsx @@ -0,0 +1,31 @@ +import Container from "components/services/widget/container"; +import Block from "components/services/widget/block"; +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { widget } = service; + + const { data: statsData, error: statsError } = useWidgetAPI(widget, "cfd_tunnel"); + + if (statsError) { + return ; + } + + if (!statsData) { + return ( + + + + + ); + } + + const originIP = statsData.result.connections?.origin_ip ?? statsData.result.connections[0]?.origin_ip; + + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/widgets/cloudflared/widget.js b/src/widgets/cloudflared/widget.js new file mode 100644 index 00000000..1bb8819f --- /dev/null +++ b/src/widgets/cloudflared/widget.js @@ -0,0 +1,18 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "https://api.cloudflare.com/client/v4/accounts/{accountid}/{endpoint}/{tunnelid}", + proxyHandler: credentialedProxyHandler, + + mappings: { + "cfd_tunnel": { + endpoint: "cfd_tunnel", + validate: [ + "success", + "result" + ] + }, + }, +}; + +export default widget; diff --git a/src/widgets/components.js b/src/widgets/components.js index fd3d3975..ea6ad91a 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -6,6 +6,7 @@ const components = { autobrr: dynamic(() => import("./autobrr/component")), bazarr: dynamic(() => import("./bazarr/component")), changedetectionio: dynamic(() => import("./changedetectionio/component")), + cloudflared: dynamic(() => import("./cloudflared/component")), coinmarketcap: dynamic(() => import("./coinmarketcap/component")), deluge: dynamic(() => import("./deluge/component")), diskstation: dynamic(() => import("./diskstation/component")), @@ -37,6 +38,7 @@ const components = { opnsense: dynamic(() => import("./opnsense/component")), overseerr: dynamic(() => import("./overseerr/component")), paperlessngx: dynamic(() => import("./paperlessngx/component")), + proxmoxbackupserver: dynamic(() => import("./proxmoxbackupserver/component")), pihole: dynamic(() => import("./pihole/component")), plex: dynamic(() => import("./plex/component")), portainer: dynamic(() => import("./portainer/component")), @@ -61,6 +63,7 @@ const components = { unifi: dynamic(() => import("./unifi/component")), watchtower: dynamic(() => import("./watchtower/component")), xteve: dynamic(() => import("./xteve/component")), + immich: dynamic(() => import("./immich/component")), }; export default components; diff --git a/src/widgets/immich/component.jsx b/src/widgets/immich/component.jsx new file mode 100644 index 00000000..310ce0b2 --- /dev/null +++ b/src/widgets/immich/component.jsx @@ -0,0 +1,33 @@ +import Container from "components/services/widget/container"; +import Block from "components/services/widget/block"; +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { widget } = service; + + const { data: immichData, error: immichError } = useWidgetAPI(widget); + + if (immichError || immichData?.statusCode === 401) { + return ; + } + + if (!immichData) { + return ( + + + + + + + ); + } + + return ( + + + + + + + ); +} diff --git a/src/widgets/immich/widget.js b/src/widgets/immich/widget.js new file mode 100644 index 00000000..85867aeb --- /dev/null +++ b/src/widgets/immich/widget.js @@ -0,0 +1,8 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "{url}/api/server-info/stats", + proxyHandler: credentialedProxyHandler, +}; + +export default widget; diff --git a/src/widgets/nzbget/component.jsx b/src/widgets/nzbget/component.jsx index b33e0830..c016d6c5 100644 --- a/src/widgets/nzbget/component.jsx +++ b/src/widgets/nzbget/component.jsx @@ -27,7 +27,7 @@ export default function Component({ service }) { return ( - + - - - + {wan && } + {wan && } ); } diff --git a/src/widgets/proxmox/component.jsx b/src/widgets/proxmox/component.jsx index ac443a34..34fa7ff9 100644 --- a/src/widgets/proxmox/component.jsx +++ b/src/widgets/proxmox/component.jsx @@ -31,8 +31,8 @@ export default function Component({ service }) { } const { data } = clusterData ; - const vms = data.filter(item => item.type === "qemu") || []; - const lxc = data.filter(item => item.type === "lxc") || []; + const vms = data.filter(item => item.type === "qemu" && item.template === 0) || []; + const lxc = data.filter(item => item.type === "lxc" && item.template === 0) || []; const nodes = data.filter(item => item.type === "node") || []; const runningVMs = vms.reduce(calcRunning, 0); diff --git a/src/widgets/proxmoxbackupserver/component.jsx b/src/widgets/proxmoxbackupserver/component.jsx new file mode 100644 index 00000000..96151e25 --- /dev/null +++ b/src/widgets/proxmoxbackupserver/component.jsx @@ -0,0 +1,45 @@ +import { useTranslation } from "next-i18next"; + +import Container from "components/services/widget/container"; +import Block from "components/services/widget/block"; +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { t } = useTranslation(); + + const { widget } = service; + + const { data: datastoreData, error: datastoreError } = useWidgetAPI(widget, "status/datastore-usage"); + const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "nodes/localhost/tasks"); + const { data: hostData, error: hostError } = useWidgetAPI(widget, "nodes/localhost/status"); + + if (datastoreError || tasksError || hostError) { + const finalError = tasksError ?? datastoreError ?? hostError; + return ; + } + + if (!datastoreData || !tasksData || !hostData) { + return ( + + + + + + + ); + } + + const datastoreUsage = datastoreData.data[0].used / datastoreData.data[0].total * 100; + const cpuUsage = hostData.data.cpu * 100; + const memoryUsage = hostData.data.memory.used / hostData.data.memory.total * 100; + const failedTasks = tasksData.total >= 100 ? "99+" : tasksData.total; + + return ( + + + + + + + ); +} diff --git a/src/widgets/proxmoxbackupserver/widget.js b/src/widgets/proxmoxbackupserver/widget.js new file mode 100644 index 00000000..0f262330 --- /dev/null +++ b/src/widgets/proxmoxbackupserver/widget.js @@ -0,0 +1,22 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const since = Date.now() - (24 * 60 * 60 * 1000); + +const widget = { + api: "{url}/api2/json/{endpoint}", + proxyHandler: credentialedProxyHandler, + + mappings: { + "status/datastore-usage": { + endpoint: "status/datastore-usage", + }, + "nodes/localhost/tasks": { + endpoint: `nodes/localhost/tasks?errors=true&limit=100&since=${since}`, + }, + "nodes/localhost/status": { + endpoint: "nodes/localhost/status", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 0b337614..bfc80ebe 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -3,6 +3,7 @@ import authentik from "./authentik/widget"; import autobrr from "./autobrr/widget"; import bazarr from "./bazarr/widget"; import changedetectionio from "./changedetectionio/widget"; +import cloudflared from "./cloudflared/widget"; import coinmarketcap from "./coinmarketcap/widget"; import deluge from "./deluge/widget"; import diskstation from "./diskstation/widget"; @@ -31,6 +32,7 @@ import ombi from "./ombi/widget"; import opnsense from "./opnsense/widget"; import overseerr from "./overseerr/widget"; import paperlessngx from "./paperlessngx/widget"; +import proxmoxbackupserver from "./proxmoxbackupserver/widget"; import pihole from "./pihole/widget"; import plex from "./plex/widget"; import portainer from "./portainer/widget"; @@ -55,6 +57,7 @@ import truenas from "./truenas/widget"; import unifi from "./unifi/widget"; import watchtower from "./watchtower/widget"; import xteve from "./xteve/widget"; +import immich from "./immich/widget"; const widgets = { adguard, @@ -62,6 +65,7 @@ const widgets = { autobrr, bazarr, changedetectionio, + cloudflared, coinmarketcap, deluge, diskstation, @@ -91,6 +95,7 @@ const widgets = { opnsense, overseerr, paperlessngx, + proxmoxbackupserver, pihole, plex, portainer, @@ -116,6 +121,7 @@ const widgets = { unifi_console: unifi, watchtower, xteve, + immich, }; export default widgets;