wip
This commit is contained in:
@@ -8,7 +8,8 @@ import { StartAutomationSessionUseCase } from '@/packages/application/use-cases/
|
||||
import { CheckAuthenticationUseCase } from '@/packages/application/use-cases/CheckAuthenticationUseCase';
|
||||
import { InitiateLoginUseCase } from '@/packages/application/use-cases/InitiateLoginUseCase';
|
||||
import { ClearSessionUseCase } from '@/packages/application/use-cases/ClearSessionUseCase';
|
||||
import { loadAutomationConfig, getAutomationMode, AutomationMode } from '@/packages/infrastructure/config';
|
||||
import { ConfirmCheckoutUseCase } from '@/packages/application/use-cases/ConfirmCheckoutUseCase';
|
||||
import { loadAutomationConfig, getAutomationMode, AutomationMode, BrowserModeConfigLoader } from '@/packages/infrastructure/config';
|
||||
import { PinoLogAdapter } from '@/packages/infrastructure/adapters/logging/PinoLogAdapter';
|
||||
import { NoOpLogAdapter } from '@/packages/infrastructure/adapters/logging/NoOpLogAdapter';
|
||||
import { loadLoggingConfig } from '@/packages/infrastructure/config/LoggingConfig';
|
||||
@@ -16,6 +17,7 @@ import type { ISessionRepository } from '@/packages/application/ports/ISessionRe
|
||||
import type { IScreenAutomation } from '@/packages/application/ports/IScreenAutomation';
|
||||
import type { IAutomationEngine } from '@/packages/application/ports/IAutomationEngine';
|
||||
import type { IAuthenticationService } from '@/packages/application/ports/IAuthenticationService';
|
||||
import type { ICheckoutConfirmationPort } from '@/packages/application/ports/ICheckoutConfirmationPort';
|
||||
import type { ILogger } from '@/packages/application/ports/ILogger';
|
||||
|
||||
export interface BrowserConnectionResult {
|
||||
@@ -92,7 +94,11 @@ function getAdapterMode(envMode: AutomationMode): AutomationAdapterMode {
|
||||
* @param logger - Logger instance for the adapter
|
||||
* @returns PlaywrightAutomationAdapter instance (implements both IScreenAutomation and IAuthenticationService)
|
||||
*/
|
||||
function createBrowserAutomationAdapter(mode: AutomationMode, logger: ILogger): PlaywrightAutomationAdapter | MockBrowserAutomationAdapter {
|
||||
function createBrowserAutomationAdapter(
|
||||
mode: AutomationMode,
|
||||
logger: ILogger,
|
||||
browserModeConfigLoader: BrowserModeConfigLoader
|
||||
): PlaywrightAutomationAdapter | MockBrowserAutomationAdapter {
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
// Resolve absolute template path for Electron environment
|
||||
@@ -108,18 +114,28 @@ function createBrowserAutomationAdapter(mode: AutomationMode, logger: ILogger):
|
||||
});
|
||||
|
||||
const adapterMode = getAdapterMode(mode);
|
||||
logger.info('Creating browser automation adapter', { envMode: mode, adapterMode });
|
||||
|
||||
// Get browser mode configuration from provided loader
|
||||
const browserModeConfig = browserModeConfigLoader.load();
|
||||
|
||||
logger.info('Creating browser automation adapter', {
|
||||
envMode: mode,
|
||||
adapterMode,
|
||||
browserMode: browserModeConfig.mode,
|
||||
browserModeSource: browserModeConfig.source,
|
||||
});
|
||||
|
||||
switch (mode) {
|
||||
case 'production':
|
||||
case 'development':
|
||||
return new PlaywrightAutomationAdapter(
|
||||
{
|
||||
headless: mode === 'production',
|
||||
headless: browserModeConfig.mode === 'headless',
|
||||
mode: adapterMode,
|
||||
userDataDir: sessionDataPath,
|
||||
},
|
||||
logger.child({ adapter: 'Playwright', mode: adapterMode })
|
||||
logger.child({ adapter: 'Playwright', mode: adapterMode }),
|
||||
browserModeConfigLoader
|
||||
);
|
||||
|
||||
case 'test':
|
||||
@@ -139,7 +155,9 @@ export class DIContainer {
|
||||
private checkAuthenticationUseCase: CheckAuthenticationUseCase | null = null;
|
||||
private initiateLoginUseCase: InitiateLoginUseCase | null = null;
|
||||
private clearSessionUseCase: ClearSessionUseCase | null = null;
|
||||
private confirmCheckoutUseCase: ConfirmCheckoutUseCase | null = null;
|
||||
private automationMode: AutomationMode;
|
||||
private browserModeConfigLoader: BrowserModeConfigLoader;
|
||||
|
||||
private constructor() {
|
||||
// Initialize logger first - it's needed by other components
|
||||
@@ -153,8 +171,15 @@ export class DIContainer {
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
// Initialize browser mode config loader as singleton
|
||||
this.browserModeConfigLoader = new BrowserModeConfigLoader();
|
||||
|
||||
this.sessionRepository = new InMemorySessionRepository();
|
||||
this.browserAutomation = createBrowserAutomationAdapter(config.mode, this.logger);
|
||||
this.browserAutomation = createBrowserAutomationAdapter(
|
||||
config.mode,
|
||||
this.logger,
|
||||
this.browserModeConfigLoader
|
||||
);
|
||||
this.automationEngine = new MockAutomationEngineAdapter(
|
||||
this.browserAutomation,
|
||||
this.sessionRepository
|
||||
@@ -241,6 +266,21 @@ export class DIContainer {
|
||||
return null;
|
||||
}
|
||||
|
||||
public setConfirmCheckoutUseCase(
|
||||
checkoutConfirmationPort: ICheckoutConfirmationPort
|
||||
): void {
|
||||
// Create ConfirmCheckoutUseCase with checkout service from browser automation
|
||||
// and the provided confirmation port
|
||||
this.confirmCheckoutUseCase = new ConfirmCheckoutUseCase(
|
||||
this.browserAutomation as any, // implements ICheckoutService
|
||||
checkoutConfirmationPort
|
||||
);
|
||||
}
|
||||
|
||||
public getConfirmCheckoutUseCase(): ConfirmCheckoutUseCase | null {
|
||||
return this.confirmCheckoutUseCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize automation connection based on mode.
|
||||
* In production/development mode, connects via Playwright browser automation.
|
||||
@@ -292,6 +332,14 @@ export class DIContainer {
|
||||
this.logger.info('DIContainer shutdown complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the browser mode configuration loader.
|
||||
* Provides access to runtime browser mode control (headed/headless).
|
||||
*/
|
||||
public getBrowserModeConfigLoader(): BrowserModeConfigLoader {
|
||||
return this.browserModeConfigLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the singleton instance (useful for testing with different configurations).
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import { DIContainer } from './di-container';
|
||||
import type { HostedSessionConfig } from '@/packages/domain/entities/HostedSessionConfig';
|
||||
import { StepId } from '@/packages/domain/value-objects/StepId';
|
||||
import { AuthenticationState } from '@/packages/domain/value-objects/AuthenticationState';
|
||||
import { ElectronCheckoutConfirmationAdapter } from '@/packages/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter';
|
||||
|
||||
let progressMonitorInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
@@ -14,6 +15,10 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
||||
const automationEngine = container.getAutomationEngine();
|
||||
const logger = container.getLogger();
|
||||
|
||||
// Setup checkout confirmation adapter and wire it into the container
|
||||
const checkoutConfirmationAdapter = new ElectronCheckoutConfirmationAdapter(mainWindow);
|
||||
container.setConfirmCheckoutUseCase(checkoutConfirmationAdapter);
|
||||
|
||||
// Authentication handlers
|
||||
ipcMain.handle('auth:check', async () => {
|
||||
try {
|
||||
@@ -21,11 +26,10 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
||||
const checkAuthUseCase = container.getCheckAuthenticationUseCase();
|
||||
|
||||
if (!checkAuthUseCase) {
|
||||
logger.warn('Authentication not available in mock mode');
|
||||
logger.error('Authentication use case not available');
|
||||
return {
|
||||
success: true,
|
||||
state: AuthenticationState.AUTHENTICATED,
|
||||
message: 'Mock mode - authentication bypassed'
|
||||
success: false,
|
||||
error: 'Authentication not available - check system configuration'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -301,4 +305,36 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Browser mode control handlers
|
||||
ipcMain.handle('browser-mode:get', async () => {
|
||||
try {
|
||||
const loader = container.getBrowserModeConfigLoader();
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return { mode: loader.getDevelopmentMode(), isDevelopment: true };
|
||||
}
|
||||
return { mode: 'headless', isDevelopment: false };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Failed to get browser mode', err);
|
||||
return { mode: 'headless', isDevelopment: false };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('browser-mode:set', async (_event: IpcMainInvokeEvent, mode: 'headed' | 'headless') => {
|
||||
try {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const loader = container.getBrowserModeConfigLoader();
|
||||
loader.setDevelopmentMode(mode);
|
||||
logger.info('Browser mode updated', { mode });
|
||||
return { success: true, mode };
|
||||
}
|
||||
logger.warn('Browser mode change requested but not in development mode');
|
||||
return { success: false, error: 'Only available in development mode' };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Failed to set browser mode', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -20,6 +20,17 @@ export interface AuthActionResponse {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface CheckoutConfirmationRequest {
|
||||
price: string;
|
||||
state: 'ready' | 'insufficient_funds';
|
||||
sessionMetadata: {
|
||||
sessionName: string;
|
||||
trackId: string;
|
||||
carIds: string[];
|
||||
};
|
||||
timeoutMs: number;
|
||||
}
|
||||
|
||||
export interface ElectronAPI {
|
||||
startAutomation: (config: HostedSessionConfig) => Promise<{
|
||||
success: boolean;
|
||||
@@ -37,6 +48,12 @@ export interface ElectronAPI {
|
||||
initiateLogin: () => Promise<AuthActionResponse>;
|
||||
confirmLogin: () => Promise<AuthActionResponse>;
|
||||
logout: () => Promise<AuthActionResponse>;
|
||||
// Browser Mode APIs
|
||||
getBrowserMode: () => Promise<{ mode: 'headed' | 'headless'; isDevelopment: boolean }>;
|
||||
setBrowserMode: (mode: 'headed' | 'headless') => Promise<{ success: boolean; mode?: string; error?: string }>;
|
||||
// Checkout Confirmation APIs
|
||||
onCheckoutConfirmationRequest: (callback: (request: CheckoutConfirmationRequest) => void) => () => void;
|
||||
confirmCheckout: (decision: 'confirmed' | 'cancelled' | 'timeout') => void;
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
@@ -56,4 +73,18 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
initiateLogin: () => ipcRenderer.invoke('auth:login'),
|
||||
confirmLogin: () => ipcRenderer.invoke('auth:confirmLogin'),
|
||||
logout: () => ipcRenderer.invoke('auth:logout'),
|
||||
// Browser Mode APIs
|
||||
getBrowserMode: () => ipcRenderer.invoke('browser-mode:get'),
|
||||
setBrowserMode: (mode: 'headed' | 'headless') => ipcRenderer.invoke('browser-mode:set', mode),
|
||||
// Checkout Confirmation APIs
|
||||
onCheckoutConfirmationRequest: (callback: (request: CheckoutConfirmationRequest) => void) => {
|
||||
const listener = (_event: any, request: CheckoutConfirmationRequest) => callback(request);
|
||||
ipcRenderer.on('checkout:request-confirmation', listener);
|
||||
return () => {
|
||||
ipcRenderer.removeListener('checkout:request-confirmation', listener);
|
||||
};
|
||||
},
|
||||
confirmCheckout: (decision: 'confirmed' | 'cancelled' | 'timeout') => {
|
||||
ipcRenderer.send('checkout:confirm', decision);
|
||||
},
|
||||
} as ElectronAPI);
|
||||
Reference in New Issue
Block a user