diff --git a/internal/api/projects.go b/internal/api/projects.go index 482f3cf..f4a6121 100644 --- a/internal/api/projects.go +++ b/internal/api/projects.go @@ -169,6 +169,26 @@ func (s *Server) updateProject(w http.ResponseWriter, r *http.Request) { // deleteProject handles DELETE /api/projects/{id}. func (s *Server) deleteProject(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") + + // Clean up Docker containers and proxy routes before deleting the project. + ctx := r.Context() + stages, _ := s.store.GetStagesByProjectID(id) + for _, stage := range stages { + instances, _ := s.store.GetInstancesByStageID(stage.ID) + for _, inst := range instances { + if inst.ContainerID != "" { + if err := s.docker.RemoveContainer(ctx, inst.ContainerID, true); err != nil { + slog.Warn("delete project: remove container", "container", inst.ContainerID, "error", err) + } + } + if inst.ProxyRouteID != "" { + if err := s.proxyProvider.DeleteRoute(ctx, inst.ProxyRouteID); err != nil { + slog.Warn("delete project: delete proxy route", "route", inst.ProxyRouteID, "error", err) + } + } + } + } + if err := s.store.DeleteProject(id); err != nil { if errors.Is(err, store.ErrNotFound) { respondNotFound(w, "project") diff --git a/web/src/lib/components/InstanceCard.svelte b/web/src/lib/components/InstanceCard.svelte index e4bea80..1b66672 100644 --- a/web/src/lib/components/InstanceCard.svelte +++ b/web/src/lib/components/InstanceCard.svelte @@ -13,17 +13,20 @@ interface Props { instance: Instance; projectId: string; + domain?: string; onchange?: () => void; } - const { instance, projectId, onchange }: Props = $props(); + const { instance, projectId, domain = '', onchange }: Props = $props(); let loading = $state(false); let error = $state(''); let confirmAction = $state<'stop' | 'restart' | 'remove' | null>(null); const subdomainUrl = $derived( - instance.subdomain ? `https://${instance.subdomain}` : '' + instance.subdomain && domain + ? `https://${instance.subdomain}.${domain}` + : instance.subdomain ? `https://${instance.subdomain}` : '' ); const timeSinceCreated = $derived(() => { diff --git a/web/src/routes/projects/[id]/+page.svelte b/web/src/routes/projects/[id]/+page.svelte index e888f7a..78e2f9e 100644 --- a/web/src/routes/projects/[id]/+page.svelte +++ b/web/src/routes/projects/[id]/+page.svelte @@ -106,6 +106,7 @@ } } let tagsLoading = $state(false); + let settingsDomain = $state(''); let showDeleteConfirm = $state(false); @@ -142,6 +143,11 @@ } catch { deploys = []; } + + try { + const settings = await api.getSettings(); + settingsDomain = settings.domain ?? ''; + } catch { /* non-critical */ } } catch (e) { error = e instanceof Error ? e.message : $t('projectDetail.loadFailed'); } finally { @@ -486,6 +492,7 @@ {/each}