mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-04-25 00:46:21 +00:00
Update menus
This commit is contained in:
@@ -1204,41 +1204,47 @@ export function SecureGatewaySetup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<DialogContent className="max-w-lg">
|
<DialogContent className="max-w-lg max-h-[90vh] sm:max-h-[85vh] flex flex-col p-0 gap-0">
|
||||||
<DialogHeader>
|
{/* Fixed Header */}
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<div className="shrink-0 px-6 pt-6 pb-4 border-b border-border">
|
||||||
<ShieldCheck className="h-5 w-5 text-cyan-500" />
|
<DialogHeader>
|
||||||
Secure Gateway Setup
|
<DialogTitle className="flex items-center gap-2">
|
||||||
</DialogTitle>
|
<ShieldCheck className="h-5 w-5 text-cyan-500" />
|
||||||
</DialogHeader>
|
Secure Gateway Setup
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
{/* Progress indicator - filter out "options" step if using Proxmox Only */}
|
{/* Progress indicator - filter out "options" step if using Proxmox Only */}
|
||||||
<div className="flex items-center gap-1 mb-4">
|
<div className="flex items-center gap-1 mt-4">
|
||||||
{wizardSteps
|
{wizardSteps
|
||||||
.filter((step) => !(config.access_mode === "host_only" && step.id === "options"))
|
.filter((step) => !(config.access_mode === "host_only" && step.id === "options"))
|
||||||
.map((step, idx) => {
|
.map((step, idx) => {
|
||||||
// Recalculate the actual step index accounting for skipped steps
|
// Recalculate the actual step index accounting for skipped steps
|
||||||
const actualIdx = wizardSteps.findIndex((s) => s.id === step.id)
|
const actualIdx = wizardSteps.findIndex((s) => s.id === step.id)
|
||||||
const adjustedCurrentStep = config.access_mode === "host_only"
|
const adjustedCurrentStep = config.access_mode === "host_only"
|
||||||
? (currentStep > wizardSteps.findIndex((s) => s.id === "options") ? currentStep - 1 : currentStep)
|
? (currentStep > wizardSteps.findIndex((s) => s.id === "options") ? currentStep - 1 : currentStep)
|
||||||
: currentStep
|
: currentStep
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.id}
|
key={step.id}
|
||||||
className={`flex-1 h-1 rounded-full transition-colors ${
|
className={`flex-1 h-1 rounded-full transition-colors ${
|
||||||
idx < adjustedCurrentStep ? "bg-cyan-500" :
|
idx < adjustedCurrentStep ? "bg-cyan-500" :
|
||||||
idx === adjustedCurrentStep ? "bg-cyan-500" :
|
idx === adjustedCurrentStep ? "bg-cyan-500" :
|
||||||
"bg-muted"
|
"bg-muted"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{renderWizardContent()}
|
{/* Scrollable Content */}
|
||||||
|
<div className="flex-1 overflow-y-auto px-6 py-4 min-h-0">
|
||||||
|
{renderWizardContent()}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Fixed Footer with Navigation */}
|
||||||
<div className="flex justify-between pt-4 border-t border-border">
|
<div className="shrink-0 flex justify-between px-6 py-4 border-t border-border bg-background">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -3754,6 +3754,9 @@ ${(report.sections && report.sections.length > 0) ? `
|
|||||||
<Printer className="h-3.5 w-3.5" />
|
<Printer className="h-3.5 w-3.5" />
|
||||||
<span className="hidden sm:inline">PDF</span>
|
<span className="hidden sm:inline">PDF</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
<ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${lynisShowReport ? "rotate-180" : ""}`} />
|
||||||
|
{/* Delete button separated with divider to prevent accidental clicks */}
|
||||||
|
<div className="hidden sm:block w-px h-5 bg-border mx-1" />
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -3770,12 +3773,11 @@ ${(report.sections && report.sections.length > 0) ? `
|
|||||||
.catch(() => setError("Failed to delete report"))
|
.catch(() => setError("Failed to delete report"))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="h-7 px-2 text-xs text-red-500 hover:text-red-400 hover:bg-red-500/10"
|
className="h-7 px-2 text-xs text-red-500 hover:text-red-400 hover:bg-red-500/10 ml-2 sm:ml-0"
|
||||||
title="Delete report"
|
title="Delete report"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
<ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${lynisShowReport ? "rotate-180" : ""}`} />
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -3802,26 +3804,34 @@ ${(report.sections && report.sections.length > 0) ? `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Report tabs */}
|
{/* Report tabs - responsive with shorter labels on mobile */}
|
||||||
<div className="flex gap-0 border-t border-border">
|
<div className="flex gap-0 border-t border-border overflow-x-auto">
|
||||||
{(["overview", "checks", "warnings", "suggestions"] as const).map((tab) => (
|
{(["overview", "checks", "warnings", "suggestions"] as const).map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab}
|
key={tab}
|
||||||
onClick={() => setLynisActiveTab(tab)}
|
onClick={() => setLynisActiveTab(tab)}
|
||||||
className={`flex-1 px-3 py-2 text-xs font-medium transition-all flex items-center justify-center gap-1.5 border-r last:border-r-0 border-border ${
|
className={`flex-1 min-w-0 px-2 sm:px-3 py-2 text-xs font-medium transition-all flex items-center justify-center gap-1 sm:gap-1.5 border-r last:border-r-0 border-border ${
|
||||||
lynisActiveTab === tab
|
lynisActiveTab === tab
|
||||||
? "bg-cyan-500 text-white"
|
? "bg-cyan-500 text-white"
|
||||||
: "bg-muted/20 text-muted-foreground hover:text-foreground hover:bg-muted/40"
|
: "bg-muted/20 text-muted-foreground hover:text-foreground hover:bg-muted/40"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab === "overview" && <BarChart3 className="h-3 w-3" />}
|
{tab === "overview" && <BarChart3 className="h-3 w-3 shrink-0" />}
|
||||||
{tab === "checks" && <Search className="h-3 w-3" />}
|
{tab === "checks" && <Search className="h-3 w-3 shrink-0" />}
|
||||||
{tab === "warnings" && <TriangleAlert className="h-3 w-3" />}
|
{tab === "warnings" && <TriangleAlert className="h-3 w-3 shrink-0" />}
|
||||||
{tab === "suggestions" && <Info className="h-3 w-3" />}
|
{tab === "suggestions" && <Info className="h-3 w-3 shrink-0" />}
|
||||||
{tab === "overview" ? "Overview"
|
<span className="hidden sm:inline">
|
||||||
: tab === "checks" ? `Checks (${lynisReport.sections?.length || 0})`
|
{tab === "overview" ? "Overview"
|
||||||
: tab === "warnings" ? `Warnings (${lynisReport.warnings.length})`
|
: tab === "checks" ? `Checks (${lynisReport.sections?.length || 0})`
|
||||||
: `Suggestions (${lynisReport.suggestions.length})`}
|
: tab === "warnings" ? `Warnings (${lynisReport.warnings.length})`
|
||||||
|
: `Suggestions (${lynisReport.suggestions.length})`}
|
||||||
|
</span>
|
||||||
|
<span className="sm:hidden">
|
||||||
|
{tab === "overview" ? ""
|
||||||
|
: tab === "checks" ? `(${lynisReport.sections?.length || 0})`
|
||||||
|
: tab === "warnings" ? `(${lynisReport.warnings.length})`
|
||||||
|
: `(${lynisReport.suggestions.length})`}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -403,13 +403,30 @@ class JournalWatcher:
|
|||||||
|
|
||||||
def _check_fail2ban(self, msg: str, syslog_id: str):
|
def _check_fail2ban(self, msg: str, syslog_id: str):
|
||||||
"""Detect Fail2Ban IP bans."""
|
"""Detect Fail2Ban IP bans."""
|
||||||
if 'fail2ban' not in msg.lower() and syslog_id != 'fail2ban-server':
|
# Only process actual fail2ban action messages, not systemd service events
|
||||||
return
|
if syslog_id not in ('fail2ban-server', 'fail2ban.actions', 'fail2ban'):
|
||||||
|
if 'fail2ban' not in msg.lower():
|
||||||
|
return
|
||||||
|
# Skip systemd service lifecycle messages (start/stop/restart/reload)
|
||||||
|
msg_lower = msg.lower()
|
||||||
|
if any(x in msg_lower for x in ['service', 'started', 'stopped', 'starting',
|
||||||
|
'stopping', 'reloading', 'reloaded', 'unit',
|
||||||
|
'deactivated', 'activated']):
|
||||||
|
return
|
||||||
|
|
||||||
# Ban detected
|
# Ban detected - match only valid IPv4 or IPv6 addresses
|
||||||
ban_match = re.search(r'Ban\s+(\S+)', msg)
|
# IPv4: 192.168.1.100, IPv6: 2001:db8::1 or ::ffff:192.168.1.1
|
||||||
|
ban_match = re.search(r'Ban\s+((?:\d{1,3}\.){3}\d{1,3}|[0-9a-fA-F:]{2,})', msg)
|
||||||
if ban_match:
|
if ban_match:
|
||||||
ip = ban_match.group(1)
|
ip = ban_match.group(1)
|
||||||
|
# Validate it's a real IP address format
|
||||||
|
# IPv4: must have 4 octets separated by dots
|
||||||
|
# IPv6: must contain at least one colon
|
||||||
|
is_ipv4 = re.match(r'^(\d{1,3}\.){3}\d{1,3}$', ip)
|
||||||
|
is_ipv6 = ':' in ip and re.match(r'^[0-9a-fA-F:]+$', ip)
|
||||||
|
if not is_ipv4 and not is_ipv6:
|
||||||
|
return # Not a valid IP (e.g., "Service.", "Ban", etc.)
|
||||||
|
|
||||||
jail_match = re.search(r'\[(\w+)\]', msg)
|
jail_match = re.search(r'\[(\w+)\]', msg)
|
||||||
jail = jail_match.group(1) if jail_match else 'unknown'
|
jail = jail_match.group(1) if jail_match else 'unknown'
|
||||||
|
|
||||||
|
|||||||
@@ -1384,7 +1384,7 @@ AI_DETAIL_TOKENS = {
|
|||||||
|
|
||||||
# System prompt template - optimized hybrid version
|
# System prompt template - optimized hybrid version
|
||||||
AI_SYSTEM_PROMPT = """You are a notification FORMATTER for ProxMenux Monitor (Proxmox VE).
|
AI_SYSTEM_PROMPT = """You are a notification FORMATTER for ProxMenux Monitor (Proxmox VE).
|
||||||
Your job: translate and reformat alerts into {language}. You are NOT an analyst <EFBFBD><EFBFBD><EFBFBD> do not interpret or diagnose.
|
Your job: translate and reformat alerts into {language}. You are NOT an analyst — do not interpret or diagnose.
|
||||||
|
|
||||||
═══ WHAT TO TRANSLATE ═══
|
═══ WHAT TO TRANSLATE ═══
|
||||||
Translate: labels, descriptions, status words, units (GB→Go in French, etc.)
|
Translate: labels, descriptions, status words, units (GB→Go in French, etc.)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import time
|
|||||||
import threading
|
import threading
|
||||||
from typing import Set, List, Tuple, Optional
|
from typing import Set, List, Tuple, Optional
|
||||||
|
|
||||||
# ─── Configuration ──────────────────────────────────<EFBFBD><EFBFBD>────────────────────────
|
# ─── Configuration ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Grace period durations (seconds)
|
# Grace period durations (seconds)
|
||||||
STARTUP_VM_GRACE_SECONDS = 180 # 3 minutes for VM/CT start aggregation
|
STARTUP_VM_GRACE_SECONDS = 180 # 3 minutes for VM/CT start aggregation
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ show_menu() {
|
|||||||
dialog --clear \
|
dialog --clear \
|
||||||
--backtitle "ProxMenux" \
|
--backtitle "ProxMenux" \
|
||||||
--title "$(translate "$menu_title")" \
|
--title "$(translate "$menu_title")" \
|
||||||
--menu "$(translate "Select an option:")" 20 70 10 \
|
--menu "$(translate "Select an option:")" 20 70 11 \
|
||||||
1 "$(translate "Settings post-install Proxmox")" \
|
1 "$(translate "Settings post-install Proxmox")" \
|
||||||
2 "$(translate "Hardware: GPUs and Coral-TPU")" \
|
2 "$(translate "Hardware: GPUs and Coral-TPU")" \
|
||||||
3 "$(translate "Create VM from template or script")" \
|
3 "$(translate "Create VM from template or script")" \
|
||||||
@@ -102,7 +102,8 @@ show_menu() {
|
|||||||
5 "$(translate "Mount and Share Manager")" \
|
5 "$(translate "Mount and Share Manager")" \
|
||||||
6 "$(translate "Proxmox VE Helper Scripts")" \
|
6 "$(translate "Proxmox VE Helper Scripts")" \
|
||||||
7 "$(translate "Network Management")" \
|
7 "$(translate "Network Management")" \
|
||||||
8 "$(translate "Utilities and Tools")" \
|
8 "$(translate "Security")" \
|
||||||
|
9 "$(translate "Utilities and Tools")" \
|
||||||
h "$(translate "Help and Info Commands")" \
|
h "$(translate "Help and Info Commands")" \
|
||||||
s "$(translate "Settings")" \
|
s "$(translate "Settings")" \
|
||||||
0 "$(translate "Exit")" 2>"$TEMP_FILE"
|
0 "$(translate "Exit")" 2>"$TEMP_FILE"
|
||||||
@@ -126,7 +127,8 @@ show_menu() {
|
|||||||
5) exec bash "$LOCAL_SCRIPTS/menus/share_menu.sh" ;;
|
5) exec bash "$LOCAL_SCRIPTS/menus/share_menu.sh" ;;
|
||||||
6) exec bash "$LOCAL_SCRIPTS/menus/menu_Helper_Scripts.sh" ;;
|
6) exec bash "$LOCAL_SCRIPTS/menus/menu_Helper_Scripts.sh" ;;
|
||||||
7) exec bash "$LOCAL_SCRIPTS/menus/network_menu.sh" ;;
|
7) exec bash "$LOCAL_SCRIPTS/menus/network_menu.sh" ;;
|
||||||
8) exec bash "$LOCAL_SCRIPTS/menus/utilities_menu.sh" ;;
|
8) exec bash "$LOCAL_SCRIPTS/menus/security_menu.sh" ;;
|
||||||
|
9) exec bash "$LOCAL_SCRIPTS/menus/utilities_menu.sh" ;;
|
||||||
h) bash "$LOCAL_SCRIPTS/help_info_menu.sh" ;;
|
h) bash "$LOCAL_SCRIPTS/help_info_menu.sh" ;;
|
||||||
s) exec bash "$LOCAL_SCRIPTS/menus/config_menu.sh" ;;
|
s) exec bash "$LOCAL_SCRIPTS/menus/config_menu.sh" ;;
|
||||||
0) clear; msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"; rm -f "$TEMP_FILE"; exit 0 ;;
|
0) clear; msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"; rm -f "$TEMP_FILE"; exit 0 ;;
|
||||||
|
|||||||
Reference in New Issue
Block a user