Compare commits

..

1 Commits

Author SHA1 Message Date
CanbiZ
9ead3a2a32 Improve Node.js upgrade and service recovery in update.sh
Enhances the Node.js upgrade process by handling both .list and .sources files, updating the apt cache, and adding error handling for download and install failures. Introduces a function to re-enable and start the systemd service on failure to prevent user lockout, and ensures this is called during rollback and upgrade errors. Also refines Node.js version checks and build environment setup.
2026-01-13 16:52:22 +01:00
9 changed files with 463 additions and 526 deletions

View File

@@ -4,7 +4,7 @@
## 🔗 Related PR / Issue ## 🔗 Related PR / Issue
Fixes: # Link: #
## ✅ Prerequisites (**X** in brackets) ## ✅ Prerequisites (**X** in brackets)

View File

@@ -1 +1 @@
0.5.5 0.5.3

847
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,33 +25,33 @@
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@prisma/adapter-better-sqlite3": "^7.2.0", "@prisma/adapter-better-sqlite3": "^7.1.0",
"@prisma/client": "^7.2.0", "@prisma/client": "^7.1.0",
"@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@t3-oss/env-nextjs": "^0.13.10", "@t3-oss/env-nextjs": "^0.13.10",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"@tanstack/react-query": "^5.90.18", "@tanstack/react-query": "^5.90.12",
"@trpc/client": "^11.8.1", "@trpc/client": "^11.8.0",
"@trpc/react-query": "^11.8.1", "@trpc/react-query": "^11.8.1",
"@trpc/server": "^11.8.1", "@trpc/server": "^11.8.0",
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"@xterm/addon-fit": "^0.11.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.12.0", "@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0", "@xterm/xterm": "^6.0.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"bcryptjs": "^3.0.3", "bcryptjs": "^3.0.3",
"better-sqlite3": "^12.6.0", "better-sqlite3": "^12.5.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cron-validator": "^1.4.0", "cron-validator": "^1.4.0",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",
"next": "^16.1.3", "next": "^16.0.10",
"node-cron": "^4.2.1", "node-cron": "^4.2.1",
"node-pty": "^1.1.0", "node-pty": "^1.0.0",
"react": "^19.2.3", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
@@ -62,8 +62,8 @@
"strip-ansi": "^7.1.2", "strip-ansi": "^7.1.2",
"superjson": "^2.2.6", "superjson": "^2.2.6",
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"ws": "^8.19.0", "ws": "^8.18.3",
"zod": "^4.3.5" "zod": "^4.1.13"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4.1.18", "@tailwindcss/postcss": "^4.1.18",
@@ -73,26 +73,26 @@
"@types/bcryptjs": "^3.0.0", "@types/bcryptjs": "^3.0.0",
"@types/better-sqlite3": "^7.6.13", "@types/better-sqlite3": "^7.6.13",
"@types/jsonwebtoken": "^9.0.10", "@types/jsonwebtoken": "^9.0.10",
"@types/node": "^24.10.9", "@types/node": "^24.10.4",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"@types/react": "^19.2.8", "@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"@vitest/coverage-v8": "^4.0.17", "@vitest/coverage-v8": "^4.0.16",
"@vitest/ui": "^4.0.17", "@vitest/ui": "^4.0.14",
"baseline-browser-mapping": "^2.9.15", "baseline-browser-mapping": "^2.9.3",
"eslint": "^9.39.2", "eslint": "^9.39.1",
"eslint-config-next": "^16.1.3", "eslint-config-next": "^16.1.0",
"jsdom": "^27.4.0", "jsdom": "^27.3.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.8.0", "prettier": "^3.7.4",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
"prisma": "^7.2.0", "prisma": "^7.1.0",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.53.0", "typescript-eslint": "^8.48.1",
"vitest": "^4.0.17" "vitest": "^4.0.14"
}, },
"ct3aMetadata": { "ct3aMetadata": {
"initVersion": "7.39.3" "initVersion": "7.39.3"
@@ -104,4 +104,4 @@
"overrides": { "overrides": {
"prismjs": "^1.30.0" "prismjs": "^1.30.0"
} }
} }

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_DIR="$(dirname "$0")" SCRIPT_DIR="$(dirname "$0")"
source "$SCRIPT_DIR/../core/build.func" source "$SCRIPT_DIR/../core/build.func"
# Copyright (c) 2021-2026 tteck # Copyright (c) 2021-2025 tteck
# Author: tteck (tteckster) # Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.debian.org/ # Source: https://www.debian.org/
@@ -40,5 +40,5 @@ start
build_container build_container
description description
msg_ok "Completed successfully!\n" msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck # Copyright (c) 2021-2025 tteck
# Author: tteck (tteckster) # Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.debian.org/ # Source: https://www.debian.org/

View File

@@ -1610,7 +1610,6 @@ class ScriptExecutionHandler {
// TerminalHandler removed - not used by current application // TerminalHandler removed - not used by current application
app.prepare().then(() => { app.prepare().then(() => {
console.log('> Next.js app prepared successfully');
const httpServer = createServer(async (req, res) => { const httpServer = createServer(async (req, res) => {
try { try {
// Be sure to pass `true` as the second argument to `url.parse`. // Be sure to pass `true` as the second argument to `url.parse`.
@@ -1716,9 +1715,4 @@ app.prepare().then(() => {
autoSyncModule.setupGracefulShutdown(); autoSyncModule.setupGracefulShutdown();
} }
}); });
}).catch((err) => {
console.error('> Failed to start server:', err.message);
console.error('> If you see "Could not find a production build", run: npm run build');
console.error('> Full error:', err);
process.exit(1);
}); });

View File

@@ -71,7 +71,6 @@ export function ConfigurationModal({
} else { } else {
// Advanced mode: all vars with defaults // Advanced mode: all vars with defaults
const defaults: EnvVars = { const defaults: EnvVars = {
var_ctid: '', // Empty = use next available ID
// Resources from JSON // Resources from JSON
var_cpu: resources?.cpu ?? 1, var_cpu: resources?.cpu ?? 1,
var_ram: resources?.ram ?? 1024, var_ram: resources?.ram ?? 1024,
@@ -88,7 +87,6 @@ export function ConfigurationModal({
var_mtu: 1500, var_mtu: 1500,
var_mac: '', var_mac: '',
var_ns: '', var_ns: '',
var_searchdomain: '',
// Identity // Identity
var_hostname: slug, var_hostname: slug,
@@ -213,14 +211,6 @@ export function ConfigurationModal({
if (advancedVars.var_vlan && !validatePositiveInt(advancedVars.var_vlan as string | number | undefined)) { if (advancedVars.var_vlan && !validatePositiveInt(advancedVars.var_vlan as string | number | undefined)) {
newErrors.var_vlan = 'Must be a positive integer'; newErrors.var_vlan = 'Must be a positive integer';
} }
// Container ID (CTID): if set, must be integer >= 100
const ctidVal = advancedVars.var_ctid;
if (ctidVal !== '' && ctidVal !== undefined && typeof ctidVal !== 'boolean') {
const ctidNum = typeof ctidVal === 'string' ? parseInt(ctidVal, 10) : ctidVal;
if (isNaN(ctidNum) || ctidNum < 100) {
newErrors.var_ctid = 'Must be 100 or greater';
}
}
} }
setErrors(newErrors); setErrors(newErrors);
@@ -291,12 +281,7 @@ export function ConfigurationModal({
const cleaned: EnvVars = {}; const cleaned: EnvVars = {};
for (const [key, value] of Object.entries(envVars)) { for (const [key, value] of Object.entries(envVars)) {
if (value !== '' && value !== undefined) { if (value !== '' && value !== undefined) {
// Send var_ctid as number so the script receives a numeric ID cleaned[key] = value;
if (key === 'var_ctid') {
cleaned[key] = Number(value);
} else {
cleaned[key] = value;
}
} }
} }
@@ -389,35 +374,6 @@ export function ConfigurationModal({
) : ( ) : (
/* Advanced Mode */ /* Advanced Mode */
<div className="space-y-6"> <div className="space-y-6">
{/* Container ID (CTID) - at top so user can set a specific ID */}
<div>
<h3 className="text-lg font-medium text-foreground mb-4">Container ID (CTID)</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-foreground mb-2">
Container ID (CTID)
</label>
<Input
type="number"
min="100"
value={typeof advancedVars.var_ctid === 'boolean' ? '' : (advancedVars.var_ctid ?? '')}
onChange={(e) => {
const v = e.target.value;
updateAdvancedVar('var_ctid', v === '' ? '' : parseInt(v, 10) || '');
}}
placeholder="Auto (next available)"
className={errors.var_ctid ? 'border-destructive' : ''}
/>
{errors.var_ctid && (
<p className="mt-1 text-xs text-destructive">{errors.var_ctid}</p>
)}
<p className="mt-1 text-xs text-muted-foreground">
Leave empty to use the next available ID. Must be 100 or greater.
</p>
</div>
</div>
</div>
{/* Resources */} {/* Resources */}
<div> <div>
<h3 className="text-lg font-medium text-foreground mb-4">Resources</h3> <h3 className="text-lg font-medium text-foreground mb-4">Resources</h3>
@@ -657,17 +613,6 @@ export function ConfigurationModal({
<p className="mt-1 text-xs text-destructive">{errors.var_ns}</p> <p className="mt-1 text-xs text-destructive">{errors.var_ns}</p>
)} )}
</div> </div>
<div>
<label className="block text-sm font-medium text-foreground mb-2">
DNS Search Domain
</label>
<Input
type="text"
value={typeof advancedVars.var_searchdomain === 'boolean' ? '' : String(advancedVars.var_searchdomain ?? '')}
onChange={(e) => updateAdvancedVar('var_searchdomain', e.target.value)}
placeholder="e.g. local, home.lan"
/>
</div>
</div> </div>
</div> </div>

View File

@@ -238,27 +238,6 @@ export const versionRouter = createTRPCRouter({
// Clear/create the log file // Clear/create the log file
await writeFile(logPath, '', 'utf-8'); await writeFile(logPath, '', 'utf-8');
// Always fetch the latest update.sh from GitHub before running
// This ensures we always use the newest update script, avoiding
// the "chicken-and-egg" problem where old scripts can't update properly
const updateScriptUrl = 'https://raw.githubusercontent.com/community-scripts/ProxmoxVE-Local/main/update.sh';
try {
const response = await fetch(updateScriptUrl);
if (response.ok) {
const latestScript = await response.text();
await writeFile(updateScriptPath, latestScript, { mode: 0o755 });
// Log that we fetched the latest script
await writeFile(logPath, '[INFO] Fetched latest update.sh from GitHub\n', { flag: 'a' });
} else {
// If fetch fails, log warning but continue with local script
await writeFile(logPath, `[WARNING] Could not fetch latest update.sh (HTTP ${response.status}), using local version\n`, { flag: 'a' });
}
} catch (fetchError) {
// If fetch fails, log warning but continue with local script
const errorMsg = fetchError instanceof Error ? fetchError.message : 'Unknown error';
await writeFile(logPath, `[WARNING] Could not fetch latest update.sh: ${errorMsg}, using local version\n`, { flag: 'a' });
}
// Spawn the update script as a detached process using nohup // Spawn the update script as a detached process using nohup
// This allows it to run independently and kill the parent Node.js process // This allows it to run independently and kill the parent Node.js process
// Redirect output to log file // Redirect output to log file