`#host` and `#lxc-net` are visual sidebar section headers for the
Storage Share Manager page — they group their submenu items in the
sidebar tree but point back at the parent Overview with an anchor,
so they aren't standalone docs the reader advances to. Including
them in the flat Previous/Next sequence produced two regressions:
* On `/docs/storage-share/#host` the Next button targeted `#host`
again, so clicking it didn't move. The earlier hash-tracking fix
intended to catch this, but a `useEffect` with an empty dep array
only runs on mount — and Next.js Link navigations don't fire
`hashchange` when the path changes too, so a cross-page navigation
that lands on `#host` (sidebar click) rendered with hash="" and
re-collapsed to the section header.
* On `/docs/storage-share/lxc-mount-points/` the Next button pointed
at `#lxc-net` instead of advancing to `lxc-nfs-client`, since the
section header sat between the two real pages in the flat list.
Filter out any sidebar entry whose href contains `#` at walk time so
the flat list only carries real pages. With them gone, an anchored
URL collapses to its parent Overview and Next walks straight into
the first subpage. The hash effect + state are no longer needed so
the component drops them, keeping only the pathname-based match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The HOST block already groups everything that runs on the host
side, so the secondary "Host-only resources" divider between item 4
and item 5 was visual noise — the user reading the menu sees two
HOST sub-titles back to back and has to wonder how items 1-4 differ
from item 5 in scope. They don't; items 1-4 register an external or
local resource as a Proxmox storage, item 5 creates a local shared
directory. Both are host-side actions.
While here, retitle item 5 from "Add Shared Directory on Host" to
"Create Shared Directory on Host" — items 1-4 add an existing
external/local resource, item 5 creates a new one.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related improvements to the post-add verification step that the
user hit while testing the stopped-CT case.
A stopped container couldn't be probed at all — the previous patch
just told the user "mount will activate on next start" and left
them to discover any issues later (the typical issue being
permission denied on the host directory, since the dialog confirms
the bind-mount was added but never proves it works). Offer to start
the container right now so the user gets feedback in the same
session; if they decline, fall back to the informational line.
The post-restart / post-start probe used `test -d $ct_mount_point`
which only checks that the directory is visible inside the
container. That always succeeds whenever the bind-mount took
effect, even if the host directory permissions don't let the
unprivileged-LXC mapped uid write — exactly the case the user just
ran into with /mnt/disk-sda (700 → others gets r-x). Replace with a
touch+rm probe in a new `_lmm_verify_writable` helper used by both
branches so the user is told straight away when writes will fail
and, when they will, is given the exact `chmod o+rwx` / `setfacl`
command and a pointer to the host-perms prompt.
Verified on .55 / LXC 112 (unprivileged) against /mnt/disk-sda:
container stopped → start prompt → start → directory visible →
touch probe → success.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The earlier fix to handle stopped containers in the add flow used
`msg_info` for the "Container is stopped — mount will activate
automatically on next start" line. `msg_info` is the spinner-start
half of the msg_info/msg_ok pair — it never gets closed here
because there's nothing to wait on, so the spinner glyph stays
visible (⠋) right before the "Press Enter to continue" prompt.
Switch to `msg_ok` so the line renders as a clean static success
mark, matching the visual style of the other terminal messages.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The fstab-only mount method explicitly says "for LXC bind-mounts" in
its dialog wording, but the mount point left behind by mkfs +
mkdir is owned root:root with mode 0755. An unprivileged LXC sees
the directory through its uid offset (root inside → host uid 100000)
which lands under the directory's "others" bits — so the container
can read but never write, and the user has to track down the
chmod / setfacl step manually.
lxc-mount-manager_minimal.sh already offers exactly this fix as
`lmm_offer_host_permissions` when the user adds the bind-mount
through that script, but the disk-side script never closed its half
of the loop. Add a small `_apply_lxc_bind_mount_perms` helper that
runs `chmod o+rwx` plus `setfacl o::rwx + default ACL` whenever
MODE_FSTAB=1, and call it from both `mount_disk_permanently`
(format path) and `mount_existing_disk` (use_existing path). Pure
pvesm-only mounts keep the original behaviour — chmod o+rwx on a
VM/backup storage isn't desirable.
Verified on .55 against the existing /mnt/disk-sda + LXC 112
(unprivileged): unprivileged container root could not write before
(Permission denied), writes succeed after the perms are applied and
land on the host as uid 100000 as expected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related gaps in the disk-host script that surfaced while testing
the new dual-flow (pvesm / fstab-only / both) added earlier today.
view_disk_storages used to read only from `pvesm status`, so a disk
added via the new fstab-only path — exactly the case where the user
wants a local disk available for LXC bind-mounts without registering
it as a Proxmox storage — never showed up. Replicate the same fstab
scan remove_disk_storage already performs and list those mounts in a
second section underneath the pvesm ones. Empty state and wording
updated so the panel no longer claims "No local storage configured
in Proxmox" when fstab-only mounts are present.
Both view_disk_storages (new code) and remove_disk_storage (existing
fstab branch) were happily picking up `/mnt/Archivos` and any other
CIFS/NFS share mounted under /mnt. samba_host.sh and nfs_host.sh own
those — surfacing them in the local-disk menus would let a user
remove a network share from the wrong screen. Filter by fstype
(skip cifs/smbfs/nfs/nfs4/nfsv4/sshfs/fuse) and additionally require
that the resolved source be a real block device, which drops bind
mounts and anything else whose backing source isn't a disk.
Verified on .55 with the test disk-sda fstab-only mount alongside
the existing //192.168.0.15/Archivos CIFS mount: only disk-sda is
listed by the local-disk view/remove flows.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two release-day fixes in the host-side share tooling, both reported
during testing of the v1.2.2 candidate.
lxc-mount-manager_minimal.sh
After adding a mount point on a stopped LXC the script offered to
`pct reboot $ct` unconditionally — which fails on a stopped CT
because `pct reboot` only accepts running ones, so the user saw a
bogus "Failed to restart" right after a successful mount. Gate the
prompt on `pct status` and, when the CT is stopped, tell the user
the mount will activate on next start instead of trying to reboot
it. The matching restart prompt in the remove flow (around line
540) was already doing the check correctly; this just brings the
add flow in line.
disk_host.sh
The script always registered the disk as a Proxmox storage via
`pvesm add dir|zfspool`. nfs_host.sh and samba_host.sh already
offered a dual-flow chooser ("Proxmox storage" / "host fstab only"
/ both) so a user could mount the share on the host for LXC
bind-mounts without surfacing it as a Proxmox storage. Replicate
that chooser for local disks:
* new `select_mount_method` checklist with `pvesm` and `fstab`,
inserted after filesystem selection. ZFS is forced into the
pvesm path because a ZFS pool can't be expressed as an fstab
mount.
* `configure_disk_storage` skips the Content Types prompt when
only fstab is selected and renames "Storage ID" → "Mount Name"
in the same case so the wording matches what the user will
actually see (or not see) in Proxmox.
* `format_and_mount_disk` title and summary lines adapt to the
chosen mode.
* the trailing `add_proxmox_dir_storage` call in `add_local_disk_storage`
runs only when `MODE_PVESM=1`; in fstab-only mode the final
message points users at the LXC Mount Manager for bind-mounts.
Verified end-to-end on a 32 GB USB disk against LXC 112
(unprivileged) on .55: fstab-only path → bind-mount → root inside
CT writes mapped to host uid 100000, regular user writes mapped to
host uid 101000, both reads/writes successful from inside the
container.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A mass-backup webhook that exceeded ~2 KB used to be silently
truncated by `desc = message[:MAX_EMBED_DESC]` with MAX_EMBED_DESC
set to 2048 — half of Discord's real description limit and far
below what a multi-VM backup digest produces. The trailing jobs
just vanished from the channel.
Bring the channel up to Discord's actual webhook contract:
* description limit raised to the real 4096-char cap
* if the body still doesn't fit, split it on line boundaries into
one embed per chunk so every backup entry is preserved
* keep title + fields on the first embed only; attach the footer
and timestamp to the last embed so the rendered card has the
normal head/tail framing even when split across many embeds
* enforce Discord's 6000-char-per-embed cap (title + description +
every field name+value) — only kicks in when many large fields
combine with a chunk already near the description ceiling
* batch up to 10 embeds per webhook POST (Discord's per-message
limit) and POST additional messages sequentially with a 0.4 s
gap so a >10-embed digest doesn't trip the 5/2 s webhook rate
limit
Verified with synthetic mass-backup payloads:
* 14 KB / 200 jobs → 4 embeds, 1 POST
* 60 KB / 60 lines → 15 embeds, 2 POSTs (10 + 5)
New AppImage SHA-256:
16ad59ea63a64e5be460cd73f87315e8b39b756bf1c61f3cb2019e9fa3e76361
Closes#220.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New build picks up the get_disks_observation_counts NVMe-rename fix.
SHA-256:
3b44eb1172b4b1b7e6a36d1c9f1cd5a237ec04d52543bb791358525b0653a402
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`get_disks_observation_counts` maps each serial's count to that
serial's "most recent" device_name (so renames like ata8 -> sdh keep
the badge attached). When several physical disks have passed through
the same kernel name across reboots — common with NVMe, the kernel
probes in a different order depending on which slots are populated —
disk_registry keeps a row per (device_name, serial) seen and the
"most recent" device_name for a serial can now be in use by an
entirely different disk.
Concrete case from the wild: serial 211716800490 was nvme0n1 during
the previous boot and earned a real I/O observation. After removing
four of five NVMes, the surviving disk (serial 243332800236) booted
into nvme0n1. The badge layer mirrored 211716800490's count onto
nvme0n1 — which is now a different physical disk — and showed
"1 obs." on the wrong drive, while the modal (which scopes by the
current (device_name, serial) registry row) found nothing and
rendered an empty history.
Only mirror a serial's count onto its device_name when that
device_name is currently owned by the same serial, determined from
the freshest disk_registry row. The serial-keyed entry stays
unconditional so observations remain reachable when the disk is
re-plugged under another device name.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The v1.2.0 binary lingered in the repo after later releases. Remove
it so AppImage/ holds only the current shipping artefact.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The tracked binary still pointed at the build made before the
last two fixes landed (resolution_reason persistence in
health_persistence and disk-temp breakdown alignment in
storage-overview). Re-build the AppImage so the GitHub-published
binary matches what is actually running on the deploy targets.
New SHA-256:
d043e2f27f21315931ab53d87f02390b1a66b0c1730e8b7699aafb565809efbb
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`getDiskHealthBreakdown` carried its own hardcoded ladder (HDD ≤45
normal, ≤55 warning) that was much stricter than the configurable
defaults consumed by `getTempColor` via `useDiskTempThresholds`
(HDD warn 60, hot 65). HDDs at 48 °C therefore rendered a green
"Healthy 48°C" badge on the card but were tallied as "warning" in
the top-of-page "X normal, Y warning, Z critical" summary, leaving
the user with the misleading "6 normal, 5 warning" line.
Use the same threshold map as the per-disk badge so the colour and
the count are always consistent, and so Settings → Health Monitor
Thresholds → Disk temperature actually applies to the breakdown.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The UPDATE in `_resolve_error_impl` only touched `resolved_at` — the
`reason` argument every caller passes was silently dropped, and the
`resolution_reason` / `resolution_type` columns stayed NULL for every
auto-resolved error. The columns were added back in a previous sprint
for exactly this audit-log purpose, but the writer was never updated
to populate them.
Fix the SQL to write `resolution_reason = ?` and tag
`resolution_type = COALESCE(existing, 'auto')` so admin-cleared
errors (whose type is set elsewhere) keep their value while the
default auto path correctly labels itself.
Verified end-to-end on the lab host: re-injected the `disk_nvme2n1`
warning, waited one scan cycle, the row now reads
`resolution_type='auto'` and
`resolution_reason='Transient I/O cleared, SMART now reports healthy'`
— previously these columns stayed NULL even though the resolve_error
call passed a descriptive reason.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a host gets transient I/O events on a disk while smartctl is
momentarily unavailable (the canonical case: late in a noisy
shutdown), the disk-scan code records a `disk_<name>` WARNING tagged
"SMART: unavailable" exactly once and trusts the next scan to clear
it. That trust is misplaced: the clear path only fires when the
device shows up in the current dmesg window with zero events. After
a reboot, dmesg is empty for that device — so the device never gets
iterated, resolve_error is never called, and the dashboard stays
orange for a disk whose SMART now reports PASSED.
Caught on a lab host where `disk_nvme2n1` had been stuck as WARNING
for hours after a reboot. SMART was 100% healthy at the moment of
inspection (Critical Warning 0x00, 0 media errors, 100% spare). The
error's first_seen and last_seen were identical and pre-dated the
current boot, confirming a one-shot record that nothing had cleared.
Fix: add a `_reconcile_stale_disk_warnings()` pass at the top of
`_check_disks_optimized()`. For every active `disk_*` error
(skipping `disk_fs_*`, which is already reconciled separately):
- device gone from /dev/ → resolve "Device no longer present"
- device present + SMART PASSED → resolve "Transient I/O cleared,
SMART now reports healthy"
- device present + SMART UNKNOWN/FAILED → leave active so the
main loop can re-classify on the next dmesg window
Acknowledged errors are left alone so the user's explicit dismiss
intent isn't overridden.
Verified end-to-end: re-injected the original `disk_nvme2n1`
warning into the persistence DB on the lab host, waited one scan
cycle, error was resolved automatically with `resolved_at` set and
`resolution_reason = 'Transient I/O cleared, SMART now reports
healthy'`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous bump commit (2f24de25) shipped a binary that still carried
Next.js 15.1.6 in the bundled chunks even though AppImage/package.json
was at 15.1.9. Root cause: build_appimage.sh only ran `npm install`
when `node_modules` did not exist; on the .50 build host node_modules
had been cached since the 1.2.1 build cycle, so the bump was silently
ignored and the build re-used the stale tree.
Fix the script: always run `npm install --legacy-peer-deps` on every
build. npm reconciles against the lockfile in under a second when
everything is already in sync, so the change is free on a warm tree
and correct on a stale one.
Rebuild from a clean node_modules on .50, redeploy to all four hosts
(SHA 4602b8d4aa130c6f...), runtime grep confirms the bundle now
contains 15.1.9 with no traces of 15.1.6 left. Same architecture and
threat model as before — Flask serves the static export on :8008,
no Next.js runtime — but the version banner now matches the lockfile.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three changes that fold into the v1.2.2 release PR:
1. AppImage: bump Next.js 15.1.6 -> 15.1.9 (CVE-2025-55182)
GHSA-9qr9-h5gf-34mp / React2Shell is a pre-auth RCE in React Server
Components when Server Functions deserialize attacker payloads. The
ProxMenux Monitor ships Next.js in `output: "export"` mode behind
Flask on :8008, so there is no runtime Next.js server and no
"use server" directive in the source tree — the exploitable path is
not reachable. Bumping to 15.1.9 anyway because OpenVAS and similar
scanners flag the version string from the JS bundle regardless of
architecture; raising the floor removes false-positive noise across
every install. Reported by @rost43 in #219.
2. web/components/ui/doc-navigation.tsx: handle sidebar entries that
point to in-page anchors. The Storage Share Manager sidebar has
entries for `/docs/storage-share#host` and
`/docs/storage-share#lxc-net` as section headers, but
usePathname() does not include the hash so every visit collapsed
to the parent page. As a result Next/Previous on /docs/storage-share
stayed stuck at #host, and Next from .../lxc-mount-points/ pointed
back at #host instead of #lxc-net. Read window.location.hash on
mount (and on hashchange) and try the pathname+hash match before
falling back to the pathname-only lookup. SSR hydrates with an
empty hash and refreshes once mounted — brief render before
hydration is the same as the previous behaviour, so no regression.
3. scripts/help_info_menu.sh: user-side improvement (mirrored from
develop).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The drafting date (today) was used as a placeholder. The actual
release date is Tuesday 2026-06-02, when PR #218 merges and the
update notifier picks up 1.2.2. Aligns the changelog header in both
EN and ES with the publication date users will see.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Final ingredient of the v1.2.2 stable release: flip version.txt from
1.2.1 to 1.2.2 so the stable channel's update notifier picks it up
on every running install, ship the consolidated v1.2.2 entry on both
CHANGELOG.md (English) and lang/es/CHANGELOG.md (Spanish), and add
the GitHub link to Jonatan Castro on the contributors page.
CHANGELOG.md entry (and its ES mirror) consolidates the four v1.2.1.x
betas into a single stable note grouped by theme — Health Monitor
configurability, Apprise full feature parity, LXC update detection,
Coral TPU latest upstream drivers, performance optimizations (smartctl
scheduler, fail2ban cache, lxc-info /proc), HTTPS terminal handshake,
PVE 9.x kernel update detection, NVIDIA installer improvements, i18n
documentation site — plus an Acknowledgments section crediting
@jcastro (5 direct commits), @pespinel (1 commit) and @ghosthvj
(field reports that shaped the GPU + Coral work).
contributors/page.tsx: Contributor interface now carries an optional
`githubUrl`; when set, the displayed name is wrapped in an
ExternalLink to that URL (target=_blank). Jonatan Castro's entry gets
`githubUrl: https://github.com/jcastro` so users can reach his repos
from the testers grid.
After this PR merges:
- Users running `menu` will be offered the 1.2.2 upgrade
- proxmenux.com/en/changelog and /es/changelog ship the new entry
(deploy.yml triggers because CHANGELOG.md, lang/** and web/** are
all touched)
- Jonatan Castro's name on the contributors page becomes clickable
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Last commit renamed images/avatars/JF_Car.png to JF_Carr.png on the
assumption the file name was the typo. It wasn't — the user's name
is actually JF_Car. Rename the file back and update the display
name + avatar URL in the contributors page accordingly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the temporary github.com/<handle>.png placeholders with the
project's standard raw.githubusercontent.com/.../images/avatars/<name>.png
pattern used by every other contributor on the page.
Added avatar files:
- images/avatars/heriberto.png
- images/avatars/JF_Carr.png (renamed from JF_Car.png on disk so the
filename matches the display name)
- images/avatars/rafapuerta.png
- images/avatars/JcMinarro.png
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Contributors page (app/[locale]/docs/about/contributors/page.tsx):
add heriberto, JF_Carr, rafapuerta and JcMinarro to the testers
grid. All with the "testing" role and GitHub's default avatar URL
(https://github.com/<handle>.png) so the entries work immediately
without requiring custom avatar files. Swap to a per-contributor
/images/avatars/<name>.png later if/when custom artwork is ready.
deploy.yml triggers: drop `guides/**` (the legacy /guides/[slug]/
page that read those markdown files was removed in PR #211 — the
folder no longer affects what the web renders) and `scripts/**`
(the bash scripts get rsynced into public/scripts/ during prebuild
but they are downloaded by users via install_proxmenux.sh, not
browsed via the doc site, so a stale copy is harmless). Add
`lang/**` so future translated CHANGELOG / docs (e.g. the Spanish
CHANGELOG just shipped in PR #217) auto-deploy.
Local build verified: 232 pages, 14450 indexed words, no errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Next.js changelog page (app/[locale]/changelog/page.tsx) already
has the per-locale lookup logic — it reads lang/<locale>/CHANGELOG.md
first and falls back to the canonical English CHANGELOG.md at the
repo root if no localized copy exists. The Spanish file has lived in
develop for weeks (65 KB, 1065 lines, full translation including the
v1.2.1 SR-IOV / GPU passthrough hardening notes) but was never
committed to the repo, so the live site at proxmenux.com/es/changelog
fell back to English on every visit.
Add the file to the tree so the lookup finds it. No code change in
the page — the resolution logic in `resolveChangelogPath()` already
handles both branches and is unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #212 added `trailingSlash: true` to next.config.mjs so GitHub Pages
would serve the locale roots correctly. That changed what usePathname()
returns at runtime — `/docs/.../page/` with a trailing slash — but
the sidebar config (sidebarItems in DocSidebar.tsx) still declares
hrefs without the trailing slash. Every equality check
`pathname === item.href` therefore returned false on every page, and
two things broke:
1. components/ui/doc-navigation.tsx — the Previous/Next bar at the
bottom of every doc page. With `findIndex` returning -1,
`prevPage` was null and `nextPage = allPages[0]` (Introduction).
So every doc page showed "Next: Introduction" regardless of
where the user was.
2. components/DocSidebar.tsx — four comparisons that drove (a) the
highlighted active item in the sidebar, (b) the active-section
auto-open when navigating directly to a nested page, (c) the
leaf-item highlight when the item has no submenu. All silently
broken on every page.
Fix: a `stripTrailingSlash` helper plus a derived `currentPath` that
is compared instead of the raw `pathname`. `collectHrefs(...)` results
are also normalized at the point of comparison so the
`.includes(currentPath)` checks behave correctly.
Verified locally with `npm run build` — 232 pages indexed, no errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous commit added a 5-line project header above the canonical
GPL-3.0 text:
ProxMenux — An Interactive Menu and Web Dashboard for Proxmox VE
Copyright (c) 2025 MacRimi
This program is licensed under the GNU General Public License v3.0.
The full text of the license follows.
That header (~270 bytes in a 35 KB file) pushed the file below the
Sørensen-Dice similarity threshold GitHub's licensee gem uses to
fingerprint licenses, so the repo kept showing "License not
identifiable by GitHub" (API: spdx_id = "NOASSERTION", key = "other")
even though the canonical text was right below.
Strip the header. LICENSE is now byte-exact to the FSF reference:
https://www.gnu.org/licenses/gpl-3.0.txt
(SHA256: 3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986)
The project copyright still lives where GPL-3.0 expects it — in each
source file's header (as enforced by CONTRIBUTING.md's "Script Header
Template" section). LICENSE is the legal reference document; the
per-file copyright notice is the project-specific declaration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous deploy went live but every visitor hit a 404. Root cause:
GitHub Pages serves the URL `/foo/` by looking for `out/foo/index.html`.
Next.js's static export with the default `trailingSlash: false` instead
emits `out/foo.html`, which Pages only serves for `/foo` (no trailing
slash).
The i18n root redirect in app/page.tsx points users at
`/<defaultLocale>/` (with slash) because that is what next-intl's
`<Link>` components generate. So every visitor landed on
`https://proxmenux.com/en/` → Pages looked for `out/en/index.html`,
did not find it (the export had emitted `out/en.html`), and fell back
to `out/404.html`. Result: the site looked deployed but every page
was a Next.js 404 template.
Setting `trailingSlash: true` makes the export emit
`out/<route>/index.html` for every page — locale roots
(`out/en/index.html`, `out/es/index.html`), nested doc pages
(`out/en/docs/monitor/dashboard/settings/index.html`, etc.), changelog,
guides — so Pages serves them directly.
Local verification: 232 pages built, root redirect intact at
out/index.html, and every locale + doc + guide URL now resolves to
its own index.html.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two regressions surfaced after the 1.2.2 release merge to main, both
in workflows that auto-trigger on push to main:
* Deploy to GitHub Pages — build failed with `pagefind: not found`
(exit 127) after Next.js prerendered all 241 routes. pagefind was
not declared in web/package.json; the local build only worked
because the project root had its own package.json with pagefind
as a devDep (the one we just gitignored in the previous commit).
Add `pagefind: ^1.5.2` to web/package.json devDependencies and
regenerate web/package-lock.json so `npm ci` in CI puts the
binary at web/node_modules/.bin/pagefind.
* Build ProxMenux Monitor AppImage — failed at the first step with
`mkdir: cannot create directory '/var/cache/proxmenux-build':
Permission denied`. The cache path was hardcoded to /var/cache/,
which is writable when the script runs as root (the .50 host
manual build) but not as the unprivileged GitHub Actions runner.
Switch to `${XDG_CACHE_HOME:-$HOME/.cache}/proxmenux-build/` —
works identically in both environments.
Verified locally: `cd web && npm ci && npm run build` produces 2804
files in out/, 231 pages indexed by pagefind, root redirect intact.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Promote the v1.2.1.x beta cycle to stable: version markers bumped
from 1.2.1.4-beta to 1.2.2 across version.txt, AppImage/package.json,
flask_server.py (3 places) and the four UI labels in login,
proxmox-dashboard, storage-overview and release-notes-modal.
Replace AppImage/ProxMenux-1.2.1.4-beta.AppImage with
ProxMenux-1.2.2.AppImage and regenerate the .sha256 sidecar
(097e2344675d4b21f1dd18c531c956c299a6507fbc3d0c9695418063581ba2b0).
The new binary is verified on all 4 lab hosts (.50 / .55 / .89 /
1.10) — same sha, all services active, runtime version markers
report 1.2.2.
CHANGELOG["1.2.2"] in release-notes-modal.tsx consolidates every beta
in the 1.2.1.x line (12 added / 13 changed / 18 fixed), and
CURRENT_VERSION_FEATURES is rewritten with the four stable highlights:
Health Monitor Thresholds, granular dismiss control (per-event
duration + Active Suppressions panel), Apprise notification channel
parity, and LXC update detection.
Full rewrite of the docs site under app/[locale]/ with next-intl
in localePrefix:"always" mode. Every page now exists at both
/en/<path> and /es/<path>; the root / shows a meta-refresh + JS
redirect to /<defaultLocale>/ so GitHub Pages serves something
on the apex URL.
Highlights:
- 107 doc pages migrated to file-per-page JSON namespaces under
messages/en/ and messages/es/. Spanish content is fully
translated (no copy-of-English placeholders).
- New documentation for the Active Suppressions section in the
Settings tab and the per-event Dismiss dropdown in the Health
Monitor modal.
- New screenshots: dismiss-duration-dropdown.png and an updated
health-suppression-settings.png.
- Pagefind integrated for client-side search; index is built on
every CI deploy (not committed).
- RSS feeds: per-locale at /<locale>/rss.xml plus root /rss.xml
for backward compat.
- Removed the dead app/[locale]/guides/[slug]/ route — every
guide now has its own static page and no markdown source
remains.
- Fixed orphan link /guides/nvidia -> /guides/nvidia-manual in
docs/hardware/nvidia-host.
- Removed obsolete components (footer2, calendar, drawer).
Verified locally with `npm ci && npm run build`: 2804 files in
out/, 231 pages indexed by pagefind, root redirect intact, both
locale roots and the new Active Suppressions docs render OK.
- README: modernize visual layout (status badges row, tagline,
Ko-fi shields badge, expanded Contributing section). Update
web URLs to proxmenux.com/en for the new locale-prefixed site.
- CONTRIBUTING.md: add as the canonical contributor guide. Fix
the workflow section to branch from develop (not main), add
a dedicated "dialog vs whiptail" section, reorder so Script
Header comes first.
- deploy.yml: switch npm install -> npm ci so the build uses
the committed lockfile; fix cache-dependency-path to track
web/package-lock.json (was package.json); add scripts/** to
the path triggers so script edits redeploy the doc site.
- .gitignore: ignore the accidental root-level package.json /
package-lock.json (pagefind is declared in web/package.json)
and the regenerated build artifacts web/public/pagefind/ and
web/public/scripts/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Updated the Spanish roadmap document for ProxMenux, improving clarity and correcting phrasing throughout. Adjusted sections on version planning and contributions.