Add web-based update system with detached process management (#65)
* feat: Add version checking and update functionality - Add version display component with GitHub release comparison - Implement update.sh script execution via API - Add hover tooltip with update instructions - Create shadcn/ui style Badge component - Add version router with getCurrentVersion, getLatestRelease, and executeUpdate endpoints - Update homepage header to show version and update status - Add Update Now button with loading states and result feedback - Support automatic page refresh after successful update * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Update update script * Workflow * Workflow * Workflow * Update update script * Update update script * Update update script * Update update script * Update update script * Update update.sh * Update update.sh * Update update.sh * Update update.sh
This commit is contained in:
committed by
GitHub
parent
0b1ce29b64
commit
24430ee77d
@@ -1,6 +1,7 @@
|
||||
import { scriptsRouter } from "~/server/api/routers/scripts";
|
||||
import { installedScriptsRouter } from "~/server/api/routers/installedScripts";
|
||||
import { serversRouter } from "~/server/api/routers/servers";
|
||||
import { versionRouter } from "~/server/api/routers/version";
|
||||
import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,7 @@ export const appRouter = createTRPCRouter({
|
||||
scripts: scriptsRouter,
|
||||
installedScripts: installedScriptsRouter,
|
||||
servers: serversRouter,
|
||||
version: versionRouter,
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
148
src/server/api/routers/version.ts
Normal file
148
src/server/api/routers/version.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||
import { readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { spawn } from "child_process";
|
||||
|
||||
interface GitHubRelease {
|
||||
tag_name: string;
|
||||
name: string;
|
||||
published_at: string;
|
||||
html_url: string;
|
||||
}
|
||||
|
||||
export const versionRouter = createTRPCRouter({
|
||||
// Get current local version
|
||||
getCurrentVersion: publicProcedure
|
||||
.query(async () => {
|
||||
try {
|
||||
const versionPath = join(process.cwd(), 'VERSION');
|
||||
const version = await readFile(versionPath, 'utf-8');
|
||||
return {
|
||||
success: true,
|
||||
version: version.trim()
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error reading VERSION file:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to read VERSION file',
|
||||
version: null
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
getLatestRelease: publicProcedure
|
||||
.query(async () => {
|
||||
try {
|
||||
const response = await fetch('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases/latest');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API error: ${response.status}`);
|
||||
}
|
||||
|
||||
const release: GitHubRelease = await response.json();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
release: {
|
||||
tagName: release.tag_name,
|
||||
name: release.name,
|
||||
publishedAt: release.published_at,
|
||||
htmlUrl: release.html_url
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching latest release:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to fetch latest release',
|
||||
release: null
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
getVersionStatus: publicProcedure
|
||||
.query(async () => {
|
||||
try {
|
||||
|
||||
const versionPath = join(process.cwd(), 'VERSION');
|
||||
const currentVersion = (await readFile(versionPath, 'utf-8')).trim();
|
||||
|
||||
|
||||
const response = await fetch('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases/latest');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API error: ${response.status}`);
|
||||
}
|
||||
|
||||
const release: GitHubRelease = await response.json();
|
||||
const latestVersion = release.tag_name.replace('v', '');
|
||||
|
||||
|
||||
const isUpToDate = currentVersion === latestVersion;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
currentVersion,
|
||||
latestVersion,
|
||||
isUpToDate,
|
||||
updateAvailable: !isUpToDate,
|
||||
releaseInfo: {
|
||||
tagName: release.tag_name,
|
||||
name: release.name,
|
||||
publishedAt: release.published_at,
|
||||
htmlUrl: release.html_url
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error checking version status:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to check version status',
|
||||
currentVersion: null,
|
||||
latestVersion: null,
|
||||
isUpToDate: false,
|
||||
updateAvailable: false,
|
||||
releaseInfo: null
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
// Execute update script
|
||||
executeUpdate: publicProcedure
|
||||
.mutation(async () => {
|
||||
try {
|
||||
const updateScriptPath = join(process.cwd(), 'update.sh');
|
||||
|
||||
// Spawn the update script as a detached process using nohup
|
||||
// This allows it to run independently and kill the parent Node.js process
|
||||
const child = spawn('nohup', ['bash', updateScriptPath], {
|
||||
cwd: process.cwd(),
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
shell: false,
|
||||
detached: true
|
||||
});
|
||||
|
||||
// Unref the child process so it doesn't keep the parent alive
|
||||
child.unref();
|
||||
|
||||
// Immediately return success since we can't wait for completion
|
||||
// The script will handle its own logging and restart
|
||||
return {
|
||||
success: true,
|
||||
message: 'Update started in background. The server will restart automatically when complete.',
|
||||
output: '',
|
||||
error: ''
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing update script:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed to execute update script: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
output: '',
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
Reference in New Issue
Block a user