diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 93be72800..2a9e2734b 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -152,10 +152,15 @@ export enum Click { DatabaseIndexDelete = 'click_index_delete', DatabaseTableDelete = 'click_table_delete', DatabaseRowDelete = 'click_row_delete', + DatabaseColdStart = 'click_database_cold_start', DatabaseDatabaseDelete = 'click_database_delete', DatabaseImportCsv = 'click_database_import_csv', DatabaseExportCsv = 'click_database_export_csv', DatabaseImportJson = 'click_database_import_json', + DatabasePause = 'click_database_pause', + DatabaseResume = 'click_database_resume', + DatabaseSpinDown = 'click_database_spin_down', + DedicatedMonitoringRefresh = 'click_dedicated_monitoring_refresh', DomainCreateClick = 'click_domain_create', DomainDeleteClick = 'click_domain_delete', DomainRetryDomainVerificationClick = 'click_domain_retry_domain_verification', @@ -299,6 +304,10 @@ export enum Submit { DedicatedBackupCreate = 'submit_dedicated_backup_create', DedicatedBackupDelete = 'submit_dedicated_backup_delete', DedicatedBackupRestore = 'submit_dedicated_backup_restore', + DedicatedBackupVerify = 'submit_dedicated_backup_verify', + DedicatedBranchCreate = 'submit_dedicated_branch_create', + DedicatedBranchDelete = 'submit_dedicated_branch_delete', + DedicatedDatabaseMigrate = 'submit_dedicated_database_migrate', DedicatedPitrRestore = 'submit_dedicated_pitr_restore', DatabaseInstallExtension = 'submit_database_install_extension', DatabaseUninstallExtension = 'submit_database_uninstall_extension', diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/dedicatedBackups.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/dedicatedBackups.svelte index 183f2fced..41f783b46 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/dedicatedBackups.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/dedicatedBackups.svelte @@ -57,7 +57,7 @@ let pitrTargetDateTime = $state(''); let showCreateBackupModal = $state(false); - let backupType = $state<'full' | 'incremental'>('full'); + let backupType = $state(Type.Full); let verifyingBackupId = $state(null); let showVerifyConfirm = $state(false); @@ -167,7 +167,7 @@ try { await computeSdk.createDatabaseBackup({ databaseId: database.$id, - type: backupType as unknown as Type + type: backupType }); addNotification({ type: 'success', @@ -175,7 +175,7 @@ }); trackEvent(Submit.DedicatedBackupCreate); showCreateBackupModal = false; - backupType = 'full'; + backupType = Type.Full; await loadBackups(); } catch (error) { addNotification({ @@ -281,7 +281,7 @@ type: 'success', message: 'Backup verification started' }); - trackEvent('submit_dedicated_backup_verify'); + trackEvent(Submit.DedicatedBackupVerify); showVerifyConfirm = false; verifyBackup = null; await loadBackups(); @@ -290,7 +290,7 @@ type: 'error', message: error.message }); - trackEvent('submit_dedicated_backup_verify_error'); + trackError(error, Submit.DedicatedBackupVerify); } finally { verifyingBackupId = null; } @@ -308,11 +308,15 @@ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`; } - // Load data on mount + let previousId = $state(''); + $effect(() => { - loadBackups(); - loadRestorations(); - loadPitrWindows(); + if (database?.$id && database.$id !== previousId) { + previousId = database.$id; + loadBackups(); + loadRestorations(); + loadPitrWindows(); + } }); @@ -734,13 +738,13 @@ id="backup-full" name="backupType" label="Full backup" - value="full" + value={Type.Full} bind:group={backupType} /> diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/branches/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/branches/+page.svelte index 76e469f47..dd1777371 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/branches/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/branches/+page.svelte @@ -6,7 +6,7 @@ import { toLocaleDateTime } from '$lib/helpers/date'; import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; - import { trackEvent } from '$lib/actions/analytics'; + import { Submit, trackEvent, trackError } from '$lib/actions/analytics'; import { ID, type Models } from '@appwrite.io/console'; import { ActionMenu, @@ -69,14 +69,14 @@ type: 'success', message: 'Branch created' }); - trackEvent('submit_dedicated_branch_create'); + trackEvent(Submit.DedicatedBranchCreate); await loadBranches(); } catch (error) { addNotification({ type: 'error', message: error.message }); - trackEvent('submit_dedicated_branch_create_error'); + trackError(error, Submit.DedicatedBranchCreate); } finally { isCreating = false; } @@ -93,7 +93,7 @@ type: 'success', message: 'Branch deleted' }); - trackEvent('submit_dedicated_branch_delete'); + trackEvent(Submit.DedicatedBranchDelete); showDeleteConfirm = false; selectedBranch = null; await loadBranches(); @@ -102,7 +102,7 @@ type: 'error', message: error.message }); - trackEvent('submit_dedicated_branch_delete_error'); + trackError(error, Submit.DedicatedBranchDelete); } } @@ -111,8 +111,11 @@ return toLocaleDateTime(new Date(timestamp * 1000).toISOString()); } + let previousId = $state(''); + $effect(() => { - if (database) { + if (database?.$id && database.$id !== previousId) { + previousId = database.$id; loadBranches(); } }); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/dedicatedOverview.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/dedicatedOverview.svelte index addcc8190..f779efe87 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/dedicatedOverview.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/dedicatedOverview.svelte @@ -8,7 +8,7 @@ import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; import { Dependencies } from '$lib/constants'; - import { trackEvent } from '$lib/actions/analytics'; + import { Click, trackEvent } from '$lib/actions/analytics'; import { capitalize } from '$lib/helpers/string'; import { Status as DatabaseStatus, type Models } from '@appwrite.io/console'; import { @@ -68,16 +68,11 @@ let isColdStarting = $state(false); let isPausing = $state(false); let isResuming = $state(false); - let isSpinningDown = $state(false); let connectionTab = $state<'direct' | 'string'>('direct'); const isDedicated = $derived(database.type === 'dedicateddb'); - const isShared = $derived(false); const isActive = $derived(database.status === 'ready' || database.status === 'active'); const isPaused = $derived(database.status === 'paused'); - const containerIsRunning = $derived( - database.containerStatus === 'running' || database.containerStatus === 'active' - ); // Map database status to Status component status const statusComponentStatus = $derived.by((): 'ready' | 'processing' | 'failed' | 'pending' => { @@ -176,7 +171,7 @@ message: 'Database is starting up' }); - trackEvent('click_database_cold_start'); + trackEvent(Click.DatabaseColdStart); await invalidate(Dependencies.DATABASE); } catch (error) { @@ -201,7 +196,7 @@ type: 'success', message: 'Database is pausing' }); - trackEvent('click_database_pause'); + trackEvent(Click.DatabasePause); await invalidate(Dependencies.DATABASE); } catch (error) { addNotification({ @@ -225,7 +220,7 @@ type: 'success', message: 'Database is resuming' }); - trackEvent('click_database_resume'); + trackEvent(Click.DatabaseResume); await invalidate(Dependencies.DATABASE); } catch (error) { addNotification({ @@ -237,30 +232,6 @@ } } - async function spinDownDatabase() { - isSpinningDown = true; - try { - await sdk.forProject(page.params.region, page.params.project).compute.updateDatabase({ - databaseId: database.$id, - status: 'inactive' as unknown as DatabaseStatus - }); - - addNotification({ - type: 'success', - message: 'Database container is spinning down' - }); - trackEvent('click_database_spin_down'); - await invalidate(Dependencies.DATABASE); - } catch (error) { - addNotification({ - type: 'error', - message: error.message - }); - } finally { - isSpinningDown = false; - } - } - // Check if connection details are available const hasConnectionDetails = $derived(!!database.hostname || !!database.connectionString); @@ -277,15 +248,19 @@ return `${database.engine}://${user}:${password}@${database.hostname}:${database.connectionPort}${suffix}`; }); + function escapeShellSingleQuote(value: string): string { + return value.replace(/'/g, "'\\''"); + } + function getConnectionCommand(): string { if (!resolvedConnectionString) return ''; switch (database.engine) { case 'postgres': - return `psql "${resolvedConnectionString}"`; + return `psql '${escapeShellSingleQuote(resolvedConnectionString)}'`; case 'mysql': case 'mariadb': - return `mysql -h ${database.hostname} -P ${database.connectionPort} -u ${database.connectionUser} -p'${database.connectionPassword}'`; + return `mysql -h '${escapeShellSingleQuote(database.hostname)}' -P '${escapeShellSingleQuote(String(database.connectionPort))}' -u '${escapeShellSingleQuote(database.connectionUser)}' -p'${escapeShellSingleQuote(database.connectionPassword)}'`; default: return resolvedConnectionString; } @@ -405,11 +380,6 @@ {isResuming ? 'Resuming...' : 'Resume'} {/if} - {#if isShared && isActive && containerIsRunning} - - {/if}