docs(monitor): new App + Updates sub-pages for VMs & LXCs with i18n

This commit is contained in:
MacRimi
2026-08-11 20:14:17 +02:00
parent d2c7be9a32
commit 55a95a2346
27 changed files with 2300 additions and 42 deletions
@@ -0,0 +1,393 @@
import type { Metadata } from "next"
import type React from "react"
import { getTranslations, getMessages, setRequestLocale } from "next-intl/server"
import { Link } from "@/i18n/navigation"
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.monitor.dashboard.vmsLxcsApp.meta" })
return { title: t("title"), description: t("description") }
}
type TableRow = { method: string; when: string }
type BreakdownRow = { part: string; meaning: string }
type ExampleRow = { text: string; regex: string; result: string }
function Figure({ src, alt, caption }: { src: string; alt: string; caption: string }) {
return (
<figure className="my-6">
<img
src={src}
alt={alt}
className="rounded-lg border border-gray-200 shadow-sm w-full"
/>
<figcaption className="text-sm text-gray-500 mt-2 text-center italic">
{caption}
</figcaption>
</figure>
)
}
export default async function AppTabPage({
params,
}: {
params: Promise<{ locale: string }>
}) {
const { locale } = await params
setRequestLocale(locale)
const t = await getTranslations({ locale, namespace: "docs.monitor.dashboard.vmsLxcsApp" })
const messages = (await getMessages({ locale })) as unknown as {
docs: { monitor: { dashboard: { vmsLxcsApp: {
whatYouGet: { items: string[] }
registerSuggested: { steps: string[] }
manual: { linksItems: string[] }
multiple: { usefulItems: string[] }
tracking: {
ingredients: string[]
methodsTable: { rows: TableRow[] }
sourceItems: string[]
regexTwoItems: string[]
step2Items: string[]
step2Breakdown: { rows: BreakdownRow[] }
step3Examples: { rows: ExampleRow[] }
step4Items: string[]
step6CorrectItems: string[]
}
state: { items: string[] }
manage: { items: string[] }
notDetected: { steps: string[] }
} } } }
}
const v = messages.docs.monitor.dashboard.vmsLxcsApp
const whatYouGetItems = v.whatYouGet.items
const registerSteps = v.registerSuggested.steps
const manualLinks = v.manual.linksItems
const multipleUseful = v.multiple.usefulItems
const ingredients = v.tracking.ingredients
const methodsRows = v.tracking.methodsTable.rows
const sourceItems = v.tracking.sourceItems
const regexTwoItems = v.tracking.regexTwoItems
const step2Items = v.tracking.step2Items
const breakdownRows = v.tracking.step2Breakdown.rows
const exampleRows = v.tracking.step3Examples.rows
const step4Items = v.tracking.step4Items
const step6CorrectItems = v.tracking.step6CorrectItems
const stateItems = v.state.items
const manageItems = v.manage.items
const notDetectedSteps = v.notDetected.steps
// Rich-text tag handlers
const strong = (chunks: React.ReactNode) => <strong>{chunks}</strong>
const em = (chunks: React.ReactNode) => <em>{chunks}</em>
const code = (chunks: React.ReactNode) => (
<code className="text-sm bg-gray-100 px-1 rounded">{chunks}</code>
)
const linkUpdates = (chunks: React.ReactNode) => (
<Link href="/docs/monitor/dashboard/vms-lxcs/updates" className="text-blue-600 hover:underline">
{chunks}
</Link>
)
return (
<div className="max-w-4xl mx-auto px-4 py-8">
<DocHeader
title={t("header.title")}
description={t("header.description")}
estimatedMinutes={12}
/>
<p className="text-gray-800 mt-6">{t.rich("intro.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("intro.p2", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("intro.p3", { strong, em, code, link: linkUpdates })}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("whatYouGet.heading")}</h2>
<p className="text-gray-800">{t("whatYouGet.lead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{whatYouGetItems.map((_, idx) => (
<li key={idx}>{t.rich(`whatYouGet.items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("whatYouGet.trailing", { strong, em, code })}</p>
<Callout variant="warning">{t.rich("whatYouGet.callout", { strong, em, code })}</Callout>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("firstOpening.heading")}</h2>
<p className="text-gray-800">{t.rich("firstOpening.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("firstOpening.p2", { strong, em, code })}</p>
<Figure
src="/monitor/vms-modal-app-01.png"
alt={t("figures.f01.alt")}
caption={t("figures.f01.caption")}
/>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("registerSuggested.heading")}</h3>
<ol className="list-decimal pl-6 space-y-1 text-gray-800">
{registerSteps.map((_, idx) => (
<li key={idx}>{t.rich(`registerSuggested.steps.${idx}`, { strong, em, code })}</li>
))}
</ol>
<p className="text-gray-800 mt-4">{t.rich("registerSuggested.trailing", { strong, em, code })}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("catalog.heading")}</h2>
<p className="text-gray-800">{t.rich("catalog.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("catalog.p2", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("catalog.p3", { strong, em, code })}</p>
<Figure
src="/monitor/vms-modal-app-02.png"
alt={t("figures.f02.alt")}
caption={t("figures.f02.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("manual.heading")}</h2>
<p className="text-gray-800">{t.rich("manual.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("manual.p2", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("manual.nameHeading")}</h3>
<p className="text-gray-800">{t.rich("manual.nameBody", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("manual.linksHeading")}</h3>
<p className="text-gray-800">{t("manual.linksLead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{manualLinks.map((_, idx) => (
<li key={idx}>{t.rich(`manual.linksItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("manual.linksTrailing", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("manual.linksConfirm", { strong, em, code })}</p>
<Figure
src="/monitor/vms-modal-app-03.png"
alt={t("figures.f03.alt")}
caption={t("figures.f03.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("multiple.heading")}</h2>
<p className="text-gray-800">{t.rich("multiple.intro", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("multiple.usefulLead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{multipleUseful.map((_, idx) => (
<li key={idx}>{t.rich(`multiple.usefulItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("multiple.dontGroup", { strong, em, code })}</p>
<Figure
src="/monitor/vms-modal-app-04.png"
alt={t("figures.f04.alt")}
caption={t("figures.f04.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("tracking.heading")}</h2>
<p className="text-gray-800">{t("tracking.intro")}</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
{ingredients.map((_, idx) => (
<li key={idx}>{t.rich(`tracking.ingredients.${idx}`, { strong, em, code })}</li>
))}
</ol>
<p className="text-gray-800 mt-4">{t.rich("tracking.trailing", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("tracking.methodsHeading")}</h3>
<p className="text-gray-800">{t("tracking.methodsLead")}</p>
<div className="overflow-x-auto my-4">
<table className="w-full border-collapse text-sm">
<thead>
<tr className="bg-gray-100">
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.methodsTable.colMethod")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.methodsTable.colWhen")}</th>
</tr>
</thead>
<tbody>
{methodsRows.map((row, idx) => (
<tr key={idx}>
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.method}</td>
<td className="border border-gray-300 px-3 py-2">{row.when}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-gray-800 mt-4">{t.rich("tracking.methodsTrailing", { strong, em, code })}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.commandHeading")}</h4>
<p className="text-gray-800">{t.rich("tracking.commandP1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("tracking.commandP2")}</p>
<CopyableCode code={t("tracking.commandExample1")} language="text" />
<p className="text-gray-800 mt-4">{t("tracking.commandP3")}</p>
<CopyableCode code={t("tracking.commandExample2")} language="text" />
<p className="text-gray-800 mt-4">{t.rich("tracking.commandP4", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("tracking.sourceHeading")}</h3>
<p className="text-gray-800">{t("tracking.sourceLead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{sourceItems.map((_, idx) => (
<li key={idx}>{t.rich(`tracking.sourceItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("tracking.sourceTrailing", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("tracking.regexHeading")}</h3>
<p className="text-gray-800">{t.rich("tracking.regexIntro", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("tracking.regexOptional", { strong, em, code })}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.regexTwoHeading")}</h4>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{regexTwoItems.map((_, idx) => (
<li key={idx}>{t.rich(`tracking.regexTwoItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("tracking.regexTwoTrailing", { strong, em, code })}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step1Heading")}</h4>
<p className="text-gray-800">{t("tracking.step1P1")}</p>
<p className="text-gray-800 mt-4">{t("tracking.step1P2")}</p>
<p className="text-gray-800 mt-4">{t("tracking.step1P3")}</p>
<CopyableCode code={t("tracking.step1Cmd")} language="sh" />
<p className="text-gray-800 mt-4">{t("tracking.step1P4")}</p>
<CopyableCode code={t("tracking.step1Output")} language="text" />
<p className="text-gray-800 mt-4">{t("tracking.step1P5")}</p>
<p className="text-gray-800 mt-4">{t("tracking.step1P6")}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step2Heading")}</h4>
<p className="text-gray-800">{t.rich("tracking.step2Lead", { strong, em, code })}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{step2Items.map((_, idx) => (
<li key={idx}>{t.rich(`tracking.step2Items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t("tracking.step2Recommended")}</p>
<CopyableCode code={t("tracking.step2Regex")} language="text" />
<p className="text-gray-800 mt-4">{t("tracking.step2ReadLead")}</p>
<div className="overflow-x-auto my-4">
<table className="w-full border-collapse text-sm">
<thead>
<tr className="bg-gray-100">
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step2Breakdown.colPart")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step2Breakdown.colMeaning")}</th>
</tr>
</thead>
<tbody>
{breakdownRows.map((row, idx) => (
<tr key={idx}>
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.part}</td>
<td className="border border-gray-300 px-3 py-2">{row.meaning}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-gray-800 mt-4">{t.rich("tracking.step2DotNote", { strong, em, code })}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step3Heading")}</h4>
<p className="text-gray-800">{t("tracking.step3Lead")}</p>
<div className="overflow-x-auto my-4">
<table className="w-full border-collapse text-sm">
<thead>
<tr className="bg-gray-100">
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step3Examples.colText")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step3Examples.colRegex")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step3Examples.colResult")}</th>
</tr>
</thead>
<tbody>
{exampleRows.map((row, idx) => (
<tr key={idx}>
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.text}</td>
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.regex}</td>
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.result}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-gray-800 mt-4">{t.rich("tracking.step3Note1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("tracking.step3Note2", { strong, em, code })}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step4Heading")}</h4>
<p className="text-gray-800">{t("tracking.step4Intro")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{step4Items.map((_, idx) => (
<li key={idx}>{t.rich(`tracking.step4Items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("tracking.step4Trailing", { strong, em, code })}</p>
<p className="text-gray-800 mt-4"><strong>{t("tracking.step4RecLabel")}</strong></p>
<CopyableCode code={t.raw("tracking.step4RecRegex") as string} language="text" />
<p className="text-gray-800 mt-4"><strong>{t("tracking.step4LessLabel")}</strong></p>
<CopyableCode code={t("tracking.step4LessRegex")} language="text" />
<p className="text-gray-800 mt-4">{t.rich("tracking.step4Note", { strong, em, code })}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step5Heading")}</h4>
<p className="text-gray-800">{t("tracking.step5Lead")}</p>
<CopyableCode code={t("tracking.step5Regex")} language="text" />
<p className="text-gray-800 mt-4">{t.rich("tracking.step5P1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("tracking.step5P2")}</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step6Heading")}</h4>
<p className="text-gray-800">{t.rich("tracking.step6Lead", { strong, em, code })}</p>
<CopyableCode code={t("tracking.step6Output")} language="text" />
<p className="text-gray-800 mt-4">{t("tracking.step6CorrectLead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{step6CorrectItems.map((_, idx) => (
<li key={idx}>{t.rich(`tracking.step6CorrectItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("tracking.step6ErrorNote", { strong, em, code })}</p>
<Callout variant="tip">{t.rich("tracking.step6Callout", { strong, em, code })}</Callout>
<Figure
src="/monitor/vms-modal-app-05.png"
alt={t("figures.f05.alt")}
caption={t("figures.f05.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("state.heading")}</h2>
<Figure
src="/monitor/vms-modal-app-06.png"
alt={t("figures.f06.alt")}
caption={t("figures.f06.caption")}
/>
<p className="text-gray-800">{t("state.lead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{stateItems.map((_, idx) => (
<li key={idx}>{t.rich(`state.items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("state.trailing", { strong, em, code })}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("manage.heading")}</h2>
<p className="text-gray-800">{t("manage.lead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{manageItems.map((_, idx) => (
<li key={idx}>{t.rich(`manage.items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("manage.trailing", { strong, em, code })}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("notDetected.heading")}</h2>
<p className="text-gray-800">{t("notDetected.intro")}</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
{notDetectedSteps.map((_, idx) => (
<li key={idx}>{t.rich(`notDetected.steps.${idx}`, { strong, em, code, link: linkUpdates })}</li>
))}
</ol>
<p className="text-gray-800 mt-4">{t.rich("notDetected.trailing", { strong, em, code })}</p>
<Figure
src="/monitor/vms-modal-app-07.png"
alt={t("figures.f07.alt")}
caption={t("figures.f07.caption")}
/>
</div>
)
}
@@ -39,7 +39,6 @@ export default async function VmsLxcsTabPage({
mountTypesItems: string[] mountTypesItems: string[]
mountStateItems: string[] mountStateItems: string[]
backupsItems: string[] backupsItems: string[]
updatesPanelItems: string[]
firewallItems: string[] firewallItems: string[]
lifecycleRows: LifecycleRow[] lifecycleRows: LifecycleRow[]
} }
@@ -56,7 +55,6 @@ export default async function VmsLxcsTabPage({
const mountTypesItems = v.drillIn.mountTypesItems const mountTypesItems = v.drillIn.mountTypesItems
const mountStateItems = v.drillIn.mountStateItems const mountStateItems = v.drillIn.mountStateItems
const backupsItems = v.drillIn.backupsItems const backupsItems = v.drillIn.backupsItems
const updatesPanelItems = v.drillIn.updatesPanelItems
const firewallItems = v.drillIn.firewallItems const firewallItems = v.drillIn.firewallItems
const lifecycleRows = v.drillIn.lifecycleRows const lifecycleRows = v.drillIn.lifecycleRows
const dataRows = v.dataCollected.rows const dataRows = v.dataCollected.rows
@@ -227,6 +225,40 @@ export default async function VmsLxcsTabPage({
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.ipsTitle")}</h4> <h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.ipsTitle")}</h4>
<p className="mb-6 text-gray-800 leading-relaxed">{t("drillIn.ipsBody")}</p> <p className="mb-6 text-gray-800 leading-relaxed">{t("drillIn.ipsBody")}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">App</h3>
<p className="mb-4 text-gray-800 leading-relaxed">
The <strong>App</strong> section lets you register the applications running inside an LXC, wire quick web
links, and optionally track installed vs. upstream versions. Registrations feed the App-level notifications
and the update flows on the next section.
</p>
<p className="mb-6 text-gray-800 leading-relaxed">
See the{" "}
<Link
href="/docs/monitor/dashboard/vms-lxcs/app"
className="text-blue-600 hover:underline"
>
dedicated App page
</Link>{" "}
for the catalog, manual registration, version-tracking methods and regex patterns.
</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">Updates</h3>
<p className="mb-4 text-gray-800 leading-relaxed">
The <strong>Updates</strong> section covers OS package updates (APT / APK) and application updates via
Community Scripts helpers or custom commands. Detection runs unconditionally on running LXCs; whether pending
updates also trigger a notification is controlled from <strong>Settings Notifications</strong>.
</p>
<p className="mb-6 text-gray-800 leading-relaxed">
See the{" "}
<Link
href="/docs/monitor/dashboard/vms-lxcs/updates"
className="text-blue-600 hover:underline"
>
dedicated Updates page
</Link>{" "}
for the decision matrix, custom-command guidance, backup / restart preferences and scheduled updates.
</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.mountsTitle")}</h3> <h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.mountsTitle")}</h3>
<figure className="my-4"> <figure className="my-4">
@@ -287,45 +319,6 @@ export default async function VmsLxcsTabPage({
{t.rich("drillIn.backupsOutro", { strong })} {t.rich("drillIn.backupsOutro", { strong })}
</p> </p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.updatesTitle")}</h3>
<figure className="my-4">
<img
src="/monitor/vms-modal-lxc-updates.png"
alt={t("drillIn.updatesImageAlt")}
className="rounded-lg border border-gray-200 shadow-sm w-full"
/>
<figcaption className="text-sm text-gray-500 mt-2 text-center italic">
{t("drillIn.updatesImageCaption")}
</figcaption>
</figure>
<p className="mb-4 text-gray-800 leading-relaxed">
{t.rich("drillIn.updatesIntro", { strong, code })}
</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.updatesPanelTitle")}</h4>
<ul className="list-disc pl-6 mb-4 text-gray-800 leading-relaxed space-y-1">
{updatesPanelItems.map((_, idx) => (
<li key={idx}>{t.rich(`drillIn.updatesPanelItems.${idx}`, { strong })}</li>
))}
</ul>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.updatesScopeTitle")}</h4>
<p className="mb-4 text-gray-800 leading-relaxed">
{t.rich("drillIn.updatesScopeBody", { strong, em, code })}
</p>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.updatesToggleTitle")}</h4>
<Callout variant="info" title={t("drillIn.updatesToggleCalloutTitle")}>
{t.rich("drillIn.updatesToggleCalloutBody", { strong, code })}
</Callout>
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.updatesApplyTitle")}</h4>
<p className="mb-6 text-gray-800 leading-relaxed">
{t.rich("drillIn.updatesApplyBody", { code })}
</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.firewallTitle")}</h3> <h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.firewallTitle")}</h3>
<p className="mb-4 text-gray-800 leading-relaxed">{t("drillIn.firewallIntro")}</p> <p className="mb-4 text-gray-800 leading-relaxed">{t("drillIn.firewallIntro")}</p>
@@ -0,0 +1,349 @@
import type { Metadata } from "next"
import type React from "react"
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.monitor.dashboard.vmsLxcsUpdates.meta" })
return { title: t("title"), description: t("description") }
}
type DecisionRow = { situation: string; action: string }
type DifferenceRow = { field: string; location: string; role: string }
function Figure({ src, alt, caption }: { src: string; alt: string; caption: string }) {
return (
<figure className="my-6">
<img
src={src}
alt={alt}
className="rounded-lg border border-gray-200 shadow-sm w-full"
/>
<figcaption className="text-sm text-gray-500 mt-2 text-center italic">
{caption}
</figcaption>
</figure>
)
}
export default async function UpdatesTabPage({
params,
}: {
params: Promise<{ locale: string }>
}) {
const { locale } = await params
setRequestLocale(locale)
const t = await getTranslations({ locale, namespace: "docs.monitor.dashboard.vmsLxcsUpdates" })
const messages = (await getMessages({ locale })) as unknown as {
docs: { monitor: { dashboard: { vmsLxcsUpdates: {
decision: { table: { rows: DecisionRow[] } }
figureOut: {
step3Items: string[]
step4Items: string[]
}
requirements: { items: string[] }
difference: { table: { rows: DifferenceRow[] } }
apply: {
steps: string[]
systemItems: string[]
appItems: string[]
}
scheduled: { createSteps: string[] }
} } } }
}
const v = messages.docs.monitor.dashboard.vmsLxcsUpdates
const decisionRows = v.decision.table.rows
const step3Items = v.figureOut.step3Items
const step4Items = v.figureOut.step4Items
const reqItems = v.requirements.items
const diffRows = v.difference.table.rows
const applySteps = v.apply.steps
const applySystem = v.apply.systemItems
const applyApp = v.apply.appItems
const schedSteps = v.scheduled.createSteps
// Rich-text tag handlers
const strong = (chunks: React.ReactNode) => <strong>{chunks}</strong>
const em = (chunks: React.ReactNode) => <em>{chunks}</em>
const code = (chunks: React.ReactNode) => (
<code className="text-sm bg-gray-100 px-1 rounded">{chunks}</code>
)
const linkApp = (chunks: React.ReactNode) => (
<Link href="/docs/monitor/dashboard/vms-lxcs/app" className="text-blue-600 hover:underline">
{chunks}
</Link>
)
const linkHelperHome = (chunks: React.ReactNode) => (
<a
href="https://community-scripts.org"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-blue-600 hover:underline"
>
{chunks}
<ExternalLink className="h-3.5 w-3.5" aria-hidden="true" />
</a>
)
const linkHelperDocs = (chunks: React.ReactNode) => (
<a
href="https://community-scripts.org/docs/tools/pve/update-apps"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-blue-600 hover:underline"
>
{chunks}
<ExternalLink className="h-3.5 w-3.5" aria-hidden="true" />
</a>
)
return (
<div className="max-w-4xl mx-auto px-4 py-8">
<DocHeader
title={t("header.title")}
description={t("header.description")}
estimatedMinutes={12}
/>
<p className="text-gray-800 mt-6">{t.rich("intro.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t.rich("intro.p2", { strong, em, code, link: linkApp })}</p>
<Callout variant="info">{t.rich("intro.callout", { strong, em, code })}</Callout>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("mechanisms.heading")}</h2>
<p className="text-gray-800">{t("mechanisms.intro")}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("mechanisms.osHeading")}</h3>
<p className="text-gray-800">{t("mechanisms.osP1")}</p>
<p className="text-gray-800 mt-4">{t.rich("mechanisms.osP2", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("mechanisms.osP3")}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("mechanisms.helperHeading")}</h3>
<p className="text-gray-800">{t.rich("mechanisms.helperP1", { strong, em, code, linkHelperHome })}</p>
<p className="text-gray-800 mt-4">{t.rich("mechanisms.helperP2", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">
{t.rich("mechanisms.helperP3", { strong, em, code, linkHelperHome, linkHelperDocs })}
</p>
<p className="text-gray-800 mt-4">{t.rich("mechanisms.helperP4", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("mechanisms.customHeading")}</h3>
<p className="text-gray-800">{t.rich("mechanisms.customP1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("mechanisms.customP2")}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("decision.heading")}</h2>
<div className="overflow-x-auto my-4">
<table className="w-full border-collapse text-sm">
<thead>
<tr className="bg-gray-100">
<th className="border border-gray-300 px-3 py-2 text-left">{t("decision.table.colSituation")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("decision.table.colAction")}</th>
</tr>
</thead>
<tbody>
{decisionRows.map((row, idx) => (
<tr key={idx}>
<td className="border border-gray-300 px-3 py-2">{row.situation}</td>
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.action}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-gray-800 mt-4">{t("decision.trailing")}</p>
<Figure
src="/monitor/vms-modal-updates-01.png"
alt={t("figures.f01.alt")}
caption={t("figures.f01.caption")}
/>
<Figure
src="/monitor/vms-modal-updates-02.png"
alt={t("figures.f02.alt")}
caption={t("figures.f02.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("custom.heading")}</h2>
<p className="text-gray-800">{t.rich("custom.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("custom.p2")}</p>
<Figure
src="/monitor/vms-modal-updates-03.png"
alt={t("figures.f03.alt")}
caption={t("figures.f03.caption")}
/>
<Figure
src="/monitor/vms-modal-updates-04.png"
alt={t("figures.f04.alt")}
caption={t("figures.f04.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("figureOut.heading")}</h2>
<p className="text-gray-800">{t("figureOut.intro")}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step1Heading")}</h3>
<p className="text-gray-800">{t.rich("figureOut.step1P1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("figureOut.step1P2")}</p>
<CopyableCode code={t("figureOut.step1Cmd1")} language="sh" />
<p className="text-gray-800 mt-4">{t("figureOut.step1P3")}</p>
<CopyableCode code={t("figureOut.step1Cmd2")} language="sh" />
<p className="text-gray-800 mt-4">{t.rich("figureOut.step1P4", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step2Heading")}</h3>
<p className="text-gray-800">{t.rich("figureOut.step2P1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("figureOut.step2P2")}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step3Heading")}</h3>
<p className="text-gray-800">{t("figureOut.step3Lead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{step3Items.map((_, idx) => (
<li key={idx}>{t.rich(`figureOut.step3Items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t("figureOut.step3P1")}</p>
<CopyableCode code={t("figureOut.step3Cmd")} language="sh" />
<p className="text-gray-800 mt-4">{t.rich("figureOut.step3P2", { strong, em, code })}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step4Heading")}</h3>
<p className="text-gray-800">{t("figureOut.step4Lead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{step4Items.map((_, idx) => (
<li key={idx}>{t.rich(`figureOut.step4Items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t("figureOut.step4Note")}</p>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step5Heading")}</h3>
<p className="text-gray-800">{t("figureOut.step5P1")}</p>
<CopyableCode code={t.raw("figureOut.step5Cmd1") as string} language="text" />
<p className="text-gray-800 mt-4">{t.rich("figureOut.step5P2", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("figureOut.step5P3")}</p>
<CopyableCode code={t("figureOut.step5Cmd2")} language="sh" />
<p className="text-gray-800 mt-4">{t("figureOut.step5P4")}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("requirements.heading")}</h2>
<p className="text-gray-800">{t("requirements.lead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{reqItems.map((_, idx) => (
<li key={idx}>{t.rich(`requirements.items.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t.rich("requirements.trailing", { strong, em, code })}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("difference.heading")}</h2>
<p className="text-gray-800">{t("difference.lead")}</p>
<div className="overflow-x-auto my-4">
<table className="w-full border-collapse text-sm">
<thead>
<tr className="bg-gray-100">
<th className="border border-gray-300 px-3 py-2 text-left">{t("difference.table.colField")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("difference.table.colLocation")}</th>
<th className="border border-gray-300 px-3 py-2 text-left">{t("difference.table.colRole")}</th>
</tr>
</thead>
<tbody>
{diffRows.map((row, idx) => (
<tr key={idx}>
<td className="border border-gray-300 px-3 py-2 font-medium">{row.field}</td>
<td className="border border-gray-300 px-3 py-2 text-xs">{row.location}</td>
<td className="border border-gray-300 px-3 py-2 text-xs">{row.role}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-gray-800 mt-4">{t.rich("difference.trailing", { strong, em, code })}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("apply.heading")}</h2>
<p className="text-gray-800">{t("apply.lead")}</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
{applySteps.map((_, idx) => (
<li key={idx}>{t.rich(`apply.steps.${idx}`, { strong, em, code })}</li>
))}
</ol>
<p className="text-gray-800 mt-4">{t("apply.trailing1")}</p>
<p className="text-gray-800 mt-4">{t("apply.systemLead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{applySystem.map((_, idx) => (
<li key={idx}>{t.rich(`apply.systemItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t("apply.appLead")}</p>
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
{applyApp.map((_, idx) => (
<li key={idx}>{t.rich(`apply.appItems.${idx}`, { strong, em, code })}</li>
))}
</ul>
<p className="text-gray-800 mt-4">{t("apply.trailing2")}</p>
<Figure
src="/monitor/vms-modal-updates-05.png"
alt={t("figures.f05.alt")}
caption={t("figures.f05.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("backup.heading")}</h2>
<p className="text-gray-800">{t.rich("backup.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("backup.p2")}</p>
<p className="text-gray-800 mt-4">{t("backup.p3")}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("restart.heading")}</h2>
<p className="text-gray-800">{t.rich("restart.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("restart.p2")}</p>
<p className="text-gray-800 mt-4">{t("restart.p3")}</p>
<Figure
src="/monitor/vms-modal-updates-06.png"
alt={t("figures.f06.alt")}
caption={t("figures.f06.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("scheduled.heading")}</h2>
<p className="text-gray-800">{t.rich("scheduled.p1", { strong, em, code })}</p>
<p className="text-gray-800 mt-4">{t("scheduled.createLead")}</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
{schedSteps.map((_, idx) => (
<li key={idx}>{t.rich(`scheduled.createSteps.${idx}`, { strong, em, code })}</li>
))}
</ol>
<p className="text-gray-800 mt-4">{t("scheduled.p2")}</p>
<p className="text-gray-800 mt-4">{t("scheduled.p3")}</p>
<Callout variant="tip">{t("scheduled.callout")}</Callout>
<Figure
src="/monitor/vms-modal-updates-07.png"
alt={t("figures.f07.alt")}
caption={t("figures.f07.caption")}
/>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("verify.heading")}</h2>
<p className="text-gray-800">{t("verify.p1")}</p>
<p className="text-gray-800 mt-4">{t.rich("verify.p2", { strong, em, code, link: linkApp })}</p>
<p className="text-gray-800 mt-4">{t("verify.p3")}</p>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("troubleshoot.heading")}</h2>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.noButtonHeading")}</h3>
<Callout variant="troubleshoot">{t.rich("troubleshoot.noButtonBody", { strong, em, code })}</Callout>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.aptHeading")}</h3>
<Callout variant="troubleshoot">{t.rich("troubleshoot.aptBody", { strong, em, code })}</Callout>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.noUpdaterHeading")}</h3>
<Callout variant="troubleshoot">{t.rich("troubleshoot.noUpdaterBody", { strong, em, code })}</Callout>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.helperDetectedHeading")}</h3>
<Callout variant="troubleshoot">{t.rich("troubleshoot.helperDetectedBody", { strong, em, code })}</Callout>
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.customFailsHeading")}</h3>
<Callout variant="troubleshoot">{t.rich("troubleshoot.customFailsBody", { strong, em, code })}</Callout>
</div>
)
}
+9 -1
View File
@@ -62,7 +62,15 @@ export const sidebarItems: MenuItem[] = [
{ title: "System Overview tab", i18nKey: "dashboardSystemOverview", href: "/docs/monitor/dashboard/system-overview" }, { title: "System Overview tab", i18nKey: "dashboardSystemOverview", href: "/docs/monitor/dashboard/system-overview" },
{ title: "Storage tab", i18nKey: "dashboardStorage", href: "/docs/monitor/dashboard/storage" }, { title: "Storage tab", i18nKey: "dashboardStorage", href: "/docs/monitor/dashboard/storage" },
{ title: "Network tab", i18nKey: "dashboardNetwork", href: "/docs/monitor/dashboard/network" }, { title: "Network tab", i18nKey: "dashboardNetwork", href: "/docs/monitor/dashboard/network" },
{ title: "VMs & LXCs tab", i18nKey: "dashboardVmsLxcs", href: "/docs/monitor/dashboard/vms-lxcs" }, {
title: "VMs & LXCs tab",
i18nKey: "dashboardVmsLxcs",
href: "/docs/monitor/dashboard/vms-lxcs",
submenu: [
{ title: "App", i18nKey: "dashboardVmsLxcsApp", href: "/docs/monitor/dashboard/vms-lxcs/app" },
{ title: "Updates", i18nKey: "dashboardVmsLxcsUpdates", href: "/docs/monitor/dashboard/vms-lxcs/updates" },
],
},
{ title: "Hardware tab", i18nKey: "dashboardHardware", href: "/docs/monitor/dashboard/hardware" }, { title: "Hardware tab", i18nKey: "dashboardHardware", href: "/docs/monitor/dashboard/hardware" },
{ title: "System Logs tab", i18nKey: "dashboardSystemLogs", href: "/docs/monitor/dashboard/system-logs" }, { title: "System Logs tab", i18nKey: "dashboardSystemLogs", href: "/docs/monitor/dashboard/system-logs" },
{ title: "Terminal tab", i18nKey: "dashboardTerminal", href: "/docs/monitor/dashboard/terminal" }, { title: "Terminal tab", i18nKey: "dashboardTerminal", href: "/docs/monitor/dashboard/terminal" },
+2
View File
@@ -62,6 +62,8 @@
"dashboardStorage": "Storage tab", "dashboardStorage": "Storage tab",
"dashboardNetwork": "Network tab", "dashboardNetwork": "Network tab",
"dashboardVmsLxcs": "VMs & LXCs tab", "dashboardVmsLxcs": "VMs & LXCs tab",
"dashboardVmsLxcsApp": "App",
"dashboardVmsLxcsUpdates": "Updates",
"dashboardHardware": "Hardware tab", "dashboardHardware": "Hardware tab",
"dashboardSystemLogs": "System Logs tab", "dashboardSystemLogs": "System Logs tab",
"dashboardTerminal": "Terminal tab", "dashboardTerminal": "Terminal tab",
@@ -0,0 +1,272 @@
{
"meta": {
"title": "App — register and monitor LXC applications | ProxMenux",
"description": "Declare the apps running inside an LXC container from the ProxMenux Monitor and optionally track their versions."
},
"header": {
"title": "App — register and monitor LXC applications",
"description": "Declare the apps running inside a container, wire quick web links, and optionally track installed vs. upstream versions."
},
"intro": {
"p1": "The <strong>App</strong> tab records which applications run inside an LXC container. Each registered app can expose a display name, an icon, one or several web links and — optionally — its version status.",
"p2": "A single LXC can host several registered applications. A main service can share the container with an administration interface, an API or any other application reachable on a different port.",
"p3": "Registering an application does not modify it or update it. This tab is about identification and display. The mechanisms that <em>execute</em> an update are configured and used from the <link>Updates tab</link>."
},
"whatYouGet": {
"heading": "What you get by registering an app",
"lead": "Depending on the data configured, ProxMenux can surface:",
"items": [
"A one-click shortcut to the application's web UI.",
"Multiple links when the LXC exposes more than one service or port.",
"The version currently installed.",
"The latest version published by the project.",
"A notice when a newer version is available.",
"Notifications on new releases if enabled in Monitor settings."
],
"trailing": "Version tracking is optional. An app can be registered purely to keep its name, icon and web links handy.",
"callout": "The <strong>Update available</strong> label means ProxMenux found a difference between the installed and the published version. It does not automatically mean it also knows how to upgrade the app — that is a separate setup, done on the Updates tab."
},
"firstOpening": {
"heading": "First time opening the App tab",
"p1": "On first open, ProxMenux tries to recognise apps in the container using the information it has: the installer used when the LXC was created, detected services, and ports that are listening.",
"p2": "When matches are found, they show up as suggestions. Always review the proposal before saving — auto-detection speeds up registration, but it cannot guarantee that every detected service corresponds exactly to the app you intended."
},
"figures": {
"f01": {
"alt": "Empty App tab showing detected app suggestions",
"caption": "Empty state with one or more detected suggestions"
},
"f02": {
"alt": "Catalog search showing matches for the typed name",
"caption": "Catalog search and match selection"
},
"f03": {
"alt": "App registration form with name, icon and two web links",
"caption": "Basic form with name, icon and two web links"
},
"f04": {
"alt": "LXC with Docmost and Redis both registered as separate apps, each with its own version state",
"caption": "Two apps in the same LXC — a file-tracked app and a dpkg-tracked one, each with its own version state"
},
"f05": {
"alt": "Advanced tracking options showing the installed-version method and the upstream source",
"caption": "Advanced options with the installed-version method and the upstream source"
},
"f06": {
"alt": "Registered app card with the installed version, the latest upstream version and an Update available indicator",
"caption": "A wired card shows Installed, Latest upstream, an Update-available arrow when they differ, and the web link"
},
"f07": {
"alt": "Minimal registered app showing just its name and a single web link, no version tracking",
"caption": "Link-only record — just a name and a web link, without version tracking"
}
},
"registerSuggested": {
"heading": "Registering a suggested app",
"steps": [
"Open the LXC from the <strong>VMs & LXCs</strong> card.",
"Select the <strong>App</strong> tab.",
"Locate the suggested app.",
"Press <strong>Register</strong>.",
"Check the name, links and auto-filled data.",
"Save the app."
],
"trailing": "If a suggestion doesn't match anything you actually want to register, you can hide it. Hidden suggestions can be brought back from <strong>Register a different app</strong>."
},
"catalog": {
"heading": "Using the catalog",
"p1": "The catalog helps you find known applications and pre-fill some of their data. Typing into the name field shows the closest matches — picking one can autofill the name, icon, typical ports and, when a verified profile exists, the version-tracking options too.",
"p2": "The catalog is a helper, not a complete list of every piece of software an LXC might host. Some entries only carry basic information; others also include a ready-made way to read the installed version.",
"p3": "If the application isn't in the catalog, register it manually."
},
"manual": {
"heading": "Register an application manually",
"p1": "Use <strong>Register a different app</strong> when the LXC has no apps yet. If it already has at least one, use <strong>Add another application</strong>.",
"p2": "The basic configuration only needs a name. Everything else is added according to what you want to display.",
"nameHeading": "Name and icon",
"nameBody": "Give the app a name that makes it easy to recognise. The icon is optional and can be supplied as a URL.",
"linksHeading": "Web links and ports",
"linksLead": "Each link can carry:",
"linksItems": [
"Protocol <code>http</code> or <code>https</code>.",
"Port.",
"Description, such as <em>Web UI</em>, <em>Administration</em> or <em>API</em>.",
"An optional per-link icon."
],
"linksTrailing": "ProxMenux combines protocol and port with the LXC's IP address to build the URL. Add as many links as the app needs when a single container exposes several related services.",
"linksConfirm": "Before saving, confirm the port really corresponds to the service and that you can reach it from the browser."
},
"multiple": {
"heading": "Registering several apps in the same LXC",
"intro": "After saving the first app, press <strong>Add another application</strong> and repeat. Each record keeps its own links, detection method and version state independently.",
"usefulLead": "This is useful when:",
"usefulItems": [
"An LXC hosts several independent services.",
"An installation includes a main app plus companion tooling.",
"Each service has its own web interface or its own release cycle."
],
"dontGroup": "Don't group under a single record programs that publish and update independently. Registering them separately makes it clear which one has a new release and lets each one carry its own update method on the Updates tab."
},
"tracking": {
"heading": "Version tracking",
"intro": "Open the advanced options in the form to configure version tracking. Two different pieces of information are needed:",
"ingredients": [
"<strong>Installed version</strong> — how to read the version currently running inside the LXC.",
"<strong>Latest available version</strong> — where to read the version published by the project."
],
"trailing": "If only the installed version is configured, ProxMenux can show it, but cannot tell whether an update exists. For an <strong>Update available</strong> label to appear, both values have to be readable and comparable.",
"methodsHeading": "Methods to read the installed version",
"methodsLead": "Pick the method that matches how the app was installed:",
"methodsTable": {
"colMethod": "Method",
"colWhen": "When to use it",
"rows": [
{ "method": "None (link only)", "when": "You only need the name and web links." },
{ "method": "dpkg package", "when": "The application is installed as a Debian or Ubuntu package." },
{ "method": "apk package", "when": "The application is installed as an Alpine package." },
{ "method": "Binary", "when": "An executable returns its version through an argument like --version." },
{ "method": "File + regex", "when": "The version string is written inside a file." },
{ "method": "Python distribution", "when": "The application is installed as a Python package." },
{ "method": "Command", "when": "A specific command must be executed to obtain the version." },
{ "method": "Manual", "when": "The user enters the installed version by hand." }
]
},
"methodsTrailing": "Use the most direct and stable method. If the application comes from a system package, prefer querying that package over parsing the output of a generic command.",
"commandHeading": "The Command method does not update the app",
"commandP1": "In this form, <strong>Command</strong> serves exclusively to read the installed version. Its arguments are entered comma-separated and ProxMenux runs them directly, without a shell interpreter.",
"commandP2": "If your usual query is:",
"commandExample1": "myapp version --short",
"commandP3": "Form arguments would be:",
"commandExample2": "myapp, version, --short",
"commandP4": "Don't use operators like <code>&&</code>, redirections or pipes here. If you need a full procedure to upgrade the application, that is configured later on the Updates tab.",
"sourceHeading": "Source for the latest available version",
"sourceLead": "ProxMenux can query a public source of the project, for example:",
"sourceItems": [
"The releases or tags of a GitHub repository.",
"An HTTP endpoint that returns the version inside a JSON response."
],
"sourceTrailing": "Always use the app's official source. A fork or a third-party endpoint may announce versions that don't match the installation in the LXC.",
"regexHeading": "Version regular expressions",
"regexIntro": "A regular expression, or <strong>regex</strong>, isolates the version number inside a longer text. Most projects don't publish a ready-made regex — the user builds one from real output or a real release name.",
"regexOptional": "It is not always needed. Leave it empty first if the source already returns a clean value like <code>2.14.3</code>. Add one only when ProxMenux needs to separate the version from other words, symbols or numbers.",
"regexTwoHeading": "There are two different regex fields",
"regexTwoItems": [
"<strong>Installed version regex</strong> is applied to the output read inside the LXC.",
"<strong>Version regex</strong> or <strong>Tag regex</strong> is applied to the release / tag name published by the external source."
],
"regexTwoTrailing": "Both must produce comparable values. For instance, if the local app returns <code>MyApp v2.14.3</code> and GitHub publishes <code>release-2.14.3</code>, both expressions should extract <code>2.14.3</code>.",
"step1Heading": "1. Capture a real sample",
"step1P1": "Before writing the pattern, capture exactly the text ProxMenux will have to interpret.",
"step1P2": "For the installed version, run the same binary and arguments configured in the form from the LXC console. Depending on the method, you may also query the corresponding package or file.",
"step1P3": "For example:",
"step1Cmd": "myapp --version",
"step1P4": "Suppose the real output is:",
"step1Output": "MyApp version v2.14.3 (stable)",
"step1P5": "For the published version, check the exact release or tag name in the official repository. If you use a JSON endpoint, inspect the value the configured path returns.",
"step1P6": "Do not build the pattern against an invented example — a single space, prefix or extra number can change the result.",
"step2Heading": "2. Identify the part to keep",
"step2Lead": "In the example above we want to keep <code>2.14.3</code> and drop:",
"step2Items": [
"The text <code>MyApp version</code>.",
"The letter <code>v</code>.",
"The text <code>(stable)</code>."
],
"step2Recommended": "The recommended expression:",
"step2Regex": "version[ :=]+v?([0-9]+\\.[0-9]+\\.[0-9]+)",
"step2ReadLead": "Read piece by piece:",
"step2Breakdown": {
"colPart": "Fragment",
"colMeaning": "Meaning",
"rows": [
{ "part": "version", "meaning": "Anchors the search on that word to avoid matching an unrelated number." },
{ "part": "[ :=]+", "meaning": "Accepts one or more spaces, colons or equal signs." },
{ "part": "v?", "meaning": "The letter v may appear once or not at all." },
{ "part": "( and )", "meaning": "Mark the portion ProxMenux should keep." },
{ "part": "[0-9]+", "meaning": "Matches one or more digits." },
{ "part": "\\.", "meaning": "Matches a literal dot between the numbers." }
]
},
"step2DotNote": "The dot is written as <code>\\.</code> because, in a regex, a bare dot means \"any character\".",
"step3Heading": "3. Pick a pattern that fits the format",
"step3Lead": "These patterns cover the most common cases:",
"step3Examples": {
"colText": "Sample text",
"colRegex": "Recommended regex",
"colResult": "Result",
"rows": [
{ "text": "v2.14.3", "regex": "v?([0-9]+\\.[0-9]+\\.[0-9]+)", "result": "2.14.3" },
{ "text": "Version: 2.14", "regex": "Version[ :=]+v?([0-9]+(?:\\.[0-9]+){1,3})", "result": "2.14" },
{ "text": "release-2.14.3.1", "regex": "release-v?([0-9]+(?:\\.[0-9]+){1,3})", "result": "2.14.3.1" },
{ "text": "build 2026.08.10", "regex": "build[ :=]+([0-9]{4}\\.[0-9]{1,2}\\.[0-9]{1,2})", "result": "2026.08.10" },
{ "text": "{\"version\":\"2.14.3\"}", "regex": "\"version\"\\s*:\\s*\"v?([0-9]+(?:\\.[0-9]+){1,3})\"", "result": "2.14.3" }
]
},
"step3Note1": "<code>(?: ... )</code> groups a fragment of the pattern without producing an extra output value. This form is convenient to accept versions with two, three or four blocks without complicating the result.",
"step3Note2": "Enter the regex exactly as shown in the table: without surrounding quotes and without the <code>/.../</code> delimiters some online tools use.",
"step4Heading": "4. Prefer a single capture",
"step4Intro": "ProxMenux uses capture groups to decide which value to return:",
"step4Items": [
"With no capture groups it keeps the whole match.",
"With one capture, it keeps that capture's content.",
"With several captures, it joins them with dots."
],
"step4Trailing": "For predictable results, wrap the whole version in a single capture and use <code>(?: ... )</code> for helper groups.",
"step4RecLabel": "Recommended:",
"step4RecRegex": "v?([0-9]+(?:\\.[0-9]+){1,3})",
"step4LessLabel": "Less clear for beginners:",
"step4LessRegex": "v?([0-9]+)\\.([0-9]+)\\.([0-9]+)",
"step4Note": "Both can produce <code>2.14.3</code>, but the first is easier to maintain if the format changes.",
"step5Heading": "5. Avoid overly broad matches",
"step5Lead": "A pattern like this one is usually too open:",
"step5Regex": "([0-9.]+)",
"step5P1": "It can capture a year, a port, a dependency version or the first number that appears in the output. Anchor it with a nearby word such as <code>version</code>, <code>release</code> or <code>build</code> when the text carries several numbers.",
"step5P2": "Also confirm the upstream source isn't mixing stable releases with beta, nightly or development builds. The regex must select the same channel that is installed in the LXC.",
"step6Heading": "6. Save and verify the result",
"step6Lead": "After saving the app, press <strong>Check</strong> and read the two values ProxMenux reports:",
"step6Output": "Installed: 2.14.3\nLatest: 2.15.0",
"step6CorrectLead": "The regex is correct when:",
"step6CorrectItems": [
"Both fields contain only the expected version.",
"The application name and extra text are not captured.",
"The version is not confused with any other number.",
"Local and published values use the same format."
],
"step6ErrorNote": "If the match errors out, capture the real output again and compare it character by character. Pay particular attention to uppercase, spaces, hyphens, the letter <code>v</code> and the number of version blocks.",
"step6Callout": "If you can't build a reliable pattern, prefer to disable upstream tracking temporarily and keep the app as a link-only record. A wrong regex can raise false alerts or hide a real update."
},
"state": {
"heading": "Reading an app's state",
"lead": "A registered app can display any of the following states:",
"items": [
"<strong>Up to date</strong> — versions match.",
"<strong>Update available</strong> — the source publishes a newer version.",
"<strong>Checking</strong> — the check is in progress.",
"<strong>Version tracking pending</strong> — no check has completed yet.",
"<strong>Error</strong> — one of the versions could not be read or parsed."
],
"trailing": "Use <strong>Check</strong> to repeat the query manually after tweaking the configuration. If an error appears, review the installed-version method, the upstream source and the regex patterns first."
},
"manage": {
"heading": "Managing existing records",
"lead": "Enter management mode to:",
"items": [
"Re-check an app.",
"Edit its name, links or version tracking.",
"Delete a record that is no longer needed.",
"Add another app to the same LXC."
],
"trailing": "Deleting a record does not uninstall or stop the app. It only removes the information ProxMenux uses to display it and monitor its version."
},
"notDetected": {
"heading": "If the app is not detected",
"intro": "Automatic detection is not required to use this feature. If no suggestion appears:",
"steps": [
"Register the app manually.",
"Add its known links and ports.",
"Leave it as <strong>None (link only)</strong> if you only need a shortcut.",
"Configure version tracking only when a reliable source has been identified for both values.",
"Configure the update method later from <link>Updates</link>, if you want ProxMenux to run it."
],
"trailing": "Don't invent a package name, path or regex just to fill the form. A simple, correct record beats an automatic tracking based on unverified data."
}
}
@@ -0,0 +1,231 @@
{
"meta": {
"title": "Updates — updating an LXC's system and apps | ProxMenux",
"description": "Which mechanisms ProxMenux can use to update the operating system and the applications registered in an LXC container."
},
"header": {
"title": "Updates — updating an LXC's system and apps",
"description": "Where ProxMenux decides how to upgrade a container: OS packages, Community Scripts helper, or a custom command."
},
"intro": {
"p1": "The <strong>Updates</strong> tab gathers the mechanisms ProxMenux can run to upgrade the operating system and the applications registered inside an LXC container.",
"p2": "The <link>App tab</link> declares which applications exist and, optionally, compares their versions. <strong>Updates</strong> is about the action: it decides which mechanism is available, presents the matching button and runs the upgrade inside the container.",
"callout": "<strong>Core idea:</strong> detecting a new version and knowing how to install it are two different jobs. An app can show <strong>Update available</strong> on the App tab and still not have a working update button until a valid method is defined."
},
"mechanisms": {
"heading": "Available update mechanisms",
"intro": "Depending on how the app was installed and where its updates come from, ProxMenux picks from three mechanisms.",
"osHeading": "Operating system packages",
"osP1": "On Debian or Ubuntu containers, ProxMenux queries and updates packages through APT. On Alpine, it uses APK.",
"osP2": "Registered apps whose install method is <code>dpkg</code> or <code>apk</code> are part of this pass. They don't need a second command in the app section — they update as part of <strong>Apply OS update</strong>.",
"osP3": "The section shows the number of pending packages, how many are security updates, the OS family and the time of the last check.",
"helperHeading": "Proxmox VE Helper-Scripts updater",
"helperP1": "When the LXC was created with a helper from the <linkHelperHome>Proxmox VE Helper-Scripts</linkHelperHome> project, ProxMenux recognises its updater. The matching app must be registered on the App tab so the Monitor can associate the helper with the service shown to the user.",
"helperP2": "<strong>The update logic itself is maintained by the Proxmox VE Helper-Scripts project</strong>, not by ProxMenux. Each helper ships its own <code>update_script</code> function; ProxMenux fetches it and runs it inside the container in silent mode (<code>PHS_SILENT=1</code>), without prompts. There is no need to copy the helper or write a custom command on the ProxMenux side.",
"helperP3": "Full documentation for the update mechanism lives on the project site — <linkHelperDocs>community-scripts.org / update-apps</linkHelperDocs>. Each helper also has its own entry on the <linkHelperHome>project site</linkHelperHome> with a description of what the script does, its default configuration and the source of the update logic — use that page as the reference for what the updater will change inside the LXC.",
"helperP4": "Not every helper supports in-place updates. If the catalog marks an application as non-upgradable, the tab will surface that state and won't present this method as available.",
"customHeading": "Custom command",
"customP1": "A registered app can store its own update command. ProxMenux runs it inside the LXC when the user presses <strong>Apply update</strong> or when a scheduled task includes that app.",
"customP2": "This method is designed for apps whose installer provides no recognised helper and that don't update as part of APT or APK."
},
"decision": {
"heading": "How ProxMenux picks the action to show",
"table": {
"colSituation": "Situation",
"colAction": "Action",
"rows": [
{ "situation": "APT or APK packages pending", "action": "Apply OS update" },
{ "situation": "The app uses a dpkg or apk package", "action": "Apply OS update — no separate app command needed" },
{ "situation": "A compatible helper exists and the app is registered", "action": "Apply update via Community Scripts" },
{ "situation": "The registered app has a custom command", "action": "Apply update using that command" },
{ "situation": "A new version exists but no helper or command is configured", "action": "Shows No updater configured; offers to add a command" },
{ "situation": "System and app updates are both available", "action": "Combined Apply OS + Apps updates action may appear" }
]
},
"trailing": "An app registered only as a link is never shown as upgradable — ProxMenux doesn't have enough information to wire an update method to it."
},
"figures": {
"f01": {
"alt": "OS packages card showing pending updates count, security-updates count and the Apply OS update button",
"caption": "Pending OS packages: total count, security-updates count, and the Apply OS update button"
},
"f02": {
"alt": "Same OS packages card after applying updates — no packages pending, OS up to date badge",
"caption": "After applying: 'No OS updates pending' and the OS up to date badge"
},
"f03": {
"alt": "Registered app card showing 'No update method available' and an Add custom update command button",
"caption": "'No update method available' — ProxMenux tracks the app but has nothing wired to upgrade it yet"
},
"f04": {
"alt": "Custom update command editor with the example placeholder visible inside the textarea",
"caption": "The custom command editor with its placeholder example, Cancel and Save buttons"
},
"f05": {
"alt": "Terminal panel labelled 'Apply updates — CT 103' showing live apt output as packages are unpacked",
"caption": "Terminal panel streaming the update output live while apt unpacks packages inside the CT"
},
"f06": {
"alt": "Options card with snapshot before applying enabled, backup storage set to pbs, and restart after applying enabled",
"caption": "Options card with vzdump snapshot, backup storage and restart-after-applying enabled together"
},
"f07": {
"alt": "Scheduled updates section enabled — Frequency set to Daily at 3:00, cron expression 0 3 * * *, and What to update set to OS + application",
"caption": "Scheduled updates enabled — frequency preset, matching cron expression and target scope selected"
}
},
"custom": {
"heading": "Adding a custom update command",
"p1": "When an app has version tracking but no update method, the tab shows <strong>No updater configured</strong>. Press <strong>Add custom update command</strong> to open the editor.",
"p2": "The command must represent the real, complete procedure that upgrades that app. Don't just paste the command that reads its version."
},
"figureOut": {
"heading": "How to figure out the correct command",
"intro": "There is no universal update command. Before saving one, identify how the software was installed and what the project's recommended upgrade path is.",
"step1Heading": "1. Check whether the system already handles it",
"step1P1": "If the app was installed from Debian, Ubuntu or Alpine repositories it usually upgrades with system packages. In that case don't add a custom command — use <strong>Apply OS update</strong>.",
"step1P2": "You can check the package origin from the LXC console with the distro's tooling. For example:",
"step1Cmd1": "dpkg -l | grep -i name",
"step1P3": "or:",
"step1Cmd2": "apk info | grep -i name",
"step1P4": "Replace <code>name</code> with the package you are investigating. A match doesn't automatically confirm it's the main package — cross-check the name against the app's documentation.",
"step2Heading": "2. Consult the official documentation",
"step2P1": "Look in the official docs or repository for sections like <strong>Upgrade</strong>, <strong>Update</strong>, <strong>Maintenance</strong> or <strong>Manual installation</strong>. The procedure must match the method used to install the app in that LXC.",
"step2P2": "Don't use instructions targeting a different distribution, a different install type or a different version.",
"step3Heading": "3. Inspect the existing installation",
"step3Lead": "If you don't remember how the app was installed, look at:",
"step3Items": [
"The history or notes of the original installer.",
"The path where its files live.",
"The service definition that starts it.",
"Any maintenance scripts shipped by the app.",
"The documentation stored inside its install directory."
],
"step3P1": "For a systemd service, this can help locate the binary and its working directory:",
"step3Cmd": "systemctl show service-name -p ExecStart -p WorkingDirectory",
"step3P2": "This helps identify the installation, but it does not automatically translate the <code>ExecStart</code> line into an update command.",
"step4Heading": "4. Test the procedure in the LXC console",
"step4Lead": "Open a console into the container and run the procedure manually before saving it in ProxMenux. Verify that it:",
"step4Items": [
"Finishes without prompts or interactive menus.",
"Returns a correct exit code.",
"Restarts or reloads only the services that need it.",
"Leaves the app reachable afterwards.",
"Changes the installed version as expected."
],
"step4Note": "When feasible, take a container backup before testing.",
"step5Heading": "5. Save only the in-container command",
"step5P1": "Enter only what would be executed inside the LXC. Don't include:",
"step5Cmd1": "pct exec <vmid> --",
"step5P2": "ProxMenux already handles entering the container. The command runs as <code>root</code> via <code>sh -c</code>, so it accepts chained operations and directory changes.",
"step5P3": "If the updater must run from a specific path, include it explicitly:",
"step5Cmd2": "cd /opt/my-app && ./update.sh",
"step5P4": "If the project ships an updater at a different path, use the path and arguments named by the official documentation."
},
"requirements": {
"heading": "Requirements for a reliable command",
"lead": "Before running it from the Monitor, confirm the command:",
"items": [
"Runs without user interaction.",
"Uses absolute paths or changes into the correct directory first.",
"Stops, migrates and restarts services as required by the official instructions.",
"Exits with an error when the update fails.",
"Does not contain visible passwords, tokens or other secrets.",
"Does not download or execute scripts from untrusted sources."
],
"trailing": "The content is stored in the LXC's configuration and executed with administrator privileges. Treat it with the same care as any command run as <code>root</code>."
},
"difference": {
"heading": "Difference between the detection command and the update command",
"lead": "Both fields have different goals:",
"table": {
"colField": "Field",
"colLocation": "Location",
"colRole": "Role",
"rows": [
{
"field": "Command for the installed version",
"location": "App → advanced tracking",
"role": "Reads and returns the current version; executed as an argument list without a shell."
},
{
"field": "Custom update command",
"location": "Updates",
"role": "Runs the upgrade procedure; interpreted via sh -c."
}
]
},
"trailing": "Don't blindly copy the value of one into the other. A command like <code>myapp --version</code> may correctly detect the version but won't install a new one."
},
"apply": {
"heading": "Applying an update",
"lead": "Before pressing an apply button:",
"steps": [
"Confirm what will be updated: system, one app or both.",
"Check the backup and restart options.",
"Press the matching button.",
"Follow the process output in the terminal panel.",
"Verify the final result and that the service responds again."
],
"trailing1": "If the LXC is stopped, ProxMenux starts it to run the process. If the update finishes correctly and the restart option is enabled, the container is restarted at the end.",
"systemLead": "On a system update:",
"systemItems": [
"Debian and Ubuntu run the upgrade via APT.",
"Alpine runs it via APK."
],
"appLead": "On an app update:",
"appItems": [
"The compatible helper is used, when it exists.",
"The custom command stored for the app is executed, when configured.",
"If several apps are selected, their methods run in sequence."
],
"trailing2": "The terminal panel shows progress and ends with a successful result or the process's error code."
},
"backup": {
"heading": "Backup before updating",
"p1": "Enable <strong>Snapshot the container before applying</strong> to create a <code>vzdump</code> backup before touching the LXC. You can also choose the target storage.",
"p2": "If the backup is requested and it fails, ProxMenux won't continue with the update. This prevents changes from starting without the requested recovery point.",
"p3": "This option applies to both manual runs and scheduled runs."
},
"restart": {
"heading": "Restart after updating",
"p1": "<strong>Restart the container after applying</strong> is a preference, not a signal that the restart is mandatory. Enable it when the app's procedure or the installed packages require it.",
"p2": "The restart only happens after a successful run. If the update fails, the container stays up so the error can be inspected.",
"p3": "The backup and restart options are saved for that LXC and also apply to its scheduled tasks."
},
"scheduled": {
"heading": "Scheduled updates",
"p1": "The <strong>Scheduled updates</strong> section runs automatically the same flow the manual buttons use.",
"createLead": "To create a schedule:",
"createSteps": [
"Open <strong>Options</strong> and press <strong>Edit</strong>.",
"Enable <strong>Scheduled updates</strong>.",
"Choose a preset frequency or enter a cron expression.",
"Select what will be updated: system packages only, applications only, or system and applications.",
"Review the backup and restart options.",
"Save the configuration."
],
"p2": "The card shows whether the schedule is active, what it covers and the outcome of the last run. A disabled schedule can be kept for later re-activation, or removed entirely.",
"p3": "If ProxMenux detects an external schedule created by Community Scripts on the host, it surfaces it so the user knows another automation is already in place.",
"callout": "Before scheduling app updates, test every helper or command manually. A scheduled task can't answer prompts or fix an incomplete procedure."
},
"verify": {
"heading": "Checking the result",
"p1": "After applying system packages, ProxMenux forces a fresh check to update the pending-package counter without waiting for the next periodic cycle.",
"p2": "For an app, go back to the <link>App tab</link> and press <strong>Check</strong> if the version number doesn't refresh immediately. This runs the configured installed-version method again and queries the upstream source.",
"p3": "Confirm additionally that the app's web links respond correctly. A command finishing without errors is not a substitute for functional verification of the service."
},
"troubleshoot": {
"heading": "Common problems",
"noButtonHeading": "Update available appears, but there's no Apply update button",
"noButtonBody": "Version detection works, but no method to install the update was found. Check whether the app updates via system packages, a compatible helper or a custom command.",
"aptHeading": "The app updates through APT or APK",
"aptBody": "Use <strong>Apply OS update</strong>. Don't add a second command for the same operation — the app is already part of the system update.",
"noUpdaterHeading": "No updater configured is shown",
"noUpdaterBody": "ProxMenux tracks the app but doesn't know how to update it. Check its official documentation, test the procedure in the console and, if appropriate, save it via <strong>Add custom update command</strong>.",
"helperDetectedHeading": "The helper is detected but can't be used",
"helperDetectedBody": "The helper may be marked as non-upgradable or fall outside the recognised methods. Follow the app's official instructions and don't assume every LXC built with Community Scripts supports automatic updates.",
"customFailsHeading": "The custom command fails",
"customFailsBody": "Re-run it in the LXC console. Check the working path, permissions, dependencies, non-interactive arguments and exit code. Don't swap the command for a different variant until you've verified the recommended procedure with the project."
}
}
+2
View File
@@ -62,6 +62,8 @@
"dashboardStorage": "Pestaña Almacenamiento", "dashboardStorage": "Pestaña Almacenamiento",
"dashboardNetwork": "Pestaña Red", "dashboardNetwork": "Pestaña Red",
"dashboardVmsLxcs": "Pestaña VMs y LXCs", "dashboardVmsLxcs": "Pestaña VMs y LXCs",
"dashboardVmsLxcsApp": "App",
"dashboardVmsLxcsUpdates": "Updates",
"dashboardHardware": "Pestaña Hardware", "dashboardHardware": "Pestaña Hardware",
"dashboardSystemLogs": "Pestaña Logs del sistema", "dashboardSystemLogs": "Pestaña Logs del sistema",
"dashboardTerminal": "Pestaña Terminal", "dashboardTerminal": "Pestaña Terminal",
@@ -0,0 +1,272 @@
{
"meta": {
"title": "App — registrar y supervisar aplicaciones de un LXC | ProxMenux",
"description": "Indica qué aplicaciones se ejecutan dentro de un contenedor LXC desde ProxMenux Monitor y sigue opcionalmente su versión."
},
"header": {
"title": "App — registrar y supervisar aplicaciones de un LXC",
"description": "Registra las aplicaciones que corren dentro del contenedor, añade accesos web rápidos y, opcionalmente, haz seguimiento de la versión instalada frente a la publicada."
},
"intro": {
"p1": "La pestaña <strong>App</strong> permite indicar qué aplicaciones se ejecutan dentro de un contenedor LXC. Cada aplicación registrada puede mostrar un nombre, un icono, uno o varios accesos web y, de forma opcional, el estado de su versión.",
"p2": "Un mismo LXC puede tener varias aplicaciones registradas. Por ejemplo, un servicio principal puede compartir el contenedor con una interfaz de administración, una API o cualquier otra aplicación accesible desde un puerto distinto.",
"p3": "Registrar una aplicación no la modifica ni la actualiza. Esta pestaña se ocupa de identificarla y mostrar su información. Los métodos que <em>ejecutan</em> una actualización se configuran y utilizan desde la <link>pestaña Updates</link>."
},
"whatYouGet": {
"heading": "Qué se obtiene al registrar una aplicación",
"lead": "Según los datos que se configuren, ProxMenux puede ofrecer:",
"items": [
"Un acceso directo a la interfaz web de la aplicación.",
"Varios enlaces cuando el LXC expone más de un servicio o puerto.",
"La versión instalada actualmente.",
"La última versión publicada por el proyecto.",
"Un aviso cuando hay una versión más reciente.",
"Notificaciones de nuevas versiones, si están habilitadas en los ajustes del monitor."
],
"trailing": "El seguimiento de versiones es opcional. También es posible registrar una aplicación únicamente para disponer de su nombre, icono y enlaces web.",
"callout": "El aviso <strong>Update available</strong> indica que ProxMenux ha encontrado una diferencia entre la versión instalada y la última versión publicada. No significa necesariamente que ya conozca el procedimiento para actualizar la aplicación."
},
"firstOpening": {
"heading": "Primera apertura de la pestaña App",
"p1": "Al abrir la pestaña, ProxMenux intenta reconocer las aplicaciones del contenedor utilizando la información disponible, como el instalador con el que se creó el LXC, los servicios detectados y los puertos que están escuchando.",
"p2": "Si encuentra coincidencias, las presenta como sugerencias. Revise siempre la propuesta antes de guardarla: la detección facilita el registro, pero no puede garantizar que cada servicio encontrado corresponda exactamente con la aplicación esperada."
},
"figures": {
"f01": {
"alt": "Pestaña App vacía mostrando sugerencias detectadas",
"caption": "Estado inicial sin aplicaciones registradas, con una o varias sugerencias detectadas"
},
"f02": {
"alt": "Búsqueda en el catálogo mostrando coincidencias para el nombre introducido",
"caption": "Búsqueda en el catálogo y selección de una coincidencia"
},
"f03": {
"alt": "Formulario de registro de aplicación con nombre, icono y dos enlaces web",
"caption": "Formulario básico con nombre, icono y dos enlaces web"
},
"f04": {
"alt": "LXC con Docmost y Redis registrados como aplicaciones separadas, cada una con su propio estado de versión",
"caption": "Dos aplicaciones en el mismo LXC — una registrada con file y otra con dpkg, cada una con su propio estado de versión"
},
"f05": {
"alt": "Opciones avanzadas de seguimiento mostrando el método de versión instalada y la fuente upstream",
"caption": "Opciones avanzadas con el método de versión instalada y la fuente de la última versión"
},
"f06": {
"alt": "Aplicación registrada mostrando la versión instalada, la última versión upstream y un indicador de Update available",
"caption": "Una aplicación cableada muestra Installed, Latest upstream, la flecha de Update available cuando difieren y el enlace web"
},
"f07": {
"alt": "Aplicación registrada mínima mostrando solo su nombre y un único enlace web, sin seguimiento de versión",
"caption": "Registro solo con enlace — un nombre y un enlace web, sin seguimiento de versión"
}
},
"registerSuggested": {
"heading": "Registrar una aplicación sugerida",
"steps": [
"Abra el LXC desde la tarjeta <strong>VMs & LXCs</strong>.",
"Seleccione la pestaña <strong>App</strong>.",
"Localice la aplicación sugerida.",
"Pulse <strong>Register</strong>.",
"Compruebe el nombre, los enlaces y los datos rellenados automáticamente.",
"Guarde la aplicación."
],
"trailing": "Si la sugerencia no corresponde con ningún servicio que quiera registrar, puede ocultarla. Las sugerencias ocultas se pueden recuperar más adelante desde <strong>Register a different app</strong>."
},
"catalog": {
"heading": "Usar el catálogo",
"p1": "El catálogo ayuda a localizar aplicaciones conocidas y a rellenar algunos de sus datos. Al escribir en el campo del nombre, ProxMenux muestra las coincidencias más cercanas. Al seleccionar una de ellas puede completar automáticamente el nombre, el icono, los puertos habituales y, cuando existe una configuración comprobada, las opciones de seguimiento de versiones.",
"p2": "El catálogo es una ayuda, no una lista completa de todo el software que puede ejecutarse en un LXC. Algunas aplicaciones solo incluyen información básica y otras disponen también de un método preparado para consultar su versión.",
"p3": "Si la aplicación no aparece, puede registrarla manualmente."
},
"manual": {
"heading": "Registrar una aplicación manualmente",
"p1": "Use <strong>Register a different app</strong> cuando todavía no haya aplicaciones registradas. Si el LXC ya contiene alguna, use <strong>Add another application</strong>.",
"p2": "La configuración básica solo necesita un nombre. Los demás campos se añaden según la información que quiera mostrar.",
"nameHeading": "Nombre e icono",
"nameBody": "Introduzca un nombre que permita reconocer la aplicación fácilmente. El icono es opcional y puede indicarse mediante una URL.",
"linksHeading": "Enlaces web y puertos",
"linksLead": "Cada enlace puede tener:",
"linksItems": [
"Protocolo <code>http</code> o <code>https</code>.",
"Puerto.",
"Descripción, como <em>Web UI</em>, <em>Administration</em> o <em>API</em>.",
"Un icono propio opcional."
],
"linksTrailing": "ProxMenux combina el protocolo y el puerto con la dirección IP del LXC para crear el acceso web. Añada tantos enlaces como necesite si una aplicación usa varias interfaces o si el contenedor aloja varios servicios relacionados.",
"linksConfirm": "Antes de guardar, compruebe que el puerto corresponde realmente con el servicio y que puede acceder a él desde el navegador."
},
"multiple": {
"heading": "Registrar varias aplicaciones en el mismo LXC",
"intro": "Después de guardar la primera aplicación, pulse <strong>Add another application</strong> y repita el proceso. Cada registro mantiene de forma independiente sus enlaces, su método de detección y su estado de versión.",
"usefulLead": "Esto resulta útil cuando:",
"usefulItems": [
"Un LXC ejecuta varios servicios independientes.",
"Una instalación incluye una aplicación principal y herramientas auxiliares.",
"Cada servicio tiene su propia interfaz web o su propio ciclo de versiones."
],
"dontGroup": "No agrupe bajo un único registro programas que se publican y actualizan por separado. Registrarlos individualmente permite saber con claridad cuál tiene una nueva versión y asignarle su propio método de actualización desde la pestaña Updates."
},
"tracking": {
"heading": "Seguimiento de versiones",
"intro": "Abra las opciones avanzadas del formulario para configurar el seguimiento. Se necesitan dos datos diferentes:",
"ingredients": [
"<strong>Versión instalada</strong>: cómo consultar la versión que está ejecutándose dentro del LXC.",
"<strong>Última versión disponible</strong>: dónde consultar la versión publicada por el proyecto."
],
"trailing": "Si solo se configura la versión instalada, ProxMenux puede mostrarla, pero no puede determinar si existe una actualización. Para mostrar <strong>Update available</strong>, debe poder obtener y comparar ambos valores.",
"methodsHeading": "Métodos para obtener la versión instalada",
"methodsLead": "Seleccione el método que corresponda con la forma en que se instaló la aplicación:",
"methodsTable": {
"colMethod": "Método",
"colWhen": "Cuándo utilizarlo",
"rows": [
{ "method": "None (link only)", "when": "Solo se necesitan el nombre y los accesos web." },
{ "method": "dpkg package", "when": "La aplicación está instalada como paquete de Debian o Ubuntu." },
{ "method": "apk package", "when": "La aplicación está instalada como paquete de Alpine." },
{ "method": "Binary", "when": "Un ejecutable devuelve su versión mediante un argumento como --version." },
{ "method": "File + regex", "when": "La versión está escrita dentro de un archivo." },
{ "method": "Python distribution", "when": "La aplicación está instalada como un paquete de Python." },
{ "method": "Command", "when": "Es necesario ejecutar un comando específico para obtener la versión." },
{ "method": "Manual", "when": "El usuario introduce la versión instalada." }
]
},
"methodsTrailing": "Utilice el método más directo y estable. Si la aplicación procede de un paquete del sistema, es preferible consultar ese paquete antes que analizar la salida de un comando genérico.",
"commandHeading": "El método Command no actualiza la aplicación",
"commandP1": "En este formulario, <strong>Command</strong> sirve exclusivamente para leer la versión instalada. Sus argumentos se introducen separados por comas y ProxMenux los ejecuta directamente, sin intérprete de shell.",
"commandP2": "Por ejemplo, si la consulta normal es:",
"commandExample1": "myapp version --short",
"commandP3": "Los argumentos del formulario serían:",
"commandExample2": "myapp, version, --short",
"commandP4": "No utilice aquí operadores como <code>&&</code>, redirecciones o tuberías. Si necesita un procedimiento completo para actualizar la aplicación, se configura después en la pestaña Updates.",
"sourceHeading": "Fuente de la última versión disponible",
"sourceLead": "ProxMenux puede consultar una fuente pública del proyecto, por ejemplo:",
"sourceItems": [
"Las releases o tags de un repositorio de GitHub.",
"Un endpoint HTTP que devuelva la versión dentro de una respuesta JSON."
],
"sourceTrailing": "Use siempre la fuente oficial de la aplicación. Un repositorio derivado o un endpoint de terceros puede anunciar versiones que no correspondan con la instalación del LXC.",
"regexHeading": "Expresiones regulares de versión",
"regexIntro": "Una expresión regular, o <strong>regex</strong>, sirve para localizar el número de versión dentro de un texto más largo. La mayoría de los proyectos no publican una regex preparada: el usuario debe construirla a partir de una salida real del programa o del nombre de una release.",
"regexOptional": "No siempre es necesaria. Déjela vacía primero si la fuente ya devuelve solo un valor limpio como <code>2.14.3</code>. Añádala únicamente cuando ProxMenux necesite separar la versión de otras palabras, símbolos o números.",
"regexTwoHeading": "Hay dos regex diferentes",
"regexTwoItems": [
"<strong>Installed version regex</strong> se aplica a la salida obtenida dentro del LXC.",
"<strong>Version regex</strong> o <strong>Tag regex</strong> se aplica al nombre de la versión publicada por la fuente externa."
],
"regexTwoTrailing": "Ambas deben producir valores comparables. Por ejemplo, si la aplicación local devuelve <code>MyApp v2.14.3</code> y GitHub publica <code>release-2.14.3</code>, las dos expresiones deberían extraer <code>2.14.3</code>.",
"step1Heading": "1. Obtener una muestra real",
"step1P1": "Antes de escribir el patrón, obtenga exactamente el texto que ProxMenux tendrá que interpretar.",
"step1P2": "Para la versión instalada, ejecute en la consola del LXC el mismo binario y los mismos argumentos configurados en el formulario. Según el método elegido, también puede consultar el paquete o el archivo correspondiente.",
"step1P3": "Ejemplo:",
"step1Cmd": "myapp --version",
"step1P4": "Supongamos que la salida real es:",
"step1Output": "MyApp version v2.14.3 (stable)",
"step1P5": "Para la versión publicada, revise el nombre exacto de la release o el tag en el repositorio oficial. Si utiliza un endpoint JSON, examine el valor que devuelve la ruta configurada.",
"step1P6": "No construya el patrón a partir de un ejemplo inventado: un espacio, un prefijo o un número adicional puede cambiar el resultado.",
"step2Heading": "2. Identificar la parte que debe conservarse",
"step2Lead": "En el ejemplo anterior queremos conservar <code>2.14.3</code> y descartar:",
"step2Items": [
"El texto <code>MyApp version</code>.",
"La letra <code>v</code>.",
"El texto <code>(stable)</code>."
],
"step2Recommended": "La expresión recomendada sería:",
"step2Regex": "version[ :=]+v?([0-9]+\\.[0-9]+\\.[0-9]+)",
"step2ReadLead": "Se puede leer por partes:",
"step2Breakdown": {
"colPart": "Fragmento",
"colMeaning": "Significado",
"rows": [
{ "part": "version", "meaning": "Busca esa palabra para no confundir la versión con otro número." },
{ "part": "[ :=]+", "meaning": "Admite uno o varios espacios, dos puntos o signos igual." },
{ "part": "v?", "meaning": "La letra v puede aparecer una vez o no aparecer." },
{ "part": "( y )", "meaning": "Marcan la parte que ProxMenux debe conservar." },
{ "part": "[0-9]+", "meaning": "Busca uno o varios dígitos." },
{ "part": "\\.", "meaning": "Busca un punto real entre los números." }
]
},
"step2DotNote": "El punto se escribe como <code>\\.</code> porque, en una regex, un punto sin la barra significa «cualquier carácter».",
"step3Heading": "3. Utilizar un patrón adecuado para el formato",
"step3Lead": "Estos patrones cubren muchos casos habituales:",
"step3Examples": {
"colText": "Texto de ejemplo",
"colRegex": "Regex recomendada",
"colResult": "Resultado",
"rows": [
{ "text": "v2.14.3", "regex": "v?([0-9]+\\.[0-9]+\\.[0-9]+)", "result": "2.14.3" },
{ "text": "Version: 2.14", "regex": "Version[ :=]+v?([0-9]+(?:\\.[0-9]+){1,3})", "result": "2.14" },
{ "text": "release-2.14.3.1", "regex": "release-v?([0-9]+(?:\\.[0-9]+){1,3})", "result": "2.14.3.1" },
{ "text": "build 2026.08.10", "regex": "build[ :=]+([0-9]{4}\\.[0-9]{1,2}\\.[0-9]{1,2})", "result": "2026.08.10" },
{ "text": "{\"version\":\"2.14.3\"}", "regex": "\"version\"\\s*:\\s*\"v?([0-9]+(?:\\.[0-9]+){1,3})\"", "result": "2.14.3" }
]
},
"step3Note1": "<code>(?: ... )</code> agrupa una parte del patrón sin crear un valor de salida adicional. Esta forma es útil para aceptar versiones con dos, tres o cuatro bloques sin complicar el resultado.",
"step3Note2": "Introduzca la regex tal como aparece en la tabla: sin comillas alrededor y sin las barras <code>/.../</code> que utilizan algunas herramientas en línea.",
"step4Heading": "4. Usar una sola captura siempre que sea posible",
"step4Intro": "ProxMenux utiliza los paréntesis de captura para decidir qué valor devolver:",
"step4Items": [
"Sin paréntesis de captura, conserva toda la coincidencia.",
"Con una captura, conserva el contenido de esa captura.",
"Con varias capturas, une sus valores mediante puntos."
],
"step4Trailing": "Para evitar resultados inesperados, lo más sencillo es encerrar toda la versión en una sola captura y utilizar <code>(?: ... )</code> para los grupos auxiliares.",
"step4RecLabel": "Recomendado:",
"step4RecRegex": "v?([0-9]+(?:\\.[0-9]+){1,3})",
"step4LessLabel": "Menos claro para un usuario principiante:",
"step4LessRegex": "v?([0-9]+)\\.([0-9]+)\\.([0-9]+)",
"step4Note": "Los dos pueden producir <code>2.14.3</code>, pero el primero es más fácil de mantener si el formato cambia.",
"step5Heading": "5. Evitar coincidencias demasiado generales",
"step5Lead": "Un patrón como este suele ser demasiado abierto:",
"step5Regex": "([0-9.]+)",
"step5P1": "Puede capturar un año, un puerto, la versión de una dependencia o el primer número que aparezca en la salida. Añada una palabra cercana como <code>version</code>, <code>release</code> o <code>build</code> cuando el texto contenga varios números.",
"step5P2": "También debe comprobar que la fuente de versiones no está mezclando releases estables con versiones beta, nightly o de desarrollo. La regex debe seleccionar el mismo tipo de versión que está instalado en el LXC.",
"step6Heading": "6. Guardar y comprobar el resultado",
"step6Lead": "Después de guardar la aplicación, pulse <strong>Check</strong> y revise los dos valores que muestra ProxMenux:",
"step6Output": "Installed: 2.14.3\nLatest: 2.15.0",
"step6CorrectLead": "La regex es correcta si:",
"step6CorrectItems": [
"Ambos campos contienen únicamente la versión esperada.",
"No se ha capturado el nombre de la aplicación ni texto adicional.",
"No se ha confundido la versión con otro número.",
"La versión local y la publicada utilizan el mismo formato."
],
"step6ErrorNote": "Si aparece un error de coincidencia, vuelva a obtener la salida real y compare carácter por carácter. Revise especialmente mayúsculas, espacios, guiones, la letra <code>v</code> y el número de bloques de la versión.",
"step6Callout": "Si no puede construir un patrón fiable, es preferible desactivar temporalmente el seguimiento de la última versión y mantener la aplicación como un registro con enlaces. Una regex incorrecta puede generar avisos falsos o esconder una actualización real."
},
"state": {
"heading": "Interpretar el estado de una aplicación",
"lead": "Una aplicación registrada puede mostrar los siguientes estados:",
"items": [
"<strong>Up to date</strong>: las versiones coinciden.",
"<strong>Update available</strong>: la fuente publica una versión más reciente.",
"<strong>Checking</strong>: la comprobación está en curso.",
"<strong>Version tracking pending</strong>: todavía no se ha completado una comprobación.",
"<strong>Error</strong>: no se ha podido obtener o interpretar alguna de las versiones."
],
"trailing": "Use <strong>Check</strong> para repetir la consulta manualmente después de cambiar la configuración. Si aparece un error, revise primero el método de la versión instalada, la fuente de la última versión y las expresiones regulares."
},
"manage": {
"heading": "Administrar los registros existentes",
"lead": "Active el modo de administración para:",
"items": [
"Comprobar de nuevo una aplicación.",
"Editar su nombre, enlaces o seguimiento de versiones.",
"Eliminar un registro que ya no sea necesario.",
"Añadir otra aplicación al mismo LXC."
],
"trailing": "Eliminar el registro no desinstala ni detiene la aplicación. Solo borra la información que ProxMenux utiliza para mostrarla y supervisar su versión."
},
"notDetected": {
"heading": "Si la aplicación no se detecta",
"intro": "La detección automática no es necesaria para utilizar esta función. Si no aparece ninguna sugerencia:",
"steps": [
"Registre la aplicación manualmente.",
"Añada sus enlaces y puertos conocidos.",
"Déjela como <strong>None (link only)</strong> si solo necesita un acceso directo.",
"Configure el seguimiento de versiones únicamente cuando haya identificado una fuente fiable para ambos valores.",
"Configure después el método de actualización desde <link>Updates</link>, si quiere que ProxMenux pueda ejecutarlo."
],
"trailing": "No invente un nombre de paquete, una ruta o una expresión regular para completar el formulario. Es preferible un registro sencillo y correcto que un seguimiento automático basado en datos que no se hayan verificado."
}
}
@@ -0,0 +1,231 @@
{
"meta": {
"title": "Updates — actualizar el sistema y las aplicaciones de un LXC | ProxMenux",
"description": "Qué mecanismos puede utilizar ProxMenux para actualizar el sistema operativo y las aplicaciones registradas en un contenedor LXC."
},
"header": {
"title": "Updates — actualizar el sistema y las aplicaciones de un LXC",
"description": "Dónde ProxMenux decide cómo actualizar un contenedor: paquetes del sistema operativo, ayudante de Community Scripts o un comando personalizado."
},
"intro": {
"p1": "La pestaña <strong>Updates</strong> reúne los métodos que ProxMenux puede ejecutar para actualizar el sistema operativo y las aplicaciones registradas en un contenedor LXC.",
"p2": "La <link>pestaña App</link> indica qué aplicaciones existen y, opcionalmente, compara sus versiones. <strong>Updates</strong> se ocupa de la acción: determina qué mecanismo está disponible, muestra el botón correspondiente y ejecuta la actualización dentro del contenedor.",
"callout": "<strong>Idea principal:</strong> detectar una versión nueva y saber cómo instalarla son tareas diferentes. Una aplicación puede mostrar <strong>Update available</strong> en la pestaña App y no tener todavía un botón de actualización hasta que se defina un método válido."
},
"mechanisms": {
"heading": "Métodos de actualización disponibles",
"intro": "En función de cómo se instaló la aplicación y de dónde vengan sus actualizaciones, ProxMenux elige entre tres mecanismos.",
"osHeading": "Paquetes del sistema operativo",
"osP1": "En contenedores Debian o Ubuntu, ProxMenux consulta y actualiza los paquetes mediante APT. En Alpine utiliza APK.",
"osP2": "Las aplicaciones registradas cuyo método de instalación sea <code>dpkg</code> o <code>apk</code> forman parte de esta actualización. No necesitan un segundo comando en la sección de la aplicación: se actualizan al aplicar <strong>Apply OS update</strong>.",
"osP3": "La sección muestra el número de paquetes pendientes, cuántos son de seguridad, la familia del sistema y la hora de la última comprobación.",
"helperHeading": "Ayudante Proxmox VE Helper-Scripts",
"helperP1": "Cuando el LXC se creó con un helper del proyecto <linkHelperHome>Proxmox VE Helper-Scripts</linkHelperHome>, ProxMenux reconoce su actualizador. La aplicación correspondiente debe estar registrada en la pestaña App para que el monitor pueda relacionar el helper con el servicio que se muestra al usuario.",
"helperP2": "<strong>La lógica de actualización la mantiene el proyecto Proxmox VE Helper-Scripts</strong>, no ProxMenux. Cada helper incluye su propia función <code>update_script</code>; ProxMenux la descarga y la ejecuta dentro del contenedor en modo silencioso (<code>PHS_SILENT=1</code>), sin prompts. No es necesario copiar el helper ni escribir un comando personalizado en ProxMenux.",
"helperP3": "La documentación completa del mecanismo de actualización vive en la web del proyecto — <linkHelperDocs>community-scripts.org / update-apps</linkHelperDocs>. Cada helper tiene además su propia entrada en la <linkHelperHome>web del proyecto</linkHelperHome> con la descripción de lo que hace el script, su configuración por defecto y la fuente de la lógica de actualización — utilice esa página como referencia de lo que el actualizador cambiará dentro del LXC.",
"helperP4": "No todos los helpers admiten actualización in situ. Si el catálogo marca una aplicación como no actualizable, la pestaña lo indicará y no presentará ese método como disponible.",
"customHeading": "Comando personalizado",
"customP1": "Una aplicación registrada puede guardar su propio comando de actualización. ProxMenux lo ejecuta dentro del LXC cuando el usuario pulsa <strong>Apply update</strong> o cuando una tarea programada incluye esa aplicación.",
"customP2": "Este método está pensado para aplicaciones cuyo instalador no proporciona un helper reconocido y que tampoco se actualizan como parte de APT o APK."
},
"decision": {
"heading": "Cómo decide ProxMenux qué acción mostrar",
"table": {
"colSituation": "Situación",
"colAction": "Acción adecuada",
"rows": [
{ "situation": "Hay paquetes APT o APK pendientes", "action": "Apply OS update" },
{ "situation": "La aplicación usa un paquete dpkg o apk", "action": "Apply OS update — no necesita un comando propio" },
{ "situation": "Hay un helper compatible y la aplicación está registrada", "action": "Apply update mediante Community Scripts" },
{ "situation": "La aplicación registrada tiene un comando personalizado", "action": "Apply update mediante ese comando" },
{ "situation": "Hay una versión nueva, pero no existe helper ni comando", "action": "Muestra No updater configured y ofrece añadir un comando" },
{ "situation": "Hay actualizaciones del sistema y métodos de aplicaciones disponibles", "action": "Puede aparecer una acción combinada Apply OS + Apps updates" }
]
},
"trailing": "Una aplicación registrada únicamente como enlace no aparece como actualizable, porque ProxMenux no dispone de información suficiente para asociarle un método."
},
"figures": {
"f01": {
"alt": "Sección OS packages mostrando el conteo de paquetes pendientes, el conteo de security y el botón Apply OS update",
"caption": "Paquetes del sistema pendientes: total, actualizaciones de seguridad y botón Apply OS update"
},
"f02": {
"alt": "La misma sección OS packages tras aplicar — sin paquetes pendientes, badge OS up to date",
"caption": "Tras aplicar: 'No OS updates pending' y el badge OS up to date"
},
"f03": {
"alt": "Aplicación registrada mostrando 'No update method available' y un botón Add custom update command",
"caption": "'No update method available' — ProxMenux hace seguimiento pero no tiene aún ningún método para actualizarla"
},
"f04": {
"alt": "Editor del comando personalizado con el placeholder de ejemplo visible dentro del textarea",
"caption": "El editor del comando personalizado con su placeholder de ejemplo y los botones Cancel y Save"
},
"f05": {
"alt": "Panel de terminal titulado 'Apply updates — CT 103' mostrando la salida de apt en vivo mientras se desempaquetan paquetes",
"caption": "Panel de terminal transmitiendo la salida de la actualización en vivo mientras apt desempaqueta paquetes dentro del CT"
},
"f06": {
"alt": "Tarjeta Options con Snapshot before applying activo, Backup storage a pbs y Restart after applying activo",
"caption": "Tarjeta Options con snapshot vzdump, almacenamiento de backup y reinicio-tras-aplicar activados a la vez"
},
"f07": {
"alt": "Sección Scheduled updates habilitada — Frequency en Daily at 3:00, expresión cron 0 3 * * * y What to update en OS + application",
"caption": "Scheduled updates activadas — preset de frecuencia, expresión cron correspondiente y ámbito seleccionado"
}
},
"custom": {
"heading": "Añadir un comando de actualización",
"p1": "Cuando una aplicación tiene seguimiento de versiones pero no dispone de un método de actualización, la pestaña muestra <strong>No updater configured</strong>. Pulse <strong>Add custom update command</strong> para abrir el editor.",
"p2": "El comando debe representar el procedimiento real y completo que actualiza esa aplicación. No debe ser simplemente el comando que muestra su versión."
},
"figureOut": {
"heading": "Cómo averiguar el comando correcto",
"intro": "No existe un comando universal para actualizar todas las aplicaciones. Antes de guardar uno, identifique cómo se instaló el software y cuál es el procedimiento recomendado por su proyecto.",
"step1Heading": "1. Comprobar si ya lo gestiona el sistema",
"step1P1": "Si la aplicación se instaló desde los repositorios de Debian, Ubuntu o Alpine, normalmente se actualizará con los paquetes del sistema. En ese caso no añada un comando personalizado: utilice <strong>Apply OS update</strong>.",
"step1P2": "Puede comprobar el origen del paquete desde la consola del LXC con las herramientas de su distribución. Por ejemplo:",
"step1Cmd1": "dpkg -l | grep -i nombre",
"step1P3": "o:",
"step1Cmd2": "apk info | grep -i nombre",
"step1P4": "Sustituya <code>nombre</code> por el paquete que está investigando. Que el texto aparezca en la búsqueda no confirma por sí solo que sea el paquete principal; verifique su nombre en la documentación de la aplicación.",
"step2Heading": "2. Consultar la documentación oficial",
"step2P1": "Busque en la documentación o el repositorio oficial apartados como <strong>Upgrade</strong>, <strong>Update</strong>, <strong>Maintenance</strong> o <strong>Manual installation</strong>. El procedimiento debe corresponder con el método que se utilizó para instalar la aplicación en ese LXC.",
"step2P2": "No use instrucciones destinadas a otra distribución, otro tipo de instalación o una versión diferente del programa.",
"step3Heading": "3. Revisar la instalación existente",
"step3Lead": "Si no recuerda cómo se instaló la aplicación, revise:",
"step3Items": [
"El historial o las notas del instalador original.",
"La ruta donde se encuentran sus archivos.",
"La definición del servicio que la inicia.",
"Los scripts de mantenimiento incluidos por la propia aplicación.",
"La documentación guardada dentro de su directorio de instalación."
],
"step3P1": "Para un servicio systemd, este comando puede ayudar a localizar el ejecutable y su directorio de trabajo:",
"step3Cmd": "systemctl show nombre-del-servicio -p ExecStart -p WorkingDirectory",
"step3P2": "Esto ayuda a identificar la instalación, pero no convierte automáticamente la línea <code>ExecStart</code> en un comando de actualización.",
"step4Heading": "4. Probar el procedimiento en la consola del LXC",
"step4Lead": "Abra la consola del contenedor y ejecute el procedimiento manualmente antes de guardarlo en ProxMenux. Compruebe que:",
"step4Items": [
"Finaliza sin preguntas ni menús interactivos.",
"Devuelve un código de salida correcto.",
"Reinicia o recarga únicamente los servicios necesarios.",
"La aplicación vuelve a estar disponible.",
"La versión instalada cambia como se esperaba."
],
"step4Note": "Cuando sea posible, haga antes una copia de seguridad del contenedor.",
"step5Heading": "5. Guardar solo el comando interno",
"step5P1": "Escriba únicamente lo que se ejecutaría dentro del LXC. No incluya:",
"step5Cmd1": "pct exec <vmid> --",
"step5P2": "ProxMenux ya se encarga de entrar en el contenedor. El comando se ejecuta como <code>root</code> mediante <code>sh -c</code>, por lo que admite operaciones encadenadas y cambios de directorio.",
"step5P3": "Si el actualizador debe ejecutarse desde una ruta concreta, inclúyala de forma explícita:",
"step5Cmd2": "cd /opt/mi-aplicacion && ./update.sh",
"step5P4": "Si el proyecto proporciona un actualizador en otra ruta, use siempre la ruta y los argumentos indicados por su documentación oficial."
},
"requirements": {
"heading": "Requisitos de un comando fiable",
"lead": "Antes de utilizarlo desde el monitor, compruebe que el comando:",
"items": [
"Puede ejecutarse sin intervención del usuario.",
"Utiliza rutas absolutas o cambia primero al directorio correcto.",
"Detiene, migra y reinicia los servicios según las instrucciones oficiales.",
"Devuelve un error cuando la actualización falla.",
"No contiene contraseñas, tokens ni otros secretos visibles.",
"No descarga ni ejecuta scripts procedentes de fuentes que no sean de confianza."
],
"trailing": "El contenido se guarda en la configuración asociada a ese LXC y se ejecuta con privilegios de administrador. Trátelo con el mismo cuidado que cualquier comando ejecutado como <code>root</code>."
},
"difference": {
"heading": "Diferencia entre el comando de detección y el de actualización",
"lead": "Los dos campos tienen objetivos distintos:",
"table": {
"colField": "Campo",
"colLocation": "Ubicación",
"colRole": "Función",
"rows": [
{
"field": "Command para la versión instalada",
"location": "App → seguimiento avanzado",
"role": "Consulta y devuelve la versión actual; se ejecuta como una lista de argumentos, sin shell."
},
{
"field": "Custom update command",
"location": "Updates",
"role": "Ejecuta el procedimiento de actualización; se interpreta mediante sh -c."
}
]
},
"trailing": "No copie automáticamente el comando de un campo al otro. Un comando como <code>myapp --version</code> puede detectar correctamente la versión, pero no instala una nueva versión."
},
"apply": {
"heading": "Aplicar una actualización",
"lead": "Antes de pulsar un botón de aplicación:",
"steps": [
"Revise qué sección se va a actualizar: sistema, una aplicación o ambas.",
"Compruebe las opciones de copia de seguridad y reinicio.",
"Pulse el botón correspondiente.",
"Siga la salida del proceso en la ventana de terminal.",
"Compruebe el resultado final y que el servicio vuelva a responder."
],
"trailing1": "Si el LXC está detenido, ProxMenux lo inicia para ejecutar el proceso. Si la actualización finaliza correctamente y está activada la opción de reinicio, el contenedor se reinicia al terminar.",
"systemLead": "En una actualización del sistema:",
"systemItems": [
"Debian y Ubuntu ejecutan la actualización mediante APT.",
"Alpine ejecuta la actualización mediante APK."
],
"appLead": "En una actualización de aplicación:",
"appItems": [
"Se ejecuta el helper compatible, si existe.",
"Se ejecuta el comando personalizado guardado para la aplicación, si está configurado.",
"Si se han seleccionado varias aplicaciones, se ejecutan sus métodos en secuencia."
],
"trailing2": "La ventana de terminal muestra el progreso y termina con un resultado correcto o con el código de error devuelto por el proceso."
},
"backup": {
"heading": "Copia de seguridad antes de actualizar",
"p1": "Active <strong>Snapshot the container before applying</strong> para crear una copia de seguridad con <code>vzdump</code> antes de modificar el LXC. También puede elegir el almacenamiento de destino.",
"p2": "Si se solicita la copia de seguridad y esta falla, ProxMenux no continúa con la actualización. De este modo se evita iniciar los cambios sin disponer del punto de recuperación solicitado.",
"p3": "Esta opción se aplica tanto a las ejecuciones manuales como a las programadas."
},
"restart": {
"heading": "Reinicio después de actualizar",
"p1": "<strong>Restart the container after applying</strong> es una preferencia, no un aviso de que el reinicio sea obligatorio. Active la opción cuando el procedimiento de la aplicación o los paquetes instalados lo requieran.",
"p2": "El reinicio solo se realiza después de una ejecución correcta. Si la actualización falla, el contenedor permanece iniciado para facilitar la revisión del error.",
"p3": "Las opciones de copia de seguridad y reinicio se guardan para ese LXC y se utilizan también en sus tareas programadas."
},
"scheduled": {
"heading": "Actualizaciones programadas",
"p1": "La sección <strong>Scheduled updates</strong> permite ejecutar automáticamente el mismo flujo utilizado por los botones manuales.",
"createLead": "Para crear una programación:",
"createSteps": [
"Abra <strong>Options</strong> y pulse <strong>Edit</strong>.",
"Active <strong>Scheduled updates</strong>.",
"Elija una frecuencia predefinida o introduzca una expresión cron.",
"Seleccione qué se actualizará: solo paquetes del sistema, solo aplicaciones o sistema y aplicaciones.",
"Revise las opciones de copia de seguridad y reinicio.",
"Guarde la configuración."
],
"p2": "La tarjeta muestra si la programación está activa, qué elementos incluye y el resultado de la última ejecución. También puede conservar una programación desactivada para volver a habilitarla más adelante o eliminarla por completo.",
"p3": "Si ProxMenux detecta una programación externa creada por Community Scripts en el host, la muestra para que el usuario sepa que ya existe otra automatización.",
"callout": "Antes de programar actualizaciones de aplicaciones, pruebe manualmente cada helper o comando. Una tarea programada no puede responder a confirmaciones ni corregir un procedimiento incompleto."
},
"verify": {
"heading": "Comprobar el resultado",
"p1": "Después de aplicar paquetes del sistema, ProxMenux fuerza una nueva comprobación para actualizar el contador de paquetes pendientes sin esperar al siguiente ciclo periódico.",
"p2": "Para una aplicación, vuelva a la <link>pestaña App</link> y pulse <strong>Check</strong> si el número de versión no se actualiza inmediatamente. Esta comprobación ejecuta de nuevo el método configurado para la versión instalada y consulta la última versión publicada.",
"p3": "Compruebe además que los enlaces web de la aplicación responden correctamente. Que el comando termine sin errores no sustituye una verificación funcional del servicio."
},
"troubleshoot": {
"heading": "Problemas habituales",
"noButtonHeading": "Aparece Update available, pero no hay botón Apply update",
"noButtonBody": "La detección de versiones funciona, pero no se ha encontrado un método para instalar la actualización. Compruebe si la aplicación se actualiza mediante los paquetes del sistema, un helper compatible o un comando personalizado.",
"aptHeading": "La aplicación se actualiza mediante APT o APK",
"aptBody": "Use <strong>Apply OS update</strong>. No añada un segundo comando para la misma operación, porque la aplicación ya forma parte de la actualización del sistema.",
"noUpdaterHeading": "Se muestra No updater configured",
"noUpdaterBody": "ProxMenux conoce y supervisa la aplicación, pero no sabe cómo actualizarla. Consulte su documentación oficial, pruebe el procedimiento en la consola y, si corresponde, guárdelo mediante <strong>Add custom update command</strong>.",
"helperDetectedHeading": "El helper está detectado, pero no se puede utilizar",
"helperDetectedBody": "El helper puede estar marcado como no actualizable o no formar parte de los métodos reconocidos. Siga las instrucciones oficiales de la aplicación y no asuma que todos los LXC creados mediante Community Scripts admiten una actualización automática.",
"customFailsHeading": "El comando personalizado falla",
"customFailsBody": "Vuelva a ejecutarlo en la consola del LXC. Revise la ruta de trabajo, los permisos, las dependencias, los argumentos no interactivos y el código de salida. No sustituya el comando por una variante distinta hasta comprobar el procedimiento recomendado por el proyecto."
}
}
+2
View File
@@ -62,6 +62,8 @@
"dashboardStorage": "Karta Úložisko", "dashboardStorage": "Karta Úložisko",
"dashboardNetwork": "Karta Sieť", "dashboardNetwork": "Karta Sieť",
"dashboardVmsLxcs": "Karta VM a LXC", "dashboardVmsLxcs": "Karta VM a LXC",
"dashboardVmsLxcsApp": "App",
"dashboardVmsLxcsUpdates": "Updates",
"dashboardHardware": "Karta Hardvér", "dashboardHardware": "Karta Hardvér",
"dashboardSystemLogs": "Karta Systémové logy", "dashboardSystemLogs": "Karta Systémové logy",
"dashboardTerminal": "Karta Terminál", "dashboardTerminal": "Karta Terminál",
@@ -0,0 +1,272 @@
{
"meta": {
"title": "App — register and monitor LXC applications | ProxMenux",
"description": "Declare the apps running inside an LXC container from the ProxMenux Monitor and optionally track their versions."
},
"header": {
"title": "App — register and monitor LXC applications",
"description": "Declare the apps running inside a container, wire quick web links, and optionally track installed vs. upstream versions."
},
"intro": {
"p1": "The <strong>App</strong> tab records which applications run inside an LXC container. Each registered app can expose a display name, an icon, one or several web links and — optionally — its version status.",
"p2": "A single LXC can host several registered applications. A main service can share the container with an administration interface, an API or any other application reachable on a different port.",
"p3": "Registering an application does not modify it or update it. This tab is about identification and display. The mechanisms that <em>execute</em> an update are configured and used from the <link>Updates tab</link>."
},
"whatYouGet": {
"heading": "What you get by registering an app",
"lead": "Depending on the data configured, ProxMenux can surface:",
"items": [
"A one-click shortcut to the application's web UI.",
"Multiple links when the LXC exposes more than one service or port.",
"The version currently installed.",
"The latest version published by the project.",
"A notice when a newer version is available.",
"Notifications on new releases if enabled in Monitor settings."
],
"trailing": "Version tracking is optional. An app can be registered purely to keep its name, icon and web links handy.",
"callout": "The <strong>Update available</strong> label means ProxMenux found a difference between the installed and the published version. It does not automatically mean it also knows how to upgrade the app — that is a separate setup, done on the Updates tab."
},
"firstOpening": {
"heading": "First time opening the App tab",
"p1": "On first open, ProxMenux tries to recognise apps in the container using the information it has: the installer used when the LXC was created, detected services, and ports that are listening.",
"p2": "When matches are found, they show up as suggestions. Always review the proposal before saving — auto-detection speeds up registration, but it cannot guarantee that every detected service corresponds exactly to the app you intended."
},
"figures": {
"f01": {
"alt": "Empty App tab showing detected app suggestions",
"caption": "Empty state with one or more detected suggestions"
},
"f02": {
"alt": "Catalog search showing matches for the typed name",
"caption": "Catalog search and match selection"
},
"f03": {
"alt": "App registration form with name, icon and two web links",
"caption": "Basic form with name, icon and two web links"
},
"f04": {
"alt": "LXC with Docmost and Redis both registered as separate apps, each with its own version state",
"caption": "Two apps in the same LXC — a file-tracked app and a dpkg-tracked one, each with its own version state"
},
"f05": {
"alt": "Advanced tracking options showing the installed-version method and the upstream source",
"caption": "Advanced options with the installed-version method and the upstream source"
},
"f06": {
"alt": "Registered app card with the installed version, the latest upstream version and an Update available indicator",
"caption": "A wired card shows Installed, Latest upstream, an Update-available arrow when they differ, and the web link"
},
"f07": {
"alt": "Minimal registered app showing just its name and a single web link, no version tracking",
"caption": "Link-only record — just a name and a web link, without version tracking"
}
},
"registerSuggested": {
"heading": "Registering a suggested app",
"steps": [
"Open the LXC from the <strong>VMs & LXCs</strong> card.",
"Select the <strong>App</strong> tab.",
"Locate the suggested app.",
"Press <strong>Register</strong>.",
"Check the name, links and auto-filled data.",
"Save the app."
],
"trailing": "If a suggestion doesn't match anything you actually want to register, you can hide it. Hidden suggestions can be brought back from <strong>Register a different app</strong>."
},
"catalog": {
"heading": "Using the catalog",
"p1": "The catalog helps you find known applications and pre-fill some of their data. Typing into the name field shows the closest matches — picking one can autofill the name, icon, typical ports and, when a verified profile exists, the version-tracking options too.",
"p2": "The catalog is a helper, not a complete list of every piece of software an LXC might host. Some entries only carry basic information; others also include a ready-made way to read the installed version.",
"p3": "If the application isn't in the catalog, register it manually."
},
"manual": {
"heading": "Register an application manually",
"p1": "Use <strong>Register a different app</strong> when the LXC has no apps yet. If it already has at least one, use <strong>Add another application</strong>.",
"p2": "The basic configuration only needs a name. Everything else is added according to what you want to display.",
"nameHeading": "Name and icon",
"nameBody": "Give the app a name that makes it easy to recognise. The icon is optional and can be supplied as a URL.",
"linksHeading": "Web links and ports",
"linksLead": "Each link can carry:",
"linksItems": [
"Protocol <code>http</code> or <code>https</code>.",
"Port.",
"Description, such as <em>Web UI</em>, <em>Administration</em> or <em>API</em>.",
"An optional per-link icon."
],
"linksTrailing": "ProxMenux combines protocol and port with the LXC's IP address to build the URL. Add as many links as the app needs when a single container exposes several related services.",
"linksConfirm": "Before saving, confirm the port really corresponds to the service and that you can reach it from the browser."
},
"multiple": {
"heading": "Registering several apps in the same LXC",
"intro": "After saving the first app, press <strong>Add another application</strong> and repeat. Each record keeps its own links, detection method and version state independently.",
"usefulLead": "This is useful when:",
"usefulItems": [
"An LXC hosts several independent services.",
"An installation includes a main app plus companion tooling.",
"Each service has its own web interface or its own release cycle."
],
"dontGroup": "Don't group under a single record programs that publish and update independently. Registering them separately makes it clear which one has a new release and lets each one carry its own update method on the Updates tab."
},
"tracking": {
"heading": "Version tracking",
"intro": "Open the advanced options in the form to configure version tracking. Two different pieces of information are needed:",
"ingredients": [
"<strong>Installed version</strong> — how to read the version currently running inside the LXC.",
"<strong>Latest available version</strong> — where to read the version published by the project."
],
"trailing": "If only the installed version is configured, ProxMenux can show it, but cannot tell whether an update exists. For an <strong>Update available</strong> label to appear, both values have to be readable and comparable.",
"methodsHeading": "Methods to read the installed version",
"methodsLead": "Pick the method that matches how the app was installed:",
"methodsTable": {
"colMethod": "Method",
"colWhen": "When to use it",
"rows": [
{ "method": "None (link only)", "when": "You only need the name and web links." },
{ "method": "dpkg package", "when": "The application is installed as a Debian or Ubuntu package." },
{ "method": "apk package", "when": "The application is installed as an Alpine package." },
{ "method": "Binary", "when": "An executable returns its version through an argument like --version." },
{ "method": "File + regex", "when": "The version string is written inside a file." },
{ "method": "Python distribution", "when": "The application is installed as a Python package." },
{ "method": "Command", "when": "A specific command must be executed to obtain the version." },
{ "method": "Manual", "when": "The user enters the installed version by hand." }
]
},
"methodsTrailing": "Use the most direct and stable method. If the application comes from a system package, prefer querying that package over parsing the output of a generic command.",
"commandHeading": "The Command method does not update the app",
"commandP1": "In this form, <strong>Command</strong> serves exclusively to read the installed version. Its arguments are entered comma-separated and ProxMenux runs them directly, without a shell interpreter.",
"commandP2": "If your usual query is:",
"commandExample1": "myapp version --short",
"commandP3": "Form arguments would be:",
"commandExample2": "myapp, version, --short",
"commandP4": "Don't use operators like <code>&&</code>, redirections or pipes here. If you need a full procedure to upgrade the application, that is configured later on the Updates tab.",
"sourceHeading": "Source for the latest available version",
"sourceLead": "ProxMenux can query a public source of the project, for example:",
"sourceItems": [
"The releases or tags of a GitHub repository.",
"An HTTP endpoint that returns the version inside a JSON response."
],
"sourceTrailing": "Always use the app's official source. A fork or a third-party endpoint may announce versions that don't match the installation in the LXC.",
"regexHeading": "Version regular expressions",
"regexIntro": "A regular expression, or <strong>regex</strong>, isolates the version number inside a longer text. Most projects don't publish a ready-made regex — the user builds one from real output or a real release name.",
"regexOptional": "It is not always needed. Leave it empty first if the source already returns a clean value like <code>2.14.3</code>. Add one only when ProxMenux needs to separate the version from other words, symbols or numbers.",
"regexTwoHeading": "There are two different regex fields",
"regexTwoItems": [
"<strong>Installed version regex</strong> is applied to the output read inside the LXC.",
"<strong>Version regex</strong> or <strong>Tag regex</strong> is applied to the release / tag name published by the external source."
],
"regexTwoTrailing": "Both must produce comparable values. For instance, if the local app returns <code>MyApp v2.14.3</code> and GitHub publishes <code>release-2.14.3</code>, both expressions should extract <code>2.14.3</code>.",
"step1Heading": "1. Capture a real sample",
"step1P1": "Before writing the pattern, capture exactly the text ProxMenux will have to interpret.",
"step1P2": "For the installed version, run the same binary and arguments configured in the form from the LXC console. Depending on the method, you may also query the corresponding package or file.",
"step1P3": "For example:",
"step1Cmd": "myapp --version",
"step1P4": "Suppose the real output is:",
"step1Output": "MyApp version v2.14.3 (stable)",
"step1P5": "For the published version, check the exact release or tag name in the official repository. If you use a JSON endpoint, inspect the value the configured path returns.",
"step1P6": "Do not build the pattern against an invented example — a single space, prefix or extra number can change the result.",
"step2Heading": "2. Identify the part to keep",
"step2Lead": "In the example above we want to keep <code>2.14.3</code> and drop:",
"step2Items": [
"The text <code>MyApp version</code>.",
"The letter <code>v</code>.",
"The text <code>(stable)</code>."
],
"step2Recommended": "The recommended expression:",
"step2Regex": "version[ :=]+v?([0-9]+\\.[0-9]+\\.[0-9]+)",
"step2ReadLead": "Read piece by piece:",
"step2Breakdown": {
"colPart": "Fragment",
"colMeaning": "Meaning",
"rows": [
{ "part": "version", "meaning": "Anchors the search on that word to avoid matching an unrelated number." },
{ "part": "[ :=]+", "meaning": "Accepts one or more spaces, colons or equal signs." },
{ "part": "v?", "meaning": "The letter v may appear once or not at all." },
{ "part": "( and )", "meaning": "Mark the portion ProxMenux should keep." },
{ "part": "[0-9]+", "meaning": "Matches one or more digits." },
{ "part": "\\.", "meaning": "Matches a literal dot between the numbers." }
]
},
"step2DotNote": "The dot is written as <code>\\.</code> because, in a regex, a bare dot means \"any character\".",
"step3Heading": "3. Pick a pattern that fits the format",
"step3Lead": "These patterns cover the most common cases:",
"step3Examples": {
"colText": "Sample text",
"colRegex": "Recommended regex",
"colResult": "Result",
"rows": [
{ "text": "v2.14.3", "regex": "v?([0-9]+\\.[0-9]+\\.[0-9]+)", "result": "2.14.3" },
{ "text": "Version: 2.14", "regex": "Version[ :=]+v?([0-9]+(?:\\.[0-9]+){1,3})", "result": "2.14" },
{ "text": "release-2.14.3.1", "regex": "release-v?([0-9]+(?:\\.[0-9]+){1,3})", "result": "2.14.3.1" },
{ "text": "build 2026.08.10", "regex": "build[ :=]+([0-9]{4}\\.[0-9]{1,2}\\.[0-9]{1,2})", "result": "2026.08.10" },
{ "text": "{\"version\":\"2.14.3\"}", "regex": "\"version\"\\s*:\\s*\"v?([0-9]+(?:\\.[0-9]+){1,3})\"", "result": "2.14.3" }
]
},
"step3Note1": "<code>(?: ... )</code> groups a fragment of the pattern without producing an extra output value. This form is convenient to accept versions with two, three or four blocks without complicating the result.",
"step3Note2": "Enter the regex exactly as shown in the table: without surrounding quotes and without the <code>/.../</code> delimiters some online tools use.",
"step4Heading": "4. Prefer a single capture",
"step4Intro": "ProxMenux uses capture groups to decide which value to return:",
"step4Items": [
"With no capture groups it keeps the whole match.",
"With one capture, it keeps that capture's content.",
"With several captures, it joins them with dots."
],
"step4Trailing": "For predictable results, wrap the whole version in a single capture and use <code>(?: ... )</code> for helper groups.",
"step4RecLabel": "Recommended:",
"step4RecRegex": "v?([0-9]+(?:\\.[0-9]+){1,3})",
"step4LessLabel": "Less clear for beginners:",
"step4LessRegex": "v?([0-9]+)\\.([0-9]+)\\.([0-9]+)",
"step4Note": "Both can produce <code>2.14.3</code>, but the first is easier to maintain if the format changes.",
"step5Heading": "5. Avoid overly broad matches",
"step5Lead": "A pattern like this one is usually too open:",
"step5Regex": "([0-9.]+)",
"step5P1": "It can capture a year, a port, a dependency version or the first number that appears in the output. Anchor it with a nearby word such as <code>version</code>, <code>release</code> or <code>build</code> when the text carries several numbers.",
"step5P2": "Also confirm the upstream source isn't mixing stable releases with beta, nightly or development builds. The regex must select the same channel that is installed in the LXC.",
"step6Heading": "6. Save and verify the result",
"step6Lead": "After saving the app, press <strong>Check</strong> and read the two values ProxMenux reports:",
"step6Output": "Installed: 2.14.3\nLatest: 2.15.0",
"step6CorrectLead": "The regex is correct when:",
"step6CorrectItems": [
"Both fields contain only the expected version.",
"The application name and extra text are not captured.",
"The version is not confused with any other number.",
"Local and published values use the same format."
],
"step6ErrorNote": "If the match errors out, capture the real output again and compare it character by character. Pay particular attention to uppercase, spaces, hyphens, the letter <code>v</code> and the number of version blocks.",
"step6Callout": "If you can't build a reliable pattern, prefer to disable upstream tracking temporarily and keep the app as a link-only record. A wrong regex can raise false alerts or hide a real update."
},
"state": {
"heading": "Reading an app's state",
"lead": "A registered app can display any of the following states:",
"items": [
"<strong>Up to date</strong> — versions match.",
"<strong>Update available</strong> — the source publishes a newer version.",
"<strong>Checking</strong> — the check is in progress.",
"<strong>Version tracking pending</strong> — no check has completed yet.",
"<strong>Error</strong> — one of the versions could not be read or parsed."
],
"trailing": "Use <strong>Check</strong> to repeat the query manually after tweaking the configuration. If an error appears, review the installed-version method, the upstream source and the regex patterns first."
},
"manage": {
"heading": "Managing existing records",
"lead": "Enter management mode to:",
"items": [
"Re-check an app.",
"Edit its name, links or version tracking.",
"Delete a record that is no longer needed.",
"Add another app to the same LXC."
],
"trailing": "Deleting a record does not uninstall or stop the app. It only removes the information ProxMenux uses to display it and monitor its version."
},
"notDetected": {
"heading": "If the app is not detected",
"intro": "Automatic detection is not required to use this feature. If no suggestion appears:",
"steps": [
"Register the app manually.",
"Add its known links and ports.",
"Leave it as <strong>None (link only)</strong> if you only need a shortcut.",
"Configure version tracking only when a reliable source has been identified for both values.",
"Configure the update method later from <link>Updates</link>, if you want ProxMenux to run it."
],
"trailing": "Don't invent a package name, path or regex just to fill the form. A simple, correct record beats an automatic tracking based on unverified data."
}
}
@@ -0,0 +1,231 @@
{
"meta": {
"title": "Updates — updating an LXC's system and apps | ProxMenux",
"description": "Which mechanisms ProxMenux can use to update the operating system and the applications registered in an LXC container."
},
"header": {
"title": "Updates — updating an LXC's system and apps",
"description": "Where ProxMenux decides how to upgrade a container: OS packages, Community Scripts helper, or a custom command."
},
"intro": {
"p1": "The <strong>Updates</strong> tab gathers the mechanisms ProxMenux can run to upgrade the operating system and the applications registered inside an LXC container.",
"p2": "The <link>App tab</link> declares which applications exist and, optionally, compares their versions. <strong>Updates</strong> is about the action: it decides which mechanism is available, presents the matching button and runs the upgrade inside the container.",
"callout": "<strong>Core idea:</strong> detecting a new version and knowing how to install it are two different jobs. An app can show <strong>Update available</strong> on the App tab and still not have a working update button until a valid method is defined."
},
"mechanisms": {
"heading": "Available update mechanisms",
"intro": "Depending on how the app was installed and where its updates come from, ProxMenux picks from three mechanisms.",
"osHeading": "Operating system packages",
"osP1": "On Debian or Ubuntu containers, ProxMenux queries and updates packages through APT. On Alpine, it uses APK.",
"osP2": "Registered apps whose install method is <code>dpkg</code> or <code>apk</code> are part of this pass. They don't need a second command in the app section — they update as part of <strong>Apply OS update</strong>.",
"osP3": "The section shows the number of pending packages, how many are security updates, the OS family and the time of the last check.",
"helperHeading": "Proxmox VE Helper-Scripts updater",
"helperP1": "When the LXC was created with a helper from the <linkHelperHome>Proxmox VE Helper-Scripts</linkHelperHome> project, ProxMenux recognises its updater. The matching app must be registered on the App tab so the Monitor can associate the helper with the service shown to the user.",
"helperP2": "<strong>The update logic itself is maintained by the Proxmox VE Helper-Scripts project</strong>, not by ProxMenux. Each helper ships its own <code>update_script</code> function; ProxMenux fetches it and runs it inside the container in silent mode (<code>PHS_SILENT=1</code>), without prompts. There is no need to copy the helper or write a custom command on the ProxMenux side.",
"helperP3": "Full documentation for the update mechanism lives on the project site — <linkHelperDocs>community-scripts.org / update-apps</linkHelperDocs>. Each helper also has its own entry on the <linkHelperHome>project site</linkHelperHome> with a description of what the script does, its default configuration and the source of the update logic — use that page as the reference for what the updater will change inside the LXC.",
"helperP4": "Not every helper supports in-place updates. If the catalog marks an application as non-upgradable, the tab will surface that state and won't present this method as available.",
"customHeading": "Custom command",
"customP1": "A registered app can store its own update command. ProxMenux runs it inside the LXC when the user presses <strong>Apply update</strong> or when a scheduled task includes that app.",
"customP2": "This method is designed for apps whose installer provides no recognised helper and that don't update as part of APT or APK."
},
"decision": {
"heading": "How ProxMenux picks the action to show",
"table": {
"colSituation": "Situation",
"colAction": "Action",
"rows": [
{ "situation": "APT or APK packages pending", "action": "Apply OS update" },
{ "situation": "The app uses a dpkg or apk package", "action": "Apply OS update — no separate app command needed" },
{ "situation": "A compatible helper exists and the app is registered", "action": "Apply update via Community Scripts" },
{ "situation": "The registered app has a custom command", "action": "Apply update using that command" },
{ "situation": "A new version exists but no helper or command is configured", "action": "Shows No updater configured; offers to add a command" },
{ "situation": "System and app updates are both available", "action": "Combined Apply OS + Apps updates action may appear" }
]
},
"trailing": "An app registered only as a link is never shown as upgradable — ProxMenux doesn't have enough information to wire an update method to it."
},
"figures": {
"f01": {
"alt": "OS packages card showing pending updates count, security-updates count and the Apply OS update button",
"caption": "Pending OS packages: total count, security-updates count, and the Apply OS update button"
},
"f02": {
"alt": "Same OS packages card after applying updates — no packages pending, OS up to date badge",
"caption": "After applying: 'No OS updates pending' and the OS up to date badge"
},
"f03": {
"alt": "Registered app card showing 'No update method available' and an Add custom update command button",
"caption": "'No update method available' — ProxMenux tracks the app but has nothing wired to upgrade it yet"
},
"f04": {
"alt": "Custom update command editor with the example placeholder visible inside the textarea",
"caption": "The custom command editor with its placeholder example, Cancel and Save buttons"
},
"f05": {
"alt": "Terminal panel labelled 'Apply updates — CT 103' showing live apt output as packages are unpacked",
"caption": "Terminal panel streaming the update output live while apt unpacks packages inside the CT"
},
"f06": {
"alt": "Options card with snapshot before applying enabled, backup storage set to pbs, and restart after applying enabled",
"caption": "Options card with vzdump snapshot, backup storage and restart-after-applying enabled together"
},
"f07": {
"alt": "Scheduled updates section enabled — Frequency set to Daily at 3:00, cron expression 0 3 * * *, and What to update set to OS + application",
"caption": "Scheduled updates enabled — frequency preset, matching cron expression and target scope selected"
}
},
"custom": {
"heading": "Adding a custom update command",
"p1": "When an app has version tracking but no update method, the tab shows <strong>No updater configured</strong>. Press <strong>Add custom update command</strong> to open the editor.",
"p2": "The command must represent the real, complete procedure that upgrades that app. Don't just paste the command that reads its version."
},
"figureOut": {
"heading": "How to figure out the correct command",
"intro": "There is no universal update command. Before saving one, identify how the software was installed and what the project's recommended upgrade path is.",
"step1Heading": "1. Check whether the system already handles it",
"step1P1": "If the app was installed from Debian, Ubuntu or Alpine repositories it usually upgrades with system packages. In that case don't add a custom command — use <strong>Apply OS update</strong>.",
"step1P2": "You can check the package origin from the LXC console with the distro's tooling. For example:",
"step1Cmd1": "dpkg -l | grep -i name",
"step1P3": "or:",
"step1Cmd2": "apk info | grep -i name",
"step1P4": "Replace <code>name</code> with the package you are investigating. A match doesn't automatically confirm it's the main package — cross-check the name against the app's documentation.",
"step2Heading": "2. Consult the official documentation",
"step2P1": "Look in the official docs or repository for sections like <strong>Upgrade</strong>, <strong>Update</strong>, <strong>Maintenance</strong> or <strong>Manual installation</strong>. The procedure must match the method used to install the app in that LXC.",
"step2P2": "Don't use instructions targeting a different distribution, a different install type or a different version.",
"step3Heading": "3. Inspect the existing installation",
"step3Lead": "If you don't remember how the app was installed, look at:",
"step3Items": [
"The history or notes of the original installer.",
"The path where its files live.",
"The service definition that starts it.",
"Any maintenance scripts shipped by the app.",
"The documentation stored inside its install directory."
],
"step3P1": "For a systemd service, this can help locate the binary and its working directory:",
"step3Cmd": "systemctl show service-name -p ExecStart -p WorkingDirectory",
"step3P2": "This helps identify the installation, but it does not automatically translate the <code>ExecStart</code> line into an update command.",
"step4Heading": "4. Test the procedure in the LXC console",
"step4Lead": "Open a console into the container and run the procedure manually before saving it in ProxMenux. Verify that it:",
"step4Items": [
"Finishes without prompts or interactive menus.",
"Returns a correct exit code.",
"Restarts or reloads only the services that need it.",
"Leaves the app reachable afterwards.",
"Changes the installed version as expected."
],
"step4Note": "When feasible, take a container backup before testing.",
"step5Heading": "5. Save only the in-container command",
"step5P1": "Enter only what would be executed inside the LXC. Don't include:",
"step5Cmd1": "pct exec <vmid> --",
"step5P2": "ProxMenux already handles entering the container. The command runs as <code>root</code> via <code>sh -c</code>, so it accepts chained operations and directory changes.",
"step5P3": "If the updater must run from a specific path, include it explicitly:",
"step5Cmd2": "cd /opt/my-app && ./update.sh",
"step5P4": "If the project ships an updater at a different path, use the path and arguments named by the official documentation."
},
"requirements": {
"heading": "Requirements for a reliable command",
"lead": "Before running it from the Monitor, confirm the command:",
"items": [
"Runs without user interaction.",
"Uses absolute paths or changes into the correct directory first.",
"Stops, migrates and restarts services as required by the official instructions.",
"Exits with an error when the update fails.",
"Does not contain visible passwords, tokens or other secrets.",
"Does not download or execute scripts from untrusted sources."
],
"trailing": "The content is stored in the LXC's configuration and executed with administrator privileges. Treat it with the same care as any command run as <code>root</code>."
},
"difference": {
"heading": "Difference between the detection command and the update command",
"lead": "Both fields have different goals:",
"table": {
"colField": "Field",
"colLocation": "Location",
"colRole": "Role",
"rows": [
{
"field": "Command for the installed version",
"location": "App → advanced tracking",
"role": "Reads and returns the current version; executed as an argument list without a shell."
},
{
"field": "Custom update command",
"location": "Updates",
"role": "Runs the upgrade procedure; interpreted via sh -c."
}
]
},
"trailing": "Don't blindly copy the value of one into the other. A command like <code>myapp --version</code> may correctly detect the version but won't install a new one."
},
"apply": {
"heading": "Applying an update",
"lead": "Before pressing an apply button:",
"steps": [
"Confirm what will be updated: system, one app or both.",
"Check the backup and restart options.",
"Press the matching button.",
"Follow the process output in the terminal panel.",
"Verify the final result and that the service responds again."
],
"trailing1": "If the LXC is stopped, ProxMenux starts it to run the process. If the update finishes correctly and the restart option is enabled, the container is restarted at the end.",
"systemLead": "On a system update:",
"systemItems": [
"Debian and Ubuntu run the upgrade via APT.",
"Alpine runs it via APK."
],
"appLead": "On an app update:",
"appItems": [
"The compatible helper is used, when it exists.",
"The custom command stored for the app is executed, when configured.",
"If several apps are selected, their methods run in sequence."
],
"trailing2": "The terminal panel shows progress and ends with a successful result or the process's error code."
},
"backup": {
"heading": "Backup before updating",
"p1": "Enable <strong>Snapshot the container before applying</strong> to create a <code>vzdump</code> backup before touching the LXC. You can also choose the target storage.",
"p2": "If the backup is requested and it fails, ProxMenux won't continue with the update. This prevents changes from starting without the requested recovery point.",
"p3": "This option applies to both manual runs and scheduled runs."
},
"restart": {
"heading": "Restart after updating",
"p1": "<strong>Restart the container after applying</strong> is a preference, not a signal that the restart is mandatory. Enable it when the app's procedure or the installed packages require it.",
"p2": "The restart only happens after a successful run. If the update fails, the container stays up so the error can be inspected.",
"p3": "The backup and restart options are saved for that LXC and also apply to its scheduled tasks."
},
"scheduled": {
"heading": "Scheduled updates",
"p1": "The <strong>Scheduled updates</strong> section runs automatically the same flow the manual buttons use.",
"createLead": "To create a schedule:",
"createSteps": [
"Open <strong>Options</strong> and press <strong>Edit</strong>.",
"Enable <strong>Scheduled updates</strong>.",
"Choose a preset frequency or enter a cron expression.",
"Select what will be updated: system packages only, applications only, or system and applications.",
"Review the backup and restart options.",
"Save the configuration."
],
"p2": "The card shows whether the schedule is active, what it covers and the outcome of the last run. A disabled schedule can be kept for later re-activation, or removed entirely.",
"p3": "If ProxMenux detects an external schedule created by Community Scripts on the host, it surfaces it so the user knows another automation is already in place.",
"callout": "Before scheduling app updates, test every helper or command manually. A scheduled task can't answer prompts or fix an incomplete procedure."
},
"verify": {
"heading": "Checking the result",
"p1": "After applying system packages, ProxMenux forces a fresh check to update the pending-package counter without waiting for the next periodic cycle.",
"p2": "For an app, go back to the <link>App tab</link> and press <strong>Check</strong> if the version number doesn't refresh immediately. This runs the configured installed-version method again and queries the upstream source.",
"p3": "Confirm additionally that the app's web links respond correctly. A command finishing without errors is not a substitute for functional verification of the service."
},
"troubleshoot": {
"heading": "Common problems",
"noButtonHeading": "Update available appears, but there's no Apply update button",
"noButtonBody": "Version detection works, but no method to install the update was found. Check whether the app updates via system packages, a compatible helper or a custom command.",
"aptHeading": "The app updates through APT or APK",
"aptBody": "Use <strong>Apply OS update</strong>. Don't add a second command for the same operation — the app is already part of the system update.",
"noUpdaterHeading": "No updater configured is shown",
"noUpdaterBody": "ProxMenux tracks the app but doesn't know how to update it. Check its official documentation, test the procedure in the console and, if appropriate, save it via <strong>Add custom update command</strong>.",
"helperDetectedHeading": "The helper is detected but can't be used",
"helperDetectedBody": "The helper may be marked as non-upgradable or fall outside the recognised methods. Follow the app's official instructions and don't assume every LXC built with Community Scripts supports automatic updates.",
"customFailsHeading": "The custom command fails",
"customFailsBody": "Re-run it in the LXC console. Check the working path, permissions, dependencies, non-interactive arguments and exit code. Don't swap the command for a different variant until you've verified the recommended procedure with the project."
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB