* 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
149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
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'
|
|
};
|
|
}
|
|
})
|
|
});
|