mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-06-15 14:28:21 +00:00
complete i18n migration to /[locale]/ with EN+ES content
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.
This commit is contained in:
315
web/app/[locale]/docs/post-install/storage/page.tsx
Normal file
315
web/app/[locale]/docs/post-install/storage/page.tsx
Normal file
@@ -0,0 +1,315 @@
|
||||
import type { Metadata } from "next"
|
||||
import { getTranslations, getMessages, setRequestLocale } from "next-intl/server"
|
||||
import { Link } from "@/i18n/navigation"
|
||||
import { ExternalLink } from "lucide-react"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
import { Callout } from "@/components/ui/callout"
|
||||
import CopyableCode from "@/components/CopyableCode"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>
|
||||
}): Promise<Metadata> {
|
||||
const { locale } = await params
|
||||
const t = await getTranslations({ locale, namespace: "docs.postInstall.storage.meta" })
|
||||
return { title: t("title"), description: t("description") }
|
||||
}
|
||||
|
||||
type ArcRow = { ram: string; min: string; max: string }
|
||||
type SnapRow = { label: string; runs: string; kept: string }
|
||||
type RelatedItem = { label: string; href: string; tail: string }
|
||||
|
||||
export default async function PostInstallStoragePage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>
|
||||
}) {
|
||||
const { locale } = await params
|
||||
setRequestLocale(locale)
|
||||
const t = await getTranslations({ locale, namespace: "docs.postInstall.storage" })
|
||||
|
||||
const messages = (await getMessages({ locale })) as unknown as {
|
||||
docs: { postInstall: { storage: {
|
||||
arc: { rows: ArcRow[] }
|
||||
autoSnap: { rows: SnapRow[] }
|
||||
autotrim: {
|
||||
practicalItems: string[]
|
||||
whenSkipItems: string[]
|
||||
}
|
||||
related: { items: RelatedItem[] }
|
||||
} } }
|
||||
}
|
||||
const arcRows = messages.docs.postInstall.storage.arc.rows
|
||||
const snapRows = messages.docs.postInstall.storage.autoSnap.rows
|
||||
const practicalItems = messages.docs.postInstall.storage.autotrim.practicalItems
|
||||
const whenSkipItems = messages.docs.postInstall.storage.autotrim.whenSkipItems
|
||||
const relatedItems = messages.docs.postInstall.storage.related.items
|
||||
|
||||
const code = (chunks: React.ReactNode) => <code>{chunks}</code>
|
||||
const strong = (chunks: React.ReactNode) => <strong>{chunks}</strong>
|
||||
const em = (chunks: React.ReactNode) => <em>{chunks}</em>
|
||||
const zfsAutoAnchor = (chunks: React.ReactNode) => (
|
||||
<a href="https://github.com/zfsonlinux/zfs-auto-snapshot" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline inline-flex items-center gap-1">
|
||||
{chunks}
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DocHeader
|
||||
title={t("header.title")}
|
||||
section={t("header.section")}
|
||||
/>
|
||||
|
||||
<Callout variant="info" title={t("intro.title")}>
|
||||
{t.rich("intro.body", { strong })}
|
||||
</Callout>
|
||||
|
||||
<Callout variant="warning" title={t("notTrackedTitle")}>
|
||||
{t.rich("notTrackedBody", { strong })}
|
||||
</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("arc.heading")}</h2>
|
||||
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("arc.intro", { strong })}
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("arc.sizingTitle")}</h3>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="min-w-full border border-gray-200 text-sm">
|
||||
<thead className="bg-gray-100">
|
||||
<tr>
|
||||
<th className="border border-gray-200 px-3 py-2 text-left text-gray-900">{t("arc.headerRam")}</th>
|
||||
<th className="border border-gray-200 px-3 py-2 text-left text-gray-900">{t("arc.headerMin")}</th>
|
||||
<th className="border border-gray-200 px-3 py-2 text-left text-gray-900">{t("arc.headerMax")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-800">
|
||||
{arcRows.map((row) => (
|
||||
<tr key={row.ram}>
|
||||
<td className="border border-gray-200 px-3 py-2">{row.ram}</td>
|
||||
<td className="border border-gray-200 px-3 py-2">{row.min}</td>
|
||||
<td className="border border-gray-200 px-3 py-2">{row.max}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("arc.after", { code })}
|
||||
</p>
|
||||
|
||||
<Callout variant="warning" title={t("arc.rebootTitle")}>
|
||||
{t.rich("arc.rebootBody", { code, strong })}
|
||||
</Callout>
|
||||
|
||||
<Callout variant="tip" title={t("arc.safeTitle")}>
|
||||
{t.rich("arc.safeBody", { code })}
|
||||
</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("arc.verifyTitle")}</h3>
|
||||
<CopyableCode
|
||||
code={`# Check the config file is in place
|
||||
cat /etc/modprobe.d/99-zfsarc.conf
|
||||
|
||||
# After reboot, check actual ARC limits (in bytes)
|
||||
cat /sys/module/zfs/parameters/zfs_arc_min
|
||||
cat /sys/module/zfs/parameters/zfs_arc_max
|
||||
|
||||
# Manual rollback
|
||||
rm /etc/modprobe.d/99-zfsarc.conf
|
||||
update-initramfs -u -k all
|
||||
# (reboot for ZFS to load with defaults again)`}
|
||||
className="my-4"
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("autoSnap.heading")}</h2>
|
||||
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("autoSnap.intro", { a: zfsAutoAnchor })}
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autoSnap.cadenceTitle")}</h3>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="min-w-full border border-gray-200 text-sm">
|
||||
<thead className="bg-gray-100">
|
||||
<tr>
|
||||
<th className="border border-gray-200 px-3 py-2 text-left text-gray-900">{t("autoSnap.headerLabel")}</th>
|
||||
<th className="border border-gray-200 px-3 py-2 text-left text-gray-900">{t("autoSnap.headerRuns")}</th>
|
||||
<th className="border border-gray-200 px-3 py-2 text-left text-gray-900">{t("autoSnap.headerKept")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-800">
|
||||
{snapRows.map((row) => (
|
||||
<tr key={row.label}>
|
||||
<td className="border border-gray-200 px-3 py-2">{row.label}</td>
|
||||
<td className="border border-gray-200 px-3 py-2">{row.runs}</td>
|
||||
<td className="border border-gray-200 px-3 py-2">{row.kept}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<Callout variant="tip" title={t("autoSnap.conservativeTitle")}>
|
||||
{t.rich("autoSnap.conservativeBody", { code })}
|
||||
</Callout>
|
||||
|
||||
<Callout variant="warning" title={t("autoSnap.onlyZfsTitle")}>
|
||||
{t("autoSnap.onlyZfsBody")}
|
||||
</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autoSnap.verifyTitle")}</h3>
|
||||
<CopyableCode
|
||||
code={`# List existing auto-snapshots across all ZFS datasets
|
||||
zfs list -t snapshot | grep zfs-auto-snap
|
||||
|
||||
# Check the schedules
|
||||
grep . /etc/cron.d/zfs-auto-snapshot /etc/cron.hourly/zfs-auto-snapshot \\
|
||||
/etc/cron.daily/zfs-auto-snapshot /etc/cron.weekly/zfs-auto-snapshot \\
|
||||
/etc/cron.monthly/zfs-auto-snapshot
|
||||
|
||||
# Manual rollback (removes the package + destroys no snapshots)
|
||||
apt purge zfs-auto-snapshot
|
||||
# Existing snapshots remain on your pools unless you destroy them explicitly`}
|
||||
className="my-4"
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("autotrim.heading")}</h2>
|
||||
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("autotrim.intro", { code })}
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autotrim.trimTitle")}</h3>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("autotrim.trimBody1", { strong })}
|
||||
</p>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t("autotrim.trimBody2")}
|
||||
</p>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("autotrim.trimBody3", { code })}
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autotrim.practicalTitle")}</h3>
|
||||
<ul className="list-disc pl-6 mb-4 text-gray-800 leading-relaxed space-y-1">
|
||||
{practicalItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`autotrim.practicalItems.${idx}`, { strong, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autotrim.whenTitle")}</h3>
|
||||
<ul className="list-disc pl-6 mb-4 text-gray-800 leading-relaxed space-y-1">
|
||||
<li>
|
||||
{t.rich("autotrim.whenIntro1", { strong })}
|
||||
</li>
|
||||
<li>
|
||||
{t.rich("autotrim.whenIntro2", { strong })}
|
||||
<ul className="list-disc pl-6 mt-1">
|
||||
{whenSkipItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`autotrim.whenSkipItems.${idx}`, { code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
{t.rich("autotrim.whenIntro3", { strong })}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<Callout variant="info" title={t("autotrim.recordedTitle")}>
|
||||
{t.rich("autotrim.recordedBody", { code })}
|
||||
</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autotrim.manualTitle")}</h3>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">{t("autotrim.manualIntro")}</p>
|
||||
<CopyableCode
|
||||
code={`# 1. List your ZFS pools
|
||||
zpool list -H -o name
|
||||
|
||||
# 2. Check the current autotrim setting on a pool
|
||||
zpool get autotrim <pool>
|
||||
|
||||
# 3. Verify the pool is backed by SSD/NVMe with TRIM support
|
||||
# For each vdev (use the device path you see in 'zpool status -P <pool>'):
|
||||
DEV=sda # replace with the actual short name (sda, nvme0n1, ...)
|
||||
cat /sys/block/\${DEV}/queue/rotational # must be 0 (SSD/NVMe, not HDD)
|
||||
cat /sys/block/\${DEV}/queue/discard_granularity # must be > 0 (TRIM supported)
|
||||
|
||||
# 4. Turn it on
|
||||
zpool set autotrim=on <pool>
|
||||
|
||||
# 5. Confirm
|
||||
zpool get autotrim <pool>`}
|
||||
className="my-4"
|
||||
/>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("autotrim.verifyTitle")}</h3>
|
||||
<CopyableCode
|
||||
code={`# Verify autotrim is active on every pool ProxMenux touched
|
||||
cat /usr/local/share/proxmenux/zfs_autotrim_pools
|
||||
while read -r p; do
|
||||
zpool get autotrim "$p"
|
||||
done < /usr/local/share/proxmenux/zfs_autotrim_pools
|
||||
|
||||
# Manual rollback — disable autotrim on a specific pool
|
||||
zpool set autotrim=off <pool>
|
||||
|
||||
# Or revert all pools ProxMenux changed (manual equivalent of the Uninstall option)
|
||||
while read -r p; do
|
||||
zpool set autotrim=off "$p"
|
||||
done < /usr/local/share/proxmenux/zfs_autotrim_pools`}
|
||||
className="my-4"
|
||||
/>
|
||||
|
||||
<Callout variant="tip" title={t("autotrim.oneShotTitle")}>
|
||||
{t.rich("autotrim.oneShotBody", { code })}
|
||||
</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("vzdump.heading")}</h2>
|
||||
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">{t("vzdump.intro")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("vzdump.changedTitle")}</h3>
|
||||
<CopyableCode
|
||||
code={`bwlimit: 0 # No bandwidth limit (was capped by default)
|
||||
ionice: 5 # Lower I/O priority (5 = best-effort class, lowest priority in that class)`}
|
||||
className="my-4"
|
||||
/>
|
||||
|
||||
<Callout variant="warning" title={t("vzdump.noBackupTitle")}>
|
||||
{t.rich("vzdump.noBackupBody", { strong, code, em })}
|
||||
</Callout>
|
||||
|
||||
<Callout variant="tip" title={t("vzdump.skipTitle")}>
|
||||
{t.rich("vzdump.skipBody", { code })}
|
||||
</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-6 mb-2 text-gray-900">{t("vzdump.verifyTitle")}</h3>
|
||||
<CopyableCode
|
||||
code={`# Check current vzdump config
|
||||
grep -E "^(bwlimit|ionice):" /etc/vzdump.conf
|
||||
|
||||
# Manual rollback (comment out the two lines — restores Proxmox defaults)
|
||||
sed -i 's/^bwlimit: 0/#bwlimit: KBPS/' /etc/vzdump.conf
|
||||
sed -i 's/^ionice: 5/#ionice: PRI/' /etc/vzdump.conf`}
|
||||
className="my-4"
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("related.heading")}</h2>
|
||||
<ul className="list-disc pl-6 text-gray-800 leading-relaxed space-y-1">
|
||||
{relatedItems.map((item) => (
|
||||
<li key={item.href}>
|
||||
<Link href={item.href} className="text-blue-600 hover:underline">
|
||||
{item.label}
|
||||
</Link>
|
||||
{item.tail}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user