|
1 | | -<script> |
| 1 | +<script lang="ts"> |
2 | 2 | import { onMount } from 'svelte'; |
3 | 3 | import { page } from '$app/stores'; |
4 | 4 | import LogsPanel from './LogsPanel.svelte'; |
|
14 | 14 | let creatingBackup = false; |
15 | 15 | let restoringBackup = null; |
16 | 16 | |
| 17 | + // Version management |
| 18 | + let showVersionModal = false; |
| 19 | + let availableVersions: string[] = []; |
| 20 | + let installedVersions: string[] = []; |
| 21 | + let latestVersion = ''; |
| 22 | + let selectedUpgradeVersion = ''; |
| 23 | + let upgrading = false; |
| 24 | + let loadingVersions = false; |
| 25 | + |
17 | 26 | $: instanceName = $page.params.name; |
18 | 27 | const API_BASE = '/api'; |
19 | 28 |
|
|
84 | 93 | } |
85 | 94 | } |
86 | 95 |
|
| 96 | + async function fetchVersions() { |
| 97 | + loadingVersions = true; |
| 98 | + try { |
| 99 | + const latestRes = await fetch(`${API_BASE}/versions/latest`); |
| 100 | + if (latestRes.ok) { |
| 101 | + const data = await latestRes.json(); |
| 102 | + latestVersion = data.version || ''; |
| 103 | + } |
| 104 | +
|
| 105 | + const installedRes = await fetch(`${API_BASE}/versions/installed`); |
| 106 | + if (installedRes.ok) { |
| 107 | + const data = await installedRes.json(); |
| 108 | + installedVersions = data.versions || []; |
| 109 | + } |
| 110 | +
|
| 111 | + const availableRes = await fetch(`${API_BASE}/versions/available`); |
| 112 | + if (availableRes.ok) { |
| 113 | + const data = await availableRes.json(); |
| 114 | + availableVersions = data.versions || []; |
| 115 | + } |
| 116 | +
|
| 117 | + // Set default selected version to current or latest |
| 118 | + selectedUpgradeVersion = instance?.version || latestVersion; |
| 119 | + } catch (e) { |
| 120 | + console.error('Failed to fetch versions:', e); |
| 121 | + } finally { |
| 122 | + loadingVersions = false; |
| 123 | + } |
| 124 | + } |
| 125 | +
|
| 126 | + async function upgradeInstance() { |
| 127 | + if (!selectedUpgradeVersion) return; |
| 128 | + if (selectedUpgradeVersion === instance?.version) { |
| 129 | + showVersionModal = false; |
| 130 | + return; |
| 131 | + } |
| 132 | +
|
| 133 | + if (!confirm(`Upgrade instance to v${selectedUpgradeVersion}? The instance will be stopped, upgraded, and restarted.`)) return; |
| 134 | +
|
| 135 | + upgrading = true; |
| 136 | + try { |
| 137 | + const res = await fetch(`${API_BASE}/instances/${instanceName}/upgrade`, { |
| 138 | + method: 'POST', |
| 139 | + headers: { 'Content-Type': 'application/json' }, |
| 140 | + body: JSON.stringify({ version: selectedUpgradeVersion }) |
| 141 | + }); |
| 142 | + const result = await res.json(); |
| 143 | + if (!res.ok) throw new Error(result.error || 'Failed to upgrade'); |
| 144 | + await fetchInstance(); |
| 145 | + showVersionModal = false; |
| 146 | + } catch (e) { |
| 147 | + error = e.message; |
| 148 | + } finally { |
| 149 | + upgrading = false; |
| 150 | + } |
| 151 | + } |
| 152 | +
|
| 153 | + function openVersionModal() { |
| 154 | + selectedUpgradeVersion = instance?.version || latestVersion; |
| 155 | + fetchVersions(); |
| 156 | + showVersionModal = true; |
| 157 | + } |
| 158 | +
|
87 | 159 | onMount(() => { |
88 | 160 | fetchInstance(); |
89 | 161 | }); |
|
232 | 304 | <dt class="text-gray-500">Port</dt> |
233 | 305 | <dd class="text-white font-mono">{instance.port}</dd> |
234 | 306 | </div> |
| 307 | + <div class="flex justify-between py-2 border-b border-gray-800/50"> |
| 308 | + <dt class="text-gray-500">Version</dt> |
| 309 | + <dd class="flex items-center gap-2"> |
| 310 | + <span class="text-white font-mono">{instance.version || 'unknown'}</span> |
| 311 | + <button on:click={openVersionModal} class="text-xs px-2 py-1 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 rounded transition-all"> |
| 312 | + Change |
| 313 | + </button> |
| 314 | + </dd> |
| 315 | + </div> |
235 | 316 | <div class="flex justify-between py-2 border-b border-gray-800/50"> |
236 | 317 | <dt class="text-gray-500">Created</dt> |
237 | 318 | <dd class="text-white">{formatDate(instance.created)}</dd> |
|
308 | 389 | </div> |
309 | 390 | </div> |
310 | 391 |
|
| 392 | +<!-- Version Upgrade Modal --> |
| 393 | +{#if showVersionModal} |
| 394 | + <!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions --> |
| 395 | + <div class="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center justify-center z-50 p-4" on:click|self={() => showVersionModal = false}> |
| 396 | + <div class="bg-[#111] rounded-2xl p-6 w-full max-w-md border border-gray-800"> |
| 397 | + <h2 class="text-xl font-bold text-white mb-5">Change PocketBase Version</h2> |
| 398 | + |
| 399 | + <div class="mb-4"> |
| 400 | + <p class="text-sm text-gray-400 mb-2">Current version: <span class="text-white font-mono">{instance?.version || 'unknown'}</span></p> |
| 401 | + {#if latestVersion} |
| 402 | + <p class="text-xs text-gray-500">Latest version: <span class="text-emerald-400 font-mono">{latestVersion}</span></p> |
| 403 | + {/if} |
| 404 | + </div> |
| 405 | + |
| 406 | + <div class="mb-6"> |
| 407 | + <label for="upgrade-version" class="block text-xs font-medium text-gray-500 uppercase mb-2">Select Version</label> |
| 408 | + {#if loadingVersions} |
| 409 | + <div class="w-full bg-[#0a0a0a] border border-gray-800 rounded-lg px-4 py-2.5 text-gray-500 text-sm"> |
| 410 | + Loading versions... |
| 411 | + </div> |
| 412 | + {:else} |
| 413 | + <select |
| 414 | + id="upgrade-version" |
| 415 | + bind:value={selectedUpgradeVersion} |
| 416 | + class="w-full bg-[#0a0a0a] border border-gray-800 rounded-lg px-4 py-2.5 focus:outline-none focus:border-emerald-500/50 transition-all text-white text-sm" |
| 417 | + > |
| 418 | + {#if latestVersion} |
| 419 | + <option value={latestVersion}>{latestVersion} (latest)</option> |
| 420 | + {/if} |
| 421 | + {#each installedVersions.filter(v => v !== latestVersion) as version} |
| 422 | + <option value={version}>{version}</option> |
| 423 | + {/each} |
| 424 | + {#each availableVersions.slice(0, 15).filter(v => !installedVersions.includes(v) && v !== latestVersion) as version} |
| 425 | + <option value={version}>{version} (download)</option> |
| 426 | + {/each} |
| 427 | + </select> |
| 428 | + <p class="text-xs text-gray-600 mt-1">Version will be downloaded if not installed</p> |
| 429 | + {/if} |
| 430 | + </div> |
| 431 | + |
| 432 | + <div class="flex gap-3"> |
| 433 | + <button on:click={() => showVersionModal = false} class="flex-1 px-4 py-2.5 border border-gray-700 rounded-lg font-medium text-gray-400 hover:bg-gray-800/50 transition-all text-sm"> |
| 434 | + Cancel |
| 435 | + </button> |
| 436 | + <button on:click={upgradeInstance} disabled={upgrading || !selectedUpgradeVersion || selectedUpgradeVersion === instance?.version} class="flex-1 px-4 py-2.5 bg-emerald-500 hover:bg-emerald-600 text-black rounded-lg font-semibold transition-all text-sm disabled:opacity-50 disabled:cursor-not-allowed"> |
| 437 | + {upgrading ? 'Upgrading...' : 'Upgrade'} |
| 438 | + </button> |
| 439 | + </div> |
| 440 | + </div> |
| 441 | + </div> |
| 442 | +{/if} |
| 443 | + |
311 | 444 | <style> |
312 | 445 | :global(body) { |
313 | 446 | background-color: #0a0a0a; |
|
0 commit comments