mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-29 10:06:41 +00:00
Section icons in the change journal, and admin scope on secret reveal
This commit is contained in:
@@ -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,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>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user