Section icons in the change journal, and admin scope on secret reveal

This commit is contained in:
MacRimi
2026-09-12 10:54:36 +02:00
parent 78d2d84f20
commit b5fb747271
12 changed files with 37 additions and 19 deletions
+16 -4
View File
@@ -5,7 +5,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"
import { Badge } from "./ui/badge" import { Badge } from "./ui/badge"
import { import {
ChevronDown, ChevronRight, FileCode, HelpCircle, Loader2, Package, ChevronDown, ChevronRight, FileCode, HelpCircle, Loader2, Package,
Play, Settings2, Play, Settings2, Sparkles, Terminal, Wrench,
} from "lucide-react" } from "lucide-react"
import { fetchApi } from "../lib/api-config" import { fetchApi } from "../lib/api-config"
import { useT, useI18n } from "../lib/i18n/provider" import { useT, useI18n } from "../lib/i18n/provider"
@@ -299,8 +299,10 @@ function ChangeCard({ change, expanded, onToggle, t, when }: {
) )
} }
function GroupSection({ title, groups, openFn, toggleFn, open, toggle, t, when }: { function GroupSection({ title, icon: Icon, iconClass, groups, openFn, toggleFn, open, toggle, t, when }: {
title: string title: string
icon: typeof Settings2
iconClass: string
groups: { key: string; label: string; version: string; last: number; items: Change[] }[] groups: { key: string; label: string; version: string; last: number; items: Change[] }[]
openFn: Set<string>; toggleFn: (k: string) => void openFn: Set<string>; toggleFn: (k: string) => void
open: Set<number>; toggle: (id: number) => void open: Set<number>; toggle: (id: number) => void
@@ -309,8 +311,15 @@ function GroupSection({ title, groups, openFn, toggleFn, open, toggle, t, when }
}) { }) {
if (groups.length === 0) return null if (groups.length === 0) return null
return ( return (
<div className="space-y-2"> // Padding on the child: the container's space-y-4 overrides any mt-*.
<h3 className="text-sm font-semibold text-foreground px-1">{title}</h3> <div className="space-y-2 pt-2">
{/* Same type and icon size as CardTitle, so a block heading here reads
exactly like a card heading on the Settings page. */}
<h3 className="px-1 flex items-center gap-2 text-2xl font-semibold
leading-none tracking-tight text-foreground">
<Icon className={`h-5 w-5 shrink-0 ${iconClass}`} />
{title}
</h3>
{groups.map((g) => { {groups.map((g) => {
const fnOpen = openFn.has(g.key) const fnOpen = openFn.has(g.key)
return ( return (
@@ -460,12 +469,15 @@ export function AuditChanges() {
</Card> </Card>
<GroupSection title={t("audit.changes.section.postInstall")} <GroupSection title={t("audit.changes.section.postInstall")}
icon={Sparkles} iconClass="text-orange-500"
groups={blocks.post} openFn={openFn} toggleFn={toggleFn} groups={blocks.post} openFn={openFn} toggleFn={toggleFn}
open={open} toggle={toggle} t={t} when={when} /> open={open} toggle={toggle} t={t} when={when} />
<GroupSection title={t("audit.changes.section.scripts")} <GroupSection title={t("audit.changes.section.scripts")}
icon={Terminal} iconClass="text-cyan-500"
groups={blocks.scripts} openFn={openFn} toggleFn={toggleFn} groups={blocks.scripts} openFn={openFn} toggleFn={toggleFn}
open={open} toggle={toggle} t={t} when={when} /> open={open} toggle={toggle} t={t} when={when} />
<GroupSection title={t("audit.changes.section.installs")} <GroupSection title={t("audit.changes.section.installs")}
icon={Wrench} iconClass="text-purple-400"
groups={blocks.installs} openFn={openFn} toggleFn={toggleFn} groups={blocks.installs} openFn={openFn} toggleFn={toggleFn}
open={open} toggle={toggle} t={t} when={when} /> open={open} toggle={toggle} t={t} when={when} />
{summary && summary.total > 0 {summary && summary.total > 0
+2 -2
View File
@@ -2,7 +2,7 @@
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"
import { Wrench, Package, Ruler, HeartPulse, Cpu, MemoryStick, HardDrive, CircleDot, Network, Server, Settings2, FileText, RefreshCw, Shield, AlertTriangle, Info, Loader2, Check, Database, CloudOff, Code, X, Copy, Sparkles, ArrowUpCircle, BellOff, Globe2, Github } from "lucide-react" import { Package, Ruler, HeartPulse, Cpu, MemoryStick, HardDrive, CircleDot, Network, Server, Settings2, FileText, RefreshCw, Shield, AlertTriangle, Info, Loader2, Check, Database, CloudOff, Code, X, Copy, Sparkles, ArrowUpCircle, BellOff, Globe2, Github } from "lucide-react"
import { Badge } from "./ui/badge" import { Badge } from "./ui/badge"
import { Button } from "./ui/button" import { Button } from "./ui/button"
import { NotificationSettings } from "./notification-settings" import { NotificationSettings } from "./notification-settings"
@@ -2034,7 +2034,7 @@ export function Settings() {
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Wrench className="h-5 w-5 text-orange-500" /> <Sparkles className="h-5 w-5 text-orange-500" />
<CardTitle>{t("settings.optimizations.title")}</CardTitle> <CardTitle>{t("settings.optimizations.title")}</CardTitle>
</div> </div>
<CardDescription>{t("settings.optimizations.description")}</CardDescription> <CardDescription>{t("settings.optimizations.description")}</CardDescription>
+1 -1
View File
@@ -1754,7 +1754,7 @@
"configure": "Konfigurieren", "configure": "Konfigurieren",
"tokenLabel": "Persönliches Zugriffstoken", "tokenLabel": "Persönliches Zugriffstoken",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "Das Token wird verschlüsselt gespeichert. ProxMenux verwendet es ausschließlich für schreibgeschützte GitHub-API-Anfragen und zeigt es danach nicht erneut an.", "tokenHelp": "Das Token wird verschlüsselt gespeichert. ProxMenux verwendet es ausschließlich für schreibgeschützte GitHub-API-Anfragen und zeigt es danach nicht erneut an. Ein Fine-grained-Token mit Lesezugriff auf öffentliche Repositories und ohne Kontoberechtigungen genügt.",
"configured": "Token konfiguriert", "configured": "Token konfiguriert",
"notConfigured": "Anonymes GitHub-Anfragekontingent wird verwendet", "notConfigured": "Anonymes GitHub-Anfragekontingent wird verwendet",
"removeToken": "Token entfernen", "removeToken": "Token entfernen",
+1 -1
View File
@@ -1753,7 +1753,7 @@
"configure": "Configure", "configure": "Configure",
"tokenLabel": "Personal access token", "tokenLabel": "Personal access token",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "The token is stored encrypted. ProxMenux uses it only for read-only GitHub API requests and never displays it again.", "tokenHelp": "The token is stored encrypted. ProxMenux uses it only for read-only GitHub API requests and never displays it again. A fine-grained token with read-only access to public repositories and no account permissions is enough.",
"configured": "Token configured", "configured": "Token configured",
"notConfigured": "Using GitHub's anonymous request quota", "notConfigured": "Using GitHub's anonymous request quota",
"removeToken": "Remove token", "removeToken": "Remove token",
+1 -1
View File
@@ -1754,7 +1754,7 @@
"configure": "Configurar", "configure": "Configurar",
"tokenLabel": "Token de acceso personal", "tokenLabel": "Token de acceso personal",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "El token se guarda cifrado. ProxMenux solo lo utiliza para consultas de solo lectura a la API de GitHub y no vuelve a mostrarlo.", "tokenHelp": "El token se guarda cifrado. ProxMenux solo lo utiliza para consultas de solo lectura a la API de GitHub y no vuelve a mostrarlo. Basta con un token fine-grained con acceso de solo lectura a repositorios públicos y sin permisos de cuenta.",
"configured": "Token configurado", "configured": "Token configurado",
"notConfigured": "Usando la cuota de solicitudes anónimas de GitHub", "notConfigured": "Usando la cuota de solicitudes anónimas de GitHub",
"removeToken": "Eliminar token", "removeToken": "Eliminar token",
+1 -1
View File
@@ -1754,7 +1754,7 @@
"configure": "Configurer", "configure": "Configurer",
"tokenLabel": "Jeton d'accès personnel", "tokenLabel": "Jeton d'accès personnel",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "Le jeton est stocké sous forme chiffrée. ProxMenux l'utilise uniquement pour des requêtes en lecture seule vers l'API GitHub et ne l'affiche plus ensuite.", "tokenHelp": "Le jeton est stocké sous forme chiffrée. ProxMenux l'utilise uniquement pour des requêtes en lecture seule vers l'API GitHub et ne l'affiche plus ensuite. Un jeton fine-grained avec un accès en lecture seule aux dépôts publics et sans autorisation de compte suffit.",
"configured": "Jeton configuré", "configured": "Jeton configuré",
"notConfigured": "Utilisation du quota de requêtes anonymes de GitHub", "notConfigured": "Utilisation du quota de requêtes anonymes de GitHub",
"removeToken": "Supprimer le jeton", "removeToken": "Supprimer le jeton",
+1 -1
View File
@@ -1754,7 +1754,7 @@
"configure": "Configura", "configure": "Configura",
"tokenLabel": "Token di accesso personale", "tokenLabel": "Token di accesso personale",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "Il token viene archiviato in forma cifrata. ProxMenux lo utilizza solo per richieste di sola lettura all'API GitHub e non lo mostra più dopo il salvataggio.", "tokenHelp": "Il token viene archiviato in forma cifrata. ProxMenux lo utilizza solo per richieste di sola lettura all'API GitHub e non lo mostra più dopo il salvataggio. È sufficiente un token fine-grained con accesso in sola lettura ai repository pubblici e senza permessi sull'account.",
"configured": "Token configurato", "configured": "Token configurato",
"notConfigured": "Utilizzo della quota di richieste anonime di GitHub", "notConfigured": "Utilizzo della quota di richieste anonime di GitHub",
"removeToken": "Rimuovi token", "removeToken": "Rimuovi token",
+1 -1
View File
@@ -1754,7 +1754,7 @@
"configure": "Configurar", "configure": "Configurar",
"tokenLabel": "Token de acesso pessoal", "tokenLabel": "Token de acesso pessoal",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "O token é armazenado de forma cifrada. O ProxMenux utiliza-o apenas para pedidos de leitura à API do GitHub e não volta a apresentá-lo.", "tokenHelp": "O token é armazenado de forma cifrada. O ProxMenux utiliza-o apenas para pedidos de leitura à API do GitHub e não volta a apresentá-lo. Basta um token fine-grained com acesso de leitura a repositórios públicos e sem permissões de conta.",
"configured": "Token configurado", "configured": "Token configurado",
"notConfigured": "A utilizar a quota de pedidos anónimos do GitHub", "notConfigured": "A utilizar a quota de pedidos anónimos do GitHub",
"removeToken": "Remover token", "removeToken": "Remover token",
+1 -1
View File
@@ -1753,7 +1753,7 @@
"configure": "Nastaviť", "configure": "Nastaviť",
"tokenLabel": "Osobný prístupový token", "tokenLabel": "Osobný prístupový token",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "Token sa ukladá šifrovane. ProxMenux ho používa iba na požiadavky GitHub API určené na čítanie a po uložení ho už nezobrazí.", "tokenHelp": "Token sa ukladá šifrovane. ProxMenux ho používa iba na požiadavky GitHub API určené na čítanie a po uložení ho už nezobrazí. Postačuje fine-grained token s prístupom na čítanie k verejným repozitárom a bez oprávnení k účtu.",
"configured": "Token je nastavený", "configured": "Token je nastavený",
"notConfigured": "Používa sa anonymná kvóta požiadaviek GitHubu", "notConfigured": "Používa sa anonymná kvóta požiadaviek GitHubu",
"removeToken": "Odstrániť token", "removeToken": "Odstrániť token",
+1 -1
View File
@@ -1754,7 +1754,7 @@
"configure": "Konfigurera", "configure": "Konfigurera",
"tokenLabel": "Personlig åtkomsttoken", "tokenLabel": "Personlig åtkomsttoken",
"tokenPlaceholder": "github_pat_...", "tokenPlaceholder": "github_pat_...",
"tokenHelp": "Token lagras krypterad. ProxMenux använder den endast för skrivskyddade anrop till GitHub API och visar den inte igen efter att den har sparats.", "tokenHelp": "Token lagras krypterad. ProxMenux använder den endast för skrivskyddade anrop till GitHub API och visar den inte igen efter att den har sparats. Det räcker med en fine-grained token med läsbehörighet till publika repositorier och utan kontobehörigheter.",
"configured": "Token konfigurerad", "configured": "Token konfigurerad",
"notConfigured": "GitHubs anonyma förfrågningskvot används", "notConfigured": "GitHubs anonyma förfrågningskvot används",
"removeToken": "Ta bort token", "removeToken": "Ta bort token",
+10 -4
View File
@@ -12,7 +12,7 @@ from collections import deque
from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request
from notification_manager import notification_manager, SENSITIVE_PLACEHOLDER, validate_external_url from notification_manager import notification_manager, SENSITIVE_PLACEHOLDER, validate_external_url
from notification_channels import CHANNEL_TYPES as _NOTIF_CHANNEL_TYPES from notification_channels import CHANNEL_TYPES as _NOTIF_CHANNEL_TYPES
from jwt_middleware import require_auth from jwt_middleware import require_auth, require_admin_scope
def _resolve_masked_api_key(provider, api_key): def _resolve_masked_api_key(provider, api_key):
@@ -314,15 +314,21 @@ def save_notification_settings():
@notification_bp.route('/api/notifications/reveal-secret', methods=['POST']) @notification_bp.route('/api/notifications/reveal-secret', methods=['POST'])
@require_auth @require_admin_scope
def reveal_notification_secret(): def reveal_notification_secret():
"""Return one sensitive config value in cleartext. """Return one sensitive config value in cleartext.
Backs the "eye" toggle in the Settings UI. The settings GET masks Backs the "eye" toggle in the Settings UI. The settings GET masks
every entry in SENSITIVE_KEYS with `'************'` so the secret every entry in SENSITIVE_KEYS with `'************'` so the secret
never leaves the server just because someone loaded the page; this never leaves the server just because someone loaded the page; this
endpoint lets an authenticated operator explicitly request the endpoint lets an operator explicitly request the real value for a
real value for a single key when they need to inspect it. single key when they need to inspect it.
Requires `full_admin` scope: this is the one route that hands back a
stored credential in cleartext, so a read-only API token — which is
handed out precisely because it cannot reach credentials — must not
reach it. Session logins carry full_admin implicitly, so the eye
toggle in the UI is unaffected.
Body schema (one of): Body schema (one of):
{"ai_provider": "groq" | "anthropic" | …} {"ai_provider": "groq" | "anthropic" | …}
@@ -2322,7 +2322,7 @@ EOF
remove_subscription_banner() { remove_subscription_banner() {
local FUNC_VERSION="1.1" local FUNC_VERSION="1.0"
# description: Patch the Proxmox web UI to suppress the "no valid subscription" dialog (PVE 8 + 9 variants supported). # description: Patch the Proxmox web UI to suppress the "no valid subscription" dialog (PVE 8 + 9 variants supported).
local pve_version local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1) pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)