mirror of
https://github.com/community-scripts/ProxmoxVE-Local.git
synced 2026-06-06 07:02:27 -04:00
feat: add prerelease update channel toggle + bump version to 1.0.0-pre2
- Add ALLOW_PRERELEASE env var to env.js - Add /api/settings/prerelease GET/POST route (persists to .env) - version.ts: getVersionStatus + getLatestRelease use /releases (all) when ALLOW_PRERELEASE=true, otherwise /releases/latest (stable only) - GitHubRelease interface: add prerelease boolean field - GeneralSettingsModal: add Update Channel toggle in General tab - VERSION: bump to 1.0.0-pre2 - page.tsx: destructure isAuthenticated from useAuth() - GeneratorTab.tsx: move container-reset useEffect after selectedSlug decl
This commit is contained in:
@@ -94,6 +94,9 @@ export function GeneralSettingsModal({
|
||||
const [aptProxyEnabled, setAptProxyEnabled] = useState(false);
|
||||
const [aptProxyIp, setAptProxyIp] = useState("");
|
||||
|
||||
// Prerelease channel state
|
||||
const [allowPrerelease, setAllowPrerelease] = useState(false);
|
||||
|
||||
// Repository management state
|
||||
const [newRepoUrl, setNewRepoUrl] = useState("");
|
||||
const [newRepoEnabled, setNewRepoEnabled] = useState(true);
|
||||
@@ -119,9 +122,38 @@ export function GeneralSettingsModal({
|
||||
void loadColorCodingSetting();
|
||||
void loadAutoSyncSettings();
|
||||
void loadAptProxySettings();
|
||||
void loadPrereleaseSettings();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const loadPrereleaseSettings = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/settings/prerelease");
|
||||
if (response.ok) {
|
||||
const data = (await response.json()) as { enabled: boolean };
|
||||
setAllowPrerelease(data.enabled ?? false);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
const savePrereleaseSettings = async (enabled: boolean) => {
|
||||
try {
|
||||
await fetch("/api/settings/prerelease", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ enabled }),
|
||||
});
|
||||
setAllowPrerelease(enabled);
|
||||
setMessage({ type: "success", text: "Update channel saved" });
|
||||
setTimeout(() => setMessage(null), 3000);
|
||||
} catch {
|
||||
setMessage({ type: "error", text: "Failed to save update channel" });
|
||||
setTimeout(() => setMessage(null), 3000);
|
||||
}
|
||||
};
|
||||
|
||||
const loadAptProxySettings = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/settings/apt-proxy");
|
||||
@@ -902,6 +934,24 @@ export function GeneralSettingsModal({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="border-border rounded-lg border p-4">
|
||||
<h4 className="text-foreground mb-2 font-medium">
|
||||
Update Channel
|
||||
</h4>
|
||||
<p className="text-muted-foreground mb-4 text-sm">
|
||||
When enabled, the updater will also consider pre-release
|
||||
versions (e.g. <code>v1.0.0-pre3</code>). Useful for
|
||||
testing new features early.
|
||||
</p>
|
||||
<Toggle
|
||||
checked={allowPrerelease}
|
||||
onCheckedChange={(checked) =>
|
||||
void savePrereleaseSettings(checked)
|
||||
}
|
||||
label="Include pre-releases when checking for updates"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
57
src/app/api/settings/prerelease/route.ts
Normal file
57
src/app/api/settings/prerelease/route.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { enabled } = await request.json();
|
||||
|
||||
if (typeof enabled !== 'boolean') {
|
||||
return NextResponse.json(
|
||||
{ error: 'Enabled value must be a boolean' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const envPath = path.join(process.cwd(), '.env');
|
||||
|
||||
let envContent = '';
|
||||
if (fs.existsSync(envPath)) {
|
||||
envContent = fs.readFileSync(envPath, 'utf8');
|
||||
}
|
||||
|
||||
const regex = /^ALLOW_PRERELEASE=.*$/m;
|
||||
if (regex.test(envContent)) {
|
||||
envContent = envContent.replace(regex, `ALLOW_PRERELEASE=${enabled}`);
|
||||
} else {
|
||||
envContent += (envContent.endsWith('\n') ? '' : '\n') + `ALLOW_PRERELEASE=${enabled}\n`;
|
||||
}
|
||||
|
||||
fs.writeFileSync(envPath, envContent);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error saving prerelease setting:', error);
|
||||
return NextResponse.json({ error: 'Failed to save prerelease setting' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const envPath = path.join(process.cwd(), '.env');
|
||||
|
||||
if (!fs.existsSync(envPath)) {
|
||||
return NextResponse.json({ enabled: false });
|
||||
}
|
||||
|
||||
const envContent = fs.readFileSync(envPath, 'utf8');
|
||||
const match = /^ALLOW_PRERELEASE=(.*)$/m.exec(envContent);
|
||||
const enabled = match ? match[1]?.trim() === 'true' : false;
|
||||
|
||||
return NextResponse.json({ enabled });
|
||||
} catch (error) {
|
||||
console.error('Error reading prerelease setting:', error);
|
||||
return NextResponse.json({ enabled: false });
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@ function TabSkeleton() {
|
||||
}
|
||||
|
||||
function Home() {
|
||||
const { isAuthenticated } = useAuth();
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
"scripts" | "downloaded" | "installed" | "backups" | "generator"
|
||||
>(() => {
|
||||
|
||||
@@ -37,6 +37,8 @@ export const env = createEnv({
|
||||
JWT_SECRET: z.string().optional(),
|
||||
// Server Color Coding Configuration
|
||||
SERVER_COLOR_CODING_ENABLED: z.string().optional(),
|
||||
// Update Channel
|
||||
ALLOW_PRERELEASE: z.string().optional(),
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -79,6 +81,8 @@ export const env = createEnv({
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
// Server Color Coding Configuration
|
||||
SERVER_COLOR_CODING_ENABLED: process.env.SERVER_COLOR_CODING_ENABLED,
|
||||
// Update Channel
|
||||
ALLOW_PRERELEASE: process.env.ALLOW_PRERELEASE,
|
||||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ interface GitHubRelease {
|
||||
published_at: string;
|
||||
html_url: string;
|
||||
body: string;
|
||||
prerelease: boolean;
|
||||
}
|
||||
|
||||
// Helper function to fetch from GitHub API with optional authentication
|
||||
@@ -53,14 +54,22 @@ export const versionRouter = createTRPCRouter({
|
||||
getLatestRelease: publicProcedure
|
||||
.query(async () => {
|
||||
try {
|
||||
const response = await fetchGitHubAPI('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases/latest');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API error: ${response.status}`);
|
||||
const allowPrerelease = env.ALLOW_PRERELEASE === 'true';
|
||||
let release: GitHubRelease;
|
||||
|
||||
if (allowPrerelease) {
|
||||
const response = await fetchGitHubAPI('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases');
|
||||
if (!response.ok) throw new Error(`GitHub API error: ${response.status}`);
|
||||
const releases: GitHubRelease[] = await response.json();
|
||||
const sorted = releases.sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
|
||||
if (!sorted[0]) throw new Error('No releases found');
|
||||
release = sorted[0];
|
||||
} else {
|
||||
const response = await fetchGitHubAPI('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases/latest');
|
||||
if (!response.ok) throw new Error(`GitHub API error: ${response.status}`);
|
||||
release = await response.json();
|
||||
}
|
||||
|
||||
const release: GitHubRelease = await response.json();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
release: {
|
||||
@@ -84,18 +93,24 @@ export const versionRouter = createTRPCRouter({
|
||||
getVersionStatus: publicProcedure
|
||||
.query(async () => {
|
||||
try {
|
||||
|
||||
const versionPath = join(process.cwd(), 'VERSION');
|
||||
const currentVersion = (await readFile(versionPath, 'utf-8')).trim();
|
||||
|
||||
|
||||
const response = await fetchGitHubAPI('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases/latest');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API error: ${response.status}`);
|
||||
const allowPrerelease = env.ALLOW_PRERELEASE === 'true';
|
||||
let release: GitHubRelease;
|
||||
|
||||
if (allowPrerelease) {
|
||||
const response = await fetchGitHubAPI('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases');
|
||||
if (!response.ok) throw new Error(`GitHub API error: ${response.status}`);
|
||||
const releases: GitHubRelease[] = await response.json();
|
||||
const sorted = releases.sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
|
||||
if (!sorted[0]) throw new Error('No releases found');
|
||||
release = sorted[0];
|
||||
} else {
|
||||
const response = await fetchGitHubAPI('https://api.github.com/repos/community-scripts/ProxmoxVE-Local/releases/latest');
|
||||
if (!response.ok) throw new Error(`GitHub API error: ${response.status}`);
|
||||
release = await response.json();
|
||||
}
|
||||
|
||||
const release: GitHubRelease = await response.json();
|
||||
const latestVersion = release.tag_name.replace('v', '');
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user