Stirling-PDF/frontend/src/desktop/services/operationRouter.ts
James Brunton e8e98128d2
Allow login to SaaS for desktop instead of offline mode (#4941)
# Description of Changes
Makes the desktop options to sign in with your Stirling account, or sign
into self-hosted:

<img width="608" height="456" alt="image"
src="https://github.com/user-attachments/assets/a49988ab-db3f-4333-b242-790aee5c07c6"
/>

The first option still runs everything locally, just enforces that
you've signed in for now. Future work will enable sending operations
that can't be run locally to the server.
2025-11-22 00:38:59 +00:00

102 lines
3.2 KiB
TypeScript

import { connectionModeService } from '@app/services/connectionModeService';
import { tauriBackendService } from '@app/services/tauriBackendService';
export type ExecutionTarget = 'local' | 'remote';
export class OperationRouter {
private static instance: OperationRouter;
static getInstance(): OperationRouter {
if (!OperationRouter.instance) {
OperationRouter.instance = new OperationRouter();
}
return OperationRouter.instance;
}
/**
* Determines where an operation should execute
* @param _operation - The operation name (for future operation classification)
* @returns 'local' or 'remote'
*/
async getExecutionTarget(_operation?: string): Promise<ExecutionTarget> {
const mode = await connectionModeService.getCurrentMode();
// Current implementation: simple mode-based routing
if (mode === 'saas') {
// SaaS mode: For now, all operations run locally
// Future enhancement: complex operations will be sent to SaaS server
return 'local';
}
// In self-hosted mode, currently all operations go to remote
// Future enhancement: check if operation is "simple" and route to local if so
// Example future logic:
// if (mode === 'selfhosted' && operation && this.isSimpleOperation(operation)) {
// return 'local';
// }
return 'remote';
}
/**
* Gets the base URL for an operation based on execution target
* @param _operation - The operation name (for future operation classification)
* @returns Base URL for API calls
*/
async getBaseUrl(_operation?: string): Promise<string> {
const target = await this.getExecutionTarget(_operation);
if (target === 'local') {
// Use dynamically assigned port from backend service
const backendUrl = tauriBackendService.getBackendUrl();
if (!backendUrl) {
throw new Error('Backend URL not available - backend may still be starting');
}
// Strip trailing slash to avoid double slashes in URLs
return backendUrl.replace(/\/$/, '');
}
// Remote: get from server config
const serverConfig = await connectionModeService.getServerConfig();
if (!serverConfig) {
console.warn('No server config found');
throw new Error('Server configuration not found');
}
// Strip trailing slash to avoid double slashes in URLs
return serverConfig.url.replace(/\/$/, '');
}
/**
* Checks if we're currently in self-hosted mode
*/
async isSelfHostedMode(): Promise<boolean> {
const mode = await connectionModeService.getCurrentMode();
return mode === 'selfhosted';
}
/**
* Checks if we're currently in SaaS mode
*/
async isSaaSMode(): Promise<boolean> {
const mode = await connectionModeService.getCurrentMode();
return mode === 'saas';
}
// Future enhancement: operation classification
// private isSimpleOperation(operation: string): boolean {
// const simpleOperations = [
// 'rotate',
// 'merge',
// 'split',
// 'extract-pages',
// 'remove-pages',
// 'reorder-pages',
// 'metadata',
// ];
// return simpleOperations.includes(operation);
// }
}
export const operationRouter = OperationRouter.getInstance();