Skip to content

refactor: custom stack implementation#208

Merged
kmendell merged 12 commits intomainfrom
refactor/custom-stack-service
May 22, 2025
Merged

refactor: custom stack implementation#208
kmendell merged 12 commits intomainfrom
refactor/custom-stack-service

Conversation

@kmendell
Copy link
Copy Markdown
Member

@kmendell kmendell commented May 20, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced confirmation dialogs for destructive actions with additional options to remove stack files and volumes.
    • Improved redeploy confirmation messaging for clarity.
    • Added dynamic checkbox support in confirmation dialogs for customizable user choices.
  • Bug Fixes

    • Corrected error message wording for removal actions.
  • Refactor

    • Updated stack management to use deploy and destroy terminology, reflecting changes in backend operations.
    • Switched stack lifecycle operations to a new, more robust service for deploying, stopping, restarting, redeploying, and destroying stacks.
  • Chores

    • Improved internal consistency and accessibility in dialogs and table column definitions.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented May 20, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This update introduces a new custom Docker stack management service, replacing previous stack lifecycle functions and API endpoints with enhanced capabilities. The confirm dialog component now supports dynamic checkboxes, allowing users to specify options such as removing stack files and volumes. Stack API methods and routes are updated to match the new service, and related UI components are adjusted to align with these backend changes. Several obsolete server-side handlers are removed or refactored, and utility functions are re-exported for external use.

Changes

Files/Groups Change Summary
src/lib/components/action-buttons.svelte Enhanced confirmation dialog for stack removal to include checkboxes for removing files/volumes, updated confirmation messages, and adjusted action handlers to accommodate new parameters and API methods.
src/lib/components/confirm-dialog/confirm-dialog.svelte
src/lib/components/confirm-dialog/index.ts
Refactored confirm dialog to support dynamic checkboxes with state tracking, added TypeScript interface for dialog store, and updated confirm action signatures to pass checkbox states.
src/lib/services/api/stack-api-service.ts Renamed start to deploy, updated API endpoints, extended destroy method to accept removeVolumes and removeFiles parameters, and added logging for these options.
src/lib/services/docker/auto-update-service.ts Swapped stack redeploy logic to use the new redeployStack from the custom service.
src/lib/services/docker/stack-custom-service.ts New file: Implements a comprehensive Docker stack management service with deploy, stop, restart, redeploy, destroy, and utility functions using Dockerode and Compose file parsing.
src/lib/services/docker/stack-service.ts Exported utility functions for stack directory, compose file, env file, healthcheck normalization, and YAML parsing. Removed all stack lifecycle management functions (restart, redeploy, destroy, remove).
src/routes/api/containers/[containerId]/logs/stream/+server.ts Refactored log streaming handler to use a ReadableStream with improved error and lifecycle management, removing previous error wrapper and container existence check.
src/routes/api/stacks/[stackId]/deploy/+server.ts New file: Added POST handler to deploy a stack using the new deployStack service.
src/routes/api/stacks/[stackId]/destroy/+server.ts Updated to use destroyStack with support for removeFiles and removeVolumes query parameters, and revised response messages.
src/routes/api/stacks/[stackId]/down/+server.ts Switched to using the new stopStack function from the custom service for stack stopping logic.
src/routes/api/stacks/[stackId]/pull/+server.ts Removed a redundant comment; no code or logic changes.
src/routes/api/stacks/[stackId]/redeploy/+server.ts Swapped redeploy logic to use the new redeployStack from the custom service.
src/routes/api/stacks/[stackId]/restart/+server.ts Changed import to use restartStack from the new custom service; logic unchanged.
src/routes/api/stacks/[stackId]/stop/+server.ts Deleted: Removed POST handler for stopping stacks; logic now handled elsewhere.
src/routes/stacks/+page.svelte Updated stack action calls to use deploy instead of start, and changed the UniversalTable column accessor from 'name' to 'Name'.
src/routes/stacks/[stackId]/+page.server.ts Removed all server-side stack action handlers and related imports, leaving only the stack loading logic.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kmendell kmendell marked this pull request as ready for review May 21, 2025 15:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (4)
src/routes/api/stacks/[stackId]/down/+server.ts (2)

13-17: ⚠️ Potential issue

Error messages don't match the operation being performed.

The error messages still refer to "starting" a stack instead of "stopping" it, which doesn't align with the actual operation being performed by the stopStack function.

-		console.error(`API Error starting stack ${id}:`, result.error);
+		console.error(`API Error stopping stack ${id}:`, result.error);
 		const response: ApiErrorResponse = {
 			success: false,
-			error: result.error.message || 'Failed to start stack',
+			error: result.error.message || 'Failed to stop stack',
 			code: ApiErrorCode.INTERNAL_SERVER_ERROR,
 			details: result.error

23-26: ⚠️ Potential issue

Success message doesn't match the operation being performed.

The success message indicates "Stack started successfully" but this endpoint is for stopping a stack.

 return json({
 	success: true,
-	message: `Stack started successfully`
+	message: `Stack stopped successfully`
 });
src/lib/services/docker/stack-service.ts (2)

94-105: 🛠️ Refactor suggestion

⚠️ Potential issue

Sanitize stackId more rigorously to avoid directory-traversal & collision issues

path.basename() strips traversal tokens (../) but also discards every path segment but the last one, so an attacker could craft two different IDs (../../foo and foo) that both resolve to the same directory, or even override another legitimate stack.
It is safer to:

  1. Reject IDs that are not plain file/dir names (^[a-zA-Z0-9._-]+$ or a similar tight regex).
  2. Throw instead of silently coercing the ID – this will surface mis-usage early.
-const safeId = path.basename(stackId); // Use path.basename for safety
-if (safeId !== stackId) {
-  console.warn(`Original stackId "${stackId}" was sanitized to "${safeId}". Ensure this is expected.`);
-}
-return path.join(stacksDirAbs, safeId);
+if (!/^[A-Za-z0-9_.-]+$/.test(stackId)) {
+  throw new Error(`Invalid stack id "${stackId}" – contains unsupported characters or path separators.`);
+}
+return path.join(stacksDirAbs, stackId);

1630-1687: 💡 Verification agent

🧩 Analysis chain

Unresolved ${VAR} placeholders are only logged – consider fail-fast to avoid broken deployments

normalizeHealthcheckTest logs a critical message when a container_name still contains ${VAR} but continues execution.
If a variable is actually missing, docker will refuse to create the container later and the user receives a much more obscure error. Failing early here will save time and make the root cause explicit.

-// depending on desired behavior, you might throw an error here
-// throw new Error(`Unresolved variable in container_name …`);
+throw new Error(
+  `Unresolved variable in container_name for service '${serviceName}': ${service.container_name}. ` +
+  `Make sure the corresponding environment variable is defined.`
+);

🏁 Script executed:

#!/bin/bash
# Locate all references to normalizeHealthcheckTest to assess impact of throwing an error
rg -n "normalizeHealthcheckTest" -n .

Length of output: 1213


Fail fast on unresolved ${VAR} in container_name by throwing an error
Currently, normalizeHealthcheckTest logs a critical message when it detects an unresolved variable in service.container_name but then continues processing. This defers the failure to Docker with an opaque error. To surface the root cause immediately, throw an exception instead of silently proceeding.

Locations to update:

  • src/lib/services/docker/stack-service.ts, inside the normalizeHealthcheckTest function’s if (modified) block, within the loop over doc.services.

Suggested diff:

-    if (service && typeof service.container_name === 'string' && service.container_name.includes('${')) {
-      console.error(
-        `CRITICAL: Unresolved variable in container_name for service '${serviceName}': ${service.container_name}. ` +
-        `This will likely cause Docker to fail. Ensure the environment variable is defined.`
-      );
-      // Depending on desired behavior, you might throw an error here
-      // throw new Error(`Unresolved variable in container_name for service '${serviceName}': ${service.container_name}`);
-    }
+    if (service && typeof service.container_name === 'string' && service.container_name.includes('${')) {
+      throw new Error(
+        `Unresolved variable in container_name for service '${serviceName}': ${service.container_name}. ` +
+        `Make sure the corresponding environment variable is defined.`
+      );
+    }
🧰 Tools
🪛 Biome (1.9.4)

[error] 1650-1650: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🧹 Nitpick comments (16)
src/routes/api/containers/[containerId]/logs/stream/+server.ts (3)

36-38: Use optional chaining for cleaner code.

Consider using optional chaining for more concise code.

-					if (logStream && logStream.destroy) {
-						logStream.destroy();
-					}
+					logStream?.destroy?.();
🧰 Tools
🪛 Biome (1.9.4)

[error] 36-36: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


48-48: Avoid using any type for better type safety.

Replace any with a more specific error type for better type safety.

-					} catch (error: any) {
+					} catch (error: unknown) {

If you need to access properties like error.code, add a type guard:

if (error instanceof Error && 'code' in error) {
  if (error.code === 'ERR_INVALID_STATE') {
    // ...
  }
}

85-87: Use optional chaining for cleaner code.

As with the previous instance, optional chaining would make this code more concise.

-			if (self.logStream && self.logStream.destroy) {
-				self.logStream.destroy();
-			}
+			self.logStream?.destroy?.();
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/routes/api/stacks/[stackId]/deploy/+server.ts (2)

12-21: Terminology mismatch in error message.

The error message refers to "starting" a stack, but the function and endpoint are about "deploying" a stack. This could create confusion in logs and error tracking.

-		console.error(`API Error starting stack ${id}:`, result.error);
+		console.error(`API Error deploying stack ${id}:`, result.error);
		const response: ApiErrorResponse = {
			success: false,
-			error: result.error.message || 'Failed to start stack',
+			error: result.error.message || 'Failed to deploy stack',
			code: ApiErrorCode.INTERNAL_SERVER_ERROR,
			details: result.error
		};

23-27: Terminology mismatch in success message.

The success message should use "deployed" instead of "started" to maintain consistency with the endpoint name and function.

	return json({
		success: true,
-		message: `Stack started successfully`
+		message: `Stack deployed successfully`
	});
src/routes/api/stacks/[stackId]/destroy/+server.ts (2)

24-32: HTTP status code may not be appropriate for all error cases.

The error handler returns status 409 (Conflict) for all errors, but a general failure might be better represented as 500 (Internal Server Error), reserving 409 for specific conflict scenarios.

-		return json(response, { status: 409 });
+		return json(response, { status: 500 });

41-47: Terminology inconsistency in error message.

While other messages were updated to use "destroy" terminology, this error message still uses "remove".

		const response: ApiErrorResponse = {
			success: false,
-			error: 'Failed to remove stack',
+			error: 'Failed to destroy stack',
			code: ApiErrorCode.BAD_REQUEST
		};
src/lib/services/api/stack-api-service.ts (2)

19-32: Remove debugging console logs from production code.

The destroy method contains console logging statements that should be removed before deploying to production.

	async destroy(id: string, removeVolumes = false, removeFiles = false) {
-		console.log('API service - removeVolumes:', removeVolumes, 'removeFiles:', removeFiles);
-
		const queryParams = {
			removeVolumes: removeVolumes ? 'true' : 'false',
			removeFiles: removeFiles ? 'true' : 'false'
		};

-		console.log('Query params:', queryParams);
-
		const res = await this.api.delete(`/stacks/${id}/destroy`, {
			params: queryParams
		});
		return res.data;
	}

22-31: Simplify query parameter handling.

The query parameters approach could be optimized to only include the parameters when they're true, reducing request size.

	const queryParams = {
-		removeVolumes: removeVolumes ? 'true' : 'false',
-		removeFiles: removeFiles ? 'true' : 'false'
+		...(removeVolumes && { removeVolumes: 'true' }),
+		...(removeFiles && { removeFiles: 'true' })
	};
src/lib/components/confirm-dialog/confirm-dialog.svelte (2)

9-27: Implement state management with potential improvements

The code adds reactive state management for checkboxes in the confirm dialog, which works well. However, there are a couple of improvements to consider:

  1. The console.log statement on line 24 should be removed before production.
  2. There appears to be duplicate initialization logic for checkbox states.
 let checkboxStates = $state<Record<string, boolean>>({});

 $effect(() => {
 	if ($confirmDialogStore.open && $confirmDialogStore.checkboxes) {
 		const newStates: Record<string, boolean> = {};

 		for (const checkbox of $confirmDialogStore.checkboxes) {
 			newStates[checkbox.id] = Boolean(checkbox.initialState);
 		}

 		checkboxStates = newStates;
 	}
 });

 function handleConfirm() {
-	console.log('Final checkbox states before confirm:', checkboxStates);
 	$confirmDialogStore.confirm.action(checkboxStates);
 	$confirmDialogStore.open = false;
 }

43-59: Improve checkbox rendering logic

The checkbox rendering implementation works, but has unnecessary conditional logic in lines 48-52 that can be simplified.

 <!-- Checkboxes -->
 {#if $confirmDialogStore.checkboxes && $confirmDialogStore.checkboxes.length > 0}
 	<div class="flex flex-col gap-3 pt-4 pb-2 mt-4 border-t border-border">
 		{#each $confirmDialogStore.checkboxes as checkbox (checkbox.id)}
 			<div class="flex items-center space-x-2">
-				{#if checkboxStates[checkbox.id] !== undefined}
-					<Checkbox id={checkbox.id} bind:checked={checkboxStates[checkbox.id]} aria-labelledby={`${checkbox.id}-label`} />
-				{:else}
-					<Checkbox id={checkbox.id} checked={false} onchange={(e) => (checkboxStates[checkbox.id] = true)} aria-labelledby={`${checkbox.id}-label`} />
-				{/if}
+				<Checkbox id={checkbox.id} bind:checked={checkboxStates[checkbox.id]} aria-labelledby={`${checkbox.id}-label`} />
 				<Label id={`${checkbox.id}-label`} for={checkbox.id} class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
 					{checkbox.label}
 				</Label>
 			</div>
 		{/each}
 	</div>
 {/if}

The conditional rendering is unnecessary since the $effect hook already initializes all checkbox states. The fallback case could lead to unexpected behavior by forcibly setting the state to true.

src/lib/components/action-buttons.svelte (1)

59-67: Remove debug logging code

The code contains debug console.log statements that should be removed before deployment to production.

 confirm: {
 	label: type === 'stack' ? 'Destroy' : 'Remove',
 	destructive: true,
 	action: async (checkboxStates) => {
-		console.log('Debug - received checkbox states:', checkboxStates);
-
 		// Ensure these are proper booleans
 		const removeFiles = checkboxStates['removeFiles'] === true;
 		const removeVolumes = checkboxStates['removeVolumes'] === true;
-
-		console.log('Debug - removeFiles:', removeFiles, 'removeVolumes:', removeVolumes);
-
 		isLoading.remove = true;
src/routes/stacks/[stackId]/+page.server.ts (1)

84-107: Consider removing commented code

The file contains a large block of commented-out code that was likely part of the previous implementation. Since this is part of a refactoring effort, it would be cleaner to remove this code entirely rather than keeping it as comments.

-// export const actions: Actions = {
-// 	update: async ({ params, request }) => {
-// 		const { stackId } = params;
-// 		const formData = await request.formData();
-
-// 		const name = formData.get('name')?.toString() || '';
-// 		const composeContent = formData.get('composeContent')?.toString() || '';
-// 		const autoUpdate = formData.get('autoUpdate') === 'on';
-
-// 		const result = await tryCatch(updateStack(stackId, { name, composeContent, autoUpdate }));
-// 		if (!result.error) {
-// 			return {
-// 				success: true,
-// 				message: 'Stack updated successfully'
-// 			};
-// 		} else {
-// 			console.error('Error updating stack:', result.error);
-// 			return {
-// 				success: false,
-// 				error: result.error instanceof Error ? result.error.message : 'Failed to update stack'
-// 			};
-// 		}
-// 	}
-// };

This would improve code maintenance and readability. If needed, you can always reference this code from version control history.

src/lib/services/docker/stack-custom-service.ts (3)

298-325: Race-condition when removing an existent container – add force: true & wait for stop

createAndStartContainers removes a pre-existing container:

if (existingContainers[0].State === 'running') {
  await container.stop();
}
await container.remove();

Docker may still have the container in removal in progress state; a rapid subsequent createContainer with the same name can fail with Conflict. The container name is already in use.
Using stop({t:10}) and remove({force: true}), then polling until the container disappears (or just retry createContainer on 409) hardens the flow.


548-595: Use optional chaining to silence undefined-access & simplify code

Static analysis already flagged the composeData.volumes && composeData.volumes[source] pattern. Optional chaining keeps the intent while avoiding accidental undefined look-ups.

-if (composeData.volumes && composeData.volumes[source]) {
+if (composeData.volumes?.[source]) {

You can apply the same pattern in a few sibling expressions inside this block.

🧰 Tools
🪛 Biome (1.9.4)

[error] 581-581: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


311-315: docker.listContainers filter format differs across calls – standardise to avoid subtle bugs

Here you send a plain object:

filters: { name: [containerName] }

Whereas other calls (cleanupFailedDeployment, restartStack) JSON-stringify the filters.
Both ways might work today thanks to dockerode’s implicit serialisation, yet they rely on undocumented behaviour and complicate grepping for identical logic.
For consistency (and to match Docker’s spec) always pass JSON.stringify({...}).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e01d7eb and 2d24f1e.

📒 Files selected for processing (17)
  • src/lib/components/action-buttons.svelte (3 hunks)
  • src/lib/components/confirm-dialog/confirm-dialog.svelte (2 hunks)
  • src/lib/components/confirm-dialog/index.ts (3 hunks)
  • src/lib/services/api/stack-api-service.ts (2 hunks)
  • src/lib/services/docker/auto-update-service.ts (2 hunks)
  • src/lib/services/docker/stack-custom-service.ts (1 hunks)
  • src/lib/services/docker/stack-service.ts (5 hunks)
  • src/routes/api/containers/[containerId]/logs/stream/+server.ts (1 hunks)
  • src/routes/api/stacks/[stackId]/deploy/+server.ts (1 hunks)
  • src/routes/api/stacks/[stackId]/destroy/+server.ts (3 hunks)
  • src/routes/api/stacks/[stackId]/down/+server.ts (2 hunks)
  • src/routes/api/stacks/[stackId]/pull/+server.ts (0 hunks)
  • src/routes/api/stacks/[stackId]/redeploy/+server.ts (1 hunks)
  • src/routes/api/stacks/[stackId]/restart/+server.ts (1 hunks)
  • src/routes/api/stacks/[stackId]/stop/+server.ts (0 hunks)
  • src/routes/stacks/+page.svelte (2 hunks)
  • src/routes/stacks/[stackId]/+page.server.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • src/routes/api/stacks/[stackId]/pull/+server.ts
  • src/routes/api/stacks/[stackId]/stop/+server.ts
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/routes/api/stacks/[stackId]/down/+server.ts (3)
src/lib/utils/try-catch.ts (1)
  • tryCatch (14-21)
src/lib/services/docker/stack-custom-service.ts (1)
  • stopStack (830-840)
src/lib/services/docker/stack-service.ts (1)
  • stopStack (1160-1263)
src/lib/services/docker/auto-update-service.ts (1)
src/lib/services/docker/stack-custom-service.ts (1)
  • redeployStack (845-867)
src/routes/api/stacks/[stackId]/deploy/+server.ts (4)
src/lib/utils/try-catch.ts (1)
  • tryCatch (14-21)
src/lib/services/docker/stack-custom-service.ts (1)
  • deployStack (31-108)
src/lib/types/errors.type.ts (1)
  • ApiErrorResponse (19-25)
src/lib/types/errors.ts (1)
  • ApiErrorCode (4-17)
src/routes/api/stacks/[stackId]/destroy/+server.ts (3)
src/lib/utils/try-catch.ts (1)
  • tryCatch (14-21)
src/lib/services/docker/stack-custom-service.ts (1)
  • destroyStack (927-1018)
src/lib/types/errors.type.ts (1)
  • ApiErrorResponse (19-25)
🪛 Biome (1.9.4)
src/routes/api/containers/[containerId]/logs/stream/+server.ts

[error] 36-36: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 85-85: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/lib/services/docker/stack-custom-service.ts

[error] 581-581: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (27)
src/routes/api/containers/[containerId]/logs/stream/+server.ts (5)

2-8: Good interface design for stream handling.

The new custom interface provides clean type safety for the stream source while properly extending the standard UnderlyingDefaultSource. This helps TypeScript understand the additional logStream property needed for cleanup operations.


11-13: Improved error handling with early return.

The early validation pattern for containerId enhances user experience by returning a descriptive 400 response immediately, rather than letting the error occur later in the request lifecycle.


19-79: Well-structured stream implementation with robust error handling.

The ReadableStream implementation properly manages the Docker log stream lifecycle with appropriate error handling and resource cleanup. The state tracking with isControllerClosed prevents potential issues with operations on closed controllers.

🧰 Tools
🪛 Biome (1.9.4)

[error] 36-36: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


81-89: Good resource cleanup on client disconnect.

The cancel handler properly cleans up resources when clients disconnect, preventing memory leaks and hanging connections.

🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


91-97: Correct SSE headers setup.

The response includes all the necessary headers for Server-Sent Events (SSE), ensuring proper browser handling of the streaming response.

src/routes/api/stacks/[stackId]/restart/+server.ts (1)

3-3: The import switch to stack-custom-service looks good.

The change properly aligns with the broader refactoring effort to centralize Docker stack operations in the new custom service implementation.

src/routes/api/stacks/[stackId]/down/+server.ts (2)

1-1: Import updated correctly to use the new stack-custom-service.

This change properly aligns with the refactoring effort to use the new custom stack implementation.


10-10: Function call updated correctly to use stopStack.

The implementation now uses the new stopStack function from the custom service.

src/lib/services/docker/auto-update-service.ts (2)

2-3: Imports properly separated between services.

The imports have been correctly refactored to maintain the list/get operations from stack-service while importing the redeploy functionality from the new stack-custom-service.


152-152: Function call updated to use the new redeployStack implementation.

The call has been correctly updated to use the new implementation from stack-custom-service.

src/routes/api/stacks/[stackId]/redeploy/+server.ts (2)

3-3: Import correctly updated to use the new implementation.

The change properly aligns with the broader refactoring effort to centralize Docker stack operations in the new custom service.


10-10: Function call updated to use the new redeployStack implementation.

The call has been correctly updated to use the new implementation from stack-custom-service.

src/routes/api/stacks/[stackId]/deploy/+server.ts (2)

1-5: Good import structure and utility usage.

The imports look appropriate, with proper use of the new custom stack service and utility functions for error handling.


7-11: LGTM: Clean implementation of the request handler.

The handler correctly extracts the stackId from params and uses tryCatch for robust error handling.

src/routes/stacks/+page.svelte (1)

48-51: API method usage updated appropriately.

The change from stackApi.start(id) to stackApi.deploy(id) correctly aligns with the renamed API method.

src/routes/api/stacks/[stackId]/destroy/+server.ts (4)

3-5: Good update to use the new service implementation.

Import updated properly to use the new custom stack service implementation.


7-10: Well-implemented query parameter handling.

The handler now correctly extracts the optional removal preferences from query parameters.


21-22: Good implementation of the new destroyStack function call.

The function now properly passes the removal preferences to the service.


37-39: Good enhanced success message.

The success message now conditionally includes information about file removal when applicable.

src/lib/services/api/stack-api-service.ts (2)

4-7: Method renamed to match the new endpoint pattern.

The start method has been appropriately renamed to deploy to match the updated API endpoint.


9-12: Endpoint updated to match the new API structure.

The endpoint for stopping a stack has been correctly updated to use /down instead of /stop.

src/lib/components/action-buttons.svelte (2)

81-84: Consider adding a conditional check for checkboxes

The checkboxes are currently always added to the confirm dialog for stacks, but they're only relevant for stack destruction. Consider conditionally adding them only for stack removal.

The implementation is well-structured, providing clear options for users when destroying a stack. This enhances the UX by explicitly allowing users to choose whether to remove associated files and volumes.


112-113: API method name change from "start" to "deploy"

The function now correctly uses stackApi.deploy() instead of stackApi.start() for stacks, aligning with the backend API changes.

This change correctly implements the new API naming convention, ensuring consistent terminology between frontend and backend.

src/routes/stacks/[stackId]/+page.server.ts (1)

1-7: Import cleanup after refactoring

The imports have been appropriately updated to reflect the architectural changes.

The simplified imports align with the move from server-side actions to API endpoints, removing unnecessary imports that are no longer used after the refactoring.

src/lib/components/confirm-dialog/index.ts (3)

4-18: Well-structured TypeScript interface for dialog store

The implementation of a proper TypeScript interface for the confirm dialog store is a significant improvement. It clearly defines the structure and enhances type safety throughout the codebase.

The interface is well-designed with optional and required properties clearly marked. The addition of the checkboxes array with typed properties (id, label, initialState) provides good structure for the enhanced functionality.


42-48: Updated action callback signature for checkbox states

The action callback now correctly accepts a record of checkbox states, aligning with the implementation in the dialog component.

This change ensures type safety when passing checkbox states from the dialog component to the callback function. The signature action: (checkboxStates: Record<string, boolean>) => void properly defines the expected input structure.


58-59: Store update includes checkboxes property

The confirmDialogStore update now correctly includes the checkboxes property, ensuring it's passed to the dialog component.

This small but crucial change ensures that the checkbox configurations are properly passed to the store when opening the dialog.

@kmendell kmendell merged commit 5391c9d into main May 22, 2025
8 checks passed
@kmendell kmendell deleted the refactor/custom-stack-service branch May 22, 2025 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant