From 98fb42f2361d83c9c02443f0c98d5a63d55980c6 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:19:00 +0100 Subject: [PATCH 01/15] feat: added summary before postgres deployment creation --- docs/deployment.md | 68 +++++++++++++++++++ .../deployments/postgres/create/+page.svelte | 30 ++++---- .../postgres/create/Resources.svelte | 2 +- .../postgres/create/Summary.svelte | 44 ++++++++++++ .../postgres/create/schema.svelte.ts | 4 ++ 5 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 docs/deployment.md create mode 100644 frontend/src/routes/deployments/postgres/create/Summary.svelte diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..ce3c275 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,68 @@ +# Deployment + +## Configuration Options + +General Options: + +- Name: The display name for the deployment. +- Namespace: The Kubernetes namespace where the deployment will be created. (only Kubernetes) +- Database Version: The version of postgres to deploy. + +Resource Options: + +- Replicas: The number of instances to deploy. +- Storage Size: The amount of storage allocated for the deployment per instance (in MB). (only Kubernetes) +- Storage Class: The storage class to use for the deployment. (only Kubernetes) +- CPU Requests: The amount of CPU requested for each instance (in millicores). +- CPU Limits: The maximum amount of CPU allowed for each instance (in millicores). +- Memory Requests: The amount of memory requested for each instance (in MB). +- Memory Limits: The maximum amount of memory allowed for each instance (in MB). + +Backup Options: + +- Backup Name: The name of the backup configuration. +- Backup Schedule: The cron schedule for automatic backups. +- Backup Retention: The number of backups to retain. +- Backup Location: The location where backups will be stored. + +Advanced Options: + +- External Access: Enable or disable external access to the deployment. +- Extra Database Engine Parameters: Additional parameters to configure the database engine. +- Monitoring: Enable or disable monitoring for the deployment. + + +storage ref: + docker: + - host file path (must exist) + - file upload + kubernetes: + - file upload + - config map ref + - secret ref + +! +ssl +ssl_ca_file (storage ref) +ssl_cert_file (storage ref) +ssl_key_file (storage ref) +wal_level = replica (restart) +allow_alter_system (default to off) + +primary: +hot_standby = on (restart) + +sending: +max_wal_senders = replica count + small number like 10% (restart) +max_replication_slots = replica count + small number like 10% (restart) + +standby: +primary_conninfo (use env) +primary_slot_name (just some name) +sync_replication_slots = true +hot_standby = on (restart) + + +stats TODO (replication lag monitoring) +archiving for replication TODO +failover TODO \ No newline at end of file diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index 5e87f9e..84b9dde 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -11,12 +11,7 @@ FormDialog, type FormRecord } from 'positron-components/components/form'; - import { - cancelDeployment, - generalInformation, - reformatData, - resources - } from './schema.svelte'; + import { cancelDeployment, reformatData } from './schema.svelte'; import { beforeNavigate, goto } from '$app/navigation'; import type { BeforeNavigate } from '@sveltejs/kit'; import type { @@ -28,6 +23,7 @@ import GeneralInformation from './GeneralInformation.svelte'; import Resources from './Resources.svelte'; import { create_deployment } from '$lib/backend/postgres.svelte'; + import Summary from './Summary.svelte'; interface StageProps { initialValue?: any; @@ -43,7 +39,6 @@ interface Stage { title: string; - schema: any; content: StageComponent; data: object; } @@ -56,15 +51,18 @@ let stages: Stage[] = [ { title: 'General Information', - schema: generalInformation, content: GeneralInformation, data: {} }, { title: 'Resources', - schema: resources, content: Resources, data: {} + }, + { + title: 'Summary', + content: Summary, + data: {} } ]; @@ -75,14 +73,15 @@ const submit = async (form: FormRecord) => { stages[stage].data = form; - if (stage < stages.length - 1) { + if (stage < stages.length - 2) { + stage += 1; + } else if (stage === stages.length - 2) { stage += 1; - } else { - // Final submission logic here let rawData = stages.reduce((acc, s) => ({ ...acc, ...s.data }), {}); let data = reformatData(rawData); - - let res = await create_deployment(data); + stages[stage].data = data; + } else { + let res = await create_deployment(form as any); if (res) { return { error: 'Error creating deployment.' }; } else { @@ -188,6 +187,9 @@ {:else} {/if} + {:else if stage === stages.length - 2} + Summary + {:else} Next diff --git a/frontend/src/routes/deployments/postgres/create/Resources.svelte b/frontend/src/routes/deployments/postgres/create/Resources.svelte index 70c5855..9dd8973 100644 --- a/frontend/src/routes/deployments/postgres/create/Resources.svelte +++ b/frontend/src/routes/deployments/postgres/create/Resources.svelte @@ -38,7 +38,7 @@ {...props} class="w-83" key="storage_size" - label="Storage (MB)" + label="Storage Size" placeholder="Enter storage in MB" type="number" /> diff --git a/frontend/src/routes/deployments/postgres/create/Summary.svelte b/frontend/src/routes/deployments/postgres/create/Summary.svelte new file mode 100644 index 0000000..91f6b23 --- /dev/null +++ b/frontend/src/routes/deployments/postgres/create/Summary.svelte @@ -0,0 +1,44 @@ + + + onsubmit(initialValue as FormRecord)} +> +

General:

+
+

+ Name: + {initialValue?.name} +

+
+

Resources:

+
+

+ Storage Size: + {initialValue?.storage_mb}MB +

+
+
+
diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index 093693d..fe52bad 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -44,6 +44,10 @@ export const resources = z.object({ .default(['GB']) }); +export const summary = z.object({ + _phantom_summary: z.string().default('') +}); + export const cancelDeployment = z.object({ _phantom_cancel: z.string().default('') }); From 995d53260e55bffe93d5a3a10565d77b67b85357 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 6 Nov 2025 20:21:12 +0000 Subject: [PATCH 02/15] Fix code style issues with Prettier --- docs/deployment.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/deployment.md b/docs/deployment.md index ce3c275..ad22278 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -31,15 +31,9 @@ Advanced Options: - Extra Database Engine Parameters: Additional parameters to configure the database engine. - Monitoring: Enable or disable monitoring for the deployment. - storage ref: - docker: - - host file path (must exist) - - file upload - kubernetes: - - file upload - - config map ref - - secret ref +docker: - host file path (must exist) - file upload +kubernetes: - file upload - config map ref - secret ref ! ssl @@ -62,7 +56,6 @@ primary_slot_name (just some name) sync_replication_slots = true hot_standby = on (restart) - stats TODO (replication lag monitoring) archiving for replication TODO -failover TODO \ No newline at end of file +failover TODO From a8337a25cb1fac4374ef862017d839ab4811e205 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Sat, 8 Nov 2025 18:39:52 +0100 Subject: [PATCH 03/15] feat: added schemas for postgres deployment creation form --- backend/src/connector/mod.rs | 12 +- backend/src/deployment/postgres.rs | 18 ++- docs/deployment.md | 26 +++- frontend/src/lib/backend/postgres.svelte.ts | 20 ++++ .../deployments/postgres/create/+page.svelte | 13 +- .../postgres/create/schema.svelte.ts | 112 ++++++++++++++++-- 6 files changed, 181 insertions(+), 20 deletions(-) diff --git a/backend/src/connector/mod.rs b/backend/src/connector/mod.rs index 36c19e3..3d3a032 100644 --- a/backend/src/connector/mod.rs +++ b/backend/src/connector/mod.rs @@ -9,14 +9,20 @@ use crate::{config::EnvConfig, connector::docker::DockerConnector}; pub mod docker; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Copy)] pub enum ConnectorType { Docker, } #[derive(FromRequestParts, Clone)] #[from_request(via(Extension))] -pub struct PlatformConnection(Arc); +pub struct PlatformConnection(Arc, ConnectorType); + +impl PlatformConnection { + pub fn typ(&self) -> ConnectorType { + self.1 + } +} router_extension!( async fn connector(self, config: &EnvConfig) -> Self { @@ -43,7 +49,7 @@ impl ConnectorType { ConnectorType::Docker => Arc::new(DockerConnector::new()?), }; - Ok(PlatformConnection(connector)) + Ok(PlatformConnection(connector, *self)) } } diff --git a/backend/src/deployment/postgres.rs b/backend/src/deployment/postgres.rs index f66362e..677de13 100644 --- a/backend/src/deployment/postgres.rs +++ b/backend/src/deployment/postgres.rs @@ -4,20 +4,34 @@ use axum::{ routing::{delete, get, post}, }; use centaurus::error::Result; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::connector::{ - Deployment, DeploymentInfo, DeploymentType, PlatformConnection, StorageOptions, + ConnectorType, Deployment, DeploymentInfo, DeploymentType, PlatformConnection, StorageOptions, }; pub fn router() -> Router { Router::new() + .route("/info", get(system_info)) .route("/", post(create_deployment)) .route("/", get(list_deployments)) .route("/", delete(remove_deployment)) } +#[derive(Serialize)] +struct SystemInfo { + connector: ConnectorType, +} + +async fn system_info() -> Result> { + let info = SystemInfo { + connector: ConnectorType::Docker, + }; + + Ok(Json(info)) +} + #[derive(FromRequest, Deserialize)] #[from_request(via(Json))] struct CreateDeployment { diff --git a/docs/deployment.md b/docs/deployment.md index ad22278..eccbadf 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -7,10 +7,10 @@ General Options: - Name: The display name for the deployment. - Namespace: The Kubernetes namespace where the deployment will be created. (only Kubernetes) - Database Version: The version of postgres to deploy. +- Replicas: The number of instances to deploy. Resource Options: -- Replicas: The number of instances to deploy. - Storage Size: The amount of storage allocated for the deployment per instance (in MB). (only Kubernetes) - Storage Class: The storage class to use for the deployment. (only Kubernetes) - CPU Requests: The amount of CPU requested for each instance (in millicores). @@ -25,11 +25,31 @@ Backup Options: - Backup Retention: The number of backups to retain. - Backup Location: The location where backups will be stored. -Advanced Options: +Connection Options: - External Access: Enable or disable external access to the deployment. +- SSL Configuration: Configure SSL settings for secure connections. + +Monitoring Options: + +- Toggle: Enable or disable monitoring for the deployment. +- Allow external access: Enable or disable external access to the monitoring endpoints. +- Deploy monitoring resources: Choose whether to deploy monitoring resources alongside the database deployment. (only Kubernetes) + +Advanced Options: + +- Allow Alter System: Enable or disable the ability to use ALTER SYSTEM commands. - Extra Database Engine Parameters: Additional parameters to configure the database engine. -- Monitoring: Enable or disable monitoring for the deployment. + +Hardcoded Parameters (required for replication): + +- wal_level = replica +- hot_standby = on +- max_wal_senders = replica count \* 1.1 (primary only) +- max_replication_slots = replica count \* 1.1 (primary only) +- primary_conninfo = use environment variables (replica only) +- primary_slot_name = some name (replica only) +- sync_replication_slots = true (replica only) storage ref: docker: - host file path (must exist) - file upload diff --git a/frontend/src/lib/backend/postgres.svelte.ts b/frontend/src/lib/backend/postgres.svelte.ts index a26fd13..23a3e24 100644 --- a/frontend/src/lib/backend/postgres.svelte.ts +++ b/frontend/src/lib/backend/postgres.svelte.ts @@ -1,4 +1,5 @@ import { delete_, get, post, ResponseType } from 'positron-components/backend'; +import type { Connect } from 'vite'; export interface CreateDeployment { name: string; @@ -40,3 +41,22 @@ export const delete_deployment = async (uuid: string) => { return res; }; + +export enum ConnectorType { + Docker = 'Docker' +} + +export interface SystemInfo { + connector: ConnectorType; +} + +export const system_info = async () => { + let res = await get( + '/api/deployment/postgres/info', + ResponseType.Json + ); + + if (typeof res === 'object') { + return res; + } +}; diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index 84b9dde..926bd74 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -22,7 +22,11 @@ } from 'svelte'; import GeneralInformation from './GeneralInformation.svelte'; import Resources from './Resources.svelte'; - import { create_deployment } from '$lib/backend/postgres.svelte'; + import { + create_deployment, + system_info, + type SystemInfo + } from '$lib/backend/postgres.svelte'; import Summary from './Summary.svelte'; interface StageProps { @@ -46,7 +50,12 @@ let stage = $state(0); let cancelOpen = $state(false); let form: undefined | SvelteComponent = $state(); - let isLoading = $state(false); + let isLoading = $state(true); + let sys_info: SystemInfo | undefined = $state(); + system_info().then((info) => { + sys_info = info; + isLoading = false; + }); let stages: Stage[] = [ { diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index fe52bad..5d00711 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -14,6 +14,15 @@ export const units = { TiB: 1024 * 1024 * 1024 * 1024 }; +export enum PostgresVersion { + V13 = '13', + V14 = '14', + V15 = '15', + V16 = '16', + V17 = '17', + V18 = '18' +} + export const reformatData = (data: FormRecord): CreateDeployment => { let storage_size = data.storage_size as number; let storage_size_unit = (data.storage_size_unit as string[])[0]; @@ -29,19 +38,102 @@ export const reformatData = (data: FormRecord): CreateDeployment => { }; export const generalInformation = z.object({ - name: z.string().min(1, 'Name is required').default('') + name: z.string().min(1, 'Name is required').default(''), + namespace: z.string().default(''), + version: z.array(z.enum(PostgresVersion)).default([PostgresVersion.V18]), + replicas: z.number().default(1) }); +const amount = z.number().gt(0, 'Must be greater than 0').default(1); +const unit = z + .array(z.enum(Object.keys(units))) + .min(1) + .max(1) + .default(['GB']); + export const resources = z.object({ - storage_size: z - .number() - .gt(0, 'Storage size must be greater than 0') - .default(1), - storage_size_unit: z - .array(z.enum(Object.keys(units))) - .min(1) - .max(1) - .default(['GB']) + storage_size: amount, + storage_size_unit: unit, + memory_request_size: amount, + memory_request_size_unit: unit, + memory_limit_size: amount, + memory_limit_size_unit: unit, + cpu_request: amount, + cpu_limit: amount +}); + +export const backup = z.object({ + backups_enabled: z.boolean().default(false), + backup_schedule: z.string().default('0 0 * * *'), + backup_retention: z.number().min(1, 'Must be at least 1').default(7), + backup_storage_location: z.string().default('') +}); + +export enum RefType { + ConfigMap = 'ConfigMap', + Secret = 'Secret' +} + +export const connection = z + .object({ + external_access: z.boolean().default(false), + ssl_enabled: z.boolean().default(false), + ssl_cert_file: z.file().optional(), + ssl_cert_ref_type: z.enum(RefType).optional(), + ssl_cert_ref_name: z.string().optional(), + ssl_cert_ref_key: z.string().optional(), + ssl_cert_host_file_path: z.string().optional(), + ssl_key_file: z.file().optional(), + ssl_key_ref_type: z.enum(RefType).optional(), + ssl_key_ref_name: z.string().optional(), + ssl_key_ref_key: z.string().optional(), + ssl_key_host_file_path: z.string().optional(), + ssl_ca_file: z.file().optional(), + ssl_ca_ref_type: z.enum(RefType).optional(), + ssl_ca_ref_name: z.string().optional(), + ssl_ca_ref_key: z.string().optional(), + ssl_ca_host_file_path: z.string().optional() + }) + .superRefine((data, ctx) => { + if (data.ssl_enabled) { + let cert_set = + data.ssl_cert_file || + (data.ssl_cert_ref_type && + data.ssl_cert_ref_name && + data.ssl_cert_ref_key) || + data.ssl_cert_host_file_path; + let key_set = + data.ssl_key_file || + (data.ssl_key_ref_type && + data.ssl_key_ref_name && + data.ssl_key_ref_key) || + data.ssl_key_host_file_path; + + if (!cert_set) { + ctx.addIssue({ + code: 'custom', + message: 'You must provide an SSL certificate' + }); + } + + if (!key_set) { + ctx.addIssue({ + code: 'custom', + message: 'You must provide an SSL key' + }); + } + } + }); + +export const monitoring = z.object({ + monitoring_enabled: z.boolean().default(false), + external_access: z.boolean().default(false), + deploy_monitoring_resources: z.boolean().default(true) +}); + +export const advanced = z.object({ + allow_alter_system: z.boolean().default(false), + extra_database_parameters: z.string().default('') }); export const summary = z.object({ From 1076d4788a88197dd09e69b05db7452553b003f2 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Sat, 8 Nov 2025 21:15:37 +0100 Subject: [PATCH 04/15] feat: added new resource options --- backend/src/deployment/postgres.rs | 2 + frontend/src/lib/backend/postgres.svelte.ts | 4 +- .../deployments/postgres/create/+page.svelte | 4 +- .../postgres/create/GeneralInformation.svelte | 31 ++++++++++++- .../postgres/create/Resources.svelte | 43 +++++++++++++++++-- 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/backend/src/deployment/postgres.rs b/backend/src/deployment/postgres.rs index 677de13..e8972b5 100644 --- a/backend/src/deployment/postgres.rs +++ b/backend/src/deployment/postgres.rs @@ -22,11 +22,13 @@ pub fn router() -> Router { #[derive(Serialize)] struct SystemInfo { connector: ConnectorType, + namespaces: Vec, } async fn system_info() -> Result> { let info = SystemInfo { connector: ConnectorType::Docker, + namespaces: vec![], }; Ok(Json(info)) diff --git a/frontend/src/lib/backend/postgres.svelte.ts b/frontend/src/lib/backend/postgres.svelte.ts index 23a3e24..91d9c5f 100644 --- a/frontend/src/lib/backend/postgres.svelte.ts +++ b/frontend/src/lib/backend/postgres.svelte.ts @@ -43,11 +43,13 @@ export const delete_deployment = async (uuid: string) => { }; export enum ConnectorType { - Docker = 'Docker' + Docker = 'Docker', + Kubernetes = 'Kubernetes' } export interface SystemInfo { connector: ConnectorType; + namespaces: string[]; } export const system_info = async () => { diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index 926bd74..0dc13ed 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -34,6 +34,7 @@ onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; + sys_info?: SystemInfo; } type StageComponent = Component< @@ -64,7 +65,7 @@ data: {} }, { - title: 'Resources', + title: 'Resources (per replica)', content: Resources, data: {} }, @@ -161,6 +162,7 @@ initialValue={current.data} onsubmit={submit} bind:isLoading + {sys_info} > {#snippet footer({ isLoading })} diff --git a/frontend/src/routes/deployments/postgres/create/GeneralInformation.svelte b/frontend/src/routes/deployments/postgres/create/GeneralInformation.svelte index fe96d47..856e2f2 100644 --- a/frontend/src/routes/deployments/postgres/create/GeneralInformation.svelte +++ b/frontend/src/routes/deployments/postgres/create/GeneralInformation.svelte @@ -2,19 +2,22 @@ import { BaseForm, FormInput, + FormSelect, type FormValue } from 'positron-components/components/form'; import type { ComponentProps, Snippet } from 'svelte'; - import { generalInformation } from './schema.svelte'; + import { generalInformation, PostgresVersion } from './schema.svelte'; + import { ConnectorType, type SystemInfo } from '$lib/backend/postgres.svelte'; interface Props { initialValue?: FormValue; onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; + sys_info?: SystemInfo; } - let { initialValue, onsubmit, footer, isLoading }: Props = $props(); + let { initialValue, onsubmit, footer, isLoading, sys_info }: Props = $props(); let form: BaseForm | undefined = $state(); @@ -38,5 +41,29 @@ label="Cluster Name" placeholder="Enter name" /> + {#if sys_info?.connector === ConnectorType.Kubernetes} + ({ label: ns, value: ns }))} + /> + {/if} + ({ + label: version, + value: version + }))} + /> + {/snippet} diff --git a/frontend/src/routes/deployments/postgres/create/Resources.svelte b/frontend/src/routes/deployments/postgres/create/Resources.svelte index 9dd8973..28200cc 100644 --- a/frontend/src/routes/deployments/postgres/create/Resources.svelte +++ b/frontend/src/routes/deployments/postgres/create/Resources.svelte @@ -36,16 +36,53 @@
+ ({ value: unit, label: unit }))} + /> +
+
+ + ({ value: unit, label: unit }))} + /> +
+
+ ({ value: unit, label: unit }))} /> From 2a60b75964018ade6caac29356be579fbe991a03 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Sun, 9 Nov 2025 23:25:55 +0100 Subject: [PATCH 05/15] fix: added options for certificate as text in schema --- .../postgres/create/schema.svelte.ts | 214 +++++++++++++++--- 1 file changed, 181 insertions(+), 33 deletions(-) diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index 5d00711..65b1485 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -51,16 +51,33 @@ const unit = z .max(1) .default(['GB']); -export const resources = z.object({ - storage_size: amount, - storage_size_unit: unit, - memory_request_size: amount, - memory_request_size_unit: unit, - memory_limit_size: amount, - memory_limit_size_unit: unit, - cpu_request: amount, - cpu_limit: amount -}); +export const resources = z + .object({ + storage_size: amount, + storage_size_unit: unit, + memory_request_size: amount, + memory_request_size_unit: unit, + memory_limit_size: amount, + memory_limit_size_unit: unit, + cpu_request: amount, + cpu_limit: amount + }) + .superRefine((data, ctx) => { + let memory_request_bytes = + data.memory_request_size * + (units as Record)[data.memory_request_size_unit[0]]; + let memory_limit_bytes = + data.memory_limit_size * + (units as Record)[data.memory_limit_size_unit[0]]; + + if (memory_limit_bytes < memory_request_bytes) { + ctx.addIssue({ + code: 'custom', + path: ['memory_limit_size', 'memory_request_size'], + message: 'Memory limit must be greater than or equal to request' + }); + } + }); export const backup = z.object({ backups_enabled: z.boolean().default(false), @@ -69,6 +86,14 @@ export const backup = z.object({ backup_storage_location: z.string().default('') }); +export enum CertSource { + Text = 'Text', + File = 'File', + Reference = 'Reference', + HostFilePath = 'HostFilePath', + Auto = 'Auto' +} + export enum RefType { ConfigMap = 'ConfigMap', Secret = 'Secret' @@ -78,16 +103,23 @@ export const connection = z .object({ external_access: z.boolean().default(false), ssl_enabled: z.boolean().default(false), + ssl_cert_source: z.enum(CertSource).optional(), + ssl_cert_text: z.string().optional(), ssl_cert_file: z.file().optional(), ssl_cert_ref_type: z.enum(RefType).optional(), ssl_cert_ref_name: z.string().optional(), ssl_cert_ref_key: z.string().optional(), ssl_cert_host_file_path: z.string().optional(), + ssl_key_source: z.enum(CertSource).optional(), + ssl_key_text: z.string().optional(), ssl_key_file: z.file().optional(), ssl_key_ref_type: z.enum(RefType).optional(), ssl_key_ref_name: z.string().optional(), ssl_key_ref_key: z.string().optional(), ssl_key_host_file_path: z.string().optional(), + ssl_ca_enabled: z.boolean().default(false), + ssl_ca_source: z.enum(CertSource).optional(), + ssl_ca_text: z.string().optional(), ssl_ca_file: z.file().optional(), ssl_ca_ref_type: z.enum(RefType).optional(), ssl_ca_ref_name: z.string().optional(), @@ -96,31 +128,147 @@ export const connection = z }) .superRefine((data, ctx) => { if (data.ssl_enabled) { - let cert_set = - data.ssl_cert_file || - (data.ssl_cert_ref_type && - data.ssl_cert_ref_name && - data.ssl_cert_ref_key) || - data.ssl_cert_host_file_path; - let key_set = - data.ssl_key_file || - (data.ssl_key_ref_type && - data.ssl_key_ref_name && - data.ssl_key_ref_key) || - data.ssl_key_host_file_path; - - if (!cert_set) { - ctx.addIssue({ - code: 'custom', - message: 'You must provide an SSL certificate' - }); + switch (data.ssl_cert_source) { + case CertSource.Text: + if (!data.ssl_cert_text) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_cert_text'], + message: 'SSL certificate is required' + }); + } + break; + case CertSource.File: + if (!data.ssl_cert_file) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_cert_file'], + message: 'SSL certificate is required' + }); + } + break; + case CertSource.Reference: + if ( + !( + data.ssl_cert_ref_type && + data.ssl_cert_ref_name && + data.ssl_cert_ref_key + ) + ) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_cert_ref_name', 'ssl_cert_ref_key'], + message: 'SSL certificate is required' + }); + } + break; + case CertSource.HostFilePath: + if (!data.ssl_cert_host_file_path) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_cert_host_file_path'], + message: 'SSL certificate is required' + }); + } + break; + case CertSource.Auto: + break; } - if (!key_set) { - ctx.addIssue({ - code: 'custom', - message: 'You must provide an SSL key' - }); + switch (data.ssl_key_source) { + case CertSource.Text: + if (!data.ssl_key_text) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_key_text'], + message: 'SSL key is required' + }); + } + break; + case CertSource.File: + if (!data.ssl_key_file) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_key_file'], + message: 'SSL key is required' + }); + } + break; + case CertSource.Reference: + if ( + !( + data.ssl_key_ref_type && + data.ssl_key_ref_name && + data.ssl_key_ref_key + ) + ) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_key_ref_name', 'ssl_key_ref_key'], + message: 'SSL key is required' + }); + } + break; + case CertSource.HostFilePath: + if (!data.ssl_key_host_file_path) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_key_host_file_path'], + message: 'SSL key is required' + }); + } + break; + case CertSource.Auto: + break; + } + } + + if (data.ssl_ca_enabled) { + switch (data.ssl_ca_source) { + case CertSource.Text: + if (!data.ssl_ca_text) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_ca_text'], + message: 'SSL CA is required' + }); + } + break; + case CertSource.File: + if (!data.ssl_ca_file) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_ca_file'], + message: 'SSL CA is required' + }); + } + break; + case CertSource.Reference: + if ( + !( + data.ssl_ca_ref_type && + data.ssl_ca_ref_name && + data.ssl_ca_ref_key + ) + ) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_ca_ref_name', 'ssl_ca_ref_key'], + message: 'SSL CA is required' + }); + } + break; + case CertSource.HostFilePath: + if (!data.ssl_ca_host_file_path) { + ctx.addIssue({ + code: 'custom', + path: ['ssl_ca_host_file_path'], + message: 'SSL CA is required' + }); + } + break; + case CertSource.Auto: + break; } } }); From da12f435eaa6810d89461b2c5f904caf4a1ec95b Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Tue, 18 Nov 2025 21:28:06 +0100 Subject: [PATCH 06/15] feat: added additional resource inputs --- .../deployments/postgres/create/+page.svelte | 43 ++----------------- .../postgres/create/Resources.svelte | 20 +++++++-- .../postgres/create/schema.svelte.ts | 5 ++- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index 0dc13ed..81a0514 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -8,12 +8,10 @@ } from 'positron-components/components/ui'; import { BaseForm, - FormDialog, type FormRecord } from 'positron-components/components/form'; - import { cancelDeployment, reformatData } from './schema.svelte'; - import { beforeNavigate, goto } from '$app/navigation'; - import type { BeforeNavigate } from '@sveltejs/kit'; + import { reformatData } from './schema.svelte'; + import { goto } from '$app/navigation'; import type { Component, ComponentProps, @@ -49,7 +47,6 @@ } let stage = $state(0); - let cancelOpen = $state(false); let form: undefined | SvelteComponent = $state(); let isLoading = $state(true); let sys_info: SystemInfo | undefined = $state(); @@ -96,38 +93,12 @@ return { error: 'Error creating deployment.' }; } else { setTimeout(() => { - confirmed = true; goto('/deployments/postgres'); }); } } return undefined; }; - - let attemptedNavigation: BeforeNavigate | undefined = undefined; - let confirmed = false; - beforeNavigate((nav) => { - // allow navigation if it was already confirmed - if (confirmed) { - confirmed = false; - return; - } - - // don't show the dialog if the user is leaving the page because the browser built-in - // dialog will be shown instead - if (nav.type !== 'leave') { - attemptedNavigation = nav; - cancelOpen = true; - } - nav.cancel(); - }); - - const cancelConfirm = () => { - confirmed = true; - goto(attemptedNavigation?.to?.url.pathname || '/deployments/postgres'); - - return undefined; - };
@@ -184,7 +155,7 @@ variant="outline" disabled={isLoading} onclick={() => { - cancelOpen = true; + goto('/deployments/postgres'); }} > @@ -212,11 +183,3 @@
- diff --git a/frontend/src/routes/deployments/postgres/create/Resources.svelte b/frontend/src/routes/deployments/postgres/create/Resources.svelte index 28200cc..d0d8651 100644 --- a/frontend/src/routes/deployments/postgres/create/Resources.svelte +++ b/frontend/src/routes/deployments/postgres/create/Resources.svelte @@ -38,7 +38,7 @@ {...props} class="w-89" key="memory_request_size" - label="Memory Request Size" + label="Memory Request" placeholder="Enter amount of memory" type="number" /> @@ -56,7 +56,7 @@ {...props} class="w-89" key="memory_limit_size" - label="Memory Limit Size" + label="Memory Limit" placeholder="Enter amount of memory" type="number" /> @@ -74,7 +74,7 @@ {...props} class="w-89" key="storage_size" - label="Storage Size" + label="Disk Storage" placeholder="Enter amount of storage" type="number" /> @@ -87,5 +87,19 @@ data={Object.keys(units).map((unit) => ({ value: unit, label: unit }))} />
+ + {/snippet} diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index 65b1485..fe8164f 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -45,6 +45,7 @@ export const generalInformation = z.object({ }); const amount = z.number().gt(0, 'Must be greater than 0').default(1); +const cpu = z.number().gt(1, 'Must be greater than 1').default(1000); const unit = z .array(z.enum(Object.keys(units))) .min(1) @@ -59,8 +60,8 @@ export const resources = z memory_request_size_unit: unit, memory_limit_size: amount, memory_limit_size_unit: unit, - cpu_request: amount, - cpu_limit: amount + cpu_request: cpu, + cpu_limit: cpu }) .superRefine((data, ctx) => { let memory_request_bytes = From a93328ca5c45c68ed513eded547f73f6a8bb7625 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:00:54 +0100 Subject: [PATCH 07/15] feat: added backup config --- .../deployments/postgres/create/+page.svelte | 6 ++ .../deployments/postgres/create/Backup.svelte | 67 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 frontend/src/routes/deployments/postgres/create/Backup.svelte diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index 81a0514..d395eda 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -26,6 +26,7 @@ type SystemInfo } from '$lib/backend/postgres.svelte'; import Summary from './Summary.svelte'; + import Backup from './Backup.svelte'; interface StageProps { initialValue?: any; @@ -66,6 +67,11 @@ content: Resources, data: {} }, + { + title: 'Backup Configuration', + content: Backup, + data: {} + }, { title: 'Summary', content: Summary, diff --git a/frontend/src/routes/deployments/postgres/create/Backup.svelte b/frontend/src/routes/deployments/postgres/create/Backup.svelte new file mode 100644 index 0000000..159f285 --- /dev/null +++ b/frontend/src/routes/deployments/postgres/create/Backup.svelte @@ -0,0 +1,67 @@ + + + + {#snippet children({ props })} + (backups_enabled = checked)} + /> + {#if backups_enabled} + + + + {/if} + {/snippet} + From 9870f4fa6a697f56516c88136c3bf18766e3ed3d Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:46:40 +0100 Subject: [PATCH 08/15] feat: added advanced, monitoring and connection options --- .../deployments/postgres/create/+page.svelte | 18 ++++ .../postgres/create/Advanced.svelte | 48 +++++++++++ .../postgres/create/Connection.svelte | 86 +++++++++++++++++++ .../postgres/create/Monitoring.svelte | 55 ++++++++++++ .../postgres/create/schema.svelte.ts | 29 +++++-- 5 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 frontend/src/routes/deployments/postgres/create/Advanced.svelte create mode 100644 frontend/src/routes/deployments/postgres/create/Connection.svelte create mode 100644 frontend/src/routes/deployments/postgres/create/Monitoring.svelte diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index d395eda..d45a745 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -27,6 +27,9 @@ } from '$lib/backend/postgres.svelte'; import Summary from './Summary.svelte'; import Backup from './Backup.svelte'; + import Connection from './Connection.svelte'; + import Monitoring from './Monitoring.svelte'; + import Advanced from './Advanced.svelte'; interface StageProps { initialValue?: any; @@ -67,11 +70,26 @@ content: Resources, data: {} }, + { + title: 'Connection', + content: Connection, + data: {} + }, { title: 'Backup Configuration', content: Backup, data: {} }, + { + title: 'Monitoring', + content: Monitoring, + data: {} + }, + { + title: 'Advanced', + content: Advanced, + data: {} + }, { title: 'Summary', content: Summary, diff --git a/frontend/src/routes/deployments/postgres/create/Advanced.svelte b/frontend/src/routes/deployments/postgres/create/Advanced.svelte new file mode 100644 index 0000000..b3920e2 --- /dev/null +++ b/frontend/src/routes/deployments/postgres/create/Advanced.svelte @@ -0,0 +1,48 @@ + + + + {#snippet children({ props })} + + + {/snippet} + diff --git a/frontend/src/routes/deployments/postgres/create/Connection.svelte b/frontend/src/routes/deployments/postgres/create/Connection.svelte new file mode 100644 index 0000000..ed33acf --- /dev/null +++ b/frontend/src/routes/deployments/postgres/create/Connection.svelte @@ -0,0 +1,86 @@ + + + + {#snippet children({ props })} + + (ssl_enabled = v)} + /> + {#if ssl_enabled} + ({ label: v, value: v }))} + /> + + + + ({ label: v, value: v }))} + /> + + (ssl_ca_enabled = v)} + /> + {#if ssl_ca_enabled} + ({ label: v, value: v }))} + /> + {/if} + {/if} + {/snippet} + diff --git a/frontend/src/routes/deployments/postgres/create/Monitoring.svelte b/frontend/src/routes/deployments/postgres/create/Monitoring.svelte new file mode 100644 index 0000000..271ddcf --- /dev/null +++ b/frontend/src/routes/deployments/postgres/create/Monitoring.svelte @@ -0,0 +1,55 @@ + + + + {#snippet children({ props })} + (monitoring_enabled = v)} + /> + {#if monitoring_enabled} + + + {/if} + {/snippet} + diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index fe8164f..d6ad4d1 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -100,36 +100,47 @@ export enum RefType { Secret = 'Secret' } +const certSource = z + .array(z.enum(CertSource)) + .min(1) + .max(1) + .default([CertSource.Auto]); +const refType = z + .array(z.enum(RefType)) + .min(1) + .max(1) + .default([RefType.Secret]); + export const connection = z .object({ external_access: z.boolean().default(false), ssl_enabled: z.boolean().default(false), - ssl_cert_source: z.enum(CertSource).optional(), + ssl_cert_source: certSource, ssl_cert_text: z.string().optional(), ssl_cert_file: z.file().optional(), - ssl_cert_ref_type: z.enum(RefType).optional(), + ssl_cert_ref_type: refType, ssl_cert_ref_name: z.string().optional(), ssl_cert_ref_key: z.string().optional(), ssl_cert_host_file_path: z.string().optional(), - ssl_key_source: z.enum(CertSource).optional(), + ssl_key_source: certSource, ssl_key_text: z.string().optional(), ssl_key_file: z.file().optional(), - ssl_key_ref_type: z.enum(RefType).optional(), + ssl_key_ref_type: refType, ssl_key_ref_name: z.string().optional(), ssl_key_ref_key: z.string().optional(), ssl_key_host_file_path: z.string().optional(), ssl_ca_enabled: z.boolean().default(false), - ssl_ca_source: z.enum(CertSource).optional(), + ssl_ca_source: certSource, ssl_ca_text: z.string().optional(), ssl_ca_file: z.file().optional(), - ssl_ca_ref_type: z.enum(RefType).optional(), + ssl_ca_ref_type: refType, ssl_ca_ref_name: z.string().optional(), ssl_ca_ref_key: z.string().optional(), ssl_ca_host_file_path: z.string().optional() }) .superRefine((data, ctx) => { if (data.ssl_enabled) { - switch (data.ssl_cert_source) { + switch (data.ssl_cert_source[0]) { case CertSource.Text: if (!data.ssl_cert_text) { ctx.addIssue({ @@ -176,7 +187,7 @@ export const connection = z break; } - switch (data.ssl_key_source) { + switch (data.ssl_key_source[0]) { case CertSource.Text: if (!data.ssl_key_text) { ctx.addIssue({ @@ -225,7 +236,7 @@ export const connection = z } if (data.ssl_ca_enabled) { - switch (data.ssl_ca_source) { + switch (data.ssl_ca_source[0]) { case CertSource.Text: if (!data.ssl_ca_text) { ctx.addIssue({ From feec20e5ec28eca93cf1800b5d5f048b25208527 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:27:40 +0100 Subject: [PATCH 09/15] chore: updated comps --- frontend/package.json | 4 +- frontend/src/app.css | 2 +- package-lock.json | 1168 ++++++++++++++++++----------------------- 3 files changed, 514 insertions(+), 660 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index daba268..b5b0f60 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.7", "bits-ui": "^2.11.0", - "positron-components": "1.10.0", + "positron-components": "1.11.0", "svelte": "5.39.9", "svelte-check": "4.3.2", "tailwind-merge": "^3.3.1", @@ -53,4 +53,4 @@ "lightningcss-linux-x64-gnu": "^1.30.1", "lightningcss-win32-x64-msvc": "^1.30.1" } -} +} \ No newline at end of file diff --git a/frontend/src/app.css b/frontend/src/app.css index 6ccb162..1d90476 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -3,7 +3,7 @@ @custom-variant dark (&:is(.dark *)); -@source '../../node_modules/positron-components'; +@source '../node_modules/positron-components'; /* The default border color has changed to `currentColor` in Tailwind CSS v4, diff --git a/package-lock.json b/package-lock.json index cb19ac3..b08379a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.7", "bits-ui": "^2.11.0", - "positron-components": "1.10.0", + "positron-components": "1.11.0", "svelte": "5.39.9", "svelte-check": "4.3.2", "tailwind-merge": "^3.3.1", @@ -98,6 +98,17 @@ "lightningcss-win32-x64-msvc": "^1.30.1" } }, + "frontend/node_modules/@internationalized/date": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz", + "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "frontend/node_modules/@sveltejs/kit": { "version": "2.44.0", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.44.0.tgz", @@ -170,120 +181,467 @@ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, - "frontend/node_modules/sveltekit-superforms": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/sveltekit-superforms/-/sveltekit-superforms-2.28.0.tgz", - "integrity": "sha512-kHoROSmf5bzK9r261z2op5OPF5tRhxqQQOQc8dS6CatKAUDr3o94Fjjv7AwjD9i4FplyXI+OyTo6hGOXQ3qCKQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ciscoheat" - }, - { - "type": "ko-fi", - "url": "https://ko-fi.com/ciscoheat" - }, - { - "type": "paypal", - "url": "https://www.paypal.com/donate/?hosted_button_id=NY7F5ALHHSVQS" - } - ], + "frontend/node_modules/positron-components": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/positron-components/-/positron-components-1.11.0.tgz", + "integrity": "sha512-r8wgfvBBULTH+ezoDLeEhtOFggBTMrXVgbPO92VVzFEHRvVL6r7XAdI/sOE4u063ysCyMaE5nki8jl+t+Wjceg==", + "dev": true, + "dependencies": { + "@emoji-mart/data": "^1.2.1", + "@internationalized/date": "3.10.0", + "@lucide/svelte": "^0.552.0", + "@shikijs/langs": "^3.13.0", + "@shikijs/themes": "^3.13.0", + "@sveltejs/adapter-auto": "7.0.0", + "@sveltejs/kit": "2.48.4", + "@sveltejs/package": "2.5.4", + "@sveltejs/vite-plugin-svelte": "6.2.1", + "@tailwindcss/typography": "0.5.19", + "@tailwindcss/vite": "4.1.16", + "@tanstack/table-core": "8.21.3", + "@types/luxon": "3.7.1", + "@zxcvbn-ts/core": "^3.0.4", + "@zxcvbn-ts/language-common": "^3.0.4", + "@zxcvbn-ts/language-en": "^3.0.2", + "bits-ui": "^2.11.3", + "clsx": "^2.1.1", + "country-flag-icons": "^1.5.21", + "embla-carousel-svelte": "^8.6.0", + "formsnap": "2.0.1", + "isomorphic-dompurify": "^2.28.0", + "layerchart": "^2.0.0-next.38", + "luxon": "3.7.2", + "mode-watcher": "^1.1.0", + "package-manager-detector": "^1.3.0", + "paneforge": "^1.0.2", + "prettier": "3.6.2", + "prettier-plugin-svelte": "3.4.0", + "prettier-plugin-tailwindcss": "0.7.1", + "publint": "0.3.15", + "runed": "^0.35.1", + "shiki": "3.14.0", + "svelte": "5.43.2", + "svelte-check": "4.3.3", + "svelte-easy-crop": "^5.0.0", + "svelte-sonner": "^1.0.5", + "svelte-tel-input": "^3.6.0", + "svelte-toolbelt": "^0.10.5", + "sveltekit-superforms": "2.28.0", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^3.1.1", + "tailwindcss": "4.1.16", + "tw-animate-css": "1.4.0", + "typescript": "5.9.3", + "vaul-svelte": "^1.0.0-next.7", + "vite": "7.1.12", + "yeezy-dates": "^1.0.1", + "zod": "4.1.12" + } + }, + "frontend/node_modules/positron-components/node_modules/@lucide/svelte": { + "version": "0.552.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.552.0.tgz", + "integrity": "sha512-8wQF1YUKgaXiFidPdHM5NKESArKHLrgf8A1EAOjvqRmdVlL2KFsPxTchez/lMUOJKxXeDgrRKfXQuaN1O5KlhQ==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "svelte": "^5" + } + }, + "frontend/node_modules/positron-components/node_modules/@sveltejs/kit": { + "version": "2.48.4", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.48.4.tgz", + "integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==", + "dev": true, "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", "devalue": "^5.3.2", - "memoize-weak": "^1.0.2", - "ts-deepmerge": "^7.0.3" + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "sade": "^1.8.1", + "set-cookie-parser": "^2.6.0", + "sirv": "^3.0.0" }, - "optionalDependencies": { - "@exodus/schemasafe": "^1.3.0", - "@finom/zod-to-json-schema": "^3.24.11", - "@gcornut/valibot-json-schema": "^0.42.0", - "@typeschema/class-validator": "^0.3.0", - "@vinejs/vine": "^3.0.1", - "arktype": "^2.1.22", - "class-validator": "^0.14.2", - "effect": "^3.18.1", - "joi": "^17.13.3", - "json-schema-to-ts": "^3.1.1", - "superstruct": "^2.0.2", - "typebox": "^1.0.36", - "valibot": "^1.1.0", - "yup": "^1.7.1", - "zod": "^4.1.11" + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" }, "peerDependencies": { - "@exodus/schemasafe": "^1.3.0", - "@sveltejs/kit": "1.x || 2.x", - "@typeschema/class-validator": "^0.3.0", - "@vinejs/vine": "^1.8.0 || ^2.0.0 || ^3.0.0", - "arktype": ">=2.0.0-rc.23", - "class-validator": "^0.14.1", - "effect": "^3.13.7", - "joi": "^17.13.1", - "superstruct": "^2.0.2", - "svelte": "3.x || 4.x || >=5.0.0-next.51", - "typebox": "^1.0.36", - "valibot": "^1.0.0", - "yup": "^1.4.0", - "zod": "^3.25.0 || ^4.0.0" + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "peerDependenciesMeta": { - "@exodus/schemasafe": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "frontend/node_modules/positron-components/node_modules/bits-ui": { + "version": "2.14.4", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.14.4.tgz", + "integrity": "sha512-W6kenhnbd/YVvur+DKkaVJ6GldE53eLewur5AhUCqslYQ0vjZr8eWlOfwZnMiPB+PF5HMVqf61vXBvmyrAmPWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.1", + "@floating-ui/dom": "^1.7.1", + "esm-env": "^1.1.2", + "runed": "^0.35.1", + "svelte-toolbelt": "^0.10.6", + "tabbable": "^6.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/huntabyte" + }, + "peerDependencies": { + "@internationalized/date": "^3.8.1", + "svelte": "^5.33.0" + } + }, + "frontend/node_modules/positron-components/node_modules/prettier-plugin-tailwindcss": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.1.tgz", + "integrity": "sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { "optional": true }, - "@typeschema/class-validator": { + "@prettier/plugin-hermes": { "optional": true }, - "@vinejs/vine": { + "@prettier/plugin-oxc": { "optional": true }, - "arktype": { + "@prettier/plugin-pug": { "optional": true }, - "class-validator": { + "@shopify/prettier-plugin-liquid": { "optional": true }, - "effect": { + "@trivago/prettier-plugin-sort-imports": { "optional": true }, - "joi": { + "@zackad/prettier-plugin-twig": { "optional": true }, - "superstruct": { + "prettier-plugin-astro": { "optional": true }, - "typebox": { + "prettier-plugin-css-order": { "optional": true }, - "valibot": { + "prettier-plugin-jsdoc": { "optional": true }, - "yup": { + "prettier-plugin-marko": { "optional": true }, - "zod": { + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-svelte": { "optional": true } } }, - "frontend/node_modules/tailwindcss": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz", - "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==", - "license": "MIT", - "peer": true - }, - "frontend/node_modules/vite": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", - "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "frontend/node_modules/positron-components/node_modules/svelte": { + "version": "5.43.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.2.tgz", + "integrity": "sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA==", + "dev": true, "license": "MIT", "peer": true, "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "esm-env": "^1.2.1", + "esrap": "^2.1.0", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "frontend/node_modules/positron-components/node_modules/svelte-check": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz", + "integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "frontend/node_modules/positron-components/node_modules/vite": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", + "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "frontend/node_modules/positron-components/node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "frontend/node_modules/sveltekit-superforms": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/sveltekit-superforms/-/sveltekit-superforms-2.28.0.tgz", + "integrity": "sha512-kHoROSmf5bzK9r261z2op5OPF5tRhxqQQOQc8dS6CatKAUDr3o94Fjjv7AwjD9i4FplyXI+OyTo6hGOXQ3qCKQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ciscoheat" + }, + { + "type": "ko-fi", + "url": "https://ko-fi.com/ciscoheat" + }, + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=NY7F5ALHHSVQS" + } + ], + "license": "MIT", + "dependencies": { + "devalue": "^5.3.2", + "memoize-weak": "^1.0.2", + "ts-deepmerge": "^7.0.3" + }, + "optionalDependencies": { + "@exodus/schemasafe": "^1.3.0", + "@finom/zod-to-json-schema": "^3.24.11", + "@gcornut/valibot-json-schema": "^0.42.0", + "@typeschema/class-validator": "^0.3.0", + "@vinejs/vine": "^3.0.1", + "arktype": "^2.1.22", + "class-validator": "^0.14.2", + "effect": "^3.18.1", + "joi": "^17.13.3", + "json-schema-to-ts": "^3.1.1", + "superstruct": "^2.0.2", + "typebox": "^1.0.36", + "valibot": "^1.1.0", + "yup": "^1.7.1", + "zod": "^4.1.11" + }, + "peerDependencies": { + "@exodus/schemasafe": "^1.3.0", + "@sveltejs/kit": "1.x || 2.x", + "@typeschema/class-validator": "^0.3.0", + "@vinejs/vine": "^1.8.0 || ^2.0.0 || ^3.0.0", + "arktype": ">=2.0.0-rc.23", + "class-validator": "^0.14.1", + "effect": "^3.13.7", + "joi": "^17.13.1", + "superstruct": "^2.0.2", + "svelte": "3.x || 4.x || >=5.0.0-next.51", + "typebox": "^1.0.36", + "valibot": "^1.0.0", + "yup": "^1.4.0", + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@exodus/schemasafe": { + "optional": true + }, + "@typeschema/class-validator": { + "optional": true + }, + "@vinejs/vine": { + "optional": true + }, + "arktype": { + "optional": true + }, + "class-validator": { + "optional": true + }, + "effect": { + "optional": true + }, + "joi": { + "optional": true + }, + "superstruct": { + "optional": true + }, + "typebox": { + "optional": true + }, + "valibot": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "frontend/node_modules/tailwindcss": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz", + "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==", + "license": "MIT", + "peer": true + }, + "frontend/node_modules/vite": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, @@ -2438,7 +2796,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -3201,7 +3558,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.2.tgz", "integrity": "sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==", - "dev": true, "license": "Apache License 2.0", "optional": true, "dependencies": { @@ -3219,7 +3575,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true, "license": "0BSD", "optional": true }, @@ -4318,609 +4673,110 @@ "dev": true, "license": "MIT" }, - "node_modules/paneforge": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/paneforge/-/paneforge-1.0.2.tgz", - "integrity": "sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA==", - "dev": true, - "license": "MIT", - "dependencies": { - "runed": "^0.23.4", - "svelte-toolbelt": "^0.9.2" - }, - "peerDependencies": { - "svelte": "^5.29.0" - } - }, - "node_modules/paneforge/node_modules/runed": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", - "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/huntabyte", - "https://github.com/sponsors/tglide" - ], - "dependencies": { - "esm-env": "^1.0.0" - }, - "peerDependencies": { - "svelte": "^5.7.0" - } - }, - "node_modules/paneforge/node_modules/svelte-toolbelt": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.9.3.tgz", - "integrity": "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==", - "dev": true, - "funding": [ - "https://github.com/sponsors/huntabyte" - ], - "dependencies": { - "clsx": "^2.1.1", - "runed": "^0.29.0", - "style-to-object": "^1.0.8" - }, - "engines": { - "node": ">=18", - "pnpm": ">=8.7.0" - }, - "peerDependencies": { - "svelte": "^5.30.2" - } - }, - "node_modules/paneforge/node_modules/svelte-toolbelt/node_modules/runed": { - "version": "0.29.2", - "resolved": "https://registry.npmjs.org/runed/-/runed-0.29.2.tgz", - "integrity": "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/huntabyte", - "https://github.com/sponsors/tglide" - ], - "license": "MIT", - "dependencies": { - "esm-env": "^1.0.0" - }, - "peerDependencies": { - "svelte": "^5.7.0" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/positron-components": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/positron-components/-/positron-components-1.10.0.tgz", - "integrity": "sha512-WrUZ2vDnJNH+9kbvhmUDiW6KGFNMeQ6+2qRjZGW5vYH0QGohAIPbFJZ1QpIQ+dXWNbwCvwyCq0JqasIeuensVw==", - "dev": true, - "dependencies": { - "@emoji-mart/data": "^1.2.1", - "@internationalized/date": "3.10.0", - "@lucide/svelte": "^0.552.0", - "@shikijs/langs": "^3.13.0", - "@shikijs/themes": "^3.13.0", - "@sveltejs/adapter-auto": "7.0.0", - "@sveltejs/kit": "2.48.4", - "@sveltejs/package": "2.5.4", - "@sveltejs/vite-plugin-svelte": "6.2.1", - "@tailwindcss/typography": "0.5.19", - "@tailwindcss/vite": "4.1.16", - "@tanstack/table-core": "8.21.3", - "@types/luxon": "3.7.1", - "@zxcvbn-ts/core": "^3.0.4", - "@zxcvbn-ts/language-common": "^3.0.4", - "@zxcvbn-ts/language-en": "^3.0.2", - "bits-ui": "^2.11.3", - "clsx": "^2.1.1", - "country-flag-icons": "^1.5.21", - "embla-carousel-svelte": "^8.6.0", - "formsnap": "2.0.1", - "isomorphic-dompurify": "^2.28.0", - "layerchart": "^2.0.0-next.38", - "luxon": "3.7.2", - "mode-watcher": "^1.1.0", - "package-manager-detector": "^1.3.0", - "paneforge": "^1.0.2", - "prettier": "3.6.2", - "prettier-plugin-svelte": "3.4.0", - "prettier-plugin-tailwindcss": "0.7.1", - "publint": "0.3.15", - "runed": "^0.35.1", - "shiki": "3.14.0", - "svelte": "5.43.2", - "svelte-check": "4.3.3", - "svelte-easy-crop": "^5.0.0", - "svelte-sonner": "^1.0.5", - "svelte-tel-input": "^3.6.0", - "svelte-toolbelt": "^0.10.5", - "sveltekit-superforms": "2.28.0", - "tailwind-merge": "^3.3.1", - "tailwind-variants": "^3.1.1", - "tailwindcss": "4.1.16", - "tw-animate-css": "1.4.0", - "typescript": "5.9.3", - "vaul-svelte": "^1.0.0-next.7", - "vite": "7.1.12", - "yeezy-dates": "^1.0.1", - "zod": "4.1.12" - } - }, - "node_modules/positron-components/node_modules/@internationalized/date": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz", - "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/positron-components/node_modules/@lucide/svelte": { - "version": "0.552.0", - "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.552.0.tgz", - "integrity": "sha512-8wQF1YUKgaXiFidPdHM5NKESArKHLrgf8A1EAOjvqRmdVlL2KFsPxTchez/lMUOJKxXeDgrRKfXQuaN1O5KlhQ==", - "dev": true, - "license": "ISC", - "peerDependencies": { - "svelte": "^5" - } - }, - "node_modules/positron-components/node_modules/@sveltejs/kit": { - "version": "2.48.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.48.4.tgz", - "integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@sveltejs/acorn-typescript": "^1.0.5", - "@types/cookie": "^0.6.0", - "acorn": "^8.14.1", - "cookie": "^0.6.0", - "devalue": "^5.3.2", - "esm-env": "^1.2.2", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "mrmime": "^2.0.0", - "sade": "^1.8.1", - "set-cookie-parser": "^2.6.0", - "sirv": "^3.0.0" - }, - "bin": { - "svelte-kit": "svelte-kit.js" - }, - "engines": { - "node": ">=18.13" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - } - } - }, - "node_modules/positron-components/node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", - "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", - "debug": "^4.4.1", - "deepmerge": "^4.3.1", - "magic-string": "^0.30.17", - "vitefu": "^1.1.1" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24" - }, - "peerDependencies": { - "svelte": "^5.0.0", - "vite": "^6.3.0 || ^7.0.0" - } - }, - "node_modules/positron-components/node_modules/@tailwindcss/typography": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", - "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "6.0.10" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" - } - }, - "node_modules/positron-components/node_modules/bits-ui": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.14.2.tgz", - "integrity": "sha512-YqpAJj/nRTZjf7IlgUC3QlepVZ7YFiAQWpZaYUOAZFW5Py+g5DYkhEDTdNFI5SReo7l1rct/nRpMK4pfL9Xffw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.1", - "@floating-ui/dom": "^1.7.1", - "esm-env": "^1.1.2", - "runed": "^0.35.1", - "svelte-toolbelt": "^0.10.6", - "tabbable": "^6.2.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/huntabyte" - }, - "peerDependencies": { - "@internationalized/date": "^3.8.1", - "svelte": "^5.33.0" - } - }, - "node_modules/positron-components/node_modules/prettier-plugin-tailwindcss": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.1.tgz", - "integrity": "sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.19" - }, - "peerDependencies": { - "@ianvs/prettier-plugin-sort-imports": "*", - "@prettier/plugin-hermes": "*", - "@prettier/plugin-oxc": "*", - "@prettier/plugin-pug": "*", - "@shopify/prettier-plugin-liquid": "*", - "@trivago/prettier-plugin-sort-imports": "*", - "@zackad/prettier-plugin-twig": "*", - "prettier": "^3.0", - "prettier-plugin-astro": "*", - "prettier-plugin-css-order": "*", - "prettier-plugin-jsdoc": "*", - "prettier-plugin-marko": "*", - "prettier-plugin-multiline-arrays": "*", - "prettier-plugin-organize-attributes": "*", - "prettier-plugin-organize-imports": "*", - "prettier-plugin-sort-imports": "*", - "prettier-plugin-svelte": "*" - }, - "peerDependenciesMeta": { - "@ianvs/prettier-plugin-sort-imports": { - "optional": true - }, - "@prettier/plugin-hermes": { - "optional": true - }, - "@prettier/plugin-oxc": { - "optional": true - }, - "@prettier/plugin-pug": { - "optional": true - }, - "@shopify/prettier-plugin-liquid": { - "optional": true - }, - "@trivago/prettier-plugin-sort-imports": { - "optional": true - }, - "@zackad/prettier-plugin-twig": { - "optional": true - }, - "prettier-plugin-astro": { - "optional": true - }, - "prettier-plugin-css-order": { - "optional": true - }, - "prettier-plugin-jsdoc": { - "optional": true - }, - "prettier-plugin-marko": { - "optional": true - }, - "prettier-plugin-multiline-arrays": { - "optional": true - }, - "prettier-plugin-organize-attributes": { - "optional": true - }, - "prettier-plugin-organize-imports": { - "optional": true - }, - "prettier-plugin-sort-imports": { - "optional": true - }, - "prettier-plugin-svelte": { - "optional": true - } - } - }, - "node_modules/positron-components/node_modules/svelte": { - "version": "5.43.2", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.2.tgz", - "integrity": "sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "@jridgewell/sourcemap-codec": "^1.5.0", - "@sveltejs/acorn-typescript": "^1.0.5", - "@types/estree": "^1.0.5", - "acorn": "^8.12.1", - "aria-query": "^5.3.1", - "axobject-query": "^4.1.0", - "clsx": "^2.1.1", - "esm-env": "^1.2.1", - "esrap": "^2.1.0", - "is-reference": "^3.0.3", - "locate-character": "^3.0.0", - "magic-string": "^0.30.11", - "zimmerframe": "^1.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/positron-components/node_modules/svelte-check": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz", - "integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==", + "node_modules/paneforge": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/paneforge/-/paneforge-1.0.2.tgz", + "integrity": "sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "chokidar": "^4.0.1", - "fdir": "^6.2.0", - "picocolors": "^1.0.0", - "sade": "^1.7.4" - }, - "bin": { - "svelte-check": "bin/svelte-check" + "runed": "^0.23.4", + "svelte-toolbelt": "^0.9.2" }, - "engines": { - "node": ">= 18.0.0" + "peerDependencies": { + "svelte": "^5.29.0" + } + }, + "node_modules/paneforge/node_modules/runed": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": ">=5.0.0" + "svelte": "^5.7.0" } }, - "node_modules/positron-components/node_modules/sveltekit-superforms": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/sveltekit-superforms/-/sveltekit-superforms-2.28.0.tgz", - "integrity": "sha512-kHoROSmf5bzK9r261z2op5OPF5tRhxqQQOQc8dS6CatKAUDr3o94Fjjv7AwjD9i4FplyXI+OyTo6hGOXQ3qCKQ==", + "node_modules/paneforge/node_modules/svelte-toolbelt": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.9.3.tgz", + "integrity": "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==", "dev": true, "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ciscoheat" - }, - { - "type": "ko-fi", - "url": "https://ko-fi.com/ciscoheat" - }, - { - "type": "paypal", - "url": "https://www.paypal.com/donate/?hosted_button_id=NY7F5ALHHSVQS" - } + "https://github.com/sponsors/huntabyte" ], - "license": "MIT", "dependencies": { - "devalue": "^5.3.2", - "memoize-weak": "^1.0.2", - "ts-deepmerge": "^7.0.3" + "clsx": "^2.1.1", + "runed": "^0.29.0", + "style-to-object": "^1.0.8" }, - "optionalDependencies": { - "@exodus/schemasafe": "^1.3.0", - "@finom/zod-to-json-schema": "^3.24.11", - "@gcornut/valibot-json-schema": "^0.42.0", - "@typeschema/class-validator": "^0.3.0", - "@vinejs/vine": "^3.0.1", - "arktype": "^2.1.22", - "class-validator": "^0.14.2", - "effect": "^3.18.1", - "joi": "^17.13.3", - "json-schema-to-ts": "^3.1.1", - "superstruct": "^2.0.2", - "typebox": "^1.0.36", - "valibot": "^1.1.0", - "yup": "^1.7.1", - "zod": "^4.1.11" + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" }, "peerDependencies": { - "@exodus/schemasafe": "^1.3.0", - "@sveltejs/kit": "1.x || 2.x", - "@typeschema/class-validator": "^0.3.0", - "@vinejs/vine": "^1.8.0 || ^2.0.0 || ^3.0.0", - "arktype": ">=2.0.0-rc.23", - "class-validator": "^0.14.1", - "effect": "^3.13.7", - "joi": "^17.13.1", - "superstruct": "^2.0.2", - "svelte": "3.x || 4.x || >=5.0.0-next.51", - "typebox": "^1.0.36", - "valibot": "^1.0.0", - "yup": "^1.4.0", - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@exodus/schemasafe": { - "optional": true - }, - "@typeschema/class-validator": { - "optional": true - }, - "@vinejs/vine": { - "optional": true - }, - "arktype": { - "optional": true - }, - "class-validator": { - "optional": true - }, - "effect": { - "optional": true - }, - "joi": { - "optional": true - }, - "superstruct": { - "optional": true - }, - "typebox": { - "optional": true - }, - "valibot": { - "optional": true - }, - "yup": { - "optional": true - }, - "zod": { - "optional": true - } + "svelte": "^5.30.2" } }, - "node_modules/positron-components/node_modules/tailwindcss": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz", - "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==", + "node_modules/paneforge/node_modules/svelte-toolbelt/node_modules/runed": { + "version": "0.29.2", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.29.2.tgz", + "integrity": "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==", "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], "license": "MIT", - "peer": true + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } }, - "node_modules/positron-components/node_modules/vite": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", - "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" + "entities": "^6.0.0" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/positron-components/node_modules/zod": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", - "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "engines": { + "node": ">=12" + }, "funding": { - "url": "https://github.com/sponsors/colinhacks" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postcss": { @@ -5437,7 +5293,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "optional": true, "engines": { @@ -5457,7 +5312,6 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -5995,7 +5849,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peer": true, "bin": { From 67c8b8d95ef9e0d0434044b8450943cf8eb9a407 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Wed, 19 Nov 2025 20:10:42 +0100 Subject: [PATCH 10/15] feat: added ssl options --- frontend/package.json | 2 +- .../deployments/postgres/create/+page.svelte | 2 +- .../deployments/postgres/create/Backup.svelte | 2 +- .../postgres/create/Connection.svelte | 82 ++++++--- .../postgres/create/Monitoring.svelte | 23 ++- .../postgres/create/Resources.svelte | 45 ++--- .../postgres/create/SslFile.svelte | 110 ++++++++++++ .../postgres/create/schema.svelte.ts | 157 ++++-------------- frontend/vite.config.ts | 1 + package-lock.json | 8 +- 10 files changed, 246 insertions(+), 186 deletions(-) create mode 100644 frontend/src/routes/deployments/postgres/create/SslFile.svelte diff --git a/frontend/package.json b/frontend/package.json index b5b0f60..8f7728c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.7", "bits-ui": "^2.11.0", - "positron-components": "1.11.0", + "positron-components": "1.12.1", "svelte": "5.39.9", "svelte-check": "4.3.2", "tailwind-merge": "^3.3.1", diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index d45a745..0b2da3b 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -32,7 +32,7 @@ import Advanced from './Advanced.svelte'; interface StageProps { - initialValue?: any; + initialValue: any; onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; diff --git a/frontend/src/routes/deployments/postgres/create/Backup.svelte b/frontend/src/routes/deployments/postgres/create/Backup.svelte index 159f285..2ee3760 100644 --- a/frontend/src/routes/deployments/postgres/create/Backup.svelte +++ b/frontend/src/routes/deployments/postgres/create/Backup.svelte @@ -38,7 +38,7 @@ {...props} key="backups_enabled" label="Enable Backups" - onCheckedChange={(checked) => (backups_enabled = checked)} + onCheckedChange={(checked: boolean) => (backups_enabled = checked)} /> {#if backups_enabled} import { BaseForm, - FormInput, - FormSelect, FormSwitch, type FormValue } from 'positron-components/components/form'; import type { ComponentProps, Snippet } from 'svelte'; - import { connection, CertSource, RefType } from './schema.svelte'; + import { connection, CertSource } from './schema.svelte'; + import SslFile from './SslFile.svelte'; + import { ConnectorType, type SystemInfo } from '$lib/backend/postgres.svelte'; interface Props { - initialValue?: FormValue; + initialValue: FormValue; onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; + sys_info?: SystemInfo; } - let { initialValue, onsubmit, footer, isLoading }: Props = $props(); + let { initialValue, onsubmit, footer, isLoading, sys_info }: Props = $props(); + + let effectiveInitialValue = $derived.by(() => { + let val = { ...initialValue }; + if (sys_info?.connector === ConnectorType.Docker) { + if (!val.ssl_cert_source) val.ssl_cert_source = [CertSource.File]; + if (!val.ssl_key_source) val.ssl_key_source = [CertSource.File]; + if (!val.ssl_ca_source) val.ssl_ca_source = [CertSource.File]; + } + return val; + }); let form: BaseForm | undefined = $state(); let ssl_enabled = $state(initialValue?.ssl_enabled ?? false); @@ -31,9 +42,10 @@ schema={connection} {onsubmit} {footer} - {initialValue} + initialValue={effectiveInitialValue} bind:this={form} bind:isLoading + enctype="multipart/form-data" > {#snippet children({ props })} + {#if sys_info?.connector === ConnectorType.Docker} +

+ Note: This only changes the IP bind, it does not open firewall ports or + route traffic. +

+ {/if} (ssl_enabled = v)} /> {#if ssl_enabled} - ({ label: v, value: v }))} + selectKey="ssl_cert_source" + textKey="ssl_cert_text" + fileKey="ssl_cert_file" + refTypeKey="ssl_cert_ref_type" + refNameKey="ssl_cert_ref_name" + refKeyKey="ssl_cert_ref_key" + hostFilePathKey="ssl_cert_host_file_path" + initialValue={effectiveInitialValue} + {sys_info} /> - - - - ({ label: v, value: v }))} + selectKey="ssl_key_source" + textKey="ssl_key_text" + fileKey="ssl_key_file" + refTypeKey="ssl_key_ref_type" + refNameKey="ssl_key_ref_name" + refKeyKey="ssl_key_ref_key" + hostFilePathKey="ssl_key_host_file_path" + key={true} + initialValue={effectiveInitialValue} + {sys_info} /> - (ssl_ca_enabled = v)} /> {#if ssl_ca_enabled} - ({ label: v, value: v }))} + {/if} {/if} diff --git a/frontend/src/routes/deployments/postgres/create/Monitoring.svelte b/frontend/src/routes/deployments/postgres/create/Monitoring.svelte index 271ddcf..9e47cfe 100644 --- a/frontend/src/routes/deployments/postgres/create/Monitoring.svelte +++ b/frontend/src/routes/deployments/postgres/create/Monitoring.svelte @@ -6,15 +6,17 @@ } from 'positron-components/components/form'; import type { ComponentProps, Snippet } from 'svelte'; import { monitoring } from './schema.svelte'; + import { ConnectorType, type SystemInfo } from '$lib/backend/postgres.svelte'; interface Props { initialValue?: FormValue; onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; + sys_info?: SystemInfo; } - let { initialValue, onsubmit, footer, isLoading }: Props = $props(); + let { initialValue, onsubmit, footer, isLoading, sys_info }: Props = $props(); let form: BaseForm | undefined = $state(); let monitoring_enabled = $state(initialValue?.monitoring_enabled ?? false); @@ -37,19 +39,26 @@ {...props} key="monitoring_enabled" label="Enable Monitoring" - onCheckedChange={(v) => (monitoring_enabled = v)} + onCheckedChange={(v: boolean) => (monitoring_enabled = v)} /> {#if monitoring_enabled} - + {#if sys_info?.connector === ConnectorType.Kubernetes} + + {/if} + {#if sys_info?.connector === ConnectorType.Docker} +

+ Note: This only changes the IP bind, it does not manage any firewall. +

+ {/if} {/if} {/snippet} diff --git a/frontend/src/routes/deployments/postgres/create/Resources.svelte b/frontend/src/routes/deployments/postgres/create/Resources.svelte index d0d8651..7d82e95 100644 --- a/frontend/src/routes/deployments/postgres/create/Resources.svelte +++ b/frontend/src/routes/deployments/postgres/create/Resources.svelte @@ -7,15 +7,17 @@ } from 'positron-components/components/form'; import type { ComponentProps, Snippet } from 'svelte'; import { resources, units } from './schema.svelte'; + import { ConnectorType, type SystemInfo } from '$lib/backend/postgres.svelte'; interface Props { initialValue?: FormValue; onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; + sys_info?: SystemInfo; } - let { initialValue, onsubmit, footer, isLoading }: Props = $props(); + let { initialValue, onsubmit, footer, isLoading, sys_info }: Props = $props(); let form: BaseForm | undefined = $state(); @@ -69,24 +71,29 @@ data={Object.keys(units).map((unit) => ({ value: unit, label: unit }))} /> -
- - ({ value: unit, label: unit }))} - /> -
+ {#if sys_info?.connector === ConnectorType.Kubernetes} +
+ + ({ + value: unit, + label: unit + }))} + /> +
+ {/if} + import { + FormFile, + FormInput, + FormSelect, + type FormPath, + type FormPathLeaves, + type FormValue, + type SuperForm + } from 'positron-components/components/form'; + import { CertSource, connection, RefType } from './schema.svelte'; + import { ConnectorType, type SystemInfo } from '$lib/backend/postgres.svelte'; + + type Value = FormValue; + type Key = FormPath; + + interface Props { + formData: SuperForm; + disabled: boolean; + initialValue?: Value; + selectKey: Key; + textKey: Key; + fileKey: FormPathLeaves; + refTypeKey: Key; + refNameKey: Key; + refKeyKey: Key; + hostFilePathKey: Key; + key?: boolean; + sys_info?: SystemInfo; + } + + let { + selectKey, + textKey, + fileKey, + refTypeKey, + refNameKey, + refKeyKey, + hostFilePathKey, + initialValue, + key, + sys_info, + ...props + }: Props = $props(); + + //@ts-ignore + let src = $state(initialValue?.[selectKey]?.[0] as CertSource | undefined); + + + { + if (sys_info?.connector === ConnectorType.Docker) { + return v !== CertSource.Reference && v !== CertSource.Auto; + } else if (sys_info?.connector === ConnectorType.Kubernetes) { + return v !== CertSource.HostFilePath; + } + return true; + }) + .map((v) => ({ label: v, value: v }))} + onSelectChange={(v) => (src = v[0])} +/> +{#if src === CertSource.Text} + +{:else if src === CertSource.File} + +{:else if src === CertSource.Reference} + ({ label: v, value: v }))} + /> + + +{:else if src === CertSource.HostFilePath} + +{/if} diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index d6ad4d1..f4d673f 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -140,147 +140,50 @@ export const connection = z }) .superRefine((data, ctx) => { if (data.ssl_enabled) { - switch (data.ssl_cert_source[0]) { - case CertSource.Text: - if (!data.ssl_cert_text) { + const check = (keyPart: string, key?: boolean) => { + // @ts-ignore + let source = data[`ssl_${keyPart}_source`][0]; + if (source !== CertSource.Auto && source !== CertSource.Reference) { + let path = + source === CertSource.Text + ? `ssl_${keyPart}_text` + : source === CertSource.File + ? `ssl_${keyPart}_file` + : `ssl_${keyPart}_host_file_path`; + // @ts-ignore + let value = data[path]; + if (!value) { ctx.addIssue({ code: 'custom', - path: ['ssl_cert_text'], - message: 'SSL certificate is required' + path: [path], + message: key + ? 'SSL key is required' + : 'SSL certificate is required' }); } - break; - case CertSource.File: - if (!data.ssl_cert_file) { + } else if (source === CertSource.Reference) { + if (!data.ssl_cert_ref_name) { ctx.addIssue({ code: 'custom', - path: ['ssl_cert_file'], - message: 'SSL certificate is required' + path: [`ssl_${keyPart}_ref_name`], + message: 'Reference name is required' }); } - break; - case CertSource.Reference: - if ( - !( - data.ssl_cert_ref_type && - data.ssl_cert_ref_name && - data.ssl_cert_ref_key - ) - ) { + if (!data.ssl_cert_ref_key) { ctx.addIssue({ code: 'custom', - path: ['ssl_cert_ref_name', 'ssl_cert_ref_key'], - message: 'SSL certificate is required' + path: [`ssl_${keyPart}_ref_key`], + message: 'Reference key is required' }); } - break; - case CertSource.HostFilePath: - if (!data.ssl_cert_host_file_path) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_cert_host_file_path'], - message: 'SSL certificate is required' - }); - } - break; - case CertSource.Auto: - break; - } + } + }; - switch (data.ssl_key_source[0]) { - case CertSource.Text: - if (!data.ssl_key_text) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_key_text'], - message: 'SSL key is required' - }); - } - break; - case CertSource.File: - if (!data.ssl_key_file) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_key_file'], - message: 'SSL key is required' - }); - } - break; - case CertSource.Reference: - if ( - !( - data.ssl_key_ref_type && - data.ssl_key_ref_name && - data.ssl_key_ref_key - ) - ) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_key_ref_name', 'ssl_key_ref_key'], - message: 'SSL key is required' - }); - } - break; - case CertSource.HostFilePath: - if (!data.ssl_key_host_file_path) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_key_host_file_path'], - message: 'SSL key is required' - }); - } - break; - case CertSource.Auto: - break; - } - } + check('cert'); + check('key', true); - if (data.ssl_ca_enabled) { - switch (data.ssl_ca_source[0]) { - case CertSource.Text: - if (!data.ssl_ca_text) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_ca_text'], - message: 'SSL CA is required' - }); - } - break; - case CertSource.File: - if (!data.ssl_ca_file) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_ca_file'], - message: 'SSL CA is required' - }); - } - break; - case CertSource.Reference: - if ( - !( - data.ssl_ca_ref_type && - data.ssl_ca_ref_name && - data.ssl_ca_ref_key - ) - ) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_ca_ref_name', 'ssl_ca_ref_key'], - message: 'SSL CA is required' - }); - } - break; - case CertSource.HostFilePath: - if (!data.ssl_ca_host_file_path) { - ctx.addIssue({ - code: 'custom', - path: ['ssl_ca_host_file_path'], - message: 'SSL CA is required' - }); - } - break; - case CertSource.Auto: - break; + if (data.ssl_ca_enabled) { + check('ca'); } } }); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index faac4ca..307875a 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,6 +3,7 @@ import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vite'; export default defineConfig({ + //@ts-ignore plugins: [tailwindcss(), sveltekit()], server: { hmr: { diff --git a/package-lock.json b/package-lock.json index b08379a..eb14707 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.7", "bits-ui": "^2.11.0", - "positron-components": "1.11.0", + "positron-components": "1.12.1", "svelte": "5.39.9", "svelte-check": "4.3.2", "tailwind-merge": "^3.3.1", @@ -182,9 +182,9 @@ } }, "frontend/node_modules/positron-components": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/positron-components/-/positron-components-1.11.0.tgz", - "integrity": "sha512-r8wgfvBBULTH+ezoDLeEhtOFggBTMrXVgbPO92VVzFEHRvVL6r7XAdI/sOE4u063ysCyMaE5nki8jl+t+Wjceg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/positron-components/-/positron-components-1.12.1.tgz", + "integrity": "sha512-M10SKWu+1btbv18nRyOG16do7/rnmQeK3Xbq3ykDxGL0K9qLqalH/MQPyPYg/Q1jnrea7GsZS7ij9MqvevP67w==", "dev": true, "dependencies": { "@emoji-mart/data": "^1.2.1", From 93992bfb492dd696051112ec499a79d56a5e437b Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:36:25 +0100 Subject: [PATCH 11/15] feat: added additional information to summary --- frontend/src/lib/backend/postgres.svelte.ts | 85 +++++++++- .../deployments/postgres/create/+page.svelte | 4 +- .../postgres/create/Monitoring.svelte | 2 +- .../postgres/create/Summary.svelte | 146 ++++++++++++++++-- .../postgres/create/schema.svelte.ts | 135 ++++++++++++++-- 5 files changed, 341 insertions(+), 31 deletions(-) diff --git a/frontend/src/lib/backend/postgres.svelte.ts b/frontend/src/lib/backend/postgres.svelte.ts index 91d9c5f..a98f3b3 100644 --- a/frontend/src/lib/backend/postgres.svelte.ts +++ b/frontend/src/lib/backend/postgres.svelte.ts @@ -1,9 +1,92 @@ import { delete_, get, post, ResponseType } from 'positron-components/backend'; -import type { Connect } from 'vite'; +import type { + CertSource, + PostgresVersion +} from '../../routes/deployments/postgres/create/schema.svelte'; export interface CreateDeployment { name: string; + namespace?: string; + version: PostgresVersion; + replicas: number; + resources: CreateDeploymentResources; + backup: CreateDeploymentBackup; + connection: CreateDeploymentConnection; + monitoring: CreateDeploymentMonitoring; + advanced: CreateDeploymentAdvanced; +} + +export interface CreateDeploymentResources { storage_mb: number; + memory_request_mb: number; + memory_limit_mb: number; + cpu_request_millicores: number; + cpu_limit_millicores: number; +} + +export type CreateDeploymentBackup = + | { + enabled: false; + } + | { + enabled: true; + schedule: string; + retention_days: number; + storage_location: string; + }; + +export type CreateDeploymentConnection = { + external_access: boolean; +} & ( + | { + ssl_enabled: false; + } + | ({ + ssl_enabled: true; + ssl_cert: SslFile; + ssl_key: SslFile; + } & ( + | { + ca_enabled: false; + } + | { + ca_enabled: true; + ssl_ca: SslFile; + } + )) +); + +export type SslFile = + | { + type: CertSource.Auto; + } + | { + type: CertSource.Text; + content: string; + } + | { + type: CertSource.Reference; + ref_name: string; + ref_key: string; + } + | { + type: CertSource.HostFilePath; + host_file_path: string; + }; + +export type CreateDeploymentMonitoring = + | { + enabled: false; + } + | { + enabled: true; + external_access: boolean; + deploy_monitoring: boolean; + }; + +export interface CreateDeploymentAdvanced { + allow_alter_system: boolean; + extra_params: Record; } export const create_deployment = async (payload: CreateDeployment) => { diff --git a/frontend/src/routes/deployments/postgres/create/+page.svelte b/frontend/src/routes/deployments/postgres/create/+page.svelte index 0b2da3b..b11f4d1 100644 --- a/frontend/src/routes/deployments/postgres/create/+page.svelte +++ b/frontend/src/routes/deployments/postgres/create/+page.svelte @@ -107,9 +107,9 @@ if (stage < stages.length - 2) { stage += 1; } else if (stage === stages.length - 2) { - stage += 1; let rawData = stages.reduce((acc, s) => ({ ...acc, ...s.data }), {}); - let data = reformatData(rawData); + let data = await reformatData(rawData); + stage += 1; stages[stage].data = data; } else { let res = await create_deployment(form as any); diff --git a/frontend/src/routes/deployments/postgres/create/Monitoring.svelte b/frontend/src/routes/deployments/postgres/create/Monitoring.svelte index 9e47cfe..732e888 100644 --- a/frontend/src/routes/deployments/postgres/create/Monitoring.svelte +++ b/frontend/src/routes/deployments/postgres/create/Monitoring.svelte @@ -51,7 +51,7 @@ {/if} {#if sys_info?.connector === ConnectorType.Docker} diff --git a/frontend/src/routes/deployments/postgres/create/Summary.svelte b/frontend/src/routes/deployments/postgres/create/Summary.svelte index 91f6b23..9acbc5c 100644 --- a/frontend/src/routes/deployments/postgres/create/Summary.svelte +++ b/frontend/src/routes/deployments/postgres/create/Summary.svelte @@ -5,15 +5,21 @@ } from 'positron-components/components/form'; import type { ComponentProps, Snippet } from 'svelte'; import { summary } from './schema.svelte'; + import { + ConnectorType, + type CreateDeployment, + type SystemInfo + } from '$lib/backend/postgres.svelte'; interface Props { - initialValue?: FormRecord; + initialValue: CreateDeployment; onsubmit: ComponentProps['onsubmit']; footer: Snippet<[{ isLoading: boolean }]>; isLoading: boolean; + sys_info?: SystemInfo; } - let { footer, initialValue, onsubmit, isLoading }: Props = $props(); + let { footer, initialValue, onsubmit, isLoading, sys_info }: Props = $props(); export const getValue = () => { return initialValue; @@ -24,21 +30,127 @@ {footer} schema={summary} {isLoading} - onsubmit={() => onsubmit(initialValue as FormRecord)} + onsubmit={() => onsubmit(initialValue as unknown as FormRecord)} > -

General:

-
-

- Name: - {initialValue?.name} -

-
-

Resources:

-
-

- Storage Size: - {initialValue?.storage_mb}MB -

+
+
+

General Information

+
+ Name: + {initialValue?.name} + {#if sys_info?.connector === ConnectorType.Kubernetes} + Namespace: + {initialValue?.namespace} + {/if} + Version: + {initialValue?.version} + Replicas: + {initialValue?.replicas} +
+
+ +
+

Resources (per replica)

+
+ Memory Request: + {initialValue?.resources.memory_request_mb} MB + Memory Limit: + {initialValue?.resources.memory_limit_mb} MB + CPU Request: + {initialValue?.resources.cpu_request_millicores}m + CPU Limit: + {initialValue?.resources.cpu_limit_millicores}m + {#if sys_info?.connector === ConnectorType.Kubernetes} + Storage Size: + {initialValue?.resources.storage_mb} MB + {/if} +
+
+ +
+

Connection

+
+ External Access: + {initialValue?.connection.external_access + ? 'Enabled' + : 'Disabled'} + SSL: + {initialValue?.connection.ssl_enabled ? 'Enabled' : 'Disabled'} + {#if initialValue?.connection.ssl_enabled} + Cert Source: + {initialValue?.connection.ssl_cert.type} + Key Source: + {initialValue?.connection.ssl_key.type} + Custom CA: + {initialValue?.connection.ca_enabled + ? 'Enabled' + : 'Disabled'} + {#if initialValue?.connection.ca_enabled} + CA Source: + {initialValue?.connection.ssl_ca.type} + {/if} + {/if} +
+
+ +
+

Backup Configuration

+
+ Backups: + {initialValue?.backup.enabled ? 'Enabled' : 'Disabled'} + {#if initialValue?.backup.enabled} + Schedule: + {initialValue?.backup.schedule} + Retention: + {initialValue?.backup.retention_days} days + Location: + {initialValue?.backup.storage_location || 'Default'} + {/if} +
+
+ +
+

Monitoring

+
+ Monitoring: + {initialValue?.monitoring.enabled ? 'Enabled' : 'Disabled'} + {#if initialValue?.monitoring.enabled} + {#if sys_info?.connector === ConnectorType.Kubernetes} + Deploy Resources: + {initialValue?.monitoring.deploy_monitoring ? 'Yes' : 'No'} + {/if} + External Access: + {initialValue?.monitoring.external_access + ? 'Enabled' + : 'Disabled'} + {/if} +
+
+ +
+

Advanced

+
+ Allow Alter System: + {initialValue?.advanced.allow_alter_system ? 'Yes' : 'No'} + {#if initialValue?.advanced.extra_params && Object.keys(initialValue.advanced.extra_params).length > 0} + Extra Params: + + {Object.entries(initialValue.advanced.extra_params) + .map(([k, v]) => `${k}=${v}`) + .join(', ')} + + {/if} +
+
-
diff --git a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts index f4d673f..9a1f897 100644 --- a/frontend/src/routes/deployments/postgres/create/schema.svelte.ts +++ b/frontend/src/routes/deployments/postgres/create/schema.svelte.ts @@ -1,4 +1,12 @@ -import type { CreateDeployment } from '$lib/backend/postgres.svelte'; +import type { + CreateDeployment, + CreateDeploymentAdvanced, + CreateDeploymentBackup, + CreateDeploymentConnection, + CreateDeploymentMonitoring, + CreateDeploymentResources, + SslFile +} from '$lib/backend/postgres.svelte'; import type { FormRecord } from 'positron-components/components/form'; import z from 'zod'; @@ -23,17 +31,124 @@ export enum PostgresVersion { V18 = '18' } -export const reformatData = (data: FormRecord): CreateDeployment => { - let storage_size = data.storage_size as number; - let storage_size_unit = (data.storage_size_unit as string[])[0]; - let storage_mb = Math.ceil( - (storage_size * (units as Record)[storage_size_unit]) / - (1000 * 1000) - ); +export const reformatData = async ( + data: FormRecord +): Promise => { + const getUnit = (key: string) => (data[key] as string[])[0]; + const calcMb = (amountKey: string, unitKey: string) => { + const amount = data[amountKey] as number; + const unit = getUnit(unitKey); + return Math.ceil( + (amount * (units as Record)[unit]) / (1000 * 1000) + ); + }; + + const resources: CreateDeploymentResources = { + storage_mb: calcMb('storage_size', 'storage_size_unit'), + memory_request_mb: calcMb( + 'memory_request_size', + 'memory_request_size_unit' + ), + memory_limit_mb: calcMb('memory_limit_size', 'memory_limit_size_unit'), + cpu_request_millicores: data.cpu_request as number, + cpu_limit_millicores: data.cpu_limit as number + }; + + const backup: CreateDeploymentBackup = data.backups_enabled + ? { + enabled: true, + schedule: data.backup_schedule as string, + retention_days: data.backup_retention as number, + storage_location: data.backup_storage_location as string + } + : { enabled: false }; + + const formatSsl = async (prefix: string): Promise => { + const source = (data[`${prefix}_source`] as CertSource[])[0]; + switch (source) { + case CertSource.Text: + return { + type: CertSource.Text, + content: data[`${prefix}_text`] as string + }; + case CertSource.File: + let file = data[`${prefix}_file`] as File; + let content = await file.text(); + + return { type: CertSource.Text, content }; + case CertSource.Reference: + return { + type: CertSource.Reference, + ref_name: data[`${prefix}_ref_name`] as string, + ref_key: data[`${prefix}_ref_key`] as string + }; + case CertSource.HostFilePath: + return { + type: CertSource.HostFilePath, + host_file_path: data[`${prefix}_host_file_path`] as string + }; + case CertSource.Auto: + return { type: CertSource.Auto }; + } + }; + + let connection: CreateDeploymentConnection = { + external_access: data.external_access as boolean, + ssl_enabled: false + }; + + if (data.ssl_enabled) { + const ssl_cert = await formatSsl('ssl_cert'); + const ssl_key = await formatSsl('ssl_key'); + + let caPart: { ca_enabled: false } | { ca_enabled: true; ssl_ca: SslFile } = + { ca_enabled: false }; + if (data.ssl_ca_enabled) { + caPart = { ca_enabled: true, ssl_ca: await formatSsl('ssl_ca') }; + } + + connection = { + external_access: data.external_access as boolean, + ssl_enabled: true, + ssl_cert, + ssl_key, + ...caPart + }; + } + + const monitoring: CreateDeploymentMonitoring = data.monitoring_enabled + ? { + enabled: true, + external_access: data.monitoring_external_access as boolean, + deploy_monitoring: data.deploy_monitoring_resources as boolean + } + : { enabled: false }; + + const advanced: CreateDeploymentAdvanced = { + allow_alter_system: data.allow_alter_system as boolean, + extra_params: (data.extra_database_parameters as string) + .split(',') + .filter((p) => p.trim().length > 0) + .reduce( + (acc, curr) => { + const [k, v] = curr.split('='); + if (k && v) acc[k.trim()] = v.trim(); + return acc; + }, + {} as Record + ) + }; return { name: data.name as string, - storage_mb + namespace: data.namespace as string, + version: (data.version as PostgresVersion[])[0], + replicas: data.replicas as number, + resources, + backup, + connection, + monitoring, + advanced }; }; @@ -190,7 +305,7 @@ export const connection = z export const monitoring = z.object({ monitoring_enabled: z.boolean().default(false), - external_access: z.boolean().default(false), + monitoring_external_access: z.boolean().default(false), deploy_monitoring_resources: z.boolean().default(true) }); From 47b675dcc66b8e188067a75ea5f42974ed8b9f94 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 20 Nov 2025 14:03:00 +0000 Subject: [PATCH 12/15] Fix code style issues with Prettier --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 8f7728c..ac0587f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,4 +53,4 @@ "lightningcss-linux-x64-gnu": "^1.30.1", "lightningcss-win32-x64-msvc": "^1.30.1" } -} \ No newline at end of file +} From 2e6673cbd60f3aaa93e1e4a856aecbd819a0ee42 Mon Sep 17 00:00:00 2001 From: ProfiiDev <92174452+Profiidev@users.noreply.github.com> Date: Thu, 20 Nov 2025 17:09:42 +0100 Subject: [PATCH 13/15] fix: use textarea for larger inputs --- .../src/lib/components/form/FormArea.svelte | 41 +++++++++++++++++++ .../postgres/create/Advanced.svelte | 7 ++-- .../postgres/create/SslFile.svelte | 6 +-- .../postgres/create/schema.svelte.ts | 26 ++++++++++-- 4 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 frontend/src/lib/components/form/FormArea.svelte diff --git a/frontend/src/lib/components/form/FormArea.svelte b/frontend/src/lib/components/form/FormArea.svelte new file mode 100644 index 0000000..342531e --- /dev/null +++ b/frontend/src/lib/components/form/FormArea.svelte @@ -0,0 +1,41 @@ + + + + + {#snippet children({ props })} + {label} +