mirror of
https://github.com/karl0ss/homepage.git
synced 2025-05-02 21:43:39 +01:00
Merge branch 'benphelps:main' into main
This commit is contained in:
commit
dd2b7df350
1
package-lock.json
generated
1
package-lock.json
generated
@ -21,7 +21,6 @@
|
||||
"minecraft-ping-js": "^1.0.2",
|
||||
"next": "^12.3.1",
|
||||
"next-i18next": "^12.0.1",
|
||||
"osx-temperature-sensor": "*",
|
||||
"pretty-bytes": "^6.0.0",
|
||||
"raw-body": "^2.5.1",
|
||||
"react": "^18.2.0",
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadCount": "Queue Count",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadCount": "Queue Count",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
4
public/locales/en/common.json
Executable file → Normal file
4
public/locales/en/common.json
Executable file → Normal file
@ -659,5 +659,9 @@
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size",
|
||||
"downloadSpeed": "Speed"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -646,9 +646,13 @@
|
||||
"down_alerts": "Alertas"
|
||||
},
|
||||
"jdownloader": {
|
||||
"downloadCount": "Recuento de las colas",
|
||||
"downloadSpeed": "Velocidad de Descarga",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
"downloadCount": "Cola",
|
||||
"downloadSpeed": "Velocidad",
|
||||
"downloadBytesRemaining": "Restante",
|
||||
"downloadTotalBytes": "Tamaño"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Serie",
|
||||
"totalFiles": "Archivos"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@
|
||||
"status": "Statut",
|
||||
"information": "Information",
|
||||
"url": "URL",
|
||||
"raw_error": "Raw Error",
|
||||
"response_data": "Response Data"
|
||||
"raw_error": "Erreur brute",
|
||||
"response_data": "Données de réponse"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Recherche…"
|
||||
@ -578,7 +578,7 @@
|
||||
"homeassistant": {
|
||||
"people_home": "People Home",
|
||||
"lights_on": "Lumières allumées",
|
||||
"switches_on": "Switches On"
|
||||
"switches_on": "Commutateur On"
|
||||
},
|
||||
"freshrss": {
|
||||
"unread": "Non lu",
|
||||
@ -648,7 +648,11 @@
|
||||
"jdownloader": {
|
||||
"downloadCount": "Total en attente",
|
||||
"downloadSpeed": "Vitesse de téléchargement",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
"downloadBytesRemaining": "Restant",
|
||||
"downloadTotalBytes": "Taille"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Séries",
|
||||
"totalFiles": "Fichiers"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -126,21 +126,21 @@
|
||||
"wanted": "Zatraženo",
|
||||
"queued": "U redu čekanja",
|
||||
"series": "Serije",
|
||||
"unknown": "Unknown",
|
||||
"queue": "Queue"
|
||||
"unknown": "Nepoznato",
|
||||
"queue": "Red čekanja"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Zatraženo",
|
||||
"queued": "U redu čekanja",
|
||||
"movies": "Filmovi",
|
||||
"missing": "Nedostaje",
|
||||
"queue": "Queue",
|
||||
"unknown": "Unknown"
|
||||
"queue": "Red čekanja",
|
||||
"unknown": "Nepoznato"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Zatraženo",
|
||||
"queued": "U redu čekanja",
|
||||
"artists": "Artists"
|
||||
"artists": "Umjetnici"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Zatraženo",
|
||||
@ -646,9 +646,13 @@
|
||||
"down_alerts": "Obavijest o rušenju"
|
||||
},
|
||||
"jdownloader": {
|
||||
"downloadCount": "Queue Count",
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
"downloadCount": "Red čekanja",
|
||||
"downloadSpeed": "Brzina",
|
||||
"downloadBytesRemaining": "Preostalo",
|
||||
"downloadTotalBytes": "Veličina"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Serije",
|
||||
"totalFiles": "Datoteke"
|
||||
}
|
||||
}
|
||||
|
@ -67,10 +67,10 @@
|
||||
"transcoding": "Átkódolás",
|
||||
"bitrate": "Bitráta",
|
||||
"no_active": "Nincs aktív lejátszás",
|
||||
"movies": "Movies",
|
||||
"series": "Series",
|
||||
"episodes": "Episodes",
|
||||
"songs": "Songs"
|
||||
"movies": "Film",
|
||||
"series": "Sorozat",
|
||||
"episodes": "Epizód",
|
||||
"songs": "Zeneszám"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "Lejátszás folyamatban",
|
||||
@ -254,16 +254,16 @@
|
||||
"diffsDetected": "Diffs Detected"
|
||||
},
|
||||
"wmo": {
|
||||
"0-day": "Sunny",
|
||||
"0-night": "Clear",
|
||||
"0-day": "Napos",
|
||||
"0-night": "Derült",
|
||||
"3-day": "Cloudy",
|
||||
"3-night": "Cloudy",
|
||||
"45-day": "Foggy",
|
||||
"53-day": "Drizzle",
|
||||
"56-night": "Light Freezing Drizzle",
|
||||
"57-day": "Freezing Drizzle",
|
||||
"1-day": "Mainly Sunny",
|
||||
"1-night": "Mainly Clear",
|
||||
"1-day": "Többnyire napos",
|
||||
"1-night": "Többnyire derült",
|
||||
"2-day": "Partly Cloudy",
|
||||
"2-night": "Partly Cloudy",
|
||||
"45-night": "Foggy",
|
||||
@ -373,7 +373,7 @@
|
||||
"hd": "HD"
|
||||
},
|
||||
"ping": {
|
||||
"error": "Error",
|
||||
"error": "Hiba",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"scrutiny": {
|
||||
@ -570,10 +570,10 @@
|
||||
"gross_percent_max": "All time"
|
||||
},
|
||||
"audiobookshelf": {
|
||||
"podcasts": "Podcasts",
|
||||
"books": "Books",
|
||||
"podcastsDuration": "Duration",
|
||||
"booksDuration": "Duration"
|
||||
"podcasts": "Podcast",
|
||||
"books": "Könyv",
|
||||
"podcastsDuration": "Időtartam",
|
||||
"booksDuration": "Időtartam"
|
||||
},
|
||||
"homeassistant": {
|
||||
"people_home": "People Home",
|
||||
@ -650,5 +650,9 @@
|
||||
"downloadCount": "Queue Count",
|
||||
"downloadTotalBytes": "Size",
|
||||
"downloadBytesRemaining": "Remaining"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,12 @@
|
||||
"rx": "RX",
|
||||
"error": "Errore",
|
||||
"unknown": "Sconosciuto",
|
||||
"running": "Running",
|
||||
"starting": "Starting",
|
||||
"running": "In esecuzione",
|
||||
"starting": "In avvio",
|
||||
"unhealthy": "Unhealthy",
|
||||
"not_found": "Not Found",
|
||||
"exited": "Exited",
|
||||
"partial": "Partial",
|
||||
"not_found": "Non trovato",
|
||||
"exited": "Uscito",
|
||||
"partial": "Parziale",
|
||||
"healthy": "Healthy"
|
||||
},
|
||||
"emby": {
|
||||
@ -20,10 +20,10 @@
|
||||
"transcoding": "Transcodifica",
|
||||
"bitrate": "Bitrate",
|
||||
"no_active": "Nessuno Stream Attivo",
|
||||
"movies": "Movies",
|
||||
"series": "Series",
|
||||
"episodes": "Episodes",
|
||||
"songs": "Songs"
|
||||
"movies": "Film",
|
||||
"series": "Serie",
|
||||
"episodes": "Episodi",
|
||||
"songs": "Canzoni"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "In riproduzione",
|
||||
@ -82,16 +82,16 @@
|
||||
"series": "Serie",
|
||||
"wanted": "Richiesti",
|
||||
"queued": "In coda",
|
||||
"queue": "Queue",
|
||||
"unknown": "Unknown"
|
||||
"queue": "Coda",
|
||||
"unknown": "Sconosciuto"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Richiesti",
|
||||
"queued": "In coda",
|
||||
"movies": "Film",
|
||||
"missing": "Mancanti",
|
||||
"queue": "Queue",
|
||||
"unknown": "Unknown"
|
||||
"queue": "Coda",
|
||||
"unknown": "Sconosciuto"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Richiesti",
|
||||
@ -112,7 +112,7 @@
|
||||
"queries": "Richieste",
|
||||
"blocked": "Bloccati",
|
||||
"gravity": "Severità",
|
||||
"blocked_percent": "Blocked %"
|
||||
"blocked_percent": "Bloccato %"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "Attivi",
|
||||
@ -175,9 +175,9 @@
|
||||
"missingMovies": "Film Mancanti"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Mancanti",
|
||||
"wanted": "Richiesto",
|
||||
"queued": "In coda",
|
||||
"artists": "Artists"
|
||||
"artists": "Artisti"
|
||||
},
|
||||
"adguard": {
|
||||
"queries": "Interrogazioni",
|
||||
@ -228,13 +228,13 @@
|
||||
"devices": "Dispositivi",
|
||||
"lan_devices": "Dispositivi LAN",
|
||||
"wlan_devices": "Dispositivi WLAN",
|
||||
"empty_data": "Subsystem status unknown"
|
||||
"empty_data": "Stato del sottosistema sconosciuto"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Trasmissioni attive",
|
||||
"movies": "Film",
|
||||
"tv": "Programma televisivo",
|
||||
"albums": "Albums"
|
||||
"albums": "Album"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
@ -243,11 +243,11 @@
|
||||
"uptime": "UP",
|
||||
"days": "d",
|
||||
"hours": "h",
|
||||
"load": "Load",
|
||||
"warn": "Warn",
|
||||
"total": "Total",
|
||||
"free": "Free",
|
||||
"used": "Used"
|
||||
"load": "Carico",
|
||||
"warn": "Avviso",
|
||||
"total": "Totale",
|
||||
"free": "Libero",
|
||||
"used": "Usato"
|
||||
},
|
||||
"changedetectionio": {
|
||||
"totalObserved": "Totale Osservato",
|
||||
@ -314,9 +314,9 @@
|
||||
"quicklaunch": {
|
||||
"bookmark": "Segnalibro",
|
||||
"service": "Servizio",
|
||||
"search": "Search",
|
||||
"custom": "Custom",
|
||||
"visit": "Visit",
|
||||
"search": "Cerca",
|
||||
"custom": "Personalizzato",
|
||||
"visit": "Visita",
|
||||
"url": "URL"
|
||||
},
|
||||
"homebridge": {
|
||||
@ -327,7 +327,7 @@
|
||||
"child_bridges": "Child Bridges",
|
||||
"child_bridges_status": "{{ok}}/{{total}}",
|
||||
"up": "Up",
|
||||
"pending": "Pending",
|
||||
"pending": "In attesa",
|
||||
"down": "Down"
|
||||
},
|
||||
"autobrr": {
|
||||
@ -432,7 +432,7 @@
|
||||
"cpuLoad": "Carico della CPU",
|
||||
"memoryUsed": "Memoria Utilizzata",
|
||||
"uptime": "Tempo di attività",
|
||||
"numberOfLeases": "Lease"
|
||||
"numberOfLeases": "Rilasci"
|
||||
},
|
||||
"xteve": {
|
||||
"streams_all": "Tutti gli stream",
|
||||
@ -440,215 +440,219 @@
|
||||
"streams_xepg": "Canali XEPG"
|
||||
},
|
||||
"opnsense": {
|
||||
"cpu": "Carico CPU",
|
||||
"cpu": "Carico della CPU",
|
||||
"memory": "Memoria in uso",
|
||||
"wanUpload": "WAN Upload",
|
||||
"wanDownload": "WAN Download"
|
||||
},
|
||||
"moonraker": {
|
||||
"printer_state": "Printer State",
|
||||
"print_status": "Print Status",
|
||||
"print_progress": "Progress",
|
||||
"layers": "Layers"
|
||||
"printer_state": "Stato stampante",
|
||||
"print_status": "Stato Stampante",
|
||||
"print_progress": "Avanzamento",
|
||||
"layers": "Livelli"
|
||||
},
|
||||
"medusa": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"series": "Series"
|
||||
"wanted": "Richiesto",
|
||||
"queued": "In coda",
|
||||
"series": "Serie"
|
||||
},
|
||||
"octoprint": {
|
||||
"printer_state": "Status",
|
||||
"printer_state": "Stato",
|
||||
"temp_tool": "Tool temp",
|
||||
"temp_bed": "Bed temp",
|
||||
"job_completion": "Completion"
|
||||
"job_completion": "Completamento"
|
||||
},
|
||||
"cloudflared": {
|
||||
"origin_ip": "Origin IP",
|
||||
"status": "Status"
|
||||
"origin_ip": "IP sorgente",
|
||||
"status": "Stato"
|
||||
},
|
||||
"proxmoxbackupserver": {
|
||||
"datastore_usage": "Datastore",
|
||||
"failed_tasks_24h": "Failed Tasks 24h",
|
||||
"failed_tasks_24h": "Attività Non Riuscite 24h",
|
||||
"cpu_usage": "CPU",
|
||||
"memory_usage": "Memory"
|
||||
"memory_usage": "Memoria"
|
||||
},
|
||||
"immich": {
|
||||
"users": "Users",
|
||||
"photos": "Photos",
|
||||
"videos": "Videos",
|
||||
"storage": "Storage"
|
||||
"users": "Utenti",
|
||||
"photos": "Foto",
|
||||
"videos": "Video",
|
||||
"storage": "Memoria"
|
||||
},
|
||||
"uptimekuma": {
|
||||
"up": "Sites Up",
|
||||
"down": "Sites Down",
|
||||
"up": "Siti On",
|
||||
"down": "Siti Down",
|
||||
"uptime": "Uptime",
|
||||
"incident": "Incident",
|
||||
"incident": "Incidente",
|
||||
"m": "m"
|
||||
},
|
||||
"komga": {
|
||||
"libraries": "Libraries",
|
||||
"series": "Series",
|
||||
"books": "Books"
|
||||
"libraries": "Librerie",
|
||||
"series": "Serie",
|
||||
"books": "Libri"
|
||||
},
|
||||
"mylar": {
|
||||
"series": "Series",
|
||||
"issues": "Issues",
|
||||
"wanted": "Wanted"
|
||||
"series": "Serie",
|
||||
"issues": "Problemi",
|
||||
"wanted": "Richiesto"
|
||||
},
|
||||
"photoprism": {
|
||||
"albums": "Albums",
|
||||
"photos": "Photos",
|
||||
"videos": "Videos",
|
||||
"people": "People"
|
||||
"albums": "Album",
|
||||
"photos": "Foto",
|
||||
"videos": "Video",
|
||||
"people": "Persone"
|
||||
},
|
||||
"diskstation": {
|
||||
"days": "Days",
|
||||
"days": "Giorni",
|
||||
"uptime": "Uptime",
|
||||
"volumeAvailable": "Available"
|
||||
"volumeAvailable": "Disponibile"
|
||||
},
|
||||
"fileflows": {
|
||||
"queue": "Queue",
|
||||
"processing": "Processing",
|
||||
"processed": "Processed",
|
||||
"time": "Time"
|
||||
"queue": "Coda",
|
||||
"processing": "In Lavorazione",
|
||||
"processed": "Elaborato",
|
||||
"time": "Tempo"
|
||||
},
|
||||
"grafana": {
|
||||
"dashboards": "Dashboards",
|
||||
"datasources": "Data Sources",
|
||||
"totalalerts": "Total Alerts",
|
||||
"alertstriggered": "Alerts Triggered"
|
||||
"datasources": "Origine dei Dati",
|
||||
"totalalerts": "Avvisi Totali",
|
||||
"alertstriggered": "Avvisi Attivati"
|
||||
},
|
||||
"nextcloud": {
|
||||
"memoryusage": "Memory Usage",
|
||||
"cpuload": "Cpu Load",
|
||||
"freespace": "Free Space",
|
||||
"activeusers": "Active Users",
|
||||
"numfiles": "Files",
|
||||
"numshares": "Shared Items"
|
||||
"memoryusage": "Uso della Memoria",
|
||||
"cpuload": "Carico della CPU",
|
||||
"freespace": "Spazio Libero",
|
||||
"activeusers": "Utenti Attivi",
|
||||
"numfiles": "File",
|
||||
"numshares": "Oggetti Condivisi"
|
||||
},
|
||||
"kopia": {
|
||||
"status": "Status",
|
||||
"size": "Size",
|
||||
"lastrun": "Last Run",
|
||||
"nextrun": "Next Run",
|
||||
"failed": "Failed"
|
||||
"status": "Stato",
|
||||
"size": "Dimensione",
|
||||
"lastrun": "Ultima esecuzione",
|
||||
"nextrun": "Prossima esecuzione",
|
||||
"failed": "Fallito"
|
||||
},
|
||||
"unmanic": {
|
||||
"active_workers": "Active Workers",
|
||||
"total_workers": "Total Workers",
|
||||
"records_total": "Queue Length"
|
||||
"active_workers": "Lavoratori Attivi",
|
||||
"total_workers": "Lavoratori Totali",
|
||||
"records_total": "Lunghezza della Coda"
|
||||
},
|
||||
"healthchecks": {
|
||||
"new": "New",
|
||||
"new": "Nuovo",
|
||||
"up": "Online",
|
||||
"grace": "In Grace Period",
|
||||
"grace": "Periodo di Tolleranza",
|
||||
"down": "Offline",
|
||||
"paused": "Paused",
|
||||
"status": "Status",
|
||||
"last_ping": "Last Ping",
|
||||
"never": "No pings yet"
|
||||
"paused": "In Pausa",
|
||||
"status": "Stato",
|
||||
"last_ping": "Ultimo Ping",
|
||||
"never": "Ancora nessun ping"
|
||||
},
|
||||
"pterodactyl": {
|
||||
"servers": "Servers",
|
||||
"nodes": "Nodes"
|
||||
"servers": "Server",
|
||||
"nodes": "Nodi"
|
||||
},
|
||||
"prometheus": {
|
||||
"targets_up": "Targets Up",
|
||||
"targets_down": "Targets Down",
|
||||
"targets_total": "Total Targets"
|
||||
"targets_total": "Targets Totali"
|
||||
},
|
||||
"minecraft": {
|
||||
"players": "Players",
|
||||
"version": "Version",
|
||||
"status": "Status",
|
||||
"players": "Giocatori",
|
||||
"version": "Versione",
|
||||
"status": "Stato",
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"ghostfolio": {
|
||||
"gross_percent_today": "Today",
|
||||
"gross_percent_1y": "One year",
|
||||
"gross_percent_max": "All time"
|
||||
"gross_percent_today": "Oggi",
|
||||
"gross_percent_1y": "Un anno",
|
||||
"gross_percent_max": "Sempre"
|
||||
},
|
||||
"audiobookshelf": {
|
||||
"podcasts": "Podcasts",
|
||||
"books": "Books",
|
||||
"podcastsDuration": "Duration",
|
||||
"booksDuration": "Duration"
|
||||
"podcasts": "Podcast",
|
||||
"books": "Libri",
|
||||
"podcastsDuration": "Durata",
|
||||
"booksDuration": "Durata"
|
||||
},
|
||||
"homeassistant": {
|
||||
"people_home": "People Home",
|
||||
"lights_on": "Lights On",
|
||||
"switches_on": "Switches On"
|
||||
"people_home": "Persone a Casa",
|
||||
"lights_on": "Luci Accese",
|
||||
"switches_on": "Switch Accesi"
|
||||
},
|
||||
"freshrss": {
|
||||
"subscriptions": "Subscriptions",
|
||||
"unread": "Unread"
|
||||
"subscriptions": "Iscrizioni",
|
||||
"unread": "Non letto"
|
||||
},
|
||||
"channelsdvrserver": {
|
||||
"shows": "Shows",
|
||||
"recordings": "Recordings",
|
||||
"scheduled": "Scheduled",
|
||||
"passes": "Passes"
|
||||
"shows": "Spettacoli",
|
||||
"recordings": "Registrazioni",
|
||||
"scheduled": "Programmati",
|
||||
"passes": "Tessere"
|
||||
},
|
||||
"whatsupdocker": {
|
||||
"monitoring": "Monitoring",
|
||||
"updates": "Updates"
|
||||
"monitoring": "Monitoraggio",
|
||||
"updates": "Aggiornamenti"
|
||||
},
|
||||
"tailscale": {
|
||||
"never": "Never",
|
||||
"address": "Address",
|
||||
"expires": "Expires",
|
||||
"last_seen": "Last Seen",
|
||||
"now": "Now",
|
||||
"never": "Mai",
|
||||
"address": "Indirizzo",
|
||||
"expires": "Scade",
|
||||
"last_seen": "Ultima visualizzazione",
|
||||
"now": "Adesso",
|
||||
"years": "{{number}}y",
|
||||
"weeks": "{{number}}w",
|
||||
"hours": "{{number}}h",
|
||||
"minutes": "{{number}}m",
|
||||
"seconds": "{{number}}s",
|
||||
"ago": "{{value}} Ago",
|
||||
"ago": "{{value}} Fa",
|
||||
"days": "{{number}}d"
|
||||
},
|
||||
"qnap": {
|
||||
"cpuUsage": "CPU Usage",
|
||||
"memUsage": "MEM Usage",
|
||||
"systemTempC": "System Temp",
|
||||
"poolUsage": "Pool Usage",
|
||||
"volumeUsage": "Volume Usage",
|
||||
"invalid": "Invalid"
|
||||
"cpuUsage": "Utilizzo CPU",
|
||||
"memUsage": "Utilizzo MEM",
|
||||
"systemTempC": "Temp sistema",
|
||||
"poolUsage": "Utilizzo Pool",
|
||||
"volumeUsage": "Utilizzo Volume",
|
||||
"invalid": "Invalido"
|
||||
},
|
||||
"pfsense": {
|
||||
"load": "Load Avg",
|
||||
"memory": "Mem Usage",
|
||||
"wanStatus": "WAN Status",
|
||||
"load": "Carico Medio",
|
||||
"memory": "Uso Memoria",
|
||||
"wanStatus": "Stato WAN",
|
||||
"up": "Up",
|
||||
"down": "Down",
|
||||
"temp": "Temp",
|
||||
"disk": "Disk Usage",
|
||||
"wanIP": "WAN IP"
|
||||
"temp": "Temperatura",
|
||||
"disk": "Uso Disco",
|
||||
"wanIP": "IP WAN"
|
||||
},
|
||||
"caddy": {
|
||||
"upstreams": "Upstreams",
|
||||
"requests": "Current requests",
|
||||
"requests_failed": "Failed requests"
|
||||
"upstreams": "Upstream",
|
||||
"requests": "Richieste correnti",
|
||||
"requests_failed": "Richieste fallite"
|
||||
},
|
||||
"evcc": {
|
||||
"pv_power": "Production",
|
||||
"battery_soc": "Battery",
|
||||
"grid_power": "Grid",
|
||||
"home_power": "Consumption",
|
||||
"charge_power": "Charger",
|
||||
"pv_power": "Produzione",
|
||||
"battery_soc": "Batteria",
|
||||
"grid_power": "Griglia",
|
||||
"home_power": "Consumo",
|
||||
"charge_power": "Caricatore",
|
||||
"watt_hour": "Wh"
|
||||
},
|
||||
"pialert": {
|
||||
"total": "Total",
|
||||
"connected": "Connected",
|
||||
"new_devices": "New Devices",
|
||||
"down_alerts": "Down Alerts"
|
||||
"total": "Totali",
|
||||
"connected": "Connesso",
|
||||
"new_devices": "Nuovi Dispositivi",
|
||||
"down_alerts": "Avvisi di Disservizio"
|
||||
},
|
||||
"jdownloader": {
|
||||
"downloadCount": "Queue Count",
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
"downloadCount": "Coda",
|
||||
"downloadSpeed": "Velocità Download",
|
||||
"downloadBytesRemaining": "Residuo",
|
||||
"downloadTotalBytes": "Dimensione"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Serie",
|
||||
"totalFiles": "File"
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,7 @@
|
||||
"queries": "クエリ",
|
||||
"blocked": "ブロック中",
|
||||
"gravity": "グラビティ",
|
||||
"blocked_percent": "Blocked %"
|
||||
"blocked_percent": "ブロック %"
|
||||
},
|
||||
"adguard": {
|
||||
"queries": "クエリ",
|
||||
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -659,5 +659,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"totalFiles": "Files",
|
||||
"seriesCount": "Series"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"totalFiles": "Files",
|
||||
"seriesCount": "Series"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -646,9 +646,13 @@
|
||||
"down_alerts": "Сповіщення про збій"
|
||||
},
|
||||
"jdownloader": {
|
||||
"downloadCount": "Всього в черзі",
|
||||
"downloadSpeed": "Швидкість завантаження",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
"downloadCount": "Черга",
|
||||
"downloadSpeed": "Швидкість",
|
||||
"downloadBytesRemaining": "Залишилося",
|
||||
"downloadTotalBytes": "Розмір"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Серій",
|
||||
"totalFiles": "Файлів"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +632,7 @@
|
||||
"requests_failed": "失败请求"
|
||||
},
|
||||
"evcc": {
|
||||
"pv_power": "Production",
|
||||
"pv_power": "正式环境",
|
||||
"battery_soc": "Battery",
|
||||
"grid_power": "Grid",
|
||||
"home_power": "Consumption",
|
||||
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -650,5 +650,9 @@
|
||||
"downloadSpeed": "Download Speed",
|
||||
"downloadBytesRemaining": "Remaining",
|
||||
"downloadTotalBytes": "Size"
|
||||
},
|
||||
"kavita": {
|
||||
"seriesCount": "Series",
|
||||
"totalFiles": "Files"
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,41 @@
|
||||
import classNames from "classnames";
|
||||
import { Disclosure, Transition } from '@headlessui/react';
|
||||
import { MdKeyboardArrowDown } from "react-icons/md";
|
||||
|
||||
import ErrorBoundary from "components/errorboundry";
|
||||
import List from "components/bookmarks/list";
|
||||
|
||||
export default function BookmarksGroup({ group }) {
|
||||
export default function BookmarksGroup({ group, disableCollapse }) {
|
||||
return (
|
||||
<div key={group.name} className="flex-1">
|
||||
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
|
||||
<ErrorBoundary>
|
||||
<List bookmarks={group.bookmarks} />
|
||||
</ErrorBoundary>
|
||||
<Disclosure defaultOpen>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button disabled={disableCollapse} className="flex w-full select-none items-center group">
|
||||
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
|
||||
<MdKeyboardArrowDown className={classNames(
|
||||
disableCollapse ? 'hidden' : '',
|
||||
'transition-opacity opacity-0 group-hover:opacity-100 ml-auto text-theme-800 dark:text-theme-300 text-xl',
|
||||
open ? 'rotate-180 transform' : ''
|
||||
)} />
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
enter="transition duration-200 ease-out"
|
||||
enterFrom="transform scale-75 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-75 opacity-0"
|
||||
>
|
||||
<Disclosure.Panel>
|
||||
<ErrorBoundary>
|
||||
<List bookmarks={group.bookmarks} />
|
||||
</ErrorBoundary>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
import classNames from "classnames";
|
||||
import { Disclosure, Transition } from '@headlessui/react';
|
||||
import { MdKeyboardArrowDown } from "react-icons/md";
|
||||
|
||||
import List from "components/services/list";
|
||||
import ResolvedIcon from "components/resolvedicon";
|
||||
|
||||
export default function ServicesGroup({ group, services, layout, fiveColumns }) {
|
||||
export default function ServicesGroup({ group, services, layout, fiveColumns, disableCollapse }) {
|
||||
|
||||
return (
|
||||
<div
|
||||
key={services.name}
|
||||
@ -13,15 +16,37 @@ export default function ServicesGroup({ group, services, layout, fiveColumns })
|
||||
"flex-1 p-1"
|
||||
)}
|
||||
>
|
||||
<div className="flex select-none items-center">
|
||||
{layout?.icon &&
|
||||
<div className="flex-shrink-0 mr-2 w-7 h-7">
|
||||
<ResolvedIcon icon={layout.icon} />
|
||||
</div>
|
||||
}
|
||||
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{services.name}</h2>
|
||||
</div>
|
||||
<List group={group} services={services.services} layout={layout} />
|
||||
<Disclosure defaultOpen>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button disabled={disableCollapse} className="flex w-full select-none items-center group">
|
||||
{layout?.icon &&
|
||||
<div className="flex-shrink-0 mr-2 w-7 h-7">
|
||||
<ResolvedIcon icon={layout.icon} />
|
||||
</div>
|
||||
}
|
||||
<h2 className="flex text-theme-800 dark:text-theme-300 text-xl font-medium">{services.name}</h2>
|
||||
<MdKeyboardArrowDown className={classNames(
|
||||
disableCollapse ? 'hidden' : '',
|
||||
'transition-opacity opacity-0 group-hover:opacity-100 ml-auto text-theme-800 dark:text-theme-300 text-xl',
|
||||
open ? 'rotate-180 transform' : ''
|
||||
)} />
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
enter="transition duration-200 ease-out"
|
||||
enterFrom="transform scale-75 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-75 opacity-0"
|
||||
>
|
||||
<Disclosure.Panel>
|
||||
<List group={group} services={services.services} layout={layout} />
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -294,7 +294,13 @@ function Home({ initialSettings }) {
|
||||
{services?.length > 0 && (
|
||||
<div className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2">
|
||||
{services.map((group) => (
|
||||
<ServicesGroup key={group.name} group={group.name} services={group} layout={initialSettings.layout?.[group.name]} fiveColumns={settings.fiveColumns} />
|
||||
<ServicesGroup
|
||||
key={group.name}
|
||||
group={group.name}
|
||||
services={group}
|
||||
layout={initialSettings.layout?.[group.name]}
|
||||
fiveColumns={settings.fiveColumns}
|
||||
disableCollapse={settings.disableCollapse} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
@ -302,7 +308,10 @@ function Home({ initialSettings }) {
|
||||
{bookmarks?.length > 0 && (
|
||||
<div className={`grow flex flex-wrap pt-0 p-4 sm:p-8 gap-2 grid-cols-1 lg:grid-cols-2 lg:grid-cols-${Math.min(6, bookmarks.length)}`}>
|
||||
{bookmarks.map((group) => (
|
||||
<BookmarksGroup key={group.name} group={group} />
|
||||
<BookmarksGroup
|
||||
key={group.name}
|
||||
group={group}
|
||||
disableCollapse={settings.disableCollapse} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
@ -291,8 +291,15 @@ export function cleanServiceGroups(groups) {
|
||||
enableQueue, // sonarr/radarr
|
||||
} = cleanedService.widget;
|
||||
|
||||
const fieldsList = typeof fields === 'string' ? JSON.parse(fields) : fields;
|
||||
|
||||
let fieldsList = fields;
|
||||
if (typeof fields === 'string') {
|
||||
try { JSON.parse(fields) }
|
||||
catch (e) {
|
||||
logger.error("Invalid fields list detected in config for service '%s'", service.name);
|
||||
fieldsList = null;
|
||||
}
|
||||
}
|
||||
|
||||
cleanedService.widget = {
|
||||
type,
|
||||
fields: fieldsList || null,
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable prefer-promise-reject-errors */
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { createUnzip } from "node:zlib";
|
||||
import { createUnzip, constants as zlibConstants } from "node:zlib";
|
||||
|
||||
import { http, https } from "follow-redirects";
|
||||
|
||||
@ -34,7 +34,14 @@ function handleRequest(requestor, url, params) {
|
||||
|
||||
let responseContent = response;
|
||||
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') {
|
||||
responseContent = createUnzip();
|
||||
// https://github.com/request/request/blob/3c0cddc7c8eb60b470e9519da85896ed7ee0081e/request.js#L1018-L1025
|
||||
// Be more lenient with decoding compressed responses, in case of invalid gzip responses that are still accepted
|
||||
// by common browsers.
|
||||
responseContent = createUnzip({
|
||||
flush: zlibConstants.Z_SYNC_FLUSH,
|
||||
finishFlush: zlibConstants.Z_SYNC_FLUSH
|
||||
});
|
||||
|
||||
// zlib errors
|
||||
responseContent.on("error", (e) => {
|
||||
logger.error(e);
|
||||
@ -103,6 +110,6 @@ export async function httpProxy(url, params = {}) {
|
||||
constructedUrl.pathname
|
||||
);
|
||||
logger.error(err);
|
||||
return [500, "application/json", { error: {message: err?.message ?? "Unknown error", url, rawError: err} }, null];
|
||||
return [500, "application/json", { error: { message: err?.message ?? "Unknown error", url, rawError: err } }, null];
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ const components = {
|
||||
jdownloader: dynamic(() => import("./jdownloader/component")),
|
||||
jellyfin: dynamic(() => import("./emby/component")),
|
||||
jellyseerr: dynamic(() => import("./jellyseerr/component")),
|
||||
kavita: dynamic(() => import("./kavita/component")),
|
||||
komga: dynamic(() => import("./komga/component")),
|
||||
kopia: dynamic(() => import("./kopia/component")),
|
||||
lidarr: dynamic(() => import("./lidarr/component")),
|
||||
@ -94,4 +95,4 @@ const components = {
|
||||
xteve: dynamic(() => import("./xteve/component")),
|
||||
};
|
||||
|
||||
export default components;
|
||||
export default components;
|
||||
|
33
src/widgets/kavita/component.jsx
Normal file
33
src/widgets/kavita/component.jsx
Normal file
@ -0,0 +1,33 @@
|
||||
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: kavitaData, error: kavitaError } = useWidgetAPI(widget, "info");
|
||||
|
||||
if (kavitaError) {
|
||||
return <Container service={service} error={kavitaError} />;
|
||||
}
|
||||
|
||||
if (!kavitaData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="kavita.seriesCount" />
|
||||
<Block label="kavita.totalFiles" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="kavita.seriesCount" value={t("common.number", { value: kavitaData.seriesCount })} />
|
||||
<Block label="kavita.totalFiles" value={t("common.number", { value: kavitaData.totalFiles })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
96
src/widgets/kavita/proxy.js
Normal file
96
src/widgets/kavita/proxy.js
Normal file
@ -0,0 +1,96 @@
|
||||
import cache from "memory-cache";
|
||||
|
||||
import { httpProxy } from "utils/proxy/http";
|
||||
import { formatApiCall } from "utils/proxy/api-helpers";
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
import createLogger from "utils/logger";
|
||||
import widgets from "widgets/widgets";
|
||||
|
||||
const proxyName = "kavitaProxyHandler";
|
||||
const sessionTokenCacheKey = `${proxyName}__sessionToken`;
|
||||
const logger = createLogger(proxyName);
|
||||
|
||||
async function login(widget, service) {
|
||||
const endpoint = "Account/login";
|
||||
const api = widgets?.[widget.type]?.api
|
||||
const loginUrl = new URL(formatApiCall(api, { endpoint, ...widget }));
|
||||
const loginBody = { username: widget.username, password: widget.password };
|
||||
const headers = { "Content-Type": "application/json", "accept": "text/plain" };
|
||||
|
||||
const [, , data,] = await httpProxy(loginUrl, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(loginBody),
|
||||
headers,
|
||||
});
|
||||
|
||||
try {
|
||||
const { token: accessToken } = JSON.parse(data.toString());
|
||||
cache.put(`${sessionTokenCacheKey}.${service}`, accessToken);
|
||||
return { accessToken };
|
||||
} catch (e) {
|
||||
logger.error("Unable to login to Kavita API: %s", e);
|
||||
}
|
||||
|
||||
return { token: false };
|
||||
}
|
||||
|
||||
async function apiCall(widget, endpoint, service) {
|
||||
const key = `${sessionTokenCacheKey}.${service}`;
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
"Authorization": `Bearer ${cache.get(key)}`,
|
||||
}
|
||||
|
||||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||
const method = "GET";
|
||||
|
||||
let [status, contentType, data, responseHeaders] = await httpProxy(url, {
|
||||
method,
|
||||
headers,
|
||||
});
|
||||
|
||||
if (status === 401 || status === 403) {
|
||||
logger.debug("Kavita API rejected the request, attempting to obtain new session token");
|
||||
const { accessToken } = await login(widget, service);
|
||||
headers.Authorization = `Bearer ${accessToken}`;
|
||||
|
||||
// retry the request, now with the new session token
|
||||
[status, contentType, data, responseHeaders] = await httpProxy(url, {
|
||||
method,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
if (status !== 200) {
|
||||
logger.error("Error getting data from Kavita: %s status %d. Data: %s", url, status, data);
|
||||
return { status, contentType, data: null, responseHeaders };
|
||||
}
|
||||
|
||||
return { status, contentType, data: JSON.parse(data.toString()), responseHeaders };
|
||||
}
|
||||
|
||||
export default async function KavitaProxyHandler(req, res) {
|
||||
const { group, service } = req.query;
|
||||
|
||||
if (!group || !service) {
|
||||
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
const widget = await getServiceWidget(group, service);
|
||||
if (!widget) {
|
||||
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
if (!cache.get(`${sessionTokenCacheKey}.${service}`)) {
|
||||
await login(widget, service);
|
||||
}
|
||||
|
||||
const { data: statsData } = await apiCall(widget, "Stats/server/stats", service);
|
||||
|
||||
return res.status(200).send({
|
||||
seriesCount: statsData?.seriesCount,
|
||||
totalFiles: statsData?.totalFiles
|
||||
});
|
||||
}
|
13
src/widgets/kavita/widget.js
Normal file
13
src/widgets/kavita/widget.js
Normal file
@ -0,0 +1,13 @@
|
||||
import kavitaProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/api/{endpoint}",
|
||||
proxyHandler: kavitaProxyHandler,
|
||||
mappings: {
|
||||
info: {
|
||||
endpoint: "/"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default widget;
|
@ -28,6 +28,7 @@ import immich from "./immich/widget";
|
||||
import jackett from "./jackett/widget";
|
||||
import jellyseerr from "./jellyseerr/widget";
|
||||
import jdownloader from "./jdownloader/widget";
|
||||
import kavita from "./kavita/widget";
|
||||
import komga from "./komga/widget";
|
||||
import kopia from "./kopia/widget";
|
||||
import lidarr from "./lidarr/widget";
|
||||
@ -104,7 +105,7 @@ const widgets = {
|
||||
diskstation,
|
||||
downloadstation,
|
||||
emby,
|
||||
evcc,
|
||||
evcc,
|
||||
fileflows,
|
||||
flood,
|
||||
freshrss,
|
||||
@ -123,6 +124,7 @@ const widgets = {
|
||||
jdrssdownloader,
|
||||
jdownloader,
|
||||
jellyseerr,
|
||||
kavita,
|
||||
komga,
|
||||
kopia,
|
||||
lidarr,
|
||||
|
@ -9,6 +9,11 @@ module.exports = {
|
||||
"./src/components/**/*.{js,ts,jsx,tsx}",
|
||||
"./src/widgets/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
variants: {
|
||||
extend: {
|
||||
display: ["group-hover"],
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user