Skip to content

Commit 7c08b18

Browse files
committed
Use FastCP logo on default PHP sites
1 parent d263d8c commit 7c08b18

File tree

2 files changed

+116
-13
lines changed

2 files changed

+116
-13
lines changed

cmd/fastcp/ui/dist/index.html

Lines changed: 113 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,7 @@ <h3 class="text-lg font-semibold text-gray-800">Backups</h3>
975975
<span class="text-xs text-gray-500" x-text="snapshotContentsSummary(snap)"></span>
976976
</div>
977977
<div class="flex items-center gap-1.5 shrink-0" @click.stop>
978-
<button @click="if(confirm('Download this snapshot as a ZIP file? This may take a while for large backups.')) downloadSelectedBackupSnapshot(snap.id)"
978+
<button @click="confirmDownloadSnapshot(snap.id)"
979979
:disabled="backupSnapshotDownloading"
980980
class="p-1.5 text-gray-400 hover:text-gray-700 hover:bg-gray-100 rounded-md disabled:opacity-40"
981981
title="Download ZIP">
@@ -1023,7 +1023,7 @@ <h4 class="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-2">
10231023
<div class="flex items-center justify-between gap-2 bg-white border border-gray-200 rounded-lg px-3 py-2">
10241024
<span class="text-sm text-gray-800 truncate" x-text="domain"></span>
10251025
<template x-if="snapshotRestoreSiteByDomain(snap, domain)">
1026-
<button @click="if(confirm('Restore ' + domain + ' from this snapshot? This will overwrite the current site files.')) restoreSiteFromSnapshot(snapshotRestoreSiteByDomain(snap, domain).id, snap.id)"
1026+
<button @click="confirmRestoreSiteSnapshot(snap, domain)"
10271027
:disabled="restoreLoading"
10281028
class="shrink-0 px-2.5 py-1 text-xs font-medium text-primary-600 bg-primary-50 rounded-md hover:bg-primary-100 disabled:opacity-50">
10291029
Restore
@@ -1049,7 +1049,7 @@ <h4 class="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-2">
10491049
<div class="flex items-center justify-between gap-2 bg-white border border-gray-200 rounded-lg px-3 py-2">
10501050
<span class="text-sm text-gray-800 font-mono truncate" x-text="dbName"></span>
10511051
<template x-if="snapshotRestoreDBByName(snap, dbName)">
1052-
<button @click="if(confirm('Restore database \'' + dbName + '\' from this snapshot? This will overwrite the current database.')) restoreDatabaseFromSnapshot(snapshotRestoreDBByName(snap, dbName).id, snap.id)"
1052+
<button @click="confirmRestoreDatabaseSnapshot(snap, dbName)"
10531053
:disabled="restoreLoading"
10541054
class="shrink-0 px-2.5 py-1 text-xs font-medium text-emerald-600 bg-emerald-50 rounded-md hover:bg-emerald-100 disabled:opacity-50">
10551055
Restore
@@ -2591,6 +2591,26 @@ <h3 class="text-lg font-semibold text-gray-900" x-text="confirmModalTitle"></h3>
25912591
</div>
25922592
</div>
25932593

2594+
<!-- Notice Modal -->
2595+
<div x-show="showNoticeModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50" x-transition @keydown.escape.window="closeNotice()">
2596+
<div class="bg-white rounded-2xl shadow-xl p-6 w-full max-w-md mx-4" @click.away="closeNotice()">
2597+
<div class="flex items-start">
2598+
<div class="w-11 h-11 rounded-full bg-blue-100 flex items-center justify-center mr-3 flex-shrink-0">
2599+
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2600+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10z"></path>
2601+
</svg>
2602+
</div>
2603+
<div>
2604+
<h3 class="text-lg font-semibold text-gray-900" x-text="noticeModalTitle"></h3>
2605+
<p class="text-sm text-gray-600 mt-1 whitespace-pre-line" x-text="noticeModalMessage"></p>
2606+
</div>
2607+
</div>
2608+
<div class="mt-6">
2609+
<button @click="closeNotice()" class="w-full px-4 py-3 bg-primary-600 text-white rounded-lg hover:bg-primary-700 font-medium" x-text="noticeModalButtonText"></button>
2610+
</div>
2611+
</div>
2612+
</div>
2613+
25942614
<!-- Impersonate User Modal -->
25952615
<div x-show="showImpersonateUserModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50" x-transition>
25962616
<div class="bg-white rounded-2xl shadow-xl p-6 w-full max-w-md mx-4" @click.away="if(!impersonationStarting) { showImpersonateUserModal = false; impersonationError = ''; }">
@@ -2675,6 +2695,11 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
26752695
confirmModalMessage: '',
26762696
confirmModalConfirmText: 'Delete',
26772697
confirmModalResolver: null,
2698+
showNoticeModal: false,
2699+
noticeModalTitle: '',
2700+
noticeModalMessage: '',
2701+
noticeModalButtonText: 'OK',
2702+
noticeModalResolver: null,
26782703
sites: [],
26792704
sitesTotal: 0,
26802705
sitesPage: 1,
@@ -3220,7 +3245,12 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
32203245
return;
32213246
}
32223247
const targetSnapshotID = snapshotID;
3223-
if (!confirm('Delete this snapshot? This action cannot be undone.')) {
3248+
const confirmed = await this.askConfirmation({
3249+
title: 'Delete Snapshot',
3250+
message: 'Delete this snapshot? This action cannot be undone.',
3251+
confirmText: 'Delete Snapshot'
3252+
});
3253+
if (!confirmed) {
32243254
return;
32253255
}
32263256
this.backupSnapshotDeleting = true;
@@ -3236,6 +3266,37 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
32363266
this.backupSnapshotDeleting = false;
32373267
}
32383268
},
3269+
async confirmDownloadSnapshot(snapshotID = '') {
3270+
const confirmed = await this.askConfirmation({
3271+
title: 'Download Snapshot ZIP',
3272+
message: 'Download this snapshot as a ZIP file? This may take a while for large backups.',
3273+
confirmText: 'Download ZIP'
3274+
});
3275+
if (!confirmed) return;
3276+
await this.downloadSelectedBackupSnapshot(snapshotID);
3277+
},
3278+
async confirmRestoreSiteSnapshot(snapshot, domain) {
3279+
const target = this.snapshotRestoreSiteByDomain(snapshot, domain);
3280+
if (!target) return;
3281+
const confirmed = await this.askConfirmation({
3282+
title: 'Restore Website',
3283+
message: `Restore ${domain} from this snapshot? This will overwrite the current site files.`,
3284+
confirmText: 'Restore Website'
3285+
});
3286+
if (!confirmed) return;
3287+
await this.restoreSiteFromSnapshot(target.id, snapshot.id);
3288+
},
3289+
async confirmRestoreDatabaseSnapshot(snapshot, dbName) {
3290+
const target = this.snapshotRestoreDBByName(snapshot, dbName);
3291+
if (!target) return;
3292+
const confirmed = await this.askConfirmation({
3293+
title: 'Restore Database',
3294+
message: `Restore database "${dbName}" from this snapshot? This will overwrite the current database.`,
3295+
confirmText: 'Restore Database'
3296+
});
3297+
if (!confirmed) return;
3298+
await this.restoreDatabaseFromSnapshot(target.id, snapshot.id);
3299+
},
32393300
snapshotTagValuesFor(snapshot, prefix) {
32403301
if (!snapshot || !Array.isArray(snapshot.tags)) return [];
32413302
const values = [];
@@ -3921,6 +3982,22 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
39213982
this.confirmModalResolver = null;
39223983
}
39233984
},
3985+
showNotice({ title, message, buttonText = 'OK' }) {
3986+
return new Promise((resolve) => {
3987+
this.noticeModalTitle = title || 'Notice';
3988+
this.noticeModalMessage = message || '';
3989+
this.noticeModalButtonText = buttonText;
3990+
this.noticeModalResolver = resolve;
3991+
this.showNoticeModal = true;
3992+
});
3993+
},
3994+
closeNotice() {
3995+
this.showNoticeModal = false;
3996+
if (this.noticeModalResolver) {
3997+
this.noticeModalResolver(true);
3998+
this.noticeModalResolver = null;
3999+
}
4000+
},
39244001

39254002
async deleteSite(site) {
39264003
const confirmed = await this.askConfirmation({
@@ -3934,7 +4011,10 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
39344011
await this.api(`/sites/${site.id}`, 'DELETE');
39354012
await this.loadSites();
39364013
} catch (e) {
3937-
alert('Failed to delete site: ' + (e.message || 'Unknown error'));
4014+
await this.showNotice({
4015+
title: 'Delete Site Failed',
4016+
message: e.message || 'Unknown error'
4017+
});
39384018
} finally {
39394019
this.deletingSiteId = null;
39404020
}
@@ -4170,7 +4250,10 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
41704250
await this.api(`/databases/${db.id}`, 'DELETE');
41714251
await this.loadDatabases();
41724252
} catch (e) {
4173-
alert('Failed to delete database: ' + (e.message || 'Unknown error'));
4253+
await this.showNotice({
4254+
title: 'Delete Database Failed',
4255+
message: e.message || 'Unknown error'
4256+
});
41744257
} finally {
41754258
this.deletingDatabaseId = null;
41764259
}
@@ -4193,7 +4276,10 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
41934276
window.open(resp.url, '_blank');
41944277
}
41954278
} catch(e) {
4196-
alert('Could not open phpMyAdmin: ' + e.message + '\n\nNote: Databases created before this feature do not have stored credentials.');
4279+
await this.showNotice({
4280+
title: 'Could Not Open phpMyAdmin',
4281+
message: (e.message || 'Request failed') + '\n\nNote: databases created before this feature do not have stored credentials.'
4282+
});
41974283
}
41984284
},
41994285

@@ -4496,21 +4582,32 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
44964582
await this.api(`/users/${u.username}`, 'DELETE');
44974583
this.loadUsers();
44984584
} catch(e) {
4499-
alert('Failed to delete user: ' + e.message);
4585+
await this.showNotice({
4586+
title: 'Delete User Failed',
4587+
message: e.message || 'Request failed'
4588+
});
45004589
} finally {
45014590
this.deletingUser = null;
45024591
}
45034592
},
45044593

45054594
async toggleUserSuspension(u) {
45064595
const action = u.is_suspended ? 'unsuspend' : 'suspend';
4507-
if (!confirm(`${action.charAt(0).toUpperCase() + action.slice(1)} user "${u.username}"?`)) return;
4596+
const confirmed = await this.askConfirmation({
4597+
title: `${action.charAt(0).toUpperCase() + action.slice(1)} User`,
4598+
message: `${action.charAt(0).toUpperCase() + action.slice(1)} user "${u.username}"?`,
4599+
confirmText: `${action.charAt(0).toUpperCase() + action.slice(1)} User`
4600+
});
4601+
if (!confirmed) return;
45084602
this.suspendingUser = u.username;
45094603
try {
45104604
await this.api(`/users/${u.username}/toggle-suspend`, 'POST');
45114605
this.loadUsers();
45124606
} catch(e) {
4513-
alert('Failed to ' + action + ' user: ' + e.message);
4607+
await this.showNotice({
4608+
title: `${action.charAt(0).toUpperCase() + action.slice(1)} User Failed`,
4609+
message: e.message || 'Request failed'
4610+
});
45144611
} finally {
45154612
this.suspendingUser = null;
45164613
}
@@ -4533,7 +4630,12 @@ <h3 class="text-xl font-semibold text-gray-800">Login as User</h3>
45334630
},
45344631

45354632
async performUpdate() {
4536-
if (!confirm(`Update FastCP to ${this.updateInfo.latest_version}? The control panel will restart.`)) return;
4633+
const confirmed = await this.askConfirmation({
4634+
title: 'Update FastCP',
4635+
message: `Update FastCP to ${this.updateInfo.latest_version}? The control panel will restart.`,
4636+
confirmText: 'Update Now'
4637+
});
4638+
if (!confirmed) return;
45374639
this.updateLoading = true;
45384640
this.updateError = '';
45394641
this.updateProgress = 5;

0 commit comments

Comments
 (0)