feat: Add multilingual deployment progress messages
- Created backend i18n system with EN/NL/AR translations - Frontend now sends language preference with deployment request - Backend deployment messages follow user's selected language - Translated key messages: initializing, creating app, SSL waiting, etc. - Added top margin (100px) on mobile to prevent language button overlap Fixes real-time deployment status showing English regardless of language selection.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
import { DokployProductionClient } from '../api/dokploy-production.js';
|
||||
import { createTranslator, type BackendLanguage } from '../lib/i18n-backend.js';
|
||||
|
||||
export interface DeploymentConfig {
|
||||
stackName: string;
|
||||
@@ -22,6 +23,7 @@ export interface DeploymentConfig {
|
||||
registryId?: string;
|
||||
sharedProjectId?: string;
|
||||
sharedEnvironmentId?: string;
|
||||
lang?: string;
|
||||
}
|
||||
|
||||
export interface DeploymentState {
|
||||
@@ -71,10 +73,12 @@ export type ProgressCallback = (state: DeploymentState) => void;
|
||||
export class ProductionDeployer {
|
||||
private client: DokployProductionClient;
|
||||
private progressCallback?: ProgressCallback;
|
||||
private t: ReturnType<typeof createTranslator>;
|
||||
|
||||
constructor(client: DokployProductionClient, progressCallback?: ProgressCallback) {
|
||||
this.client = client;
|
||||
this.progressCallback = progressCallback;
|
||||
this.t = createTranslator('en');
|
||||
}
|
||||
|
||||
private notifyProgress(state: DeploymentState): void {
|
||||
@@ -87,13 +91,15 @@ export class ProductionDeployer {
|
||||
* Deploy a complete AI stack with full production safeguards
|
||||
*/
|
||||
async deploy(config: DeploymentConfig): Promise<DeploymentResult> {
|
||||
this.t = createTranslator((config.lang || 'en') as BackendLanguage);
|
||||
|
||||
const state: DeploymentState = {
|
||||
id: `dep_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
||||
stackName: config.stackName,
|
||||
phase: 'initializing',
|
||||
status: 'in_progress',
|
||||
progress: 0,
|
||||
message: 'Initializing deployment',
|
||||
message: this.t('initializing'),
|
||||
resources: {},
|
||||
timestamps: {
|
||||
started: new Date().toISOString(),
|
||||
@@ -228,12 +234,12 @@ export class ProductionDeployer {
|
||||
private async getEnvironment(state: DeploymentState): Promise<void> {
|
||||
state.phase = 'getting_environment';
|
||||
state.progress = 25;
|
||||
state.message = 'Getting environment ID';
|
||||
state.message = this.t('gettingEnvironment');
|
||||
|
||||
// Skip if we already have environment ID from project creation
|
||||
if (state.resources.environmentId) {
|
||||
console.log('Environment ID already available from project creation');
|
||||
state.message = 'Environment ID already available';
|
||||
state.message = this.t('environmentAvailable');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,7 +249,7 @@ export class ProductionDeployer {
|
||||
|
||||
const environment = await this.client.getDefaultEnvironment(state.resources.projectId);
|
||||
state.resources.environmentId = environment.environmentId;
|
||||
state.message = 'Environment ID retrieved';
|
||||
state.message = this.t('environmentRetrieved');
|
||||
}
|
||||
|
||||
private async createOrFindApplication(
|
||||
@@ -252,7 +258,7 @@ export class ProductionDeployer {
|
||||
): Promise<void> {
|
||||
state.phase = 'creating_application';
|
||||
state.progress = 40;
|
||||
state.message = 'Creating application';
|
||||
state.message = this.t('creatingApplication');
|
||||
|
||||
if (!state.resources.environmentId) {
|
||||
throw new Error('Environment ID not available');
|
||||
@@ -279,7 +285,7 @@ export class ProductionDeployer {
|
||||
): Promise<void> {
|
||||
state.phase = 'configuring_application';
|
||||
state.progress = 50;
|
||||
state.message = 'Configuring application with Docker image';
|
||||
state.message = this.t('configuringApplication');
|
||||
|
||||
if (!state.resources.applicationId) {
|
||||
throw new Error('Application ID not available');
|
||||
@@ -332,7 +338,7 @@ export class ProductionDeployer {
|
||||
): Promise<void> {
|
||||
state.phase = 'creating_domain';
|
||||
state.progress = 70;
|
||||
state.message = 'Creating domain';
|
||||
state.message = this.t('creatingDomain');
|
||||
|
||||
if (!state.resources.applicationId) {
|
||||
throw new Error('Application ID not available');
|
||||
@@ -359,7 +365,7 @@ export class ProductionDeployer {
|
||||
private async deployApplication(state: DeploymentState): Promise<void> {
|
||||
state.phase = 'deploying';
|
||||
state.progress = 85;
|
||||
state.message = 'Triggering deployment';
|
||||
state.message = this.t('deployingApplication');
|
||||
|
||||
if (!state.resources.applicationId) {
|
||||
throw new Error('Application ID not available');
|
||||
@@ -375,7 +381,7 @@ export class ProductionDeployer {
|
||||
): Promise<void> {
|
||||
state.phase = 'verifying_health';
|
||||
state.progress = 95;
|
||||
state.message = 'Verifying application status via Dokploy';
|
||||
state.message = this.t('verifyingHealth');
|
||||
|
||||
if (!state.resources.applicationId) {
|
||||
throw new Error('Application ID not available');
|
||||
@@ -392,13 +398,13 @@ export class ProductionDeployer {
|
||||
console.log(`Application status: ${appStatus}`);
|
||||
|
||||
if (appStatus === 'done') {
|
||||
state.message = 'Waiting for SSL certificate provisioning...';
|
||||
state.message = this.t('waitingForSSL');
|
||||
state.progress = 98;
|
||||
this.notifyProgress(state);
|
||||
|
||||
await this.sleep(15000);
|
||||
|
||||
state.message = 'Application deployed successfully';
|
||||
state.message = this.t('deploymentSuccess');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -410,7 +416,7 @@ export class ProductionDeployer {
|
||||
}
|
||||
|
||||
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
||||
state.message = `Waiting for application to start (${elapsed}s)...`;
|
||||
state.message = `${this.t('waitingForStart')} (${elapsed}s)...`;
|
||||
this.notifyProgress(state);
|
||||
|
||||
await this.sleep(interval);
|
||||
|
||||
Reference in New Issue
Block a user