diff --git a/lib/services/leantime-service.ts b/lib/services/leantime-service.ts index a9a5ab4d..8ebc36c6 100644 --- a/lib/services/leantime-service.ts +++ b/lib/services/leantime-service.ts @@ -7,6 +7,19 @@ export class LeantimeService { constructor() { this.apiUrl = process.env.LEANTIME_API_URL || ''; this.apiToken = process.env.LEANTIME_TOKEN || ''; + console.log('LeantimeService initialized with URL:', this.apiUrl); + } + + /** + * Get the properly formatted API endpoint + * @returns The formatted API endpoint + */ + private getApiEndpoint(): string { + return this.apiUrl.endsWith('/api/jsonrpc') + ? this.apiUrl + : this.apiUrl.endsWith('/') + ? `${this.apiUrl}api/jsonrpc` + : `${this.apiUrl}/api/jsonrpc`; } /** @@ -27,7 +40,7 @@ export class LeantimeService { // Create the project const response = await axios.post( - this.apiUrl, + this.getApiEndpoint(), { method: 'leantime.rpc.Projects.Projects.addProject', jsonrpc: '2.0', @@ -77,7 +90,7 @@ export class LeantimeService { /** * Set project avatar using mission logo * @param projectId The Leantime project ID - * @param logoUrl The mission logo URL + * @param logoPath The mission logo URL */ async setProjectAvatar(projectId: number, logoPath: string): Promise { try { @@ -94,9 +107,21 @@ export class LeantimeService { formData.append('file', logoBlob, 'logo.png'); formData.append('project', JSON.stringify({ id: projectId })); + // Get base URL by removing any API path + const baseUrl = this.apiUrl + .replace('/api/jsonrpc', '') + .replace('/api/jsonrpc.php', ''); + + // Construct proper avatar endpoint + const avatarEndpoint = baseUrl.endsWith('/') + ? `${baseUrl}api/v1/projects.setProjectAvatar` + : `${baseUrl}/api/v1/projects.setProjectAvatar`; + + console.log('Using avatar endpoint:', avatarEndpoint); + // Upload the avatar const response = await axios.post( - `${this.apiUrl.replace('/api/jsonrpc.php', '')}/api/v1/projects.setProjectAvatar`, + avatarEndpoint, formData, { headers: { @@ -153,7 +178,7 @@ export class LeantimeService { async assignUserToProject(projectId: number, userId: string, role: string): Promise { try { const response = await axios.post( - this.apiUrl, + this.getApiEndpoint(), { method: 'leantime.rpc.Projects.Projects.assignUserToProject', jsonrpc: '2.0', @@ -190,12 +215,18 @@ export class LeantimeService { */ async getClientIdByName(clientName: string): Promise { try { + // Log the API URL to debug + console.log('Leantime API URL:', this.apiUrl); + + const apiEndpoint = this.getApiEndpoint(); + console.log('Using endpoint:', apiEndpoint); + const response = await axios.post( - this.apiUrl, + apiEndpoint, { method: 'leantime.rpc.Clients.Clients.getAll', jsonrpc: '2.0', - id: 1, + id: 1 }, { headers: { @@ -214,9 +245,69 @@ export class LeantimeService { c.name.toLowerCase() === clientName.toLowerCase() ); - return client ? parseInt(client.id) : null; + if (client) { + return parseInt(client.id); + } else { + console.log(`Client "${clientName}" not found. Creating it...`); + return await this.createClient(clientName); + } } catch (error) { console.error('Error getting client by name:', error); + // Try to create the client if we couldn't find it + try { + console.log(`Attempting to create client "${clientName}" after error...`); + return await this.createClient(clientName); + } catch (createError) { + console.error('Error creating client:', createError); + return null; + } + } + } + + /** + * Create a new client in Leantime + * @param clientName The name of the client to create + * @returns The ID of the created client or null if failed + */ + async createClient(clientName: string): Promise { + try { + const response = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Clients.Clients.addClient', + jsonrpc: '2.0', + id: 1, + params: { + values: { + name: clientName, + street: '', + zip: '', + city: '', + state: '', + country: '', + phone: '', + internet: '', + email: '' + } + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (!response.data || !response.data.result) { + throw new Error(`Failed to create client: ${JSON.stringify(response.data)}`); + } + + const clientId = parseInt(response.data.result); + console.log(`Created client "${clientName}" with ID: ${clientId}`); + return clientId; + } catch (error) { + console.error(`Error creating client "${clientName}":`, error); return null; } } @@ -229,7 +320,7 @@ export class LeantimeService { async getUserByEmail(email: string): Promise { try { const response = await axios.post( - this.apiUrl, + this.getApiEndpoint(), { method: 'leantime.rpc.Users.Users.getAll', jsonrpc: '2.0', @@ -265,7 +356,7 @@ export class LeantimeService { async deleteProject(projectId: number): Promise { try { const response = await axios.post( - this.apiUrl, + this.getApiEndpoint(), { method: 'leantime.rpc.Projects.Projects.deleteProject', jsonrpc: '2.0',