mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-04 11:52:44 +00:00
New version 1.2.8
New version 1.2.8
This commit is contained in:
@@ -397,6 +397,10 @@ const SELFHST_THEME_LOGOS: Record<string, { lightTheme: string; darkTheme: strin
|
||||
lightTheme: `${SELFHST_WEBP_BASE}/frigate-dark.webp`,
|
||||
darkTheme: `${SELFHST_WEBP_BASE}/frigate-light.webp`,
|
||||
},
|
||||
"llama-cpp": {
|
||||
lightTheme: `${SELFHST_WEBP_BASE}/llama-cpp.webp`,
|
||||
darkTheme: `${SELFHST_WEBP_BASE}/llama-cpp-light.webp`,
|
||||
},
|
||||
portainer: {
|
||||
lightTheme: `${SELFHST_WEBP_BASE}/portainer-dark.webp`,
|
||||
darkTheme: `${SELFHST_WEBP_BASE}/portainer-light.webp`,
|
||||
@@ -409,7 +413,7 @@ const SELFHST_THEME_LOGOS: Record<string, { lightTheme: string; darkTheme: strin
|
||||
|
||||
function selfhstThemeLogos(src: string) {
|
||||
if (!src.toLowerCase().includes("cdn.jsdelivr.net/gh/selfhst/icons")) return null
|
||||
const match = src.toLowerCase().match(/\/(frigate|portainer|vaultwarden)(?:-(?:dark|light))?\.webp(?:[?#].*)?$/)
|
||||
const match = src.toLowerCase().match(/\/(frigate|llama-cpp|portainer|vaultwarden)(?:-(?:dark|light))?\.webp(?:[?#].*)?$/)
|
||||
return match ? SELFHST_THEME_LOGOS[match[1]] : null
|
||||
}
|
||||
|
||||
|
||||
@@ -804,7 +804,7 @@ export function StorageOverview({ disksOnly = false }: { disksOnly?: boolean } =
|
||||
</div>
|
||||
)}
|
||||
{/* Reallocated only meaningful on rotating disks. */}
|
||||
{disk.reallocated_sectors !== undefined && (disk.rotation_rate ?? 0) > 0 && (
|
||||
{disk.reallocated_sectors !== undefined && resolveDiskType(disk.name, disk.rotation_rate) === "HDD" && (
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<span className="text-[11px] uppercase tracking-wider text-muted-foreground">
|
||||
{t("storage.reallocatedShort")}
|
||||
@@ -860,12 +860,7 @@ export function StorageOverview({ disksOnly = false }: { disksOnly?: boolean } =
|
||||
// Resolve disk class → threshold pair from the user-configurable
|
||||
// backend (single source of truth). The semantics: temp BELOW warn
|
||||
// is green, between warn and hot is amber, hot or above is red.
|
||||
let cls: keyof DiskTempMap = "HDD"
|
||||
if (diskName?.startsWith("nvme")) {
|
||||
cls = "NVMe"
|
||||
} else if (!rotationRate || rotationRate === 0) {
|
||||
cls = "SSD"
|
||||
}
|
||||
const cls: keyof DiskTempMap = resolveDiskType(diskName ?? "", rotationRate)
|
||||
const t = dtThresholds[cls]
|
||||
if (temp >= t.hot) return "text-red-500"
|
||||
if (temp >= t.warn) return "text-yellow-500"
|
||||
@@ -896,9 +891,9 @@ export function StorageOverview({ disksOnly = false }: { disksOnly?: boolean } =
|
||||
return t("storage.duration.daysShort", { days: totalDays })
|
||||
}
|
||||
|
||||
const formatRotationRate = (rpm: number | undefined) => {
|
||||
if (!rpm || rpm === 0) return "SSD"
|
||||
return `${rpm.toLocaleString()} RPM`
|
||||
const formatRotationRate = (diskName: string, rpm: number | undefined) => {
|
||||
if (getDiskType(diskName, rpm) !== "HDD") return "SSD"
|
||||
return rpm && rpm > 0 ? `${rpm.toLocaleString()} RPM` : t("common.notAvailable")
|
||||
}
|
||||
|
||||
// Thin wrapper over the shared classifier so the rest of the file
|
||||
@@ -2626,7 +2621,7 @@ export function StorageOverview({ disksOnly = false }: { disksOnly?: boolean } =
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">{t("storage.rotationRate")}</p>
|
||||
<p className="font-medium">{formatRotationRate(selectedDisk.rotation_rate)}</p>
|
||||
<p className="font-medium">{formatRotationRate(selectedDisk.name, selectedDisk.rotation_rate)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">{t("storage.powerCycles")}</p>
|
||||
@@ -2871,14 +2866,12 @@ function openSmartReport(disk: DiskInfo, testStatus: SmartTestStatus, smartAttri
|
||||
|
||||
// Determine disk type (SAS detected via backend flag or connection_type)
|
||||
const isSasDisk = sd?.is_sas === true || disk.connection_type === 'sas'
|
||||
let diskType = "HDD"
|
||||
if (disk.name.startsWith("nvme")) {
|
||||
diskType = "NVMe"
|
||||
} else if (isSasDisk) {
|
||||
let diskType: string = resolveDiskType(disk.name, disk.rotation_rate)
|
||||
if (diskType !== "NVMe" && isSasDisk) {
|
||||
diskType = "SAS"
|
||||
} else if (!disk.rotation_rate || disk.rotation_rate === 0) {
|
||||
diskType = "SSD"
|
||||
}
|
||||
const rpm = disk.rotation_rate && disk.rotation_rate > 0 ? disk.rotation_rate : 0
|
||||
const known = (value?: string) => (value && value !== "Unknown" ? value : undefined)
|
||||
|
||||
// Health status styling
|
||||
const healthStatus = String(testStatus.smart_status || (testStatus.smart_data?.smart_status) || 'unknown')
|
||||
@@ -3824,11 +3817,11 @@ function pmxPrint(){
|
||||
<div class="grid-4">
|
||||
<div class="card">
|
||||
<div class="card-label">${tSmart("labels.model")}</div>
|
||||
<div class="card-value" style="font-size:11px;">${disk.model || sd?.model || t("app.unknown")}</div>
|
||||
<div class="card-value" style="font-size:11px;">${known(disk.model) || known(sd?.model) || t("app.unknown")}</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-label">${tSmart("labels.serial")}</div>
|
||||
<div class="card-value" style="font-size:11px;font-family:monospace;">${disk.serial || sd?.serial || t("app.unknown")}</div>
|
||||
<div class="card-value" style="font-size:11px;font-family:monospace;">${known(disk.serial) || known(sd?.serial) || t("app.unknown")}</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-label">${tSmart("labels.capacity")}</div>
|
||||
@@ -3836,7 +3829,7 @@ function pmxPrint(){
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-label">${tSmart("labels.type")}</div>
|
||||
<div class="card-value" style="font-size:11px;">${diskType === 'SAS' ? (disk.rotation_rate ? `SAS ${disk.rotation_rate} RPM` : 'SAS SSD') : diskType === 'HDD' && disk.rotation_rate ? `HDD ${disk.rotation_rate} RPM` : diskType}</div>
|
||||
<div class="card-value" style="font-size:11px;">${diskType === 'SAS' ? (rpm ? `SAS ${rpm} RPM` : disk.rotation_rate === -1 ? 'SAS HDD' : 'SAS SSD') : diskType === 'HDD' && rpm ? `HDD ${rpm} RPM` : diskType}</div>
|
||||
</div>
|
||||
</div>
|
||||
${(modelFamily || formFactor || sataVersion || ifaceSpeed) ? `
|
||||
|
||||
@@ -27,6 +27,8 @@ import sys
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
import disk_identity
|
||||
|
||||
SCHEMA_VERSION = 2
|
||||
|
||||
# Disk entries in a guest configuration: rootfs and mpN for containers,
|
||||
@@ -630,12 +632,16 @@ def _physical_disks(ctx) -> list[dict[str, Any]]:
|
||||
size = row.get("SIZE", "")
|
||||
name = row.get("NAME", "")
|
||||
health = smart.get(name) or {}
|
||||
# The Monitor's rotation when it read the disk, so both agree.
|
||||
rate = health.get("rotation_rate")
|
||||
if not isinstance(rate, int):
|
||||
rate = -1 if row.get("ROTA") == "1" else 0
|
||||
disks.append({
|
||||
"name": name,
|
||||
"model": (row.get("MODEL") or "").strip(),
|
||||
"serial": (row.get("SERIAL") or "").strip(),
|
||||
"size_bytes": int(size) if size.isdigit() else None,
|
||||
"rotational": row.get("ROTA") == "1",
|
||||
"rotational": disk_identity.media_type(name, rate) == "HDD",
|
||||
"bus": (row.get("TRAN") or "").strip(),
|
||||
"health": health.get("smart_status"),
|
||||
"temperature": health.get("temperature"),
|
||||
|
||||
@@ -15,6 +15,7 @@ import subprocess
|
||||
from typing import Any, Dict, List
|
||||
|
||||
_LSBLK_TIMEOUT = 5
|
||||
_SYS_BLOCK = "/sys/block"
|
||||
_FIELD_RE = re.compile(r'(\w+)="([^"]*)"')
|
||||
_SKIP_PREFIXES = ("loop", "zd", "nbd", "ram", "sr")
|
||||
|
||||
@@ -69,6 +70,27 @@ def list_physical_disks() -> List[Dict[str, Any]]:
|
||||
return disks
|
||||
|
||||
|
||||
def rotation_rate(name: str, reported: Any = None) -> int:
|
||||
"""The rotation every view of a disk shows: the RPM smartctl reported,
|
||||
0 meaning solid state, or else the kernel's flag, where -1 is a spinning
|
||||
disk of unknown speed. What smartctl reported wins, because USB-SATA
|
||||
bridges mark every disk behind them as rotational."""
|
||||
if isinstance(reported, int) and not isinstance(reported, bool) and reported >= 0:
|
||||
return reported
|
||||
try:
|
||||
with open(f"{_SYS_BLOCK}/{name}/queue/rotational") as flag:
|
||||
return -1 if flag.read().strip() == "1" else 0
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
|
||||
def media_type(name: str, rate: int) -> str:
|
||||
"""NVMe, SSD or HDD, from a rotation as ``rotation_rate`` returns it."""
|
||||
if name.startswith("nvme"):
|
||||
return "NVMe"
|
||||
return "HDD" if rate else "SSD"
|
||||
|
||||
|
||||
def names_for_keys(keys) -> set:
|
||||
"""Current kernel names of the disks whose identity is in ``keys``."""
|
||||
if not keys:
|
||||
|
||||
@@ -52,6 +52,7 @@ import time
|
||||
import threading
|
||||
import urllib.parse
|
||||
import uuid
|
||||
import disk_identity
|
||||
import hardware_monitor
|
||||
from health_persistence import health_persistence
|
||||
import xml.etree.ElementTree as ET
|
||||
@@ -5263,11 +5264,7 @@ def _smart_identity_payload(disk_name):
|
||||
base['serial'] = value.strip()
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
if Path(f'/sys/block/{disk_name}/queue/rotational').read_text().strip() == '1':
|
||||
base['rotation_rate'] = -1 # HDD, RPM unknown
|
||||
except OSError:
|
||||
pass
|
||||
base['rotation_rate'] = disk_identity.rotation_rate(disk_name)
|
||||
return base
|
||||
|
||||
|
||||
@@ -5327,7 +5324,8 @@ def get_smart_data(disk_name):
|
||||
|
||||
retry_at = _smart_fail_backoff.get(disk_name, 0)
|
||||
if retry_at > now:
|
||||
return dict(cached[1]) if cached else _smart_default_payload()
|
||||
# smartctl keeps failing: show what the host knows of the disk.
|
||||
return dict(cached[1]) if cached else _smart_identity_payload(disk_name)
|
||||
|
||||
result = _get_smart_data_uncached(disk_name)
|
||||
|
||||
@@ -5801,6 +5799,11 @@ def _get_smart_data_uncached(disk_name):
|
||||
# print(f"[v0] Health: CRITICAL (SMART failed)")
|
||||
pass
|
||||
|
||||
# Without an answer from smartctl, the kernel's flag; the temperature
|
||||
# class below depends on it.
|
||||
if not smart_data['_rotation_known']:
|
||||
smart_data['rotation_rate'] = disk_identity.rotation_rate(disk_name)
|
||||
|
||||
# Temperature-based health (only if we have a valid temperature)
|
||||
# Thresholds differ by disk class (HDD/SSD/NVMe/SAS) and are
|
||||
# user-configurable via health_thresholds; the values below are
|
||||
@@ -5831,27 +5834,6 @@ def _get_smart_data_uncached(disk_name):
|
||||
smart_data['health'] = 'critical'
|
||||
elif temp > warn:
|
||||
smart_data['health'] = 'warning'
|
||||
|
||||
# Fallback: ask the kernel only when smartctl had no opinion.
|
||||
# USB-SATA enclosures (ASM105x family etc.) advertise
|
||||
# rotational=1 to the kernel even when there's an SSD behind
|
||||
# them, so trusting /sys here without first checking what
|
||||
# smartctl said would flip a correctly-identified SSD back to
|
||||
# HDD. The previous condition (`rotation_rate == 0`) couldn't
|
||||
# tell "smartctl confirmed SSD" from "smartctl never spoke",
|
||||
# which is what bit the OCZ-SOLID2 behind an ASM105x dock on
|
||||
# the lab host (sdd showed as HDD in the UI while smartctl
|
||||
# was reporting it as SSD).
|
||||
if not smart_data['_rotation_known']:
|
||||
try:
|
||||
rotational_path = f"/sys/block/{disk_name}/queue/rotational"
|
||||
if os.path.exists(rotational_path):
|
||||
with open(rotational_path, 'r') as f:
|
||||
rotational = int(f.read().strip())
|
||||
if rotational == 1:
|
||||
smart_data['rotation_rate'] = -1 # HDD, RPM unknown
|
||||
except Exception:
|
||||
pass
|
||||
except FileNotFoundError:
|
||||
# print(f"[v0] ERROR: smartctl not found - install smartmontools for disk monitoring.")
|
||||
pass
|
||||
@@ -11523,7 +11505,7 @@ def api_smart_status(disk_name):
|
||||
'temperature': temp,
|
||||
'power_on_hours': poh,
|
||||
'power_cycles': cycles,
|
||||
'rotation_rate': data.get('rotation_rate', 0),
|
||||
'rotation_rate': disk_identity.rotation_rate(disk_name, data.get('rotation_rate')),
|
||||
'form_factor': form_factor,
|
||||
'physical_block_size': physical_block_size,
|
||||
'trim_supported': trim_supported,
|
||||
|
||||
@@ -3163,7 +3163,10 @@ class NotificationManager:
|
||||
if not ok:
|
||||
raise ValueError(f"Invalid ai_ollama_url: {err}")
|
||||
if short_key == 'ai_openai_base_url' and str(value):
|
||||
ok, err = validate_external_url(str(value), allow_loopback=False)
|
||||
# Custom OpenAI-compatible servers can run on the LAN or
|
||||
# localhost, just like Ollama. Match test-ai/provider-models;
|
||||
# unsafe schemes, metadata hosts and link-local stay blocked.
|
||||
ok, err = validate_external_url(str(value), allow_loopback=True)
|
||||
if not ok:
|
||||
raise ValueError(f"Invalid ai_openai_base_url: {err}")
|
||||
|
||||
|
||||
@@ -69,6 +69,41 @@ class OpenAICompatibleModelTests(unittest.TestCase):
|
||||
self.addCleanup(patcher.stop)
|
||||
return db_path
|
||||
|
||||
def test_local_compatible_endpoint_can_be_saved_and_reloaded(self):
|
||||
db_path = self._temp_db_patch()
|
||||
# Synthetic addresses cover each RFC1918 range; no network is used.
|
||||
for url in ('http://10.0.0.10:8080/v1', 'http://172.16.0.10:8080/v1',
|
||||
'http://192.168.1.10:8080/v1', 'http://localhost:8080/v1',
|
||||
'http://127.0.0.1:8080/v1'):
|
||||
with self.subTest(url=url):
|
||||
manager = self._manager({})
|
||||
result = manager.save_settings({'ai_openai_base_url': url})
|
||||
self.assertTrue(result['success'], result)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
saved = conn.execute(
|
||||
'SELECT setting_value FROM user_settings WHERE setting_key=?',
|
||||
('notification.ai_openai_base_url',),
|
||||
).fetchone()
|
||||
self.assertEqual(saved, (url,))
|
||||
reloaded = self._manager({})
|
||||
reloaded._load_config()
|
||||
self.assertEqual(reloaded._config['ai_openai_base_url'], url)
|
||||
|
||||
def test_local_compatible_endpoint_keeps_unsafe_urls_blocked(self):
|
||||
db_path = self._temp_db_patch()
|
||||
for url in ('file:///etc/shadow', 'http://169.254.169.254/latest/meta-data/',
|
||||
'http://metadata.google.internal/', 'http://[fe80::1]/',
|
||||
'http://0.0.0.0:8080/v1', 'http://224.0.0.1/v1'):
|
||||
with self.subTest(url=url):
|
||||
result = self._manager({}).save_settings({'ai_openai_base_url': url})
|
||||
self.assertFalse(result['success'], result)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
saved = conn.execute(
|
||||
'SELECT setting_value FROM user_settings WHERE setting_key=?',
|
||||
('notification.ai_openai_base_url',),
|
||||
).fetchone()
|
||||
self.assertIsNone(saved)
|
||||
|
||||
def test_custom_openai_endpoint_omits_temperature_for_opaque_alias(self):
|
||||
provider = CapturingOpenAIProvider(
|
||||
api_key="token",
|
||||
|
||||
@@ -23,6 +23,7 @@ A special thank you goes to [@Vaso73](https://github.com/Vaso73), [@f3rs3n](http
|
||||
- **The Ollama entry describes what the image provides** ([PR #566](https://github.com/MacRimi/ProxMenux/pull/566), contributed by [@jcastro](https://github.com/jcastro)): its HTTP API on port 11434, with its link and startup check on `/api/version`.
|
||||
- **An update clears the snapshot an interrupted backup leaves** ([#565](https://github.com/MacRimi/ProxMenux/issues/565), reported by [@kiosvantra](https://github.com/kiosvantra)). A snapshot made by the user stops the update, because a rebuilt container cannot keep it.
|
||||
- **A container that ignores the shutdown signal is stopped after 60 seconds**, as `docker stop` does, during an update and when an interrupted operation is recovered.
|
||||
- **Modify and updates accept every application whose image declares a data path**: SearXNG, Tor, Whoogle and BIND9 keep it in a volume of its own, Kanboard keeps its certificates, and Chrony, Jaeger, PlantUML, UrBackup, GoToSocial, AceStream HTTP Proxy and RomM mark the paths that hold no data. When an installation still keeps such a path in the container, the message names it.
|
||||
|
||||
## 🖥️ Monitor — Proxmox updates and pending reboots
|
||||
|
||||
@@ -59,6 +60,7 @@ A special thank you goes to [@Vaso73](https://github.com/Vaso73), [@f3rs3n](http
|
||||
- **Bitwarden recognizes the 2FA field on the Monitor login** ([#570](https://github.com/MacRimi/ProxMenux/issues/570), reported by [@shtefko](https://github.com/shtefko)): the backup code hint keeps its text and place, outside the group of the field.
|
||||
- **Secure Gateway installs on Proxmox hosts on BTRFS** ([#564](https://github.com/MacRimi/ProxMenux/issues/564), reported by [@78v5](https://github.com/78v5)): the Alpine template goes to an active storage that holds container templates.
|
||||
- **A disk I/O error from the kernel is recorded on its disk**, with its serial number, also when the error names a partition.
|
||||
- **Every view of a disk shows the same type**: the Storage page, the disk details, the SMART report and the Audit & Report inventory take it from one source. A hard disk smartctl cannot read keeps its type, model and serial number, and a hard disk that reports no speed shows as HDD.
|
||||
- **The Monitor installs as an app (PWA) from the browser**: it serves its own web app manifest and service worker, with the 192 and 512 px icons Chrome requires.
|
||||
- **The Y-axis titles stay inside the overview charts** ([PR #539](https://github.com/MacRimi/ProxMenux/pull/539), contributed by [@f3rs3n](https://github.com/f3rs3n)).
|
||||
- **The application version-tracking catalog builds from the current Helper-Scripts repositories**.
|
||||
|
||||
+9
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
|
||||
|
||||
44 of 532 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
|
||||
52 of 532 applications in the OCI catalog have been tested for real: 49 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
|
||||
|
||||
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
|
||||
|
||||
@@ -38,6 +38,7 @@ Questions and ideas about OCI testing go in [discussion #440](https://github.com
|
||||
| Alby Hub ✨ | Finance & Budgeting |
|
||||
| Alist | Files & Sharing |
|
||||
| aMule | Files & Downloads |
|
||||
| Authentik | Authentication & Security |
|
||||
| Chromium | Browsers & Web Desktops |
|
||||
| CodeProject.AI Server | AI |
|
||||
| CopyParty | Files & Sharing |
|
||||
@@ -50,26 +51,33 @@ Questions and ideas about OCI testing go in [discussion #440](https://github.com
|
||||
| Flexget | *Arr Suite |
|
||||
| Frigate | NVR & Cameras |
|
||||
| Grafana | Monitoring & Analytics |
|
||||
| HAOS One | IoT & Smart Home |
|
||||
| Immich | Media |
|
||||
| JDownloader | Files & Downloads |
|
||||
| Jenkins CI/CD | AI / Coding & Dev-Tools |
|
||||
| Linkwarden | Documents & Notes |
|
||||
| Mariadb | Databases |
|
||||
| Memos | Documents & Notes |
|
||||
| MineOS | Gaming & Leisure |
|
||||
| Motioneye | NVR & Cameras |
|
||||
| Navidrome | Media & Streaming |
|
||||
| Nextcloud | Productivity & Workflows |
|
||||
| Nextcloud Stack | Productivity & Workflows |
|
||||
| OpenList | Files & Sharing |
|
||||
| Openssh Server | Remote Access & VPN |
|
||||
| Paperless-ngx | Productivity & Workflows |
|
||||
| Phpmyadmin | Databases |
|
||||
| Prowlarr | *Arr Suite |
|
||||
| Qbittorrent | Files & Downloads |
|
||||
| Rclone WebUI | Backup & Recovery |
|
||||
| Real-Debrid Torrent Client | Files & Downloads |
|
||||
| Scanopy | Network & Firewall |
|
||||
| ShareCoveX | Files & Sharing |
|
||||
| SnapOtter | Media & Streaming |
|
||||
| Sonarr | *Arr Suite |
|
||||
| Tandoor Recipes | Productivity & Workflows |
|
||||
| Thelounge | Communication & Community |
|
||||
| Trilium | Documents & Notes |
|
||||
| Vaultwarden | Authentication & Security |
|
||||
| Wireguard | Remote Access & VPN |
|
||||
| WireGuard Easy | Remote Access & VPN |
|
||||
|
||||
@@ -323,6 +323,9 @@
|
||||
"AceStream HTTP Proxy has no web interface: a media player opens its streams.",
|
||||
"HLS stream: port 6878, path /ace/manifest.m3u8?id= followed by the channel ID.",
|
||||
"MPEG-TS stream: port 6878, path /ace/getstream?id= followed by the channel ID."
|
||||
],
|
||||
"non_persistent_image_volumes": [
|
||||
"/home/appuser/.ACEStream"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -76,6 +76,38 @@
|
||||
"backup": true,
|
||||
"default_size_gb": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-1",
|
||||
"container_path": "/etc/bind",
|
||||
"compose_source_example": "./config",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-2",
|
||||
"container_path": "/var/lib/bind",
|
||||
"compose_source_example": "./lib",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
@@ -123,7 +155,9 @@
|
||||
"53:53/tcp"
|
||||
],
|
||||
"volumes": [
|
||||
"./bind:/var/cache/bind"
|
||||
"./bind:/var/cache/bind",
|
||||
"./config:/etc/bind",
|
||||
"./lib:/var/lib/bind"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -151,6 +185,28 @@
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "bind9-volume-1",
|
||||
"service": "bind9",
|
||||
"container_path": "/etc/bind",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": true,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "bind9-volume-2",
|
||||
"service": "bind9",
|
||||
"container_path": "/var/lib/bind",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": true,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
@@ -349,7 +405,11 @@
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {},
|
||||
"installer_profile": {
|
||||
"non_persistent_image_volumes": [
|
||||
"/var/log"
|
||||
]
|
||||
},
|
||||
"catalog": {
|
||||
"replaces_discovered_ids": []
|
||||
}
|
||||
|
||||
@@ -339,7 +339,13 @@
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {}
|
||||
"installer_profile": {
|
||||
"non_persistent_image_volumes": [
|
||||
"/etc/chrony",
|
||||
"/run/chrony",
|
||||
"/var/lib/chrony"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"automatic_install_candidate": true,
|
||||
|
||||
@@ -409,7 +409,10 @@
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
}
|
||||
},
|
||||
"non_persistent_image_volumes": [
|
||||
"/gotosocial/.cache"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
|
||||
@@ -345,7 +345,11 @@
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {},
|
||||
"installer_profile": {
|
||||
"non_persistent_image_volumes": [
|
||||
"/tmp"
|
||||
]
|
||||
},
|
||||
"catalog": {
|
||||
"replaces_discovered_ids": []
|
||||
}
|
||||
|
||||
@@ -94,6 +94,22 @@
|
||||
"backup": true,
|
||||
"default_size_gb": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-2",
|
||||
"container_path": "/etc/nginx/ssl",
|
||||
"compose_source_example": "./ssl",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
@@ -134,7 +150,8 @@
|
||||
],
|
||||
"volumes": [
|
||||
"./data:/var/www/app/data",
|
||||
"./plugins:/var/www/app/plugins"
|
||||
"./plugins:/var/www/app/plugins",
|
||||
"./ssl:/etc/nginx/ssl"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -171,6 +188,17 @@
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "kanboard-volume-2",
|
||||
"service": "kanboard",
|
||||
"container_path": "/etc/nginx/ssl",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
@@ -391,6 +419,12 @@
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
},
|
||||
{
|
||||
"container_path": "/etc/nginx/ssl",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-root",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"startup_healthcheck": {
|
||||
|
||||
@@ -329,7 +329,10 @@
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
}
|
||||
},
|
||||
"non_persistent_image_volumes": [
|
||||
"/tmp/jetty"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
|
||||
@@ -788,6 +788,9 @@
|
||||
"owner_strategy": "mapped-root",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"non_persistent_image_volumes": [
|
||||
"/romm"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -85,6 +85,22 @@
|
||||
"backup": true,
|
||||
"default_size_gb": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-1",
|
||||
"container_path": "/etc/searxng",
|
||||
"compose_source_example": "./config",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
@@ -127,7 +143,8 @@
|
||||
"8080:8080"
|
||||
],
|
||||
"volumes": [
|
||||
"./cache:/var/cache/searxng"
|
||||
"./cache:/var/cache/searxng",
|
||||
"./config:/etc/searxng"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -153,6 +170,17 @@
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "searxng-volume-1",
|
||||
"service": "searxng",
|
||||
"container_path": "/etc/searxng",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
@@ -378,6 +406,12 @@
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
},
|
||||
{
|
||||
"container_path": "/etc/searxng",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"startup_healthcheck": {
|
||||
|
||||
@@ -60,7 +60,24 @@
|
||||
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
|
||||
},
|
||||
"environment": [],
|
||||
"volumes": [],
|
||||
"volumes": [
|
||||
{
|
||||
"id": "volume-0",
|
||||
"container_path": "/var/lib/tor",
|
||||
"compose_source_example": "./tor-data",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"container_port": 9050,
|
||||
@@ -103,6 +120,9 @@
|
||||
"ports": [
|
||||
"9050:9050",
|
||||
"8118:8118"
|
||||
],
|
||||
"volumes": [
|
||||
"./tor-data:/var/lib/tor"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -119,7 +139,19 @@
|
||||
"dependency_external_access": "disabled-unless-service-publishes-ports",
|
||||
"prompt_user_for_private_network": false
|
||||
},
|
||||
"storage": [],
|
||||
"storage": [
|
||||
{
|
||||
"id": "tor-volume-0",
|
||||
"service": "tor",
|
||||
"container_path": "/var/lib/tor",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
"reserve_vmids_atomically": 1,
|
||||
"start_order": [
|
||||
@@ -323,7 +355,19 @@
|
||||
"SOCKS5 proxy: configure your application with the LXC IP address shown above and port 9050.",
|
||||
"HTTP CONNECT proxy: use the same IP address and port 8118. These addresses are proxy settings, not web links.",
|
||||
"Only applications configured to use this proxy send their traffic through Tor."
|
||||
]
|
||||
],
|
||||
"volume_preparations": [
|
||||
{
|
||||
"container_path": "/var/lib/tor",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"volume_owner": {
|
||||
"uid": 101,
|
||||
"gid": 102
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
|
||||
@@ -323,7 +323,10 @@
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
}
|
||||
},
|
||||
"non_persistent_image_volumes": [
|
||||
"/var/log"
|
||||
]
|
||||
},
|
||||
"adaptations": [
|
||||
{
|
||||
|
||||
@@ -77,7 +77,24 @@
|
||||
"source": "upstream-documentation"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"volumes": [
|
||||
{
|
||||
"id": "volume-0",
|
||||
"container_path": "/config",
|
||||
"compose_source_example": "./config",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"container_port": 5000,
|
||||
@@ -117,7 +134,10 @@
|
||||
"environment": {
|
||||
"WHOOGLE_USER": "admin",
|
||||
"WHOOGLE_PASS": "${GENERATED_WHOOGLE_PASS}"
|
||||
}
|
||||
},
|
||||
"volumes": [
|
||||
"./config:/config"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -133,7 +153,19 @@
|
||||
"dependency_external_access": "disabled-unless-service-publishes-ports",
|
||||
"prompt_user_for_private_network": false
|
||||
},
|
||||
"storage": [],
|
||||
"storage": [
|
||||
{
|
||||
"id": "whoogle-volume-0",
|
||||
"service": "whoogle",
|
||||
"container_path": "/config",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
"reserve_vmids_atomically": 1,
|
||||
"start_order": [
|
||||
@@ -378,6 +410,18 @@
|
||||
"bytes": 24,
|
||||
"prompt": false
|
||||
}
|
||||
},
|
||||
"volume_preparations": [
|
||||
{
|
||||
"container_path": "/config",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"volume_owner": {
|
||||
"uid": 927,
|
||||
"gid": 927
|
||||
}
|
||||
},
|
||||
"catalog": {
|
||||
|
||||
@@ -323,6 +323,9 @@
|
||||
"AceStream HTTP Proxy has no web interface: a media player opens its streams.",
|
||||
"HLS stream: port 6878, path /ace/manifest.m3u8?id= followed by the channel ID.",
|
||||
"MPEG-TS stream: port 6878, path /ace/getstream?id= followed by the channel ID."
|
||||
],
|
||||
"non_persistent_image_volumes": [
|
||||
"/home/appuser/.ACEStream"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -76,6 +76,38 @@
|
||||
"backup": true,
|
||||
"default_size_gb": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-1",
|
||||
"container_path": "/etc/bind",
|
||||
"compose_source_example": "./config",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-2",
|
||||
"container_path": "/var/lib/bind",
|
||||
"compose_source_example": "./lib",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
@@ -123,7 +155,9 @@
|
||||
"53:53/tcp"
|
||||
],
|
||||
"volumes": [
|
||||
"./bind:/var/cache/bind"
|
||||
"./bind:/var/cache/bind",
|
||||
"./config:/etc/bind",
|
||||
"./lib:/var/lib/bind"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -151,6 +185,28 @@
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "bind9-volume-1",
|
||||
"service": "bind9",
|
||||
"container_path": "/etc/bind",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": true,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "bind9-volume-2",
|
||||
"service": "bind9",
|
||||
"container_path": "/var/lib/bind",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": true,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
@@ -349,7 +405,11 @@
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {},
|
||||
"installer_profile": {
|
||||
"non_persistent_image_volumes": [
|
||||
"/var/log"
|
||||
]
|
||||
},
|
||||
"catalog": {
|
||||
"replaces_discovered_ids": []
|
||||
}
|
||||
|
||||
@@ -339,7 +339,13 @@
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {}
|
||||
"installer_profile": {
|
||||
"non_persistent_image_volumes": [
|
||||
"/etc/chrony",
|
||||
"/run/chrony",
|
||||
"/var/lib/chrony"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"automatic_install_candidate": true,
|
||||
|
||||
@@ -409,7 +409,10 @@
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
}
|
||||
},
|
||||
"non_persistent_image_volumes": [
|
||||
"/gotosocial/.cache"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
|
||||
@@ -345,7 +345,11 @@
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {},
|
||||
"installer_profile": {
|
||||
"non_persistent_image_volumes": [
|
||||
"/tmp"
|
||||
]
|
||||
},
|
||||
"catalog": {
|
||||
"replaces_discovered_ids": []
|
||||
}
|
||||
|
||||
@@ -94,6 +94,22 @@
|
||||
"backup": true,
|
||||
"default_size_gb": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-2",
|
||||
"container_path": "/etc/nginx/ssl",
|
||||
"compose_source_example": "./ssl",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
@@ -134,7 +150,8 @@
|
||||
],
|
||||
"volumes": [
|
||||
"./data:/var/www/app/data",
|
||||
"./plugins:/var/www/app/plugins"
|
||||
"./plugins:/var/www/app/plugins",
|
||||
"./ssl:/etc/nginx/ssl"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -171,6 +188,17 @@
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "kanboard-volume-2",
|
||||
"service": "kanboard",
|
||||
"container_path": "/etc/nginx/ssl",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
@@ -391,6 +419,12 @@
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
},
|
||||
{
|
||||
"container_path": "/etc/nginx/ssl",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-root",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"startup_healthcheck": {
|
||||
|
||||
@@ -329,7 +329,10 @@
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
}
|
||||
},
|
||||
"non_persistent_image_volumes": [
|
||||
"/tmp/jetty"
|
||||
]
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
|
||||
@@ -85,6 +85,22 @@
|
||||
"backup": true,
|
||||
"default_size_gb": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "volume-1",
|
||||
"container_path": "/etc/searxng",
|
||||
"compose_source_example": "./config",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
@@ -127,7 +143,8 @@
|
||||
"8080:8080"
|
||||
],
|
||||
"volumes": [
|
||||
"./cache:/var/cache/searxng"
|
||||
"./cache:/var/cache/searxng",
|
||||
"./config:/etc/searxng"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -153,6 +170,17 @@
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
},
|
||||
{
|
||||
"id": "searxng-volume-1",
|
||||
"service": "searxng",
|
||||
"container_path": "/etc/searxng",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
@@ -378,6 +406,12 @@
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
},
|
||||
{
|
||||
"container_path": "/etc/searxng",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"startup_healthcheck": {
|
||||
|
||||
@@ -60,7 +60,24 @@
|
||||
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
|
||||
},
|
||||
"environment": [],
|
||||
"volumes": [],
|
||||
"volumes": [
|
||||
{
|
||||
"id": "volume-0",
|
||||
"container_path": "/var/lib/tor",
|
||||
"compose_source_example": "./tor-data",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"container_port": 9050,
|
||||
@@ -103,6 +120,9 @@
|
||||
"ports": [
|
||||
"9050:9050",
|
||||
"8118:8118"
|
||||
],
|
||||
"volumes": [
|
||||
"./tor-data:/var/lib/tor"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -119,7 +139,19 @@
|
||||
"dependency_external_access": "disabled-unless-service-publishes-ports",
|
||||
"prompt_user_for_private_network": false
|
||||
},
|
||||
"storage": [],
|
||||
"storage": [
|
||||
{
|
||||
"id": "tor-volume-0",
|
||||
"service": "tor",
|
||||
"container_path": "/var/lib/tor",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
"reserve_vmids_atomically": 1,
|
||||
"start_order": [
|
||||
@@ -323,7 +355,19 @@
|
||||
"SOCKS5 proxy: configure your application with the LXC IP address shown above and port 9050.",
|
||||
"HTTP CONNECT proxy: use the same IP address and port 8118. These addresses are proxy settings, not web links.",
|
||||
"Only applications configured to use this proxy send their traffic through Tor."
|
||||
]
|
||||
],
|
||||
"volume_preparations": [
|
||||
{
|
||||
"container_path": "/var/lib/tor",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"volume_owner": {
|
||||
"uid": 101,
|
||||
"gid": 102
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
|
||||
@@ -323,7 +323,10 @@
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
}
|
||||
},
|
||||
"non_persistent_image_volumes": [
|
||||
"/var/log"
|
||||
]
|
||||
},
|
||||
"adaptations": [
|
||||
{
|
||||
|
||||
@@ -77,7 +77,24 @@
|
||||
"source": "upstream-documentation"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"volumes": [
|
||||
{
|
||||
"id": "volume-0",
|
||||
"container_path": "/config",
|
||||
"compose_source_example": "./config",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"container_port": 5000,
|
||||
@@ -117,7 +134,10 @@
|
||||
"environment": {
|
||||
"WHOOGLE_USER": "admin",
|
||||
"WHOOGLE_PASS": "${GENERATED_WHOOGLE_PASS}"
|
||||
}
|
||||
},
|
||||
"volumes": [
|
||||
"./config:/config"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -133,7 +153,19 @@
|
||||
"dependency_external_access": "disabled-unless-service-publishes-ports",
|
||||
"prompt_user_for_private_network": false
|
||||
},
|
||||
"storage": [],
|
||||
"storage": [
|
||||
{
|
||||
"id": "whoogle-volume-0",
|
||||
"service": "whoogle",
|
||||
"container_path": "/config",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": false,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": null
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
"reserve_vmids_atomically": 1,
|
||||
"start_order": [
|
||||
@@ -378,6 +410,18 @@
|
||||
"bytes": 24,
|
||||
"prompt": false
|
||||
}
|
||||
},
|
||||
"volume_preparations": [
|
||||
{
|
||||
"container_path": "/config",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"volume_owner": {
|
||||
"uid": 927,
|
||||
"gid": 927
|
||||
}
|
||||
},
|
||||
"catalog": {
|
||||
|
||||
@@ -99,6 +99,9 @@
|
||||
"owner_strategy": "mapped-root",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"non_persistent_image_volumes": [
|
||||
"/romm"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"amule": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"authentik": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"chromium": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
@@ -62,6 +65,9 @@
|
||||
"grafana": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"haos-one": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"immich": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
@@ -74,6 +80,9 @@
|
||||
"linkwarden": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"mariadb": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"memos": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
@@ -83,6 +92,9 @@
|
||||
"motioneye": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"navidrome": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"nextcloud": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
@@ -108,6 +120,9 @@
|
||||
"report": "https://github.com/MacRimi/ProxMenux/discussions/392#discussioncomment-18623868"
|
||||
}
|
||||
},
|
||||
"prowlarr": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
@@ -117,12 +132,18 @@
|
||||
"rdtclient": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"scanopy": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"sharecovex": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"snapotter": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"sonarr": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"tandoor": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
@@ -140,6 +161,9 @@
|
||||
"trilium": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"vaultwarden": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
"wg-easy": {
|
||||
"status": "laboratory-validated"
|
||||
},
|
||||
|
||||
@@ -728,8 +728,9 @@ def preflight(record, candidate, config, coordinated=None, in_place=False, basel
|
||||
raise ValueError(translate('Required persistent paths cannot be removed'))
|
||||
image_paths = set(record['observed']['image']['defaults'].get('Volumes') or {})
|
||||
image_paths -= non_persistent_image_volumes(record['template'])
|
||||
if any(not any(p == target or p.startswith(target.rstrip('/') + '/') for target in old) for p in image_paths):
|
||||
raise ValueError(translate('The image declares data paths that are still stored in the rootfs'))
|
||||
in_rootfs = sorted(p for p in image_paths if not any(p == target or p.startswith(target.rstrip('/') + '/') for target in old))
|
||||
if in_rootfs:
|
||||
raise ValueError(f"{translate('The image declares data paths that are still stored in the rootfs')}: {', '.join(in_rootfs)}")
|
||||
check = effective_healthcheck(candidate['template'])
|
||||
if not check and not coordinated:
|
||||
raise ValueError(translate('Updates are not available yet for this application in this beta'))
|
||||
|
||||
@@ -219,6 +219,11 @@ def service_template(parent, service, main=False):
|
||||
own_preparations.append(entry)
|
||||
if own_preparations:
|
||||
single['proxmox'].setdefault('installer_profile', {})['volume_preparations'] = own_preparations
|
||||
# Image paths the oci-compose states hold no data; an image that does not
|
||||
# declare one of them is not affected by it.
|
||||
transient = parent.get('proxmox', {}).get('installer_profile', {}).get('non_persistent_image_volumes')
|
||||
if transient:
|
||||
single['proxmox'].setdefault('installer_profile', {})['non_persistent_image_volumes'] = list(transient)
|
||||
return single
|
||||
|
||||
|
||||
@@ -784,10 +789,11 @@ def apply_stack_support(template):
|
||||
template['status'] = 'generated-review-required'
|
||||
return
|
||||
template['proxmox'].pop('generic_stack_review',None)
|
||||
preserved_preparations = template['proxmox'].get('installer_profile',{}).get('volume_preparations')
|
||||
profile = template['proxmox'].get('installer_profile',{})
|
||||
template['proxmox']['installer_profile'] = {'stack_driver':'generic-multi-lxc-stack'}
|
||||
if preserved_preparations:
|
||||
template['proxmox']['installer_profile']['volume_preparations'] = preserved_preparations
|
||||
for key in ('volume_preparations', 'non_persistent_image_volumes'):
|
||||
if profile.get(key):
|
||||
template['proxmox']['installer_profile'][key] = profile[key]
|
||||
template['compatibility']['untranslated_blockers'] = []
|
||||
template['compatibility']['automatic_install_candidate'] = True
|
||||
template['status'] = 'generated-unvalidated'
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Image paths a stack's oci-compose states hold no data reach the template
|
||||
of every container of the stack, where updates and Modify read them."""
|
||||
import copy
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
from proxmenux_oci.catalog import Catalog
|
||||
from proxmenux_oci.stack import apply_stack_support, build_stack, DefaultsUI
|
||||
|
||||
|
||||
class StackTransientImageVolumesTests(unittest.TestCase):
|
||||
def test_each_container_of_romm_receives_the_transient_paths(self):
|
||||
result = build_stack(Catalog(ROOT).compose("romm"), DefaultsUI(), mode="simple")
|
||||
for service in result["services"]:
|
||||
profile = service["template"]["proxmox"]["installer_profile"]
|
||||
self.assertEqual(profile.get("non_persistent_image_volumes"), ["/romm"], service["name"])
|
||||
|
||||
def test_the_stack_profile_keeps_them_when_it_is_rewritten(self):
|
||||
template = copy.deepcopy(Catalog(ROOT).compose("romm"))
|
||||
template["proxmox"]["installer_profile"]["stack_driver"] = "generic-multi-lxc-stack"
|
||||
apply_stack_support(template)
|
||||
self.assertEqual(template["proxmox"]["installer_profile"].get("non_persistent_image_volumes"), ["/romm"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -164,7 +164,9 @@ class UpdateCheckTests(unittest.TestCase):
|
||||
check.check(self.root, self.configs, path=path, progress=lambda *event: events.append(event))
|
||||
native.assert_called_once(); notify.assert_not_called(); external.assert_not_called()
|
||||
self.assertEqual(events[0], (0, 1)); self.assertEqual(events[-1], (1, 1))
|
||||
self.assertEqual([r['vmid'] for r in self.status()['available']], [110])
|
||||
# The bridge stamps its result with the real clock, so read it at the real time too.
|
||||
available = check.cached(self.root, self.configs, apps=self.apps)['available']
|
||||
self.assertEqual([r['vmid'] for r in available], [110])
|
||||
self.assertFalse((self.root / '.update-check.json').exists())
|
||||
|
||||
def test_missing_monitor_never_falls_back_to_registry_commands(self):
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Every view of a disk takes its rotation and type from disk_identity."""
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "AppImage/scripts"))
|
||||
import disk_identity
|
||||
import audit_inventory
|
||||
|
||||
|
||||
class DiskMediaTypeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
folder = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(folder.cleanup)
|
||||
self.root = Path(folder.name)
|
||||
for name, flag in (("sda", "1"), ("sdb", "0"), ("nvme0n1", "0")):
|
||||
(self.root / name / "queue").mkdir(parents=True)
|
||||
(self.root / name / "queue/rotational").write_text(flag + "\n")
|
||||
sys_block = patch.object(disk_identity, "_SYS_BLOCK", str(self.root))
|
||||
sys_block.start()
|
||||
self.addCleanup(sys_block.stop)
|
||||
|
||||
def test_the_kernel_flag_answers_when_smartctl_did_not(self):
|
||||
self.assertEqual(disk_identity.rotation_rate("sda"), -1)
|
||||
self.assertEqual(disk_identity.rotation_rate("sdb"), 0)
|
||||
self.assertEqual(disk_identity.rotation_rate("sdz"), 0)
|
||||
|
||||
def test_what_smartctl_reported_wins(self):
|
||||
self.assertEqual(disk_identity.rotation_rate("sda", 0), 0)
|
||||
self.assertEqual(disk_identity.rotation_rate("sdb", 5400), 5400)
|
||||
self.assertEqual(disk_identity.rotation_rate("sda", None), -1)
|
||||
|
||||
def test_media_type(self):
|
||||
self.assertEqual(disk_identity.media_type("sda", -1), "HDD")
|
||||
self.assertEqual(disk_identity.media_type("sda", 7200), "HDD")
|
||||
self.assertEqual(disk_identity.media_type("sdb", 0), "SSD")
|
||||
self.assertEqual(disk_identity.media_type("nvme0n1", 0), "NVMe")
|
||||
|
||||
def test_the_inventory_agrees_with_the_monitor(self):
|
||||
rows = ('NAME="sda" MODEL="WDC WD7500BPVT" SERIAL="WX1" SIZE="750156374016" ROTA="1" TRAN="usb" TYPE="disk"\n'
|
||||
'NAME="sdb" MODEL="Samsung SSD" SERIAL="S1" SIZE="500107862016" ROTA="0" TRAN="sata" TYPE="disk"\n')
|
||||
ctx = types.SimpleNamespace(run=lambda *a, **k: (0, rows),
|
||||
monitor_snapshot={"smart": {"/dev/sda": [0, {"rotation_rate": 0}]}})
|
||||
with patch.object(audit_inventory, "_disk_observations", return_value={}):
|
||||
disks = {d["name"]: d["rotational"] for d in audit_inventory._physical_disks(ctx)}
|
||||
# The USB bridge marks sda rotational; smartctl, through the Monitor, says solid state.
|
||||
self.assertEqual(disks, {"sda": False, "sdb": False})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -209,10 +209,16 @@ class QuietDiskIdentityTests(unittest.TestCase):
|
||||
self.read = Mock(return_value={"model": "WDC WD40EFRX-68N32N0", "serial": "WD-WCC7K4FV37J0",
|
||||
"rotation_rate": 5400, "health": "healthy", "temperature": 31})
|
||||
self.cache = {}
|
||||
self.backoff = {}
|
||||
import disk_identity
|
||||
sys_block = patch.object(disk_identity, "_SYS_BLOCK", str(root / "sys/block"))
|
||||
sys_block.start()
|
||||
self.addCleanup(sys_block.stop)
|
||||
namespace = {"Path": lambda path: root / path.lstrip("/"), "time": __import__("time"),
|
||||
"disk_identity": disk_identity,
|
||||
"_smart_result_cache": self.cache, "_SMART_RESULT_TTL": 30, "_hdd_in_standby": self.standby,
|
||||
"_get_smart_data_uncached": self.read, "_smart_data_useful": lambda data: data.get("model") != "Unknown",
|
||||
"_smart_fail_backoff": {}, "_smart_fail_counts": {}, "_smart_probe_cache": {},
|
||||
"_smart_fail_backoff": self.backoff, "_smart_fail_counts": {}, "_smart_probe_cache": {},
|
||||
"_SMART_FAIL_THRESHOLD": 3, "_SMART_FAIL_BACKOFF_SEC": 3600}
|
||||
tree = ast.parse((SCRIPTS / "flask_server.py").read_text())
|
||||
nodes = [n for n in tree.body if isinstance(n, ast.FunctionDef)
|
||||
@@ -240,6 +246,13 @@ class QuietDiskIdentityTests(unittest.TestCase):
|
||||
self.read.assert_not_called()
|
||||
self.assertEqual((data["model"], data["rotation_rate"], data["excluded"]), ("WDC WD40EFRX-68N32N0", -1, True))
|
||||
|
||||
def test_a_disk_smartctl_keeps_failing_on_keeps_its_identity(self):
|
||||
self.backoff["sdb"] = __import__("time").time() + 3600
|
||||
data = self.get("sdb")
|
||||
self.read.assert_not_called()
|
||||
self.assertEqual((data["model"], data["serial"], data["rotation_rate"]),
|
||||
("WDC WD40EFRX-68N32N0", "WD-WCC7K4FV37J0", -1))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -55,12 +55,15 @@ console.log('PASS: saved-data badge guards and actual provider fallback/translat
|
||||
const report = source.statements.find(n => ts.isFunctionDeclaration(n) && n.name?.text === 'openSmartReport');
|
||||
assert.ok(report);
|
||||
let reportHtml;
|
||||
const diskTypeModule = {};
|
||||
new Function('exports', compile(read('AppImage/lib/disk-type.ts')))(diskTypeModule);
|
||||
const openReport = new Function('window', 'Blob', 'URL', 'setTimeout', 'getDiskTempThresholdsSync', 'getNvmeSmartAttributeKey',
|
||||
'resolveDiskType',
|
||||
compile('const APP_VERSION = "fixture";\n' + emit(report, source)) + '\nreturn openSmartReport;')(
|
||||
{location: {origin: 'http://fixture.invalid'}, open() {}},
|
||||
class { constructor(parts) { reportHtml = parts.join(''); } },
|
||||
{createObjectURL() { return 'blob:fixture'; }, revokeObjectURL() {}}, () => {},
|
||||
() => ({warning: 55, critical: 65}), () => undefined);
|
||||
() => ({warning: 55, critical: 65}), () => undefined, diskTypeModule.getDiskType);
|
||||
const expectedReport = {
|
||||
passedAssessment: 'The supplied SMART status is PASSED. This status alone does not establish that every attribute is within range or that the disk is free of faults. Power-on time shown: {uptime}. Temperature shown: {temperature}. {sectors}',
|
||||
noReallocatedSectorsReported: 'The supplied reallocated-sector value is zero or unavailable; this does not establish that there are no bad sectors.',
|
||||
@@ -107,5 +110,15 @@ translated.recommendations = {passedTitle: 'TRANSLATED_REC_TITLE', passedText: '
|
||||
const localized = renderReport({}, 'passed', 'synthetic', {...catalogs, synthetic: {storage: {smartReport: translated}}});
|
||||
for (const [, value] of leaves(translated)) assert.ok(localized.includes(value.split(' {')[0]), value);
|
||||
assert.ok(renderReport({name: 'nvme0n1'}, 'passed', 'en', catalogs, {nvme_raw: {critical_warning: 0, media_errors: 7}}).includes(expectedReport.passedMeaning));
|
||||
// One classifier for the report and the Storage page: a disk the kernel marks
|
||||
// rotational without an RPM is an HDD, and a real model wins over "Unknown".
|
||||
const typeCard = html => html.match(/labels\.type[^]*?card-value[^>]*>([^<]*)</)?.[1] ?? html.match(/>Type<\/div>\s*<div class="card-value"[^>]*>([^<]*)</)?.[1];
|
||||
assert.equal(typeCard(renderReport({rotation_rate: -1})), 'HDD');
|
||||
assert.equal(typeCard(renderReport({rotation_rate: 7200})), 'HDD 7200 RPM');
|
||||
assert.equal(typeCard(renderReport({rotation_rate: 0})), 'SSD');
|
||||
assert.ok(!renderReport({rotation_rate: -1}).includes('-1 RPM'));
|
||||
assert.equal(typeCard(renderReport({rotation_rate: -1, connection_type: 'sas'})), 'SAS HDD');
|
||||
const unknownModel = renderReport({model: 'Unknown', serial: 'Unknown'}, 'passed', 'en', catalogs, {model: 'WDC WD7500BPVT', serial: 'WX11A1234'});
|
||||
assert.ok(unknownModel.includes('WDC WD7500BPVT') && unknownModel.includes('WX11A1234'));
|
||||
if (process.env.STORAGE_REPORT_HTML) fs.writeFileSync(process.env.STORAGE_REPORT_HTML, renderReport());
|
||||
console.log('PASS: actual printable report, passed/failed/unknown/warning, missing/zero/nonzero sectors, NVMe errors and provider fallback');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next"
|
||||
import { getTranslations, getMessages, setRequestLocale } from "next-intl/server"
|
||||
import { Link } from "@/i18n/navigation"
|
||||
import { icuQuotesToHtml } from "@/i18n/loadMessages"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -114,7 +115,7 @@ export default async function GlossaryPage({
|
||||
</dt>
|
||||
<dd
|
||||
className="text-gray-800 leading-relaxed [&_a]:text-blue-600 [&_a]:hover:underline [&_code]:bg-gray-100 [&_code]:px-1 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-sm"
|
||||
dangerouslySetInnerHTML={{ __html: entry.def }}
|
||||
dangerouslySetInnerHTML={{ __html: icuQuotesToHtml(entry.def) }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next"
|
||||
import { getTranslations, getMessages, setRequestLocale } from "next-intl/server"
|
||||
import { Link } from "@/i18n/navigation"
|
||||
import { icuQuotesToHtml } from "@/i18n/loadMessages"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
import { Callout } from "@/components/ui/callout"
|
||||
import CopyableCode from "@/components/CopyableCode"
|
||||
@@ -135,7 +136,7 @@ export default async function SystemLogsTabPage({
|
||||
className={idx < dataRows.length - 1 ? "border-b border-gray-100" : ""}
|
||||
>
|
||||
<td className="px-3 py-2 align-top">{row.subtab}</td>
|
||||
<td className="px-3 py-2 align-top font-mono text-xs" dangerouslySetInnerHTML={{ __html: row.endpoint }} />
|
||||
<td className="px-3 py-2 align-top font-mono text-xs" dangerouslySetInnerHTML={{ __html: icuQuotesToHtml(row.endpoint) }} />
|
||||
<td className="px-3 py-2 align-top">
|
||||
{t.rich(`dataCollected.rows.${idx}.source`, { code })}
|
||||
</td>
|
||||
|
||||
@@ -615,7 +615,7 @@ running on node pve-01
|
||||
<tr key={row.stage} className={idx < dispatchRows.length - 1 ? "border-b border-gray-100" : ""}>
|
||||
<td className="px-3 py-2 align-top whitespace-nowrap"><strong>{row.stage}</strong></td>
|
||||
<td className="px-3 py-2 align-top">{t.rich(`dispatch.rows.${idx}.what`, { code })}</td>
|
||||
<td className="px-3 py-2 align-top">{row.tunable}</td>
|
||||
<td className="px-3 py-2 align-top">{t.rich(`dispatch.rows.${idx}.tunable`, { code })}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@@ -214,7 +214,7 @@ was changed │ PHASE 2 — Execute │
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">{t("format.intro")}</p>
|
||||
<ol className="list-decimal pl-6 mb-4 text-gray-800 leading-relaxed space-y-2">
|
||||
{formatItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`format.items.${idx}`, { code, strong })}</li>
|
||||
<li key={idx}>{t.rich(`format.items.${idx}`, { code, strong, em })}</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { LocaleHtmlSync } from "@/components/locale-html-sync"
|
||||
import type React from "react"
|
||||
import { notFound } from "next/navigation"
|
||||
import { NextIntlClientProvider, hasLocale } from "next-intl"
|
||||
import { setRequestLocale } from "next-intl/server"
|
||||
import { getMessages, setRequestLocale } from "next-intl/server"
|
||||
import { routing } from "@/i18n/routing"
|
||||
|
||||
/**
|
||||
@@ -122,6 +122,39 @@ export const metadata = {
|
||||
},
|
||||
}
|
||||
|
||||
// The namespaces client components read. Server pages translate during the
|
||||
// build, so the rest of the catalog stays out of every page.
|
||||
const CLIENT_MESSAGES = [
|
||||
"nav",
|
||||
"footer",
|
||||
"hero",
|
||||
"language",
|
||||
"resources",
|
||||
"rssLink",
|
||||
"supportProject",
|
||||
"tocPanel",
|
||||
"docSidebar",
|
||||
"docNav",
|
||||
"docs.createVm.synology",
|
||||
"docs.createVm.systemNas.synology",
|
||||
]
|
||||
|
||||
type Messages = Record<string, unknown>
|
||||
|
||||
function pickMessages(messages: Messages, paths: string[]): Messages {
|
||||
const picked: Messages = {}
|
||||
for (const path of paths) {
|
||||
const keys = path.split(".")
|
||||
let value: unknown = messages
|
||||
for (const key of keys) value = (value as Messages | undefined)?.[key]
|
||||
if (value === undefined) continue
|
||||
let target = picked
|
||||
for (const key of keys.slice(0, -1)) target = (target[key] ??= {}) as Messages
|
||||
target[keys[keys.length - 1]] = value
|
||||
}
|
||||
return picked
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
params,
|
||||
@@ -136,9 +169,10 @@ export default async function LocaleLayout({
|
||||
}
|
||||
|
||||
setRequestLocale(locale)
|
||||
const messages = (await getMessages({ locale })) as Messages
|
||||
|
||||
return (
|
||||
<NextIntlClientProvider>
|
||||
<NextIntlClientProvider messages={pickMessages(messages, CLIENT_MESSAGES)}>
|
||||
{/* LocaleHtmlSync writes `document.documentElement.lang = locale`
|
||||
after hydration so the active language is reflected in <html lang>
|
||||
for accessibility tools and SEO crawlers that execute JS. The
|
||||
|
||||
@@ -83,6 +83,18 @@
|
||||
}
|
||||
|
||||
|
||||
/* Long commands and paths in running text wrap instead of widening the page
|
||||
on a phone; tables scroll inside their own box. */
|
||||
:not(pre) > code {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.prose table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
|
||||
button code {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
|
||||
@@ -275,11 +275,7 @@ export const sidebarItems: MenuItem[] = [
|
||||
{ title: "Overview", i18nKey: "settingsOverview", href: "/docs/settings" },
|
||||
{ title: "ProxMenux Monitor", i18nKey: "settingsMonitor", href: "/docs/settings/proxmenux-monitor" },
|
||||
{ title: "Change Release Channel", i18nKey: "settingsBeta", href: "/docs/settings/beta-program" },
|
||||
// "Change Language" is intentionally hidden until the translation
|
||||
// install flow is reactivated. The page file at
|
||||
// /docs/settings/change-language/page.tsx is preserved so we can
|
||||
// restore this entry in a single line edit once the feature ships.
|
||||
// { title: "Change Language", i18nKey: "settingsLanguage", href: "/docs/settings/change-language" },
|
||||
{ title: "Change Language", i18nKey: "settingsLanguage", href: "/docs/settings/change-language" },
|
||||
{ title: "Show Version Information", i18nKey: "settingsVersion", href: "/docs/settings/show-version-information" },
|
||||
{ title: "Uninstall ProxMenux", i18nKey: "settingsUninstall", href: "/docs/settings/uninstall-proxmenux" },
|
||||
],
|
||||
|
||||
@@ -23,6 +23,7 @@ Un agradecimiento especial a [@Vaso73](https://github.com/Vaso73), [@f3rs3n](htt
|
||||
- **La entrada de Ollama describe lo que ofrece la imagen** ([PR #566](https://github.com/MacRimi/ProxMenux/pull/566), aportado por [@jcastro](https://github.com/jcastro)): su API HTTP en el puerto 11434, con el enlace y la comprobación de arranque en `/api/version`.
|
||||
- **Una actualización elimina el snapshot que deja un backup interrumpido** ([#565](https://github.com/MacRimi/ProxMenux/issues/565), reportado por [@kiosvantra](https://github.com/kiosvantra)). Un snapshot hecho por el usuario detiene la actualización, porque un contenedor reconstruido no puede conservarlo.
|
||||
- **Un contenedor que ignora la señal de apagado se detiene a los 60 segundos**, como hace `docker stop`, durante una actualización y al recuperar una operación interrumpida.
|
||||
- **Modificar y las actualizaciones aceptan toda aplicación cuya imagen declara una ruta de datos**: SearXNG, Tor, Whoogle y BIND9 la guardan en un volumen propio, Kanboard conserva sus certificados, y Chrony, Jaeger, PlantUML, UrBackup, GoToSocial, AceStream HTTP Proxy y RomM marcan las rutas que no guardan datos. Cuando una instalación aún tiene una de esas rutas dentro del contenedor, el mensaje la nombra.
|
||||
|
||||
## 🖥️ Monitor — actualizaciones de Proxmox y reinicios pendientes
|
||||
|
||||
@@ -59,6 +60,7 @@ Un agradecimiento especial a [@Vaso73](https://github.com/Vaso73), [@f3rs3n](htt
|
||||
- **Bitwarden reconoce el campo 2FA en el inicio de sesión del Monitor** ([#570](https://github.com/MacRimi/ProxMenux/issues/570), reportado por [@shtefko](https://github.com/shtefko)): la pista del código de backup mantiene su texto y su lugar, fuera del grupo del campo.
|
||||
- **Secure Gateway se instala en hosts Proxmox sobre BTRFS** ([#564](https://github.com/MacRimi/ProxMenux/issues/564), reportado por [@78v5](https://github.com/78v5)): la plantilla de Alpine va a un almacenamiento activo que admite plantillas de contenedor.
|
||||
- **Un error de E/S de disco del kernel se registra en su disco**, con su número de serie, también cuando el error nombra una partición.
|
||||
- **Todas las vistas de un disco muestran el mismo tipo**: la página de almacenamiento, el detalle del disco, el informe SMART y el inventario de Audit & Report lo toman de una sola fuente. Un disco duro que smartctl no consigue leer conserva su tipo, modelo y número de serie, y un disco duro que no informa de su velocidad aparece como HDD.
|
||||
- **El Monitor se instala como aplicación (PWA) desde el navegador**: sirve su propio manifiesto de aplicación web y su service worker, con los iconos de 192 y 512 px que exige Chrome.
|
||||
- **Los títulos del eje Y se mantienen dentro de los gráficos de la vista general** ([PR #539](https://github.com/MacRimi/ProxMenux/pull/539), aportado por [@f3rs3n](https://github.com/f3rs3n)).
|
||||
- **El catálogo de seguimiento de versiones de aplicaciones se genera desde los repositorios actuales de Helper-Scripts**.
|
||||
|
||||
@@ -45,7 +45,7 @@ function readDir(dir: string): Record<string, unknown> {
|
||||
|
||||
if (!entry.name.endsWith(".json")) continue
|
||||
|
||||
const parsed = JSON.parse(fs.readFileSync(fullPath, "utf-8"))
|
||||
const parsed = decodeEntities(JSON.parse(fs.readFileSync(fullPath, "utf-8")))
|
||||
const base = entry.name.replace(/\.json$/, "")
|
||||
|
||||
if (base === "common" || base === "index") {
|
||||
@@ -59,6 +59,25 @@ function readDir(dir: string): Record<string, unknown> {
|
||||
return out
|
||||
}
|
||||
|
||||
// Catalogs write "<x>" so the ICU parser does not read "<x>" as a rich
|
||||
// tag, but React renders text, not HTML. ICU quoting ('<') keeps the parser
|
||||
// off and renders the literal character.
|
||||
function decodeEntities(value: unknown): unknown {
|
||||
if (typeof value === "string") {
|
||||
return value.replace(/</g, "'<'").replace(/>/g, "'>'").replace(/&/g, "&")
|
||||
}
|
||||
if (Array.isArray(value)) return value.map(decodeEntities)
|
||||
if (value && typeof value === "object") {
|
||||
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, decodeEntities(v)]))
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Messages inserted as raw HTML skip ICU, so its quoting goes back to entities.
|
||||
export function icuQuotesToHtml(html: string): string {
|
||||
return html.replace(/'<'/g, "<").replace(/'>'/g, ">")
|
||||
}
|
||||
|
||||
function toCamel(name: string): string {
|
||||
return name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase())
|
||||
}
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Übersicht",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Release-Kanal wechseln",
|
||||
"settingsLanguage": "Sprache ändern",
|
||||
"settingsVersion": "Versionsinformationen anzeigen",
|
||||
"settingsUninstall": "ProxMenux deinstallieren",
|
||||
"commandsReference": "Befehlsreferenz",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "Der <code>[proxmenux]</code> jail liest <code>/var/log/proxmenux-auth.log</code>, der von der ProxMenux Monitor Flask-App geschrieben wurde – nicht von einem journald-Logger. Wenn Sie den Monitor nicht ausführen, bleibt die Datei leer und jail wird nie ausgelöst. Das wird erwartet; Die jail-Konfiguration ist harmlos. Wenn Sie den Monitor ausführen und das Protokoll leer ist, überprüfen Sie die Protokollierungskonfiguration Flask.",
|
||||
"selfBanTitle": "Ich habe meinen eigenen Zugriff gesperrt",
|
||||
"selfBanIntro": "Von einer Konsole / IPMI / iKVM (oder einer anderen Whitelist-IP):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "apt-get schlägt fehl: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "Dem Host fehlen die Debian-Repos (üblich bei Barebones, die Proxmox installiert). Der Installateur erkennt dies und schreibt <code>/etc/apt/sources.list.d/debian.sources</code> mit dem richtigen Codenamen (<code>bookworm</code> / <code>trixie</code>), bevor er es erneut versucht. Wenn es immer noch fehlschlägt, überprüfen Sie <code>/etc/os-release</code> für <code>VERSION_CODENAME</code> und bestätigen Sie, dass die Repo-URL erreichbar ist.",
|
||||
"lockoutTitle": "SSH sperrt mich nach der Installation aus",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Ablauf im automatischen Modus",
|
||||
"flowIntro": "Sechs aufeinanderfolgende Phasen. Phase 2 aktualisiert die PVE-8-Pakete, Phase 3 schreibt die Repository-Konfiguration für PVE 9. Ein Fehler hält den Ablauf an, macht diese Änderungen aber nicht rückgängig; schlägt das PVE-8-Update fehl oder wird es abgebrochen, bleiben die Repositories unverändert. Phase 5 führt <code>dist-upgrade</code> auf PVE 9 aus.",
|
||||
"flowDiagram": "1. PRÜFUNGEN\ntermproxy / vncshell blockieren; Upgrade bestätigen; PVE 9 ablehnen.\n\n2. PVE 8 VORBEREITEN\nscripts/global/update-pve8.sh\nPVE-8-Pakete aktualisieren; Abbruch, wenn das Update fehlschlägt oder abgebrochen wird.\npve8to9 --full (kann Reparaturen anbieten).\nSpeicher und Verbindung prüfen; Ceph 19.x Squid auf hyperkonvergenten Knoten.\n\n3. REPOSITORIES UMSTELLEN\nBookworm → Trixie; deb822-Dateien .sources schreiben.\nAlte .list-Dateien auskommentieren; apt-get update.\n\n4. PVE-9-KANDIDAT PRÜFEN\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nAbbruch, wenn der Kandidat nicht 9.x ist oder proxmox-ve entfernt würde.\n\n5. UPGRADE\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nOhne Subskription: pve-enterprise.sources deaktivieren.\nProtokoll: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. PRÜFEN UND NEU STARTEN\nEFI: apt install grub-efi-amd64\npve8to9 --full; Ergebnis und Neustart-Rückfrage prüfen.",
|
||||
"flowDiagram": "1. PRÜFUNGEN\ntermproxy / vncshell blockieren; Upgrade bestätigen; PVE 9 ablehnen.\n\n2. PVE 8 VORBEREITEN\nscripts/global/update-pve8.sh\nPVE-8-Pakete aktualisieren; Abbruch, wenn das Update fehlschlägt oder abgebrochen wird.\npve8to9 --full (kann Reparaturen anbieten).\nSpeicher und Verbindung prüfen; Ceph 19.x Squid auf hyperkonvergenten Knoten.\n\n3. REPOSITORIES UMSTELLEN\nBookworm → Trixie; deb822-Dateien .sources schreiben.\nAlte .list-Dateien auskommentieren; apt-get update.\n\n4. PVE-9-KANDIDAT PRÜFEN\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nAbbruch, wenn der Kandidat nicht 9.x ist oder proxmox-ve entfernt würde.\n\n5. UPGRADE\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nOhne Subskription: pve-enterprise.sources deaktivieren.\nProtokoll: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. PRÜFEN UND NEU STARTEN\nEFI: apt install grub-efi-amd64\npve8to9 --full; Ergebnis und Neustart-Rückfrage prüfen.",
|
||||
"preflightHeading": "Vor dem Upgrade: diese Punkte werden geprüft",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Prüfung",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Beteiligte Dateien",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Offizielle Quellen",
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Overview",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Change Release Channel",
|
||||
"settingsLanguage": "Change Language",
|
||||
"settingsVersion": "Show Version Information",
|
||||
"settingsUninstall": "Uninstall ProxMenux",
|
||||
"commandsReference": "Commands Reference",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "The <code>[proxmenux]</code> jail reads <code>/var/log/proxmenux-auth.log</code>, which is written by the ProxMenux Monitor Flask app — not by a journald logger. If you don't run the Monitor, the file stays empty and the jail never fires. That's expected; the jail config is harmless. If you do run the Monitor and the log is empty, check the Flask logging config.",
|
||||
"selfBanTitle": "I banned myself",
|
||||
"selfBanIntro": "From a console / IPMI / iKVM (or another whitelisted IP):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "apt-get fails: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "The host is missing the Debian repos (common on barebones Proxmox installs). The installer detects this and writes <code>/etc/apt/sources.list.d/debian.sources</code> with the right codename (<code>bookworm</code> / <code>trixie</code>) before retrying. If it still fails, check <code>/etc/os-release</code> for <code>VERSION_CODENAME</code> and confirm the repo URL is reachable.",
|
||||
"lockoutTitle": "SSH locks me out after install",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Auto-mode flow",
|
||||
"flowIntro": "Six sequential phases. Phase 2 updates the PVE 8 packages and phase 3 writes the PVE 9 repository configuration. A failure stops the flow without rolling those changes back; if the PVE 8 update fails or is cancelled, the repositories are left unchanged. Phase 5 runs the PVE 9 <code>dist-upgrade</code>.",
|
||||
"flowDiagram": "1. CHECKS\nBlock termproxy / vncshell; confirm the upgrade; reject PVE 9.\n\n2. PREPARE PVE 8\nscripts/global/update-pve8.sh\nUpdate PVE 8 packages; stop if the update fails or is cancelled.\npve8to9 --full (may offer repairs).\nCheck space and connectivity; Ceph 19.x Squid on hyper-converged nodes.\n\n3. MIGRATE REPOSITORIES\nBookworm → Trixie; write deb822 .sources files.\nComment legacy .list files; apt-get update.\n\n4. VALIDATE THE PVE 9 CANDIDATE\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nStop if the candidate is not 9.x or proxmox-ve would be removed.\n\n5. UPGRADE\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nNo subscription: disable pve-enterprise.sources.\nLog: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. VERIFY AND REBOOT\nEFI: apt install grub-efi-amd64\npve8to9 --full; review results and the reboot prompt.",
|
||||
"flowDiagram": "1. CHECKS\nBlock termproxy / vncshell; confirm the upgrade; reject PVE 9.\n\n2. PREPARE PVE 8\nscripts/global/update-pve8.sh\nUpdate PVE 8 packages; stop if the update fails or is cancelled.\npve8to9 --full (may offer repairs).\nCheck space and connectivity; Ceph 19.x Squid on hyper-converged nodes.\n\n3. MIGRATE REPOSITORIES\nBookworm → Trixie; write deb822 .sources files.\nComment legacy .list files; apt-get update.\n\n4. VALIDATE THE PVE 9 CANDIDATE\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nStop if the candidate is not 9.x or proxmox-ve would be removed.\n\n5. UPGRADE\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nNo subscription: disable pve-enterprise.sources.\nLog: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. VERIFY AND REBOOT\nEFI: apt install grub-efi-amd64\npve8to9 --full; review results and the reboot prompt.",
|
||||
"preflightHeading": "Pre-flight: what gets checked",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Check",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Files involved",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Official references",
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Resumen",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Cambiar canal de release",
|
||||
"settingsLanguage": "Cambiar idioma",
|
||||
"settingsVersion": "Mostrar información de versión",
|
||||
"settingsUninstall": "Desinstalar ProxMenux",
|
||||
"commandsReference": "Referencia de comandos",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "El jail <code>[proxmenux]</code> lee <code>/var/log/proxmenux-auth.log</code>, que lo escribe la app Flask del Monitor de ProxMenux — no un logger de journald. Si no usas el Monitor, el archivo se queda vacío y el jail nunca dispara. Eso es lo esperado; la configuración del jail es inocua. Si sí usas el Monitor y el log está vacío, comprueba la configuración de logging de Flask.",
|
||||
"selfBanTitle": "Me he baneado a mí mismo",
|
||||
"selfBanIntro": "Desde una consola / IPMI / iKVM (u otra IP whitelisted):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "apt-get falla: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "Al host le faltan los repos de Debian (común en instalaciones de Proxmox a pelo). El instalador detecta esto y escribe <code>/etc/apt/sources.list.d/debian.sources</code> con el codename correcto (<code>bookworm</code> / <code>trixie</code>) antes de reintentarlo. Si sigue fallando, comprueba <code>/etc/os-release</code> en busca de <code>VERSION_CODENAME</code> y confirma que la URL del repo es alcanzable.",
|
||||
"lockoutTitle": "SSH me bloquea el acceso tras la instalación",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Flujo del modo automático",
|
||||
"flowIntro": "Seis fases consecutivas. La fase 2 actualiza los paquetes de PVE 8 y la fase 3 escribe la configuración de repositorios de PVE 9. Un fallo detiene el proceso sin revertir esos cambios; si la actualización de PVE 8 falla o se cancela, los repositorios no se modifican. La fase 5 ejecuta <code>dist-upgrade</code> para PVE 9.",
|
||||
"flowDiagram": "1. COMPROBACIONES\nBloquea termproxy / vncshell; confirma la actualización; rechaza PVE 9.\n\n2. PREPARAR PVE 8\nscripts/global/update-pve8.sh\nActualiza los paquetes de PVE 8; se detiene si la actualización falla o se cancela.\npve8to9 --full (puede ofrecer reparaciones).\nComprueba espacio y conectividad; Ceph 19.x Squid en nodos hiperconvergentes.\n\n3. MIGRAR REPOSITORIOS\nBookworm → Trixie; escribe archivos deb822 .sources.\nComenta los archivos .list antiguos; apt-get update.\n\n4. VALIDAR EL CANDIDATO DE PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nSe detiene si el candidato no es 9.x o se eliminaría proxmox-ve.\n\n5. ACTUALIZAR\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSin suscripción: desactiva pve-enterprise.sources.\nRegistro: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. VERIFICAR Y REINICIAR\nEFI: apt install grub-efi-amd64\npve8to9 --full; revisa el resultado y la pregunta de reinicio.",
|
||||
"flowDiagram": "1. COMPROBACIONES\nBloquea termproxy / vncshell; confirma la actualización; rechaza PVE 9.\n\n2. PREPARAR PVE 8\nscripts/global/update-pve8.sh\nActualiza los paquetes de PVE 8; se detiene si la actualización falla o se cancela.\npve8to9 --full (puede ofrecer reparaciones).\nComprueba espacio y conectividad; Ceph 19.x Squid en nodos hiperconvergentes.\n\n3. MIGRAR REPOSITORIOS\nBookworm → Trixie; escribe archivos deb822 .sources.\nComenta los archivos .list antiguos; apt-get update.\n\n4. VALIDAR EL CANDIDATO DE PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nSe detiene si el candidato no es 9.x o se eliminaría proxmox-ve.\n\n5. ACTUALIZAR\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSin suscripción: desactiva pve-enterprise.sources.\nRegistro: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. VERIFICAR Y REINICIAR\nEFI: apt install grub-efi-amd64\npve8to9 --full; revisa el resultado y la pregunta de reinicio.",
|
||||
"preflightHeading": "Comprobaciones previas: qué se chequea",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Chequeo",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Archivos implicados",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Referencias oficiales",
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Vue d’ensemble",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Changer de canal de publication",
|
||||
"settingsLanguage": "Changer de langue",
|
||||
"settingsVersion": "Afficher les informations de version",
|
||||
"settingsUninstall": "Désinstaller ProxMenux",
|
||||
"commandsReference": "Référence des commandes",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "Le <code>[proxmenux]</code> jail lit <code>/var/log/proxmenux-auth.log</code>, qui est écrit par l'application ProxMenux Monitor Flask — et non par un enregistreur journald. Si vous n'exécutez pas le moniteur, le fichier reste vide et le jail ne se déclenche jamais. C'est prévu ; la configuration jail est inoffensive. Si vous exécutez le moniteur et que le journal est vide, vérifiez la configuration de journalisation Flask.",
|
||||
"selfBanTitle": "Je me suis bloqué moi-même",
|
||||
"selfBanIntro": "À partir d'une console / IPMI / iKVM (ou d'une autre IP sur liste blanche):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "Échec d'apt-get : ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "Les dépôts Debian manquent sur l'hôte (fréquent sur les installations Proxmox minimales). L'installateur crée <code>/etc/apt/sources.list.d/debian.sources</code> avec le bon nom de code (<code>bookworm</code> / <code>trixie</code>), puis réessaie. Si cela échoue encore, vérifiez <code>VERSION_CODENAME</code> dans <code>/etc/os-release</code> et l'accessibilité du dépôt.",
|
||||
"lockoutTitle": "SSH me verrouille après l'installation",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Déroulement du mode automatique",
|
||||
"flowIntro": "Six phases successives. La phase 2 met à jour les paquets PVE 8 et la phase 3 écrit la configuration des dépôts PVE 9. Un échec arrête le déroulement sans annuler ces modifications ; si la mise à jour PVE 8 échoue ou est annulée, les dépôts restent inchangés. La phase 5 lance <code>dist-upgrade</code> vers PVE 9.",
|
||||
"flowDiagram": "1. CONTRÔLES\nBloque termproxy / vncshell ; confirme la mise à niveau ; refuse PVE 9.\n\n2. PRÉPARER PVE 8\nscripts/global/update-pve8.sh\nMet à jour les paquets PVE 8 ; arrêt si la mise à jour échoue ou est annulée.\npve8to9 --full (peut proposer des réparations).\nVérifie espace et connectivité ; Ceph 19.x Squid sur les nœuds hyperconvergés.\n\n3. MIGRER LES DÉPÔTS\nBookworm → Trixie ; écrit les fichiers deb822 .sources.\nCommente les anciens fichiers .list ; apt-get update.\n\n4. VALIDER LE CANDIDAT PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nArrêt si le candidat n’est pas 9.x ou si proxmox-ve serait supprimé.\n\n5. METTRE À NIVEAU\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSans abonnement : désactive pve-enterprise.sources.\nJournal : /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. VÉRIFIER ET REDÉMARRER\nEFI : apt install grub-efi-amd64\npve8to9 --full ; examiner le résultat et la demande de redémarrage.",
|
||||
"flowDiagram": "1. CONTRÔLES\nBloque termproxy / vncshell ; confirme la mise à niveau ; refuse PVE 9.\n\n2. PRÉPARER PVE 8\nscripts/global/update-pve8.sh\nMet à jour les paquets PVE 8 ; arrêt si la mise à jour échoue ou est annulée.\npve8to9 --full (peut proposer des réparations).\nVérifie espace et connectivité ; Ceph 19.x Squid sur les nœuds hyperconvergés.\n\n3. MIGRER LES DÉPÔTS\nBookworm → Trixie ; écrit les fichiers deb822 .sources.\nCommente les anciens fichiers .list ; apt-get update.\n\n4. VALIDER LE CANDIDAT PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nArrêt si le candidat n’est pas 9.x ou si proxmox-ve serait supprimé.\n\n5. METTRE À NIVEAU\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSans abonnement : désactive pve-enterprise.sources.\nJournal : /var/log/pve8-a-pve9-<timestamp>.log\n\n6. VÉRIFIER ET REDÉMARRER\nEFI : apt install grub-efi-amd64\npve8to9 --full ; examiner le résultat et la demande de redémarrage.",
|
||||
"preflightHeading": "Contrôles avant la mise à niveau",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Vérifier",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Fichiers concernés",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Références officielles",
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Panoramica",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Cambia canale di rilascio",
|
||||
"settingsLanguage": "Cambia lingua",
|
||||
"settingsVersion": "Mostra informazioni sulla versione",
|
||||
"settingsUninstall": "Disinstalla ProxMenux",
|
||||
"commandsReference": "Riferimento dei comandi",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "Il <code>[proxmenux]</code> jail legge <code>/var/log/proxmenux-auth.log</code>, che è scritto dall'app ProxMenux Monitor Flask - non da un logger journald. Se non esegui il Monitor, il file rimane vuoto e il jail non si attiva mai. Questo è previsto; la configurazione jail è innocua. Se esegui il Monitor e il registro è vuoto, controlla la configurazione di registrazione Flask.",
|
||||
"selfBanTitle": "Ho bloccato il mio stesso accesso",
|
||||
"selfBanIntro": "Da una console / IPMI / iKVM (o da un altro IP whitelist):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "apt-get non riesce: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "L'host manca il repos Debian (comune sulle installazioni di Barebones Proxmox). L'installatore rileva questo e scrive <code>/etc/apt/sources.list.d/debian.sources</code> con il nome giusto in codice (<code>bookworm</code> / <code>trixie</code>) prima di riprovare. Se non riesce ancora, controllare <code>/etc/os-release</code> per <code>VERSION_CODENAME</code> e confermare che l'URL repo è raggiungibile.",
|
||||
"lockoutTitle": "SSH mi blocca dopo l'installazione",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Percorso della modalità automatica",
|
||||
"flowIntro": "Sei fasi consecutive. La fase 2 aggiorna i pacchetti PVE 8 e la fase 3 scrive la configurazione dei repository di PVE 9. Un errore interrompe il percorso senza annullare quelle modifiche; se l’aggiornamento di PVE 8 non riesce o viene annullato, i repository restano invariati. La fase 5 esegue <code>dist-upgrade</code> a PVE 9.",
|
||||
"flowDiagram": "1. CONTROLLI\nBlocca termproxy / vncshell; conferma l’aggiornamento; rifiuta PVE 9.\n\n2. PREPARARE PVE 8\nscripts/global/update-pve8.sh\nAggiorna i pacchetti PVE 8; si ferma se l’aggiornamento non riesce o viene annullato.\npve8to9 --full (può proporre riparazioni).\nControlla spazio e connettività; Ceph 19.x Squid sui nodi iperconvergenti.\n\n3. MIGRARE I REPOSITORY\nBookworm → Trixie; scrive file deb822 .sources.\nCommenta i vecchi file .list; apt-get update.\n\n4. VERIFICARE IL CANDIDATO PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nSi ferma se il candidato non è 9.x o se proxmox-ve verrebbe rimosso.\n\n5. AGGIORNARE\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSenza abbonamento: disattiva pve-enterprise.sources.\nRegistro: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. VERIFICARE E RIAVVIARE\nEFI: apt install grub-efi-amd64\npve8to9 --full; esaminare risultati e richiesta di riavvio.",
|
||||
"flowDiagram": "1. CONTROLLI\nBlocca termproxy / vncshell; conferma l’aggiornamento; rifiuta PVE 9.\n\n2. PREPARARE PVE 8\nscripts/global/update-pve8.sh\nAggiorna i pacchetti PVE 8; si ferma se l’aggiornamento non riesce o viene annullato.\npve8to9 --full (può proporre riparazioni).\nControlla spazio e connettività; Ceph 19.x Squid sui nodi iperconvergenti.\n\n3. MIGRARE I REPOSITORY\nBookworm → Trixie; scrive file deb822 .sources.\nCommenta i vecchi file .list; apt-get update.\n\n4. VERIFICARE IL CANDIDATO PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nSi ferma se il candidato non è 9.x o se proxmox-ve verrebbe rimosso.\n\n5. AGGIORNARE\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSenza abbonamento: disattiva pve-enterprise.sources.\nRegistro: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. VERIFICARE E RIAVVIARE\nEFI: apt install grub-efi-amd64\npve8to9 --full; esaminare risultati e richiesta di riavvio.",
|
||||
"preflightHeading": "Controlli prima dell’aggiornamento",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Controllo",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "File coinvolti",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Riferimenti ufficiali",
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Visão geral",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Alterar o canal de versões",
|
||||
"settingsLanguage": "Alterar idioma",
|
||||
"settingsVersion": "Mostrar informações da versão",
|
||||
"settingsUninstall": "Desinstalar o ProxMenux",
|
||||
"commandsReference": "Referência de comandos",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "O <code>[proxmenux]</code> jail lê <code>/var/log/proxmenux-auth.log</code>, que é escrito pelo aplicativo ProxMenux Monitor Flask — não por um criador de logs journald. Se você não executar o Monitor, o arquivo permanecerá vazio e o jail nunca será acionado. Isso é esperado; a configuração jail é inofensiva. Se você executar o Monitor e o log estiver vazio, verifique a configuração de registro do Flask.",
|
||||
"selfBanTitle": "Eu bani-me.",
|
||||
"selfBanIntro": "A partir de uma consola / IPMI / iKVM (ou de outro IP autorizado):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "O apt-get falha: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "O host está faltando o Debian repos (comum nas instalações do 'barebones' Proxmox). O instalador detecta isso e escreve <code>/etc/apt/sources.list.d/debian.sources</code> com o codinome certo (<code>bookworm</code> / <code>trixie</code>) antes de tentar novamente. Se ainda falhar, verifique <code>/etc/os-release</code> para <code>VERSION_CODENAME</code> e confirme que o URL do repo está acessível.",
|
||||
"lockoutTitle": "SSH me bloqueia depois de instalar",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Processo do modo automático",
|
||||
"flowIntro": "Seis fases consecutivas. A fase 2 atualiza os pacotes PVE 8 e a fase 3 escreve a configuração dos repositórios do PVE 9. Uma falha interrompe o processo sem reverter essas alterações; se a atualização do PVE 8 falhar ou for cancelada, os repositórios ficam inalterados. A fase 5 executa <code>dist-upgrade</code> para PVE 9.",
|
||||
"flowDiagram": "1. VERIFICAÇÕES\nBloqueia termproxy / vncshell; confirma a atualização; rejeita PVE 9.\n\n2. PREPARAR PVE 8\nscripts/global/update-pve8.sh\nAtualiza os pacotes PVE 8; para se a atualização falhar ou for cancelada.\npve8to9 --full (pode propor reparações).\nVerifica espaço e conectividade; Ceph 19.x Squid em nós hiperconvergentes.\n\n3. MIGRAR REPOSITÓRIOS\nBookworm → Trixie; escreve ficheiros deb822 .sources.\nComenta os ficheiros .list antigos; apt-get update.\n\n4. VALIDAR O CANDIDATO PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nPara se o candidato não for 9.x ou se proxmox-ve fosse removido.\n\n5. ATUALIZAR\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSem subscrição: desativa pve-enterprise.sources.\nRegisto: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. VERIFICAR E REINICIAR\nEFI: apt install grub-efi-amd64\npve8to9 --full; examine os resultados e a pergunta de reinício.",
|
||||
"flowDiagram": "1. VERIFICAÇÕES\nBloqueia termproxy / vncshell; confirma a atualização; rejeita PVE 9.\n\n2. PREPARAR PVE 8\nscripts/global/update-pve8.sh\nAtualiza os pacotes PVE 8; para se a atualização falhar ou for cancelada.\npve8to9 --full (pode propor reparações).\nVerifica espaço e conectividade; Ceph 19.x Squid em nós hiperconvergentes.\n\n3. MIGRAR REPOSITÓRIOS\nBookworm → Trixie; escreve ficheiros deb822 .sources.\nComenta os ficheiros .list antigos; apt-get update.\n\n4. VALIDAR O CANDIDATO PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nPara se o candidato não for 9.x ou se proxmox-ve fosse removido.\n\n5. ATUALIZAR\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nSem subscrição: desativa pve-enterprise.sources.\nRegisto: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. VERIFICAR E REINICIAR\nEFI: apt install grub-efi-amd64\npve8to9 --full; examine os resultados e a pergunta de reinício.",
|
||||
"preflightHeading": "Verificações antes da atualização",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Verificar",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Ficheiros envolvidos",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Referências oficiais",
|
||||
|
||||
@@ -166,6 +166,7 @@
|
||||
"settingsOverview": "Prehľad",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Zmeniť kanál vydaní",
|
||||
"settingsLanguage": "Zmeniť jazyk",
|
||||
"settingsVersion": "Zobraziť informácie o verzii",
|
||||
"settingsUninstall": "Odinštalovať ProxMenux",
|
||||
"commandsReference": "Prehľad príkazov",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "<code>[proxmenux]</code> jail číta <code>/var/log/proxmenux-auth.log</code>, ktorý je napísaný aplikáciou ProxMenux Monitor Flask – nie zapisovačom journald. Ak nespustíte Monitor, súbor zostane prázdny a jail sa nikdy nespustí. To sa očakáva; konfigurácia jail je neškodná. Ak spustíte Monitor a protokol je prázdny, skontrolujte konfiguráciu protokolovania Flask.",
|
||||
"selfBanTitle": "Zakázal som sám seba.",
|
||||
"selfBanIntro": "Z konzoly / IPMI / iKVM (alebo inej povolenej IP adresy):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "apt-get zlyhá: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "Hostiteľovi chýbajú repozitáre Debian (bežné pri minimálnej inštalácii Proxmox). Inštalátor to rozpozná a pred opakovaním vytvorí <code>/etc/apt/sources.list.d/debian.sources</code> so správnym kódovým označením (<code>bookworm</code> / <code>trixie</code>). Ak chyba pretrváva, skontrolujte <code>VERSION_CODENAME</code> v <code>/etc/os-release</code> a dostupnosť URL repozitára.",
|
||||
"lockoutTitle": "SSH ma po inštalácii zamkne",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Postup automatického režimu",
|
||||
"flowIntro": "Šesť po sebe idúcich fáz. Fáza 2 aktualizuje balíky PVE 8 a fáza 3 zapíše konfiguráciu repozitárov PVE 9. Chyba postup zastaví, tieto zmeny však nevráti; ak aktualizácia PVE 8 zlyhá alebo sa zruší, repozitáre zostanú nezmenené. Fáza 5 vykoná <code>dist-upgrade</code> na PVE 9.",
|
||||
"flowDiagram": "1. KONTROLY\nBlokuje termproxy / vncshell; potvrdí aktualizáciu; odmietne PVE 9.\n\n2. PRÍPRAVA PVE 8\nscripts/global/update-pve8.sh\nAktualizuje balíky PVE 8; zastaví sa, ak aktualizácia zlyhá alebo sa zruší.\npve8to9 --full (môže ponúknuť opravy).\nOverí miesto a pripojenie; Ceph 19.x Squid na hyperkonvergovaných uzloch.\n\n3. MIGRÁCIA REPOZITÁROV\nBookworm → Trixie; zapíše súbory deb822 .sources.\nZakomentuje staré súbory .list; apt-get update.\n\n4. OVERENIE KANDIDÁTA PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nZastaví sa, ak kandidát nie je 9.x alebo by sa odstránil proxmox-ve.\n\n5. AKTUALIZÁCIA\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nBez predplatného: vypne pve-enterprise.sources.\nProtokol: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. OVERENIE A REŠTART\nEFI: apt install grub-efi-amd64\npve8to9 --full; skontrolujte výsledok a otázku na reštart.",
|
||||
"flowDiagram": "1. KONTROLY\nBlokuje termproxy / vncshell; potvrdí aktualizáciu; odmietne PVE 9.\n\n2. PRÍPRAVA PVE 8\nscripts/global/update-pve8.sh\nAktualizuje balíky PVE 8; zastaví sa, ak aktualizácia zlyhá alebo sa zruší.\npve8to9 --full (môže ponúknuť opravy).\nOverí miesto a pripojenie; Ceph 19.x Squid na hyperkonvergovaných uzloch.\n\n3. MIGRÁCIA REPOZITÁROV\nBookworm → Trixie; zapíše súbory deb822 .sources.\nZakomentuje staré súbory .list; apt-get update.\n\n4. OVERENIE KANDIDÁTA PVE 9\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nZastaví sa, ak kandidát nie je 9.x alebo by sa odstránil proxmox-ve.\n\n5. AKTUALIZÁCIA\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nBez predplatného: vypne pve-enterprise.sources.\nProtokol: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. OVERENIE A REŠTART\nEFI: apt install grub-efi-amd64\npve8to9 --full; skontrolujte výsledok a otázku na reštart.",
|
||||
"preflightHeading": "Kontroly pred aktualizáciou",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Kontrola",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Príslušné súbory",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Oficiálne zdroje",
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"settingsOverview": "Översikt",
|
||||
"settingsMonitor": "ProxMenux Monitor",
|
||||
"settingsBeta": "Byt versionskanal",
|
||||
"settingsLanguage": "Byt språk",
|
||||
"settingsVersion": "Visa versionsinformation",
|
||||
"settingsUninstall": "Avinstallera ProxMenux",
|
||||
"commandsReference": "Kommandoreferens",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"monitorEmptyBody": "Jailen <code>[proxmenux]</code> läser <code>/var/log/proxmenux-auth.log</code>, som skrivs av Flask-appen ProxMenux Monitor, inte av en journald-logger. Utan Monitor förblir filen tom och jailen utlöses inte. Det är väntat och konfigurationen är ofarlig. Om Monitor körs men loggen är tom kontrollerar du loggningskonfigurationen i Flask.",
|
||||
"selfBanTitle": "Jag har blockerat min egen åtkomst",
|
||||
"selfBanIntro": "Från en konsol / IPMI / iKVM (eller annan vitlistad IP):",
|
||||
"selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"selfBanCode": "fail2ban-client unban <YOUR_IP>\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload",
|
||||
"aptFailTitle": "apt-get misslyckas: ''Unable to locate package fail2ban''",
|
||||
"aptFailBody": "Värden saknas Debian repos (gemensamt på barebones Proxmox installationer). Installatören upptäcker detta och skriver <code>/etc/apt/sources.list.d/debian.sources</code> med rätt kodnamn (<code>bookworm</code> / <code>trixie</code>) innan du går tillbaka. Om det fortfarande misslyckas, kontrollera <code>/etc/os-release</code> för <code>VERSION_CODENAME</code> och bekräfta att repo URL är tillgänglig.",
|
||||
"lockoutTitle": "SSH låser mig efter installation",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"flowHeading": "Förlopp i automatiskt läge",
|
||||
"flowIntro": "Sex faser i följd. Fas 2 uppdaterar PVE 8-paketen och fas 3 skriver konfigurationen för PVE 9-paketkällorna. Ett fel stoppar förloppet men återställer inte ändringarna; om PVE 8-uppdateringen misslyckas eller avbryts lämnas paketkällorna orörda. Fas 5 kör <code>dist-upgrade</code> till PVE 9.",
|
||||
"flowDiagram": "1. KONTROLLER\nBlockera termproxy / vncshell; bekräfta uppgraderingen; avvisa PVE 9.\n\n2. FÖRBERED PVE 8\nscripts/global/update-pve8.sh\nUppdatera PVE 8-paket; stoppa om uppdateringen misslyckas eller avbryts.\npve8to9 --full (kan erbjuda reparationer).\nKontrollera utrymme och anslutning; Ceph 19.x Squid på hyperkonvergerade noder.\n\n3. ÄNDRA PAKETKÄLLOR\nBookworm → Trixie; skriv deb822-filer .sources.\nKommentera bort gamla .list-filer; apt-get update.\n\n4. KONTROLLERA PVE 9-KANDIDATEN\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nStoppa om kandidaten inte är 9.x eller om proxmox-ve skulle tas bort.\n\n5. UPPGRADERA\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nUtan prenumeration: inaktivera pve-enterprise.sources.\nLogg: /var/log/pve8-a-pve9-'<'timestamp'>'.log\n\n6. KONTROLLERA OCH STARTA OM\nEFI: apt install grub-efi-amd64\npve8to9 --full; granska resultatet och frågan om omstart.",
|
||||
"flowDiagram": "1. KONTROLLER\nBlockera termproxy / vncshell; bekräfta uppgraderingen; avvisa PVE 9.\n\n2. FÖRBERED PVE 8\nscripts/global/update-pve8.sh\nUppdatera PVE 8-paket; stoppa om uppdateringen misslyckas eller avbryts.\npve8to9 --full (kan erbjuda reparationer).\nKontrollera utrymme och anslutning; Ceph 19.x Squid på hyperkonvergerade noder.\n\n3. ÄNDRA PAKETKÄLLOR\nBookworm → Trixie; skriv deb822-filer .sources.\nKommentera bort gamla .list-filer; apt-get update.\n\n4. KONTROLLERA PVE 9-KANDIDATEN\napt-cache policy proxmox-ve\napt-get -s dist-upgrade\nStoppa om kandidaten inte är 9.x eller om proxmox-ve skulle tas bort.\n\n5. UPPGRADERA\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade\nUtan prenumeration: inaktivera pve-enterprise.sources.\nLogg: /var/log/pve8-a-pve9-<timestamp>.log\n\n6. KONTROLLERA OCH STARTA OM\nEFI: apt install grub-efi-amd64\npve8to9 --full; granska resultatet och frågan om omstart.",
|
||||
"preflightHeading": "Förkontroll: vad som kontrolleras",
|
||||
"preflightTable": {
|
||||
"checkHeader": "Kontroll",
|
||||
@@ -273,7 +273,7 @@
|
||||
},
|
||||
"files": {
|
||||
"heading": "Filer involverade",
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-'<'timestamp'>'.log # full upgrade log"
|
||||
"code": "scripts/utilities/upgrade_pve8_to_pve9.sh # main upgrade orchestrator\nscripts/utilities/pve8to9_check.sh # pre-check (separate menu option)\nscripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh # 17-step runbook\nscripts/global/update-pve8.sh # PVE 8 worker (final patches)\n/etc/apt/sources.list # bookworm → trixie sed\n/etc/apt/sources.list.d/proxmox.sources # created (deb822 PVE 9)\n/etc/apt/sources.list.d/debian.sources # created (deb822 trixie)\n/etc/apt/sources.list.d/ceph.sources # created if Ceph\n/etc/apt/sources.list.d/*.list # legacy files commented out / removed\n/var/log/pve8-a-pve9-<timestamp>.log # full upgrade log"
|
||||
},
|
||||
"references": {
|
||||
"heading": "Officiella referenser",
|
||||
|
||||
Generated
+1402
-2292
File diff suppressed because it is too large
Load Diff
+50
-60
@@ -10,75 +10,65 @@
|
||||
"index": "node scripts/localize-export.mjs && pagefind --site out --output-path public/pagefind && cp -r public/pagefind out/pagefind"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@radix-ui/react-accordion": "^1.2.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.1",
|
||||
"@radix-ui/react-avatar": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.1.3",
|
||||
"@radix-ui/react-accordion": "^1.3.1",
|
||||
"@radix-ui/react-alert-dialog": "^1.2.1",
|
||||
"@radix-ui/react-aspect-ratio": "^1.2.0",
|
||||
"@radix-ui/react-avatar": "^1.3.1",
|
||||
"@radix-ui/react-checkbox": "^1.4.1",
|
||||
"@radix-ui/react-collapsible": "^1.1.2",
|
||||
"@radix-ui/react-context-menu": "^2.2.4",
|
||||
"@radix-ui/react-context-menu": "^2.4.1",
|
||||
"@radix-ui/react-dialog": "^1.1.4",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||
"@radix-ui/react-hover-card": "^1.1.4",
|
||||
"@radix-ui/react-label": "^2.1.1",
|
||||
"@radix-ui/react-menubar": "^1.1.4",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.3",
|
||||
"@radix-ui/react-popover": "^1.1.4",
|
||||
"@radix-ui/react-progress": "^1.1.1",
|
||||
"@radix-ui/react-radio-group": "^1.2.2",
|
||||
"@radix-ui/react-scroll-area": "^1.2.2",
|
||||
"@radix-ui/react-select": "^2.1.4",
|
||||
"@radix-ui/react-separator": "^1.1.1",
|
||||
"@radix-ui/react-slider": "^1.2.2",
|
||||
"@radix-ui/react-slot": "latest",
|
||||
"@radix-ui/react-switch": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.2",
|
||||
"@radix-ui/react-toast": "^1.2.4",
|
||||
"@radix-ui/react-dropdown-menu": "^2.2.1",
|
||||
"@radix-ui/react-hover-card": "^1.2.1",
|
||||
"@radix-ui/react-label": "^2.1.17",
|
||||
"@radix-ui/react-menubar": "^1.2.1",
|
||||
"@radix-ui/react-navigation-menu": "^1.4.1",
|
||||
"@radix-ui/react-popover": "^1.3.1",
|
||||
"@radix-ui/react-progress": "^1.2.1",
|
||||
"@radix-ui/react-radio-group": "^1.5.1",
|
||||
"@radix-ui/react-scroll-area": "^1.4.0",
|
||||
"@radix-ui/react-select": "^2.4.1",
|
||||
"@radix-ui/react-separator": "^1.2.0",
|
||||
"@radix-ui/react-slider": "^1.6.1",
|
||||
"@radix-ui/react-slot": "^1.5.0",
|
||||
"@radix-ui/react-switch": "^1.4.1",
|
||||
"@radix-ui/react-tabs": "^1.2.1",
|
||||
"@radix-ui/react-toast": "^1.3.1",
|
||||
"@radix-ui/react-toggle": "^1.1.1",
|
||||
"@radix-ui/react-toggle-group": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"@radix-ui/react-toggle-group": "^1.2.1",
|
||||
"@radix-ui/react-tooltip": "^1.4.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "1.0.4",
|
||||
"date-fns": "^2.30.0",
|
||||
"embla-carousel-react": "8.5.1",
|
||||
"fs": "latest",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-react-parser": "^3.0.0",
|
||||
"input-otp": "1.4.1",
|
||||
"cmdk": "1.1.1",
|
||||
"embla-carousel-react": "8.6.0",
|
||||
"html-react-parser": "^3.0.16",
|
||||
"input-otp": "1.5.0",
|
||||
"lucide-react": "^0.454.0",
|
||||
"mermaid": "^11.14.0",
|
||||
"next": "15.5.15",
|
||||
"next-intl": "^4.12.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"path": "latest",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"react-hook-form": "^7.54.1",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"mermaid": "^11.17.2",
|
||||
"next": "15.5.28",
|
||||
"next-intl": "^4.14.9",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.3.1",
|
||||
"react-dom": "^19.3.1",
|
||||
"react-hook-form": "^7.91.0",
|
||||
"react-resizable-panels": "^2.1.9",
|
||||
"recharts": "2.15.0",
|
||||
"remark": "latest",
|
||||
"remark-breaks": "3.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-html": "latest",
|
||||
"shiki": "^4.0.2",
|
||||
"sonner": "^1.7.1",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"unified": "^11.0.0",
|
||||
"zod": "^3.24.1"
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-html": "^16.0.1",
|
||||
"shiki": "^4.5.0",
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^2.6.1",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@tailwindcss/typography": "^0.5.20",
|
||||
"@types/node": "^22.20.5",
|
||||
"@types/react": "^18.3.31",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"pagefind": "^1.5.2",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5"
|
||||
"postcss": "^8.5.32",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user