-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: Add Dokploy deployment with comprehensive linking and control options #324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
feat: Add Dokploy deployment with comprehensive linking and control options #324
Conversation
…ol options Add support for linking existing Dokploy applications and granular deployment control: ## New Features ### Application Linking - Support linking to ALL Dokploy resource types (applications, compose, mariadb, mongo, mysql, postgres, redis) - Parse nested project.all API structure to extract all application types from all environments - Display app type badges and environment names in selection dropdown - Store application type metadata in deployment info for future reference - Allow per-service project selection (each service can link to different projects) ### Deployment Control - Add "Deploy immediately after setup" toggle (defaults to ON) - Toggle only visible for "Create New" mode (not applicable for linking) - Automatically trigger deployment via application.deploy API when enabled - Show different success messages based on deployment vs. setup-only mode - Position toggle at bottom of service card for better UX ### Service Management - Add "Select All / Deselect All" button for bulk service toggling - Calculate and display services left to configure - Smart button text based on configuration state: - All linking: "Link Applications (count)" - All creating: "Configure Apps" - Mixed mode: "Link/Configure (count)" - Track configured vs. unconfigured services in real-time ### UI/UX Improvements - Visual service status indicators (enabled services highlighted) - Clear deployment action descriptions - Responsive button states based on service configuration - Error handling for deployment trigger failures (non-blocking) ## Technical Changes ### Type Definitions - Add DokployApplicationType enum for all resource types - Update DokployServiceDeployment to include applicationType field - Enhance dokployProjects state type with app type and environment metadata ### API Integration - Refactor fetchDokployProjects to parse nested project.all structure - Add deployment trigger logic via application.deploy endpoint - Graceful error handling for deployment triggers ### Computed State - Add configuredServices tracker (services ready to deploy/link) - Add servicesLeftToDeploy calculator - Add isAllLinked and isAllCreateNew mode detectors - Optimize service state calculations with useMemo ## Files Modified - apps/frontend/src/renderer/components/DokployDeployModal.tsx (main implementation) - apps/frontend/src/shared/types/dokploy.ts (type definitions) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
📝 WalkthroughWalkthroughAdds Dokploy deployment support to the frontend: new IPC handlers and channels, preload API, types, renderer UI (Deploy view, DokployDeployModal, settings), i18n for English/French, browser mocks, and local persistence of deployment metadata (.dokploy.json). Changes
Sequence DiagramsequenceDiagram
autonumber
actor User
participant Sidebar
participant DeployView as Deploy
participant Modal as DokployDeployModal
participant Preload as Preload API
participant Main as Main (IPC Handlers)
participant Dokploy as Dokploy API
Note over Preload,Main: New IPC channels: dokploy:api, dokploy:read-env, dokploy:save-deployment,\n dokploy:get-deployment, dokploy:delete-deployment
User->>Sidebar: Click "Deploy"
Sidebar->>DeployView: Render(projectId)
DeployView->>Preload: request deploymentProviders
alt no providers
DeployView->>User: Prompt open settings
else providers exist
User->>DeployView: Open modal
DeployView->>Modal: open(projectId)
Modal->>Preload: dokployReadEnv(servicePath)
Preload->>Main: IPC invoke (dokploy:read-env)
Main->>Modal: return env vars (via Preload)
Modal->>Preload: dokployApi(GET servers, GET github providers, etc.)
Preload->>Main: IPC invoke (dokploy:api)
Main->>Dokploy: HTTP GET/POST requests
Dokploy-->>Main: responses
Main-->>Preload: IPC responses
Preload-->>Modal: data
User->>Modal: Configure & Click Deploy
Modal->>Preload: dokployApi (create project/apps, configure GH, deploy)
Preload->>Main: IPC invoke (dokploy:api POSTs)
Main->>Dokploy: HTTP POSTs
Dokploy-->>Main: results
Main-->>Preload: responses
Preload-->>Modal: deployment results
Modal->>Preload: dokploySaveDeployment (.dokploy.json)
Preload->>Main: IPC invoke (dokploy:save-deployment)
Main-->>Preload: write result
Preload-->>Modal: success / error
Modal->>User: Show success / error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used📓 Path-based instructions (2)apps/frontend/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/frontend/**/*.{ts,tsx}⚙️ CodeRabbit configuration file
Files:
🧬 Code graph analysis (2)apps/frontend/src/renderer/App.tsx (1)
apps/frontend/src/shared/types/ipc.ts (1)
🔇 Additional comments (6)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (25)
apps/frontend/src/main/ipc-handlers/dokploy-handlers.tsapps/frontend/src/main/ipc-handlers/index.tsapps/frontend/src/preload/api/index.tsapps/frontend/src/preload/api/modules/dokploy-api.tsapps/frontend/src/renderer/App.tsxapps/frontend/src/renderer/components/Deploy.tsxapps/frontend/src/renderer/components/DokployDeployModal.tsxapps/frontend/src/renderer/components/Sidebar.tsxapps/frontend/src/renderer/components/settings/AppSettings.tsxapps/frontend/src/renderer/components/settings/DeploymentSettings.tsxapps/frontend/src/renderer/components/settings/index.tsapps/frontend/src/renderer/components/ui/dialog.tsxapps/frontend/src/renderer/lib/browser-mock.tsapps/frontend/src/shared/constants/ipc.tsapps/frontend/src/shared/i18n/index.tsapps/frontend/src/shared/i18n/locales/en/deploy.jsonapps/frontend/src/shared/i18n/locales/en/navigation.jsonapps/frontend/src/shared/i18n/locales/en/settings.jsonapps/frontend/src/shared/i18n/locales/fr/deploy.jsonapps/frontend/src/shared/i18n/locales/fr/navigation.jsonapps/frontend/src/shared/i18n/locales/fr/settings.jsonapps/frontend/src/shared/types/dokploy.tsapps/frontend/src/shared/types/index.tsapps/frontend/src/shared/types/ipc.tsapps/frontend/src/shared/types/settings.ts
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/frontend/src/**/*.{ts,tsx}: Always use translation keys withuseTranslation()for all user-facing text in React/TypeScript frontend components - use formatnamespace:section.key(e.g.,navigation:items.githubPRs)
Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys fromapps/frontend/src/shared/i18n/locales/
Files:
apps/frontend/src/renderer/components/settings/index.tsapps/frontend/src/renderer/components/settings/AppSettings.tsxapps/frontend/src/renderer/components/ui/dialog.tsxapps/frontend/src/renderer/components/Deploy.tsxapps/frontend/src/renderer/lib/browser-mock.tsapps/frontend/src/renderer/components/settings/DeploymentSettings.tsxapps/frontend/src/shared/types/settings.tsapps/frontend/src/shared/types/ipc.tsapps/frontend/src/renderer/App.tsxapps/frontend/src/main/ipc-handlers/dokploy-handlers.tsapps/frontend/src/shared/constants/ipc.tsapps/frontend/src/shared/types/index.tsapps/frontend/src/renderer/components/Sidebar.tsxapps/frontend/src/renderer/components/DokployDeployModal.tsxapps/frontend/src/preload/api/index.tsapps/frontend/src/shared/i18n/index.tsapps/frontend/src/shared/types/dokploy.tsapps/frontend/src/preload/api/modules/dokploy-api.tsapps/frontend/src/main/ipc-handlers/index.ts
apps/frontend/**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.
Files:
apps/frontend/src/renderer/components/settings/index.tsapps/frontend/src/renderer/components/settings/AppSettings.tsxapps/frontend/src/renderer/components/ui/dialog.tsxapps/frontend/src/renderer/components/Deploy.tsxapps/frontend/src/renderer/lib/browser-mock.tsapps/frontend/src/renderer/components/settings/DeploymentSettings.tsxapps/frontend/src/shared/types/settings.tsapps/frontend/src/shared/types/ipc.tsapps/frontend/src/renderer/App.tsxapps/frontend/src/main/ipc-handlers/dokploy-handlers.tsapps/frontend/src/shared/constants/ipc.tsapps/frontend/src/shared/types/index.tsapps/frontend/src/renderer/components/Sidebar.tsxapps/frontend/src/renderer/components/DokployDeployModal.tsxapps/frontend/src/preload/api/index.tsapps/frontend/src/shared/i18n/index.tsapps/frontend/src/shared/types/dokploy.tsapps/frontend/src/preload/api/modules/dokploy-api.tsapps/frontend/src/main/ipc-handlers/index.ts
apps/frontend/src/shared/i18n/locales/**/*.json
📄 CodeRabbit inference engine (CLAUDE.md)
Add new translation keys to ALL language files (minimum:
en/*.jsonandfr/*.json) inapps/frontend/src/shared/i18n/locales/
Files:
apps/frontend/src/shared/i18n/locales/fr/deploy.jsonapps/frontend/src/shared/i18n/locales/fr/navigation.jsonapps/frontend/src/shared/i18n/locales/en/navigation.jsonapps/frontend/src/shared/i18n/locales/en/deploy.jsonapps/frontend/src/shared/i18n/locales/en/settings.jsonapps/frontend/src/shared/i18n/locales/fr/settings.json
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Add new translation keys to ALL language files (minimum: `en/*.json` and `fr/*.json`) in `apps/frontend/src/shared/i18n/locales/`
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Always use translation keys with `useTranslation()` for all user-facing text in React/TypeScript frontend components - use format `namespace:section.key` (e.g., `navigation:items.githubPRs`)
Applied to files:
apps/frontend/src/renderer/components/settings/AppSettings.tsxapps/frontend/src/shared/i18n/index.ts
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Add new translation keys to ALL language files (minimum: `en/*.json` and `fr/*.json`) in `apps/frontend/src/shared/i18n/locales/`
Applied to files:
apps/frontend/src/shared/i18n/locales/fr/deploy.jsonapps/frontend/src/shared/i18n/locales/fr/navigation.jsonapps/frontend/src/shared/i18n/locales/en/navigation.jsonapps/frontend/src/shared/i18n/locales/en/deploy.jsonapps/frontend/src/shared/i18n/locales/en/settings.jsonapps/frontend/src/shared/i18n/locales/fr/settings.jsonapps/frontend/src/shared/i18n/index.ts
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from `apps/frontend/src/shared/i18n/locales/`
Applied to files:
apps/frontend/src/shared/i18n/index.ts
🪛 Biome (2.1.2)
apps/frontend/src/renderer/components/Deploy.tsx
[error] 57-60: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🔇 Additional comments (27)
apps/frontend/src/renderer/components/ui/dialog.tsx (1)
62-70: LGTM! Close button styling improvements enhance UX.The updated close button styling provides better visual feedback and interaction:
- Larger clickable area with clear visual states
- Background and border make the button more discoverable
pointer-events-noneon the icon ensures click events properly register on the button element- Smooth color transitions improve polish
apps/frontend/src/shared/i18n/locales/fr/navigation.json (1)
17-18: LGTM!The French translation for the new "deploy" navigation item is correctly added alongside "worktrees", matching the structure in the English locale file.
apps/frontend/src/shared/i18n/index.ts (1)
12-12: LGTM!The deploy namespace is correctly integrated: imports from both EN and FR locales, registered in the resources object for both languages, and added to the namespace array. This follows the established i18n patterns.
Also applies to: 22-22, 34-35, 44-45, 56-56
apps/frontend/src/shared/i18n/locales/fr/deploy.json (1)
1-65: LGTM!The French deploy translation file is comprehensive and mirrors the English structure. All deployment UI strings including error messages, loading states, and form labels are properly translated.
apps/frontend/src/renderer/components/DokployDeployModal.tsx (2)
863-1083: Deployment flow is well-structured.The
handleDeployfunction properly sequences project creation, per-service app creation, GitHub configuration, domain setup, and deployment triggering. Non-blocking error handling for domain, env vars, and deployment trigger failures is intentional and appropriate for UX resilience.
1789-1808: Footer button logic is adaptive and correct.The primary action button correctly adapts based on deployment mode (all linked, all create new, or mixed), with appropriate disabled states and dynamic labels.
apps/frontend/src/shared/types/settings.ts (1)
104-105: LGTM!The
deploymentProvidersoptional field is correctly integrated intoAppSettings, allowing Dokploy configuration to be stored alongside other app settings.apps/frontend/src/renderer/components/settings/index.ts (1)
11-11: LGTM!The
DeploymentSettingscomponent is correctly exported from the settings barrel, following the established pattern for other settings components.apps/frontend/src/shared/types/index.ts (1)
19-19: LGTM!The Dokploy types are correctly re-exported from the central types barrel, following the established pattern and placement order.
apps/frontend/src/shared/i18n/locales/en/settings.json (1)
39-43: Verify French translation keys are added for deployment section.Per coding guidelines, new translation keys must be added to ALL language files. Confirm that
apps/frontend/src/shared/i18n/locales/fr/settings.jsoncontains matching deployment keys corresponding to the additions in the English file (sections.deployment and top-level deployment object with Dokploy configuration).apps/frontend/src/renderer/components/settings/AppSettings.tsx (1)
19-21: LGTM! Clean integration of deployment settings.The deployment settings are properly integrated following the existing patterns. Translation keys are correctly used, type safety is maintained, and the component follows the established navigation structure.
Also applies to: 40-40, 56-56, 70-70, 179-180
apps/frontend/src/main/ipc-handlers/index.ts (1)
31-31: LGTM! Standard IPC handler integration.The Dokploy IPC handlers are registered following the established pattern used by other handlers. The placement and export structure are consistent with the existing codebase.
Also applies to: 102-103, 126-127
apps/frontend/src/renderer/App.tsx (1)
47-47: LGTM! Deploy component properly integrated.The Deploy component is integrated following the same pattern as other view components (GitHubPRs, Changelog, Worktrees). The conditional rendering logic and callback wiring are correct.
Also applies to: 696-704
apps/frontend/src/renderer/components/Sidebar.tsx (1)
20-21: LGTM! Sidebar integration follows established patterns.The deploy view is properly integrated into the sidebar:
- SidebarView type extended correctly
- Navigation item follows the existing structure
- Keyboard shortcut 'Y' is unique and appropriate
- Translation key follows the established pattern
Also applies to: 54-54, 77-78
apps/frontend/src/preload/api/index.ts (1)
11-11: LGTM! DokployAPI properly integrated into preload API surface.The Dokploy API is correctly integrated following the established pattern used by other APIs:
- Proper imports and type definitions
- ElectronAPI interface extended correctly
- Factory function integrated with spread operator
- Consistent export structure for types and creators
Also applies to: 22-23, 37-37, 52-53, 66-67
apps/frontend/src/shared/i18n/locales/en/navigation.json (1)
17-18: Ensure the French translation file is updated with the "deploy" key.The new "deploy" translation key added to
apps/frontend/src/shared/i18n/locales/en/navigation.jsonmust also be added toapps/frontend/src/shared/i18n/locales/fr/navigation.jsonper the translation guidelines requiring all keys across all language files.apps/frontend/src/shared/i18n/locales/en/deploy.json (1)
1-65: Ensure French translation file exists and is complete.The
apps/frontend/src/shared/i18n/locales/fr/deploy.jsonfile must exist and contain all keys from the English version. Per the translation guidelines, every new translation key must be added to ALL language files (minimum:en/*.jsonandfr/*.json). Verify the French file has matching keys for all entries including nested objects undernoProviders,providers,modal, andmodal.error.apps/frontend/src/renderer/components/Deploy.tsx (1)
1-84: Verify French translation file exists for deploy namespace.Ensure that
apps/frontend/src/shared/i18n/locales/fr/deploy.jsonexists and contains all keys used in this component:
noProviders.title,noProviders.description,noProviders.openSettingstitle,descriptionproviders.dokploy.title,providers.dokploy.descriptionPer project requirements, all translation keys must be added to all language files (minimum:
en/deploy.jsonandfr/deploy.json).apps/frontend/src/renderer/lib/browser-mock.ts (1)
113-119: LGTM!The Dokploy browser mocks are correctly implemented with appropriate return types matching the
DokployApiResponseinterface. ThedokployApicorrectly returns an error state for browser context while other operations return success with sensible defaults.apps/frontend/src/shared/constants/ipc.ts (1)
363-368: LGTM!The new Dokploy IPC channel constants follow the established naming convention (
dokploy:*) and are properly defined within theas constassertion for type safety.apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx (2)
31-62: LGTM!The component properly uses
useTranslationhooks for bothsettingsandcommonnamespaces. State management is well-organized with clear separation between add/edit modes. TheupdateDokployAccountshelper correctly propagates changes through the parent settings callback.
133-136: LGTM!The
maskApiKeyfunction provides appropriate masking for API keys, showing only the first and last 4 characters for longer keys while completely masking short keys. This is a good security practice for displaying sensitive credentials.apps/frontend/src/shared/i18n/locales/fr/settings.json (1)
244-261: LGTM!The French translations for the Dokploy deployment settings are complete and follow the established translation structure. All keys match the expected structure used in
DeploymentSettings.tsx. Based on learnings, ensure the corresponding English translations inen/settings.jsoncontain identical keys.apps/frontend/src/shared/types/ipc.ts (1)
594-599: LGTM!The Dokploy operations are correctly added to the
ElectronAPIinterface with proper typing. The method signatures align with theDokployAPIinterface in the preload module and the IPC handlers in the main process.apps/frontend/src/preload/api/modules/dokploy-api.ts (1)
1-24: LGTM!The Dokploy preload API module is well-structured with a clean separation between the interface definition and factory function. The IPC channel mappings are correct, and type safety is properly maintained through the generic type parameter on
dokployApi.apps/frontend/src/shared/types/dokploy.ts (2)
14-29: LGTM!The
DokployGitHubProviderinterface is well-designed with support for both the nestedgithubobject structure and legacy top-level fields. The inline comments clearly document the purpose of each field variant.
101-130: LGTM!The
DokployServiceDeploymentandDokployProjectDeploymentinterfaces are well-structured for persisting deployment state. Using ISO date strings fordeployedAt,createdAt, andupdatedAtensures consistent serialization. TheapplicationTypefield properly references the union type for type safety.
| const response = await fetch(url, options); | ||
|
|
||
| if (!response.ok) { | ||
| const errorText = await response.text(); | ||
| return { | ||
| success: false, | ||
| error: `API error: ${response.status} - ${errorText}` | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a timeout to the fetch request to prevent indefinite hangs.
The fetch call has no timeout configured. If the Dokploy server is unresponsive, the request will hang indefinitely, potentially blocking the UI or causing resource exhaustion.
🔎 Proposed fix using AbortController
+const DOKPLOY_REQUEST_TIMEOUT_MS = 30000; // 30 seconds
async function makeDokployRequest<T>(
baseUrl: string,
apiKey: string,
endpoint: string,
method: 'GET' | 'POST',
body?: Record<string, unknown>,
query?: Record<string, string>
): Promise<DokployApiResponse<T>> {
try {
// Build URL - Dokploy uses TRPC format
// baseUrl should be like https://dokploy.example.com/api
let url = `${baseUrl}/${endpoint}`;
// For GET requests with TRPC, we need to pass input as JSON in query string
if (method === 'GET') {
const input = query && Object.keys(query).length > 0 ? query : {};
url += `?input=${encodeURIComponent(JSON.stringify(input))}`;
}
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), DOKPLOY_REQUEST_TIMEOUT_MS);
const options: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey
- }
+ },
+ signal: controller.signal
};
if (method === 'POST' && body) {
options.body = JSON.stringify(body);
}
const response = await fetch(url, options);
+ clearTimeout(timeoutId);🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts around lines 45-53,
the fetch call lacks a timeout and can hang indefinitely; wrap the request with
an AbortController: create a controller, pass controller.signal to fetch, start
a setTimeout that calls controller.abort() after a chosen timeout (e.g., 10s),
and clear the timeout when fetch completes. Catch the abort error and return a
structured { success: false, error: 'Request timed out' } (or include the
abort/error message) while preserving existing API error handling for non-ok
responses. Ensure no memory leak by clearing the timer in all code paths.
| console.log('[Dokploy Backend] readEnvFile called with:', servicePath); | ||
|
|
||
| // Common env file names to check (in priority order) | ||
| const envFileNames = ['.env', '.env.local', '.env.development', '.env.production']; | ||
| let allEnvVars: Record<string, string> = {}; | ||
|
|
||
| for (const fileName of envFileNames) { | ||
| const envPath = path.join(servicePath, fileName); | ||
| console.log('[Dokploy Backend] Checking:', envPath, 'exists:', existsSync(envPath)); | ||
|
|
||
| if (existsSync(envPath)) { | ||
| try { | ||
| const content = readFileSync(envPath, 'utf-8'); | ||
| console.log(`[Dokploy Backend] Found ${fileName}, length:`, content.length); | ||
| const parsed = parseEnvFile(content); | ||
| console.log(`[Dokploy Backend] Parsed from ${fileName}:`, Object.keys(parsed)); | ||
| // Merge (earlier files take precedence) | ||
| allEnvVars = { ...parsed, ...allEnvVars }; | ||
| } catch (err) { | ||
| console.error(`[Dokploy Backend] Error reading ${fileName}:`, err); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| console.log('[Dokploy Backend] Total env vars found:', Object.keys(allEnvVars)); | ||
| return allEnvVars; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Remove or conditionally enable verbose logging in production.
The console.log statements throughout readEnvFile are useful for debugging but will clutter production logs. Consider using a debug flag or removing them.
🔎 Suggested approach
+const DEBUG_DOKPLOY = process.env.NODE_ENV === 'development';
function readEnvFile(servicePath: string): Record<string, string> {
- console.log('[Dokploy Backend] readEnvFile called with:', servicePath);
+ if (DEBUG_DOKPLOY) console.log('[Dokploy Backend] readEnvFile called with:', servicePath);
// Common env file names to check (in priority order)
const envFileNames = ['.env', '.env.local', '.env.development', '.env.production'];
let allEnvVars: Record<string, string> = {};
for (const fileName of envFileNames) {
const envPath = path.join(servicePath, fileName);
- console.log('[Dokploy Backend] Checking:', envPath, 'exists:', existsSync(envPath));
+ if (DEBUG_DOKPLOY) console.log('[Dokploy Backend] Checking:', envPath, 'exists:', existsSync(envPath));
// ... apply similar changes to other log statements
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts around lines 76 to
101, the function readEnvFile contains multiple console.log/console.error calls
that should not run unconditionally in production; remove the noisy console
statements or wrap them behind a configurable debug flag or environment check
(e.g., process.env.DEBUG or an app logger.isDebugEnabled()) and replace direct
console usage with the app's logger where available so that logging is only
emitted when debugging is enabled and errors still get logged appropriately in
production.
| // Get deployment info from project directory | ||
| ipcMain.handle( | ||
| IPC_CHANNELS.DOKPLOY_GET_DEPLOYMENT, | ||
| async (_, projectPath: string): Promise<DokployApiResponse<DokployProjectDeployment | null>> => { | ||
| try { | ||
| const filePath = path.join(projectPath, DEPLOYMENT_FILE); | ||
| if (!existsSync(filePath)) { | ||
| return { success: true, data: null }; | ||
| } | ||
| const content = readFileSync(filePath, 'utf-8'); | ||
| const deployment = JSON.parse(content) as DokployProjectDeployment; | ||
| return { success: true, data: deployment }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| error: error instanceof Error ? error.message : 'Failed to read deployment info' | ||
| }; | ||
| } | ||
| } | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Handle JSON parse errors gracefully for corrupted deployment files.
If .dokploy.json contains malformed JSON, JSON.parse will throw and the error handler will return a generic message. Consider providing a more specific error message for parse failures.
🔎 Proposed improvement
ipcMain.handle(
IPC_CHANNELS.DOKPLOY_GET_DEPLOYMENT,
async (_, projectPath: string): Promise<DokployApiResponse<DokployProjectDeployment | null>> => {
try {
const filePath = path.join(projectPath, DEPLOYMENT_FILE);
if (!existsSync(filePath)) {
return { success: true, data: null };
}
const content = readFileSync(filePath, 'utf-8');
- const deployment = JSON.parse(content) as DokployProjectDeployment;
- return { success: true, data: deployment };
+ try {
+ const deployment = JSON.parse(content) as DokployProjectDeployment;
+ return { success: true, data: deployment };
+ } catch (parseError) {
+ return {
+ success: false,
+ error: `Invalid deployment file format: ${parseError instanceof Error ? parseError.message : 'parse error'}`
+ };
+ }
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to read deployment info'
};
}
}
);🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts around lines 177 to
196, the code treats any exception from reading/parsing .dokploy.json the same
and returns a generic error message; detect JSON parse failures specifically and
return a clearer message. Change the try/catch so that after reading the file
you catch JSON.parse errors (e.g. detect instanceof SyntaxError or inspect the
error message) and return success: false with a descriptive message like
"Malformed .dokploy.json: failed to parse JSON" (including filePath for
context), while preserving the existing generic error branch for other I/O
errors.
| <button | ||
| onClick={() => setShowDokployModal(true)} | ||
| className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left" | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add explicit type="button" to the button element.
The button element should have an explicit type="button" attribute to prevent potential form submission issues if this component is ever used within a form context.
🔎 Proposed fix
<button
+ type="button"
onClick={() => setShowDokployModal(true)}
className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left"
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| onClick={() => setShowDokployModal(true)} | |
| className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left" | |
| > | |
| <button | |
| type="button" | |
| onClick={() => setShowDokployModal(true)} | |
| className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left" | |
| > |
🧰 Tools
🪛 Biome (2.1.2)
[error] 57-60: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/Deploy.tsx around lines 57 to 60, the
button element lacks an explicit type attribute which can cause it to default to
"submit" inside forms; update the JSX to add type="button" on the button element
to ensure it does not trigger form submission when clicked.
| useEffect(() => { | ||
| const loadExistingDeployment = async () => { | ||
| if (open && project?.path) { | ||
| setIsLoadingDeployment(true); | ||
| try { | ||
| const result = await window.electronAPI.dokployGetDeployment(project.path); | ||
| if (result.success && result.data) { | ||
| setExistingDeployment(result.data); | ||
| // If deployment exists, auto-select that account and switch to resync mode | ||
| setSelectedAccountId(result.data.accountId); | ||
| setStep('resync'); | ||
| } else { | ||
| setExistingDeployment(null); | ||
| } | ||
| } catch (err) { | ||
| console.error('Failed to load deployment info:', err); | ||
| setExistingDeployment(null); | ||
| } finally { | ||
| setIsLoadingDeployment(false); | ||
| } | ||
| } | ||
| }; | ||
| loadExistingDeployment(); | ||
| }, [open, project?.path]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Async effect lacks cleanup/cancellation.
This effect performs an async operation but doesn't handle component unmount. If the modal closes while loadExistingDeployment is in-flight, the state setters (setExistingDeployment, setSelectedAccountId, etc.) will be called on an unmounted component.
Consider using an abort flag or AbortController:
🔎 Suggested pattern
useEffect(() => {
+ let cancelled = false;
const loadExistingDeployment = async () => {
if (open && project?.path) {
setIsLoadingDeployment(true);
try {
const result = await window.electronAPI.dokployGetDeployment(project.path);
+ if (cancelled) return;
if (result.success && result.data) {
setExistingDeployment(result.data);
setSelectedAccountId(result.data.accountId);
setStep('resync');
} else {
setExistingDeployment(null);
}
} catch (err) {
+ if (cancelled) return;
console.error('Failed to load deployment info:', err);
setExistingDeployment(null);
} finally {
+ if (!cancelled) setIsLoadingDeployment(false);
- setIsLoadingDeployment(false);
}
}
};
loadExistingDeployment();
+ return () => { cancelled = true; };
}, [open, project?.path]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| const loadExistingDeployment = async () => { | |
| if (open && project?.path) { | |
| setIsLoadingDeployment(true); | |
| try { | |
| const result = await window.electronAPI.dokployGetDeployment(project.path); | |
| if (result.success && result.data) { | |
| setExistingDeployment(result.data); | |
| // If deployment exists, auto-select that account and switch to resync mode | |
| setSelectedAccountId(result.data.accountId); | |
| setStep('resync'); | |
| } else { | |
| setExistingDeployment(null); | |
| } | |
| } catch (err) { | |
| console.error('Failed to load deployment info:', err); | |
| setExistingDeployment(null); | |
| } finally { | |
| setIsLoadingDeployment(false); | |
| } | |
| } | |
| }; | |
| loadExistingDeployment(); | |
| }, [open, project?.path]); | |
| useEffect(() => { | |
| let cancelled = false; | |
| const loadExistingDeployment = async () => { | |
| if (open && project?.path) { | |
| setIsLoadingDeployment(true); | |
| try { | |
| const result = await window.electronAPI.dokployGetDeployment(project.path); | |
| if (cancelled) return; | |
| if (result.success && result.data) { | |
| setExistingDeployment(result.data); | |
| // If deployment exists, auto-select that account and switch to resync mode | |
| setSelectedAccountId(result.data.accountId); | |
| setStep('resync'); | |
| } else { | |
| setExistingDeployment(null); | |
| } | |
| } catch (err) { | |
| if (cancelled) return; | |
| console.error('Failed to load deployment info:', err); | |
| setExistingDeployment(null); | |
| } finally { | |
| if (!cancelled) setIsLoadingDeployment(false); | |
| } | |
| } | |
| }; | |
| loadExistingDeployment(); | |
| return () => { cancelled = true; }; | |
| }, [open, project?.path]); |
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines 205
to 228, the async useEffect launches loadExistingDeployment but does not cancel
or guard state updates when the component/unmount or modal closes; add a
cancellation guard (e.g., let cancelled = false or an AbortController) and in
the cleanup return set cancelled = true (or abort the controller), then before
each setState (setIsLoadingDeployment, setExistingDeployment,
setSelectedAccountId, setStep) check the guard and only update state when not
cancelled; if the dokployGetDeployment supports AbortSignal pass the signal to
it and handle abort errors appropriately to avoid setting state after unmount.
| if (step === 'resync' && existingDeployment) { | ||
| return ( | ||
| <div className="space-y-4"> | ||
| {/* Deployment Info */} | ||
| <div className="p-3 rounded-lg bg-muted/50 border border-border"> | ||
| <div className="flex items-center gap-2 mb-2"> | ||
| <CheckCircle2 className="h-4 w-4 text-green-500" /> | ||
| <span className="font-medium text-sm">Deployed to Dokploy</span> | ||
| </div> | ||
| <div className="text-xs text-muted-foreground space-y-1"> | ||
| <p>Project: {existingDeployment.projectName}</p> | ||
| <p>Account: {existingDeployment.accountName}</p> | ||
| <p>Last updated: {new Date(existingDeployment.updatedAt).toLocaleString()}</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Services with resync option */} | ||
| <div className="space-y-3"> | ||
| <Label className="flex items-center gap-2"> | ||
| <Package className="h-4 w-4" /> | ||
| Deployed Services | ||
| </Label> | ||
| <ScrollArea className="h-[350px] rounded-md border p-2"> | ||
| <div className="space-y-3"> | ||
| {existingDeployment.services.map((deployedService) => { | ||
| const service = services.find(s => s.key === deployedService.serviceKey); | ||
| const config = serviceConfigs[deployedService.serviceKey]; | ||
| if (!config) return null; | ||
|
|
||
| const enabledCount = Object.values(config.envVarsEnabled).filter(Boolean).length; | ||
| const totalCount = Object.keys(config.envVars).length; | ||
|
|
||
| return ( | ||
| <div key={deployedService.serviceKey} className="p-3 rounded-lg border bg-muted/30"> | ||
| <div className="flex items-center justify-between mb-2"> | ||
| <div> | ||
| <span className="font-medium text-sm">{deployedService.serviceName}</span> | ||
| <span className="text-xs text-muted-foreground ml-2"> | ||
| ({deployedService.appName}) | ||
| </span> | ||
| </div> | ||
| <Button | ||
| size="sm" | ||
| variant="outline" | ||
| onClick={() => handleResyncEnvVars(deployedService.serviceKey)} | ||
| disabled={resyncService !== null} | ||
| > | ||
| {resyncService === deployedService.serviceKey ? ( | ||
| <> | ||
| <Loader2 className="h-3 w-3 animate-spin mr-1" /> | ||
| Syncing... | ||
| </> | ||
| ) : ( | ||
| <> | ||
| <RefreshCw className="h-3 w-3 mr-1" /> | ||
| Sync Env Vars | ||
| </> | ||
| )} | ||
| </Button> | ||
| </div> | ||
|
|
||
| {/* Environment Variables */} | ||
| <div className="mt-2"> | ||
| <button | ||
| type="button" | ||
| onClick={() => toggleEnvVars(deployedService.serviceKey)} | ||
| className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors w-full" | ||
| > | ||
| {expandedEnvVars[deployedService.serviceKey] ? ( | ||
| <ChevronDown className="h-3 w-3" /> | ||
| ) : ( | ||
| <ChevronRight className="h-3 w-3" /> | ||
| )} | ||
| Environment Variables ({enabledCount}/{totalCount}) | ||
| </button> | ||
| {expandedEnvVars[deployedService.serviceKey] && ( | ||
| <div className="bg-muted/30 rounded p-2 space-y-2 max-h-48 overflow-y-auto mt-1"> | ||
| {Object.entries(config.envVars).map(([key, value]) => { | ||
| const isEnabled = config.envVarsEnabled[key] !== false; | ||
| return ( | ||
| <div key={key} className={`flex items-center gap-2 ${!isEnabled ? 'opacity-50' : ''}`}> | ||
| <Checkbox | ||
| checked={isEnabled} | ||
| onCheckedChange={(checked) => { | ||
| const newEnvVarsEnabled = { ...config.envVarsEnabled, [key]: !!checked }; | ||
| updateServiceConfig(deployedService.serviceKey, { envVarsEnabled: newEnvVarsEnabled }); | ||
| }} | ||
| className="h-4 w-4" | ||
| /> | ||
| <span className="text-xs font-mono text-foreground min-w-[120px] truncate" title={key}> | ||
| {key} | ||
| </span> | ||
| <Input | ||
| value={value} | ||
| onChange={(e) => { | ||
| const newEnvVars = { ...config.envVars, [key]: e.target.value }; | ||
| updateServiceConfig(deployedService.serviceKey, { envVars: newEnvVars }); | ||
| }} | ||
| className="h-7 text-xs font-mono flex-1" | ||
| placeholder="Value" | ||
| disabled={!isEnabled} | ||
| /> | ||
| </div> | ||
| ); | ||
| })} | ||
|
|
||
| {/* Add Custom Variable */} | ||
| <div className="flex items-center gap-2 pt-2 border-t border-border/50"> | ||
| <Input | ||
| value={customEnvKey} | ||
| onChange={(e) => setCustomEnvKey(e.target.value)} | ||
| placeholder="KEY" | ||
| className="h-7 text-xs font-mono w-[120px]" | ||
| /> | ||
| <Input | ||
| value={customEnvValue} | ||
| onChange={(e) => setCustomEnvValue(e.target.value)} | ||
| placeholder="value" | ||
| className="h-7 text-xs font-mono flex-1" | ||
| /> | ||
| <Button | ||
| size="sm" | ||
| variant="outline" | ||
| className="h-7 px-2" | ||
| onClick={() => addCustomEnvVar(deployedService.serviceKey)} | ||
| disabled={!customEnvKey.trim()} | ||
| > | ||
| Add | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| })} | ||
| </div> | ||
| </ScrollArea> | ||
| </div> | ||
|
|
||
| {error && ( | ||
| <p className="text-sm text-destructive">{error}</p> | ||
| )} | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resync UI contains hardcoded strings.
The resync step (lines 1177-1322) has multiple hardcoded strings that should use translation keys:
- Line 1184:
"Deployed to Dokploy" - Line 1187-1189:
"Project:","Account:","Last updated:" - Line 1197:
"Deployed Services" - Line 1227/1232:
"Syncing...","Sync Env Vars" - Line 1250:
"Environment Variables" - Line 1288/1295:
"KEY","value"placeholders - Line 1304:
"Add"
As per coding guidelines, add these to deploy.json locale files.
| {config.enabled && ( | ||
| <> | ||
| {/* Mode Toggle */} | ||
| <div className="flex items-center gap-2 mt-2 p-1 rounded-md bg-muted/30 border border-border/50"> | ||
| <button | ||
| type="button" | ||
| onClick={() => updateServiceConfig(service.key, { linkMode: false })} | ||
| className={`flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${ | ||
| !config.linkMode ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground' | ||
| }`} | ||
| > | ||
| Create New | ||
| </button> | ||
| <button | ||
| type="button" | ||
| onClick={() => updateServiceConfig(service.key, { linkMode: true })} | ||
| className={`flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${ | ||
| config.linkMode ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground' | ||
| }`} | ||
| > | ||
| Link Existing | ||
| </button> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mode toggle buttons use hardcoded text.
The "Create New" / "Link Existing" toggle buttons (lines 1504, 1513) use hardcoded strings. These should be translation keys like t('modal.mode.createNew') and t('modal.mode.linkExisting').
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1493 to 1515, the two mode toggle buttons render hardcoded labels "Create New"
and "Link Existing"; replace those literals with translation calls (e.g.
t('modal.mode.createNew') and t('modal.mode.linkExisting')), ensuring the
component imports/has access to the t function from your i18n hook/context and
that corresponding translation keys are added to the locale files; keep the
surrounding JSX and className logic unchanged.
| const handleDeleteAccount = (accountId: string) => { | ||
| updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId)); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider adding a confirmation dialog before deleting accounts.
The delete action immediately removes the account without user confirmation. For destructive operations involving credentials, a confirmation step helps prevent accidental deletions.
🔎 Suggested approach
+ const [accountToDelete, setAccountToDelete] = useState<string | null>(null);
const handleDeleteAccount = (accountId: string) => {
+ // Show confirmation before deleting
+ if (!window.confirm(t('deployment.dokploy.confirmDelete'))) return;
updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId));
};Alternatively, implement a proper modal confirmation dialog for a more polished UX.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleDeleteAccount = (accountId: string) => { | |
| updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId)); | |
| }; | |
| const handleDeleteAccount = (accountId: string) => { | |
| // Show confirmation before deleting | |
| if (!window.confirm(t('deployment.dokploy.confirmDelete'))) return; | |
| updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId)); | |
| }; |
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx around
lines 83 to 85, the handleDeleteAccount function immediately deletes an account
without confirmation; add a user confirmation step before calling
updateDokployAccounts. Modify the handler to prompt the user (e.g.,
window.confirm or a modal) with a clear warning including the account
identifier, and only call updateDokployAccounts(dokployAccounts.filter(...))
when the user explicitly confirms; if using a modal, manage modal open/close
state and proceed with deletion on confirm, canceling on dismissal.
| <X className="h-4 w-4" /> | ||
| <span className="sr-only">Close</span> | ||
| <X className="h-4 w-4 pointer-events-none" /> | ||
| <span className="sr-only pointer-events-none">Close</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace hardcoded "Close" text with translation key.
The hardcoded "Close" text violates the coding guidelines. Screen reader labels are user-facing content and must be internationalized.
As per coding guidelines: "Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components" and "Never use hardcoded strings in JSX/TSX files for user-facing text."
🔎 Proposed fix using translation key
First, add the translation key to all language files (minimum en/*.json and fr/*.json) in apps/frontend/src/shared/i18n/locales/. For example, in the appropriate namespace file (e.g., common.json):
{
"dialog": {
"close": "Close"
}
}Then update the component to use the translation:
+import { useTranslation } from 'react-i18next';
+
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
DialogContentProps
>(({ className, children, hideCloseButton, ...props }, ref) => (
+ const { t } = useTranslation('common');
+ return (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
...
>
{children}
{!hideCloseButton && (
<DialogPrimitive.Close
...
>
<X className="h-4 w-4 pointer-events-none" />
- <span className="sr-only pointer-events-none">Close</span>
+ <span className="sr-only pointer-events-none">{t('dialog.close')}</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
+ );
));Based on learnings, ensure the translation key is added to ALL language files, minimum en/*.json and fr/*.json.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/ui/dialog.tsx around line 71, the
screen-reader label uses a hardcoded "Close" string which must be
internationalized; import and use useTranslation() from react-i18next (or
existing i18n hook) and replace the literal with a translation lookup like
t('dialog.close') for the sr-only span, and ensure the key "dialog.close" is
added to all locale files (at minimum
apps/frontend/src/shared/i18n/locales/en/... and fr/...) under the appropriate
namespace (e.g., common.json) with the translated values.
| // Deployment provider account types | ||
| export interface DokployAccount { | ||
| id: string; | ||
| name: string; | ||
| apiKey: string; | ||
| baseUrl: string; // Default: https://app.dokploy.com/api | ||
| createdAt: Date; | ||
| } | ||
|
|
||
| export interface DeploymentProviders { | ||
| dokploy: DokployAccount[]; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider string type for createdAt to match JSON serialization.
DokployAccount.createdAt is typed as Date, but when settings are persisted to JSON and reloaded, it will become a string. This could cause type mismatches at runtime.
Consider using string (ISO format) or adding serialization/deserialization logic:
export interface DokployAccount {
id: string;
name: string;
apiKey: string;
baseUrl: string;
- createdAt: Date;
+ createdAt: string; // ISO date string
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Deployment provider account types | |
| export interface DokployAccount { | |
| id: string; | |
| name: string; | |
| apiKey: string; | |
| baseUrl: string; // Default: https://app.dokploy.com/api | |
| createdAt: Date; | |
| } | |
| export interface DeploymentProviders { | |
| dokploy: DokployAccount[]; | |
| } | |
| // Deployment provider account types | |
| export interface DokployAccount { | |
| id: string; | |
| name: string; | |
| apiKey: string; | |
| baseUrl: string; // Default: https://app.dokploy.com/api | |
| createdAt: string; // ISO date string | |
| } | |
| export interface DeploymentProviders { | |
| dokploy: DokployAccount[]; | |
| } |
🤖 Prompt for AI Agents
In apps/frontend/src/shared/types/settings.ts around lines 81 to 92,
DokployAccount.createdAt is declared as Date but JSON persistence will serialize
it to a string; change the type to string (ISO timestamp) or implement explicit
serialization/deserialization so the runtime type matches the declared type —
e.g., update the interface to use createdAt: string and ensure code that
consumes it parses new Date(createdAt) where a Date object is required, or keep
createdAt: Date and add toJSON/fromJSON conversion when saving/loading settings.
Replace hardcoded "Close" button text with i18n translation keys to comply with coding guidelines requiring all user-facing text to be internationalized.
Changes:
- Add "close" key to en/deploy.json ("Close")
- Add "close" key to fr/deploy.json ("Fermer")
- Replace hardcoded "Close" text with {t('modal.close')} in DokployDeployModal (2 instances)
Fixes compliance with i18n guidelines: "Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (8)
apps/frontend/src/renderer/components/DokployDeployModal.tsx (8)
205-228: Async effect lacks cleanup/cancellation.This effect performs an async operation but doesn't handle component unmount. If the modal closes while
loadExistingDeploymentis in-flight, the state setters will be called on an unmounted component, potentially causing memory leaks and console warnings.🔎 Suggested pattern
useEffect(() => { + let cancelled = false; const loadExistingDeployment = async () => { if (open && project?.path) { setIsLoadingDeployment(true); try { const result = await window.electronAPI.dokployGetDeployment(project.path); + if (cancelled) return; if (result.success && result.data) { setExistingDeployment(result.data); setSelectedAccountId(result.data.accountId); setStep('resync'); } else { setExistingDeployment(null); } } catch (err) { + if (cancelled) return; console.error('Failed to load deployment info:', err); setExistingDeployment(null); } finally { + if (!cancelled) setIsLoadingDeployment(false); - setIsLoadingDeployment(false); } } }; loadExistingDeployment(); + return () => { cancelled = true; }; }, [open, project?.path]);
264-329: Async service config initialization should guard against unmount.The
initializeConfigsasync function reads env vars for each service but lacks cancellation handling. If the modal closes mid-initialization, state updates will target an unmounted component.Apply the same cancellation pattern as suggested for the deployment loading effect: add a
cancelledflag and return a cleanup function that sets it totrue, then check the flag before callingsetServiceConfigs.
274-298: Remove debug console.log statements before merging.Lines 275-298 contain extensive debug logging (
console.log('[Dokploy]...')). These should be removed or replaced with a proper logging utility that can be disabled in production.Similar debug statements appear throughout the file (lines 632, 637, 643, 689, 796, 881, 982, 989, 1002, 1009, 1014, 1029, 1073).
Consider either:
- Removing all debug logs before production
- Wrapping them in a development-only guard (
if (import.meta.env.DEV)or similar)- Replacing with a project logging utility that respects environment/verbosity settings
1104-1175: Hardcoded user-facing strings violate i18n guidelines.Multiple UI strings in the deploying, success, and error step renders are hardcoded instead of using translation keys. Per coding guidelines, all user-facing text must use
useTranslation().Examples in this section:
- Line 1110:
"Configuring ${deployingService}..."(use interpolation with t())- Line 1114:
"Configured:"- Line 1128:
"Deployment Started"/"Applications Configured"- Lines 1133-1141: Success description text
- Line 1147:
"Configured services:"- Line 1171:
"Try Again"Consider adding these keys to
deploy.json:
modal.deploying.configuring(with{{service}}interpolation)modal.deploying.settingUpmodal.deploying.configuredLabelmodal.success.titleDeployedmodal.success.titleConfiguredmodal.success.descriptionDeployedmodal.success.descriptionConfiguredmodal.success.configuredServicesLabelmodal.error.tryAgainBased on learnings, all user-facing text should use translation keys.
1177-1322: Resync UI contains hardcoded strings.The resync step has multiple hardcoded strings that should use translation keys:
- Line 1184:
"Deployed to Dokploy"- Lines 1187-1189:
"Project:","Account:","Last updated:"- Line 1197:
"Deployed Services"- Lines 1227/1232:
"Syncing...","Sync Env Vars"- Line 1250:
"Environment Variables"- Lines 1288/1295:
"KEY","value"placeholders- Line 1304:
"Add"As per coding guidelines, add these to
deploy.jsonlocale files and uset()function calls throughout.Based on learnings, all user-facing text should use translation keys.
1493-1515: Mode toggle buttons use hardcoded text.The "Create New" / "Link Existing" toggle buttons (lines 1504, 1513) use hardcoded strings. These should be translation keys like
t('modal.mode.createNew')andt('modal.mode.linkExisting').Add to translation files:
"modal": { "mode": { "createNew": "Create New", "linkExisting": "Link Existing" } }Based on learnings, all user-facing text should use translation keys.
1605-1730: Create new mode UI contains hardcoded strings.The create new mode section contains multiple hardcoded strings:
- Line 1606:
"App Name"- Line 1612:
"app-name"(placeholder)- Line 1621:
"Branch"- Line 1630:
"Select branch"(placeholder)- Line 1646:
"Domain (optional)"- Line 1653:
"app.example.com"(placeholder)- Line 1671: Environment variables label with count
- Line 1710:
"Build path:"label- Line 1717:
"Deploy immediately after setup"- Line 1720: Toggle description text
All user-facing labels, placeholders, and descriptions should use translation keys from the deploy.json files.
Based on learnings, all user-facing text should use translation keys.
1520-1595: Link mode UI contains hardcoded strings.The link existing mode section contains multiple hardcoded strings:
- Line 1523:
"Loading projects..."- Line 1529:
"Dokploy Project"- Line 1541:
"Select a project"- Line 1544:
"None (skip this service)"- Line 1557:
"Application"- Line 1562:
"No applications in this project"- Line 1575:
"Select an application"These should all be moved to the translation files and accessed via
t().Based on learnings, all user-facing text should use translation keys.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
apps/frontend/src/renderer/components/DokployDeployModal.tsxapps/frontend/src/shared/i18n/locales/en/deploy.jsonapps/frontend/src/shared/i18n/locales/fr/deploy.json
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/shared/i18n/locales/**/*.json
📄 CodeRabbit inference engine (CLAUDE.md)
Add new translation keys to ALL language files (minimum:
en/*.jsonandfr/*.json) inapps/frontend/src/shared/i18n/locales/
Files:
apps/frontend/src/shared/i18n/locales/en/deploy.jsonapps/frontend/src/shared/i18n/locales/fr/deploy.json
apps/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/frontend/src/**/*.{ts,tsx}: Always use translation keys withuseTranslation()for all user-facing text in React/TypeScript frontend components - use formatnamespace:section.key(e.g.,navigation:items.githubPRs)
Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys fromapps/frontend/src/shared/i18n/locales/
Files:
apps/frontend/src/renderer/components/DokployDeployModal.tsx
apps/frontend/**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.
Files:
apps/frontend/src/renderer/components/DokployDeployModal.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Add new translation keys to ALL language files (minimum: `en/*.json` and `fr/*.json`) in `apps/frontend/src/shared/i18n/locales/`
Applied to files:
apps/frontend/src/shared/i18n/locales/en/deploy.jsonapps/frontend/src/shared/i18n/locales/fr/deploy.jsonapps/frontend/src/renderer/components/DokployDeployModal.tsx
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from `apps/frontend/src/shared/i18n/locales/`
Applied to files:
apps/frontend/src/renderer/components/DokployDeployModal.tsx
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Always use translation keys with `useTranslation()` for all user-facing text in React/TypeScript frontend components - use format `namespace:section.key` (e.g., `navigation:items.githubPRs`)
Applied to files:
apps/frontend/src/renderer/components/DokployDeployModal.tsx
🔇 Additional comments (10)
apps/frontend/src/shared/i18n/locales/fr/deploy.json (1)
1-66: LGTM! French translations are complete and properly structured.The French translation file correctly mirrors the English version's structure, with all keys present including the newly added "close" key at line 49. The translations are properly formatted and comprehensive, covering all deployment UI strings for the Dokploy integration.
Based on learnings, this follows the guideline to add new translation keys to all language files (minimum: en and fr).
apps/frontend/src/shared/i18n/locales/en/deploy.json (1)
1-66: LGTM! English translations are complete and well-structured.The English translation file provides comprehensive coverage of the Dokploy deployment UI, with clear and professional text for all user-facing strings. The structure correctly matches the French translation file, ensuring consistency across locales.
Based on learnings, this properly implements the guideline to maintain translation keys in all language files.
apps/frontend/src/renderer/components/DokployDeployModal.tsx (8)
1-68: LGTM! Imports and type definitions are well-structured.The imports are properly organized, including the necessary i18n hook (
useTranslation), UI components, and type definitions. The interface definitions (DokployDeployModalProps,DeployStep,ServiceDeployConfig) are clear and appropriately typed for the component's functionality.
69-127: LGTM! Component initialization and state management are properly implemented.The component correctly initializes the translation hook with the 'deploy' namespace and declares comprehensive state management for the complex deployment workflow. State organization is logical and covers all necessary aspects of the multi-step deployment process.
372-475: LGTM! Helper functions and handlers are well-implemented.The helper functions for managing service configurations and the async handlers for resyncing and resetting deployments are properly structured with appropriate error handling.
543-595: LGTM! Computed values are efficiently implemented.The
useMemohooks forenabledServices,configuredServices,servicesLeftToDeploy,isAllLinked, andisAllCreateNewprovide efficient derived state with proper dependencies, improving rendering performance.
597-861: LGTM! API fetch functions are properly structured.The callback functions for fetching servers, GitHub providers, and Dokploy projects are well-implemented with appropriate error handling and loading states. The nested parsing logic for extracting applications from all environments and resource types (lines 692-794) is thorough and handles all Dokploy application types correctly.
Note: Debug console.log statements in this section were already flagged in the earlier review comment.
863-1083: LGTM! Main deployment handler implements comprehensive workflow.The
handleDeployfunction correctly orchestrates the multi-step deployment process: creating projects, configuring applications per service, setting up GitHub providers with service-specific paths, configuring domains, uploading environment variables, and optionally triggering deployment. Error handling is robust, with warnings logged for non-critical failures (domains, env vars) to prevent full deployment collapse.Note: Debug console.log and hardcoded string issues in this section are flagged in separate comments.
1749-1758: LGTM! Dialog structure is properly implemented.The main Dialog component is correctly structured with proper header, content rendering, and conditional footer based on the current step. The use of the translation hook for the title and description demonstrates correct i18n usage.
896-896: Hardcoded error string should use translation key.Line 896 contains a hardcoded error message that should be moved to the translation files.
Add to translation files:
"modal": { "error": { ... "noEnvironment": "No environment found in project" } }Then update:
- throw new Error('No environment found in project'); + throw new Error(t('modal.error.noEnvironment'));Based on learnings, all user-facing text should use translation keys.
⛔ Skipped due to learnings
Learnt from: CR Repo: AndyMik90/Auto-Claude PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-12-25T18:29:32.954Z Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from `apps/frontend/src/shared/i18n/locales/`Learnt from: CR Repo: AndyMik90/Auto-Claude PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-12-25T18:29:32.954Z Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Always use translation keys with `useTranslation()` for all user-facing text in React/TypeScript frontend components - use format `namespace:section.key` (e.g., `navigation:items.githubPRs`)
| setRepoDetectionError('Could not parse GitHub repository URL'); | ||
| } | ||
| } else { | ||
| setRepoDetectionError('No GitHub remote found in project'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded error strings should use translation keys.
Lines 185 and 188 contain hardcoded error messages that should be moved to the translation files for consistency with the i18n guidelines.
🔎 Suggested fix
Add these keys to en/deploy.json and fr/deploy.json:
"modal": {
...
"error": {
...
"parseRepo": "Could not parse GitHub repository URL",
"noGitRemote": "No GitHub remote found in project"
}
}Then update the code:
if (parsed) {
setDetectedRepo({
owner: parsed.owner,
name: parsed.name,
fullName: `${parsed.owner}/${parsed.name}`
});
} else {
- setRepoDetectionError('Could not parse GitHub repository URL');
+ setRepoDetectionError(t('modal.error.parseRepo'));
}
} else {
- setRepoDetectionError('No GitHub remote found in project');
+ setRepoDetectionError(t('modal.error.noGitRemote'));
}Based on learnings, all user-facing text should use translation keys from the locale files.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines 185
to 188, replace the two hardcoded error strings with translation keys and add
those keys to the locale files; create entries "modal.error.parseRepo" = "Could
not parse GitHub repository URL" and "modal.error.noGitRemote" = "No GitHub
remote found in project" in en/deploy.json (and corresponding fr/deploy.json),
then update the component to call the i18n translation function (e.g.
t('modal.error.parseRepo') and t('modal.error.noGitRemote')) when setting repo
detection errors.
| } | ||
|
|
||
| if (deployedServices.length === 0) { | ||
| setError('Please link at least one service to an application'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded error string should use translation key.
Line 509 contains a hardcoded error message that should be moved to the translation files.
Add to translation files:
"modal": {
"error": {
...
"noServicesLinked": "Please link at least one service to an application"
}
}Then update:
- setError('Please link at least one service to an application');
+ setError(t('modal.error.noServicesLinked'));Based on learnings, all user-facing text should use translation keys.
| setDeployedApps(deployedServices.map(s => s.serviceName)); | ||
| } catch (err) { | ||
| console.error('Failed to save linked deployment:', err); | ||
| setError('Failed to save deployment configuration'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded error string should use translation key.
Line 538 contains a hardcoded error message that should be moved to the translation files.
Add to translation files:
"modal": {
"error": {
...
"saveDeployment": "Failed to save deployment configuration"
}
}Then update:
- setError('Failed to save deployment configuration');
+ setError(t('modal.error.saveDeployment'));Based on learnings, all user-facing text should use translation keys.
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around line 538
the error message is hardcoded; add the key "modal.error.saveDeployment":
"Failed to save deployment configuration" to the translation JSON files, then
replace the literal string with the translation lookup (e.g. use the existing
i18n/t hook or import/useTranslation and call t('modal.error.saveDeployment'))
and ensure the translation hook is available in this component so the translated
string is used instead of the hardcoded text.
| const getDeployBlocker = (): string | null => { | ||
| if (!selectedAccountId) return 'Select a Dokploy account'; | ||
| if (!projectName.trim()) return 'Enter a project name'; | ||
| if (!selectedGitHubProviderId) return 'No GitHub provider configured in Dokploy'; | ||
| if (!detectedRepo) return 'Could not detect repository'; | ||
| if (enabledServices.length === 0) return 'Enable at least one service'; | ||
| const missingConfig = enabledServices.find(s => !serviceConfigs[s.key]?.appName || !serviceConfigs[s.key]?.branch); | ||
| if (missingConfig) return `Configure app name and branch for ${missingConfig.name}`; | ||
| return null; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded strings in helper function should use translation keys.
The getDeployBlocker function contains multiple hardcoded error/instruction messages that should be moved to translation files for consistency with i18n guidelines.
🔎 Suggested fix
Add these keys to translation files:
"modal": {
"blockers": {
"selectAccount": "Select a Dokploy account",
"enterProjectName": "Enter a project name",
"noGitHubProvider": "No GitHub provider configured in Dokploy",
"noRepo": "Could not detect repository",
"enableService": "Enable at least one service",
"configureService": "Configure app name and branch for {{serviceName}}"
}
}Then refactor the function to use translations with interpolation.
Based on learnings, all user-facing text should use translation keys.
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1093 to 1102, move all hardcoded user-facing strings in getDeployBlocker into
translation keys and refactor to use the i18n translator: add the suggested keys
(modal.blockers.selectAccount, enterProjectName, noGitHubProvider, noRepo,
enableService, configureService) to the locale files, replace each literal
return with t('modal.blockers.xxx') and for the configureService case use
interpolation t('modal.blockers.configureService', { serviceName:
missingConfig.name }), keeping the existing logic (trim checks, enabledServices
lookup) intact.
| Loading project services... | ||
| </div> | ||
| ) : services.length === 0 ? ( | ||
| <div className="text-sm text-muted-foreground py-2"> | ||
| No services detected in this project. | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded loading and empty state messages should use translation keys.
Lines 1454 and 1458 contain hardcoded messages that should be moved to translation files.
Add to translation files:
"modal": {
"loadingServices": "Loading project services...",
"noServices": "No services detected in this project."
}Then update:
- Loading project services...
+ {t('modal.loadingServices')}
...
- No services detected in this project.
+ {t('modal.noServices')}Based on learnings, all user-facing text should use translation keys.
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1454 to 1459, replace the two hardcoded UI strings ("Loading project
services..." and "No services detected in this project.") with translation keys
(e.g. t('modal.loadingServices') and t('modal.noServices')); also add the
corresponding keys to the project's translation files (e.g. locales/en.json and
other locale files) with "modal": { "loadingServices": "Loading project
services...", "noServices": "No services detected in this project." } so all
user-facing text uses the i18n system.
| {step === 'configure' && ( | ||
| <DialogFooter className="flex-col sm:flex-row gap-2"> | ||
| {existingDeployment && ( | ||
| <Button variant="ghost" size="sm" className="mr-auto" onClick={() => setStep('resync')}> | ||
| <RefreshCw className="h-3 w-3 mr-1" /> | ||
| Manage Existing Deployment | ||
| </Button> | ||
| )} | ||
|
|
||
| {/* Show services left to deploy */} | ||
| {servicesLeftToDeploy.length > 0 && !existingDeployment && ( | ||
| <p className="text-xs text-muted-foreground mr-auto"> | ||
| {servicesLeftToDeploy.length} service{servicesLeftToDeploy.length !== 1 ? 's' : ''} left to configure | ||
| </p> | ||
| )} | ||
|
|
||
| {!canDeploy && getDeployBlocker() && !existingDeployment && servicesLeftToDeploy.length === 0 && ( | ||
| <p className="text-xs text-destructive mr-auto"> | ||
| {getDeployBlocker()} | ||
| </p> | ||
| )} | ||
|
|
||
| <div className="flex gap-2"> | ||
| <Button variant="outline" onClick={() => onOpenChange(false)}> | ||
| {t('modal.cancel')} | ||
| </Button> | ||
|
|
||
| {isAllLinked ? ( | ||
| <Button | ||
| onClick={handleLinkExisting} | ||
| disabled={configuredServices.length === 0} | ||
| > | ||
| Link Applications ({configuredServices.length}) | ||
| </Button> | ||
| ) : isAllCreateNew ? ( | ||
| <Button onClick={handleDeploy} disabled={!canDeploy}> | ||
| <Rocket className="mr-2 h-4 w-4" /> | ||
| {existingDeployment ? 'Create New Deployment' : 'Configure Apps'} | ||
| </Button> | ||
| ) : ( | ||
| <Button | ||
| onClick={configuredServices.some(s => serviceConfigs[s.key]?.linkMode) ? handleLinkExisting : handleDeploy} | ||
| disabled={configuredServices.length === 0} | ||
| > | ||
| {configuredServices.some(s => serviceConfigs[s.key]?.linkMode) ? 'Link' : 'Configure'} ({configuredServices.length}) | ||
| </Button> | ||
| )} | ||
| </div> | ||
| </DialogFooter> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dialog footer contains hardcoded strings.
The configure step footer contains multiple hardcoded strings:
- Line 1767:
"Manage Existing Deployment" - Line 1774: Service count message with pluralization
- Lines 1780: Deploy blocker message
- Lines 1794, 1799, 1806: Button labels and counts
These should all be moved to translation files with proper support for pluralization where needed (e.g., using i18next's plural forms for "service" vs "services").
Based on learnings, all user-facing text should use translation keys.
| {step === 'resync' && ( | ||
| <DialogFooter className="flex-col sm:flex-row gap-2"> | ||
| <div className="flex gap-2 mr-auto"> | ||
| <Button variant="ghost" size="sm" onClick={() => setStep('configure')}> | ||
| New Deployment | ||
| </Button> | ||
| <Button variant="ghost" size="sm" className="text-destructive hover:text-destructive" onClick={handleResetDeployment}> | ||
| Reset Config | ||
| </Button> | ||
| </div> | ||
| <Button variant="outline" onClick={() => onOpenChange(false)}> | ||
| {t('modal.close')} | ||
| </Button> | ||
| </DialogFooter> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resync footer contains hardcoded strings.
The resync step footer contains hardcoded button labels:
- Line 1825:
"New Deployment" - Line 1828:
"Reset Config"
Add to translation files:
"modal": {
"resync": {
"newDeployment": "New Deployment",
"resetConfig": "Reset Config"
}
}Then update the JSX to use t('modal.resync.newDeployment') and t('modal.resync.resetConfig').
Based on learnings, all user-facing text should use translation keys.
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1821 to 1834 the resync footer uses hardcoded button labels ("New Deployment"
and "Reset Config"); update the JSX to use translation keys
t('modal.resync.newDeployment') and t('modal.resync.resetConfig') instead of the
literal strings, and add the corresponding entries to your translation JSON
files under "modal.resync": { "newDeployment": "New Deployment", "resetConfig":
"Reset Config" } so all user-facing text is localized.
Summary
This PR adds comprehensive Dokploy deployment enhancements including support for linking to existing applications, granular deployment control, and improved service management UI.
Key Features
🔗 Application Linking to All Resource Types
⚙️ Granular Deployment Control
📊 Improved Service Management
🎨 UI/UX Enhancements
Technical Implementation
Type System
```typescript
// New application type enum
export type DokployApplicationType =
| 'application'
| 'compose'
| 'mariadb'
| 'mongo'
| 'mysql'
| 'postgres'
| 'redis';
// Enhanced deployment info
export interface DokployServiceDeployment {
// ... existing fields
applicationType?: DokployApplicationType; // NEW: track app type
}
```
API Integration
State Management
New computed values for real-time service tracking:
Error Handling
Files Changed
New Files
Modified Files
Testing Recommendations
Screenshots
Service card with deployment control:
```
☑ Frontend • application • React
├─ Mode: [Create New] | Link Existing
├─ App Name: frontend
├─ Branch: main
├─ Domain: app.example.com (optional)
├─ Environment Variables (3/5)
├─ Build path: /apps/frontend
└─ Deploy immediately after setup [ON/OFF]
```
Application selection with type badges:
```
Select an application:
```
Breaking Changes
None. All changes are additive and backward compatible.
🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.