mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-17 13:52:14 +01:00
Desktop self-hosted connection logging (#5410)
Also added in automotic protocol addition if missing. Defaults to https:// --------- Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
@@ -6217,6 +6217,7 @@ description = "Enter the full URL of your self-hosted Stirling PDF server"
|
||||
|
||||
[setup.server.error]
|
||||
emptyUrl = "Please enter a server URL"
|
||||
invalidUrl = "Invalid URL format. Please enter a valid URL like https://your-server.com"
|
||||
unreachable = "Could not connect to server"
|
||||
testFailed = "Connection test failed"
|
||||
configFetch = "Failed to fetch server configuration. Please check the URL and try again."
|
||||
|
||||
@@ -20,36 +20,67 @@ export const ServerSelection: React.FC<ServerSelectionProps> = ({ onSelect, load
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Normalize URL: trim and remove trailing slashes
|
||||
const url = customUrl.trim().replace(/\/+$/, '');
|
||||
// Normalize and validate URL
|
||||
let url = customUrl.trim().replace(/\/+$/, '');
|
||||
|
||||
if (!url) {
|
||||
setTestError(t('setup.server.error.emptyUrl', 'Please enter a server URL'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto-add https:// if no protocol specified
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
console.log('[ServerSelection] No protocol specified, adding https://');
|
||||
url = `https://${url}`;
|
||||
setCustomUrl(url); // Update the input field
|
||||
}
|
||||
|
||||
// Validate URL format
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
console.log('[ServerSelection] Valid URL:', {
|
||||
protocol: urlObj.protocol,
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
pathname: urlObj.pathname,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[ServerSelection] Invalid URL format:', err);
|
||||
setTestError(t('setup.server.error.invalidUrl', 'Invalid URL format. Please enter a valid URL like https://your-server.com'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Test connection before proceeding
|
||||
setTesting(true);
|
||||
setTestError(null);
|
||||
setSecurityDisabled(false);
|
||||
|
||||
try {
|
||||
const isReachable = await connectionModeService.testConnection(url);
|
||||
console.log(`[ServerSelection] Testing connection to: ${url}`);
|
||||
|
||||
if (!isReachable) {
|
||||
setTestError(t('setup.server.error.unreachable', 'Could not connect to server'));
|
||||
try {
|
||||
const testResult = await connectionModeService.testConnection(url);
|
||||
|
||||
if (!testResult.success) {
|
||||
console.error('[ServerSelection] Connection test failed:', testResult);
|
||||
setTestError(testResult.error || t('setup.server.error.unreachable', 'Could not connect to server'));
|
||||
setTesting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[ServerSelection] ✅ Connection test successful');
|
||||
|
||||
// Fetch OAuth providers and check if login is enabled
|
||||
const enabledProviders: SSOProviderConfig[] = [];
|
||||
try {
|
||||
console.log('[ServerSelection] Fetching login configuration...');
|
||||
const response = await fetch(`${url}/api/v1/proprietary/ui-data/login`);
|
||||
|
||||
// Check if security is disabled (status 403 or error response)
|
||||
if (!response.ok) {
|
||||
console.warn(`[ServerSelection] Login config request failed with status ${response.status}`);
|
||||
|
||||
if (response.status === 403 || response.status === 401) {
|
||||
console.log('[ServerSelection] Security is disabled on this server');
|
||||
setSecurityDisabled(true);
|
||||
setTesting(false);
|
||||
return;
|
||||
@@ -65,10 +96,11 @@ export const ServerSelection: React.FC<ServerSelectionProps> = ({ onSelect, load
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Login UI data:', data);
|
||||
console.log('[ServerSelection] Login UI data:', data);
|
||||
|
||||
// Check if the response indicates security is disabled
|
||||
if (data.enableLogin === false || data.securityEnabled === false) {
|
||||
console.log('[ServerSelection] Security is explicitly disabled in config');
|
||||
setSecurityDisabled(true);
|
||||
setTesting(false);
|
||||
return;
|
||||
@@ -90,32 +122,39 @@ export const ServerSelection: React.FC<ServerSelectionProps> = ({ onSelect, load
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[ServerSelection] Detected OAuth providers:', enabledProviders);
|
||||
console.log('[ServerSelection] ✅ Detected OAuth providers:', enabledProviders);
|
||||
} catch (err) {
|
||||
console.error('[ServerSelection] Failed to fetch login configuration', err);
|
||||
console.error('[ServerSelection] ❌ Failed to fetch login configuration:', err);
|
||||
|
||||
// Check if it's a security disabled error
|
||||
if (err instanceof Error && (err.message.includes('403') || err.message.includes('401'))) {
|
||||
console.log('[ServerSelection] Security is disabled (error-based detection)');
|
||||
setSecurityDisabled(true);
|
||||
setTesting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// For any other error (network, CORS, invalid JSON, etc.), show error and don't proceed
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
console.error('[ServerSelection] Configuration fetch error details:', errorMessage);
|
||||
|
||||
setTestError(
|
||||
t('setup.server.error.configFetch', 'Failed to fetch server configuration. Please check the URL and try again.')
|
||||
t('setup.server.error.configFetch', 'Failed to fetch server configuration: {{error}}', {
|
||||
error: errorMessage
|
||||
})
|
||||
);
|
||||
setTesting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection successful - pass URL and OAuth providers
|
||||
console.log('[ServerSelection] ✅ Server selection complete, proceeding to login');
|
||||
onSelect({
|
||||
url,
|
||||
enabledOAuthProviders: enabledProviders.length > 0 ? enabledProviders : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Connection test failed:', error);
|
||||
console.error('[ServerSelection] ❌ Unexpected error during connection test:', error);
|
||||
setTestError(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
|
||||
@@ -101,7 +101,12 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
|
||||
};
|
||||
|
||||
const handleSelfHostedLogin = async (username: string, password: string) => {
|
||||
console.log('[SetupWizard] 🔐 Starting self-hosted login');
|
||||
console.log(`[SetupWizard] Server: ${serverConfig?.url}`);
|
||||
console.log(`[SetupWizard] Username: ${username}`);
|
||||
|
||||
if (!serverConfig) {
|
||||
console.error('[SetupWizard] ❌ No server configured');
|
||||
setError('No server configured');
|
||||
return;
|
||||
}
|
||||
@@ -110,19 +115,35 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
console.log('[SetupWizard] Step 1: Authenticating with server...');
|
||||
await authService.login(serverConfig.url, username, password);
|
||||
console.log('[SetupWizard] ✅ Authentication successful');
|
||||
|
||||
console.log('[SetupWizard] Step 2: Switching to self-hosted mode...');
|
||||
await connectionModeService.switchToSelfHosted(serverConfig);
|
||||
console.log('[SetupWizard] ✅ Switched to self-hosted mode');
|
||||
|
||||
console.log('[SetupWizard] Step 3: Initializing external backend...');
|
||||
await tauriBackendService.initializeExternalBackend();
|
||||
console.log('[SetupWizard] ✅ External backend initialized');
|
||||
|
||||
console.log('[SetupWizard] ✅ Setup complete, calling onComplete()');
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error('Self-hosted login failed:', err);
|
||||
setError(err instanceof Error ? err.message : 'Self-hosted login failed');
|
||||
console.error('[SetupWizard] ❌ Self-hosted login failed:', err);
|
||||
const errorMessage = err instanceof Error ? err.message : 'Self-hosted login failed';
|
||||
console.error('[SetupWizard] Error message:', errorMessage);
|
||||
setError(errorMessage);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelfHostedOAuthSuccess = async (_userInfo: UserInfo) => {
|
||||
console.log('[SetupWizard] 🔐 OAuth login successful, completing setup');
|
||||
console.log(`[SetupWizard] Server: ${serverConfig?.url}`);
|
||||
|
||||
if (!serverConfig) {
|
||||
console.error('[SetupWizard] ❌ No server configured');
|
||||
setError('No server configured');
|
||||
return;
|
||||
}
|
||||
@@ -131,13 +152,22 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// OAuth already completed by authService.loginWithOAuth
|
||||
console.log('[SetupWizard] Step 1: OAuth already completed');
|
||||
console.log('[SetupWizard] Step 2: Switching to self-hosted mode...');
|
||||
await connectionModeService.switchToSelfHosted(serverConfig);
|
||||
console.log('[SetupWizard] ✅ Switched to self-hosted mode');
|
||||
|
||||
console.log('[SetupWizard] Step 3: Initializing external backend...');
|
||||
await tauriBackendService.initializeExternalBackend();
|
||||
console.log('[SetupWizard] ✅ External backend initialized');
|
||||
|
||||
console.log('[SetupWizard] ✅ Setup complete, calling onComplete()');
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error('Self-hosted OAuth login completion failed:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to complete login');
|
||||
console.error('[SetupWizard] ❌ Self-hosted OAuth login completion failed:', err);
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to complete login';
|
||||
console.error('[SetupWizard] Error message:', errorMessage);
|
||||
setError(errorMessage);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -223,19 +223,24 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async login(serverUrl: string, username: string, password: string): Promise<UserInfo> {
|
||||
try {
|
||||
console.log('Logging in to:', serverUrl);
|
||||
console.log(`[Desktop AuthService] 🔐 Starting login to: ${serverUrl}`);
|
||||
console.log(`[Desktop AuthService] Username: ${username}`);
|
||||
|
||||
try {
|
||||
// Validate SaaS configuration if connecting to SaaS
|
||||
if (serverUrl === STIRLING_SAAS_URL) {
|
||||
if (!STIRLING_SAAS_URL) {
|
||||
console.error('[Desktop AuthService] ❌ VITE_SAAS_SERVER_URL is not configured');
|
||||
throw new Error('VITE_SAAS_SERVER_URL is not configured');
|
||||
}
|
||||
if (!SUPABASE_KEY) {
|
||||
console.error('[Desktop AuthService] ❌ VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY is not configured');
|
||||
throw new Error('VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY is not configured');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Desktop AuthService] Invoking Rust login command...');
|
||||
|
||||
// Call Rust login command (bypasses CORS)
|
||||
const response = await invoke<LoginResponse>('login', {
|
||||
serverUrl,
|
||||
@@ -247,21 +252,26 @@ export class AuthService {
|
||||
|
||||
const { token, username: returnedUsername, email } = response;
|
||||
|
||||
console.log('[Desktop AuthService] Login successful, saving token...');
|
||||
console.log('[Desktop AuthService] ✅ Login response received');
|
||||
console.log(`[Desktop AuthService] Username from response: ${returnedUsername || username}`);
|
||||
|
||||
// Save token to all storage locations
|
||||
try {
|
||||
console.log('[Desktop AuthService] Saving token to storage...');
|
||||
await this.saveTokenEverywhere(token);
|
||||
console.log('[Desktop AuthService] ✅ Token saved successfully');
|
||||
} catch (error) {
|
||||
console.error('[Desktop AuthService] Failed to save token:', error);
|
||||
console.error('[Desktop AuthService] ❌ Failed to save token:', error);
|
||||
throw new Error('Failed to save authentication token');
|
||||
}
|
||||
|
||||
// Save user info to store
|
||||
console.log('[Desktop AuthService] Saving user info...');
|
||||
await invoke('save_user_info', {
|
||||
username: returnedUsername || username,
|
||||
email,
|
||||
});
|
||||
console.log('[Desktop AuthService] ✅ User info saved');
|
||||
|
||||
const userInfo: UserInfo = {
|
||||
username: returnedUsername || username,
|
||||
@@ -270,10 +280,60 @@ export class AuthService {
|
||||
|
||||
this.setAuthStatus('authenticated', userInfo);
|
||||
|
||||
console.log('Login successful');
|
||||
console.log('[Desktop AuthService] ✅ Login completed successfully');
|
||||
return userInfo;
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
console.error('[Desktop AuthService] ❌ Login failed:', error);
|
||||
|
||||
// Provide more detailed error messages based on the error type
|
||||
if (error instanceof Error) {
|
||||
const errMsg = error.message.toLowerCase();
|
||||
|
||||
// Authentication errors
|
||||
if (errMsg.includes('401') || errMsg.includes('unauthorized') || errMsg.includes('invalid credentials')) {
|
||||
console.error('[Desktop AuthService] Authentication failed - invalid credentials');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Invalid username or password. Please check your credentials and try again.');
|
||||
}
|
||||
// Server not found or unreachable
|
||||
else if (errMsg.includes('connection refused') || errMsg.includes('econnrefused')) {
|
||||
console.error('[Desktop AuthService] Server connection refused');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Cannot connect to server. Please check the server URL and ensure the server is running.');
|
||||
}
|
||||
// Timeout
|
||||
else if (errMsg.includes('timeout') || errMsg.includes('timed out')) {
|
||||
console.error('[Desktop AuthService] Login request timed out');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Login request timed out. Please check your network connection and try again.');
|
||||
}
|
||||
// DNS failure
|
||||
else if (errMsg.includes('getaddrinfo') || errMsg.includes('dns') || errMsg.includes('not found') || errMsg.includes('enotfound')) {
|
||||
console.error('[Desktop AuthService] DNS resolution failed');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Cannot resolve server address. Please check the server URL is correct.');
|
||||
}
|
||||
// SSL/TLS errors
|
||||
else if (errMsg.includes('ssl') || errMsg.includes('tls') || errMsg.includes('certificate') || errMsg.includes('cert')) {
|
||||
console.error('[Desktop AuthService] SSL/TLS error');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('SSL/TLS certificate error. Server may have an invalid or self-signed certificate.');
|
||||
}
|
||||
// 404 - endpoint not found
|
||||
else if (errMsg.includes('404') || errMsg.includes('not found')) {
|
||||
console.error('[Desktop AuthService] Login endpoint not found');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Login endpoint not found. Please ensure you are connecting to a valid Stirling PDF server.');
|
||||
}
|
||||
// 403 - security disabled
|
||||
else if (errMsg.includes('403') || errMsg.includes('forbidden')) {
|
||||
console.error('[Desktop AuthService] Login disabled on server');
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw new Error('Login is not enabled on this server. Please enable security mode (DOCKER_ENABLE_SECURITY=true).');
|
||||
}
|
||||
}
|
||||
|
||||
// Generic error fallback
|
||||
this.setAuthStatus('unauthenticated', null);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -105,22 +105,95 @@ export class ConnectionModeService {
|
||||
console.log('Switched to self-hosted mode successfully');
|
||||
}
|
||||
|
||||
async testConnection(url: string): Promise<boolean> {
|
||||
/**
|
||||
* Test connection to a server URL and return detailed error information
|
||||
* @returns Object with success status and optional error message
|
||||
*/
|
||||
async testConnection(url: string): Promise<{ success: boolean; error?: string; errorCode?: string }> {
|
||||
console.log(`[ConnectionModeService] Testing connection to: ${url}`);
|
||||
|
||||
try {
|
||||
// Test connection by hitting the health/status endpoint
|
||||
const healthUrl = `${url.replace(/\/$/, '')}/api/v1/info/status`;
|
||||
console.log(`[ConnectionModeService] Health check URL: ${healthUrl}`);
|
||||
|
||||
const response = await fetch(healthUrl, {
|
||||
method: 'GET',
|
||||
connectTimeout: 10000,
|
||||
});
|
||||
|
||||
const isOk = response.ok;
|
||||
console.log(`[ConnectionModeService] Server connection test result: ${isOk}`);
|
||||
return isOk;
|
||||
if (response.ok) {
|
||||
console.log(`[ConnectionModeService] ✅ Server connection test successful`);
|
||||
return { success: true };
|
||||
} else {
|
||||
const errorMsg = `Server returned status ${response.status}`;
|
||||
console.error(`[ConnectionModeService] ❌ ${errorMsg}`);
|
||||
return {
|
||||
success: false,
|
||||
error: errorMsg,
|
||||
errorCode: `HTTP_${response.status}`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[ConnectionModeService] Server connection test failed:', error);
|
||||
return false;
|
||||
console.error('[ConnectionModeService] ❌ Server connection test failed:', error);
|
||||
|
||||
// Extract detailed error information
|
||||
if (error instanceof Error) {
|
||||
const errMsg = error.message.toLowerCase();
|
||||
|
||||
// Connection refused
|
||||
if (errMsg.includes('connection refused') || errMsg.includes('econnrefused')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Connection refused. Server may not be running or the port is incorrect.',
|
||||
errorCode: 'CONNECTION_REFUSED',
|
||||
};
|
||||
}
|
||||
// Timeout
|
||||
else if (errMsg.includes('timeout') || errMsg.includes('timed out')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Connection timed out. Server is not responding within 10 seconds.',
|
||||
errorCode: 'TIMEOUT',
|
||||
};
|
||||
}
|
||||
// DNS failure
|
||||
else if (errMsg.includes('getaddrinfo') || errMsg.includes('dns') || errMsg.includes('not found') || errMsg.includes('enotfound')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Cannot resolve server address. Please check the URL is correct.',
|
||||
errorCode: 'DNS_FAILURE',
|
||||
};
|
||||
}
|
||||
// SSL/TLS errors
|
||||
else if (errMsg.includes('ssl') || errMsg.includes('tls') || errMsg.includes('certificate') || errMsg.includes('cert')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'SSL/TLS certificate error. Server may have an invalid or self-signed certificate.',
|
||||
errorCode: 'SSL_ERROR',
|
||||
};
|
||||
}
|
||||
// Protocol errors
|
||||
else if (errMsg.includes('protocol')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Protocol error. Try using https:// instead of http:// or vice versa.',
|
||||
errorCode: 'PROTOCOL_ERROR',
|
||||
};
|
||||
}
|
||||
// Generic error
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
errorCode: 'NETWORK_ERROR',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unknown error occurred while testing connection',
|
||||
errorCode: 'UNKNOWN',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ class TauriHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
private createError(message: string, config?: TauriHttpRequestConfig, code?: string, response?: TauriHttpResponse): TauriHttpError {
|
||||
private createError(message: string, config?: TauriHttpRequestConfig, code?: string, response?: TauriHttpResponse, originalError?: unknown): TauriHttpError {
|
||||
const error = new Error(message) as TauriHttpError;
|
||||
error.config = config;
|
||||
error.code = code;
|
||||
@@ -99,6 +99,21 @@ class TauriHttpClient {
|
||||
config: error.config,
|
||||
code: error.code,
|
||||
});
|
||||
|
||||
// Log detailed error information for debugging
|
||||
console.error('[TauriHttpClient] Error details:', {
|
||||
message,
|
||||
code,
|
||||
url: config?.url,
|
||||
method: config?.method,
|
||||
status: response?.status,
|
||||
originalError: originalError instanceof Error ? {
|
||||
name: originalError.name,
|
||||
message: originalError.message,
|
||||
stack: originalError.stack,
|
||||
} : originalError,
|
||||
});
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -224,10 +239,38 @@ class TauriHttpClient {
|
||||
|
||||
// Check for HTTP errors
|
||||
if (!response.ok) {
|
||||
// Create more descriptive error messages based on status code
|
||||
let errorMessage = `Request failed with status code ${response.status}`;
|
||||
let errorCode = 'ERR_BAD_REQUEST';
|
||||
|
||||
if (response.status === 401) {
|
||||
errorMessage = 'Authentication failed - Invalid credentials';
|
||||
errorCode = 'ERR_UNAUTHORIZED';
|
||||
} else if (response.status === 403) {
|
||||
errorMessage = 'Access denied - Insufficient permissions';
|
||||
errorCode = 'ERR_FORBIDDEN';
|
||||
} else if (response.status === 404) {
|
||||
errorMessage = 'Endpoint not found - Server may not support this operation';
|
||||
errorCode = 'ERR_NOT_FOUND';
|
||||
} else if (response.status === 500) {
|
||||
errorMessage = 'Internal server error - Please check server logs';
|
||||
errorCode = 'ERR_SERVER_ERROR';
|
||||
} else if (response.status === 502 || response.status === 503 || response.status === 504) {
|
||||
errorMessage = 'Server unavailable or timeout - Please try again';
|
||||
errorCode = 'ERR_SERVICE_UNAVAILABLE';
|
||||
}
|
||||
|
||||
console.error(`[TauriHttpClient] HTTP Error ${response.status}:`, {
|
||||
url,
|
||||
method,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
});
|
||||
|
||||
const error = this.createError(
|
||||
`Request failed with status code ${response.status}`,
|
||||
errorMessage,
|
||||
finalConfig,
|
||||
'ERR_BAD_REQUEST',
|
||||
errorCode,
|
||||
httpResponse
|
||||
);
|
||||
|
||||
@@ -258,12 +301,66 @@ class TauriHttpClient {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Create new error for network/other failures
|
||||
const errorMessage = error instanceof Error ? error.message : 'Network Error';
|
||||
// Create detailed error messages for network/other failures
|
||||
let errorMessage = 'Network Error';
|
||||
let errorCode = 'ERR_NETWORK';
|
||||
|
||||
if (error instanceof Error) {
|
||||
const errMsg = error.message.toLowerCase();
|
||||
|
||||
// Connection refused - server not running or wrong port
|
||||
if (errMsg.includes('connection refused') || errMsg.includes('econnrefused')) {
|
||||
errorMessage = `Unable to connect to server at ${url}. Server may not be running or port is incorrect.`;
|
||||
errorCode = 'ERR_CONNECTION_REFUSED';
|
||||
}
|
||||
// Timeout - server too slow or unreachable
|
||||
else if (errMsg.includes('timeout') || errMsg.includes('timed out')) {
|
||||
errorMessage = `Connection timed out to ${url}. Server is not responding or is too slow.`;
|
||||
errorCode = 'ERR_TIMEOUT';
|
||||
}
|
||||
// DNS failure - invalid domain or network issue
|
||||
else if (errMsg.includes('getaddrinfo') || errMsg.includes('dns') || errMsg.includes('not found') || errMsg.includes('enotfound')) {
|
||||
errorMessage = `Cannot resolve server address: ${url}. Please check the URL is correct.`;
|
||||
errorCode = 'ERR_DNS_FAILURE';
|
||||
}
|
||||
// SSL/TLS errors - certificate issues
|
||||
else if (errMsg.includes('ssl') || errMsg.includes('tls') || errMsg.includes('certificate') || errMsg.includes('cert')) {
|
||||
errorMessage = `SSL/TLS certificate error for ${url}. Server may have invalid or self-signed certificate.`;
|
||||
errorCode = 'ERR_SSL_ERROR';
|
||||
}
|
||||
// Protocol errors - wrong protocol (http vs https)
|
||||
else if (errMsg.includes('protocol') || errMsg.includes('https') || errMsg.includes('http')) {
|
||||
errorMessage = `Protocol error connecting to ${url}. Try using https:// instead of http:// or vice versa.`;
|
||||
errorCode = 'ERR_PROTOCOL';
|
||||
}
|
||||
// CORS errors
|
||||
else if (errMsg.includes('cors')) {
|
||||
errorMessage = `CORS error connecting to ${url}. Server may not allow requests from this application.`;
|
||||
errorCode = 'ERR_CORS';
|
||||
}
|
||||
// Generic error with original message
|
||||
else {
|
||||
errorMessage = `Network error: ${error.message}`;
|
||||
errorCode = 'ERR_NETWORK';
|
||||
}
|
||||
|
||||
console.error('[TauriHttpClient] Network error:', {
|
||||
url,
|
||||
method,
|
||||
errorType: errorCode,
|
||||
originalMessage: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
} else {
|
||||
console.error('[TauriHttpClient] Unknown error type:', error);
|
||||
}
|
||||
|
||||
const httpError = this.createError(
|
||||
errorMessage,
|
||||
finalConfig,
|
||||
'ERR_NETWORK'
|
||||
errorCode,
|
||||
undefined,
|
||||
error
|
||||
);
|
||||
|
||||
// Run error interceptors
|
||||
|
||||
Reference in New Issue
Block a user