diff --git a/lib/services/leantime-service.ts b/lib/services/leantime-service.ts index c6c57169..05d16660 100644 --- a/lib/services/leantime-service.ts +++ b/lib/services/leantime-service.ts @@ -3,10 +3,12 @@ import axios from 'axios'; export class LeantimeService { private apiUrl: string; private apiToken: string; + private apiUserId: string | null; constructor() { this.apiUrl = process.env.LEANTIME_API_URL || ''; this.apiToken = process.env.LEANTIME_TOKEN || ''; + this.apiUserId = null; // Will be fetched when needed console.log('LeantimeService initialized with URL:', this.apiUrl); } @@ -224,6 +226,44 @@ export class LeantimeService { } } + /** + * Get the API user's ID for use in project assignments + * This is useful when all other methods of user assignment fail + */ + private async getApiUserId(): Promise { + if (this.apiUserId) { + return this.apiUserId; + } + + try { + const response = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Auth.getCurrentUser', + jsonrpc: '2.0', + id: 1 + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (response.data && response.data.result && response.data.result.id) { + this.apiUserId = response.data.result.id; + console.log(`Found API user ID: ${this.apiUserId}`); + return this.apiUserId; + } + + return null; + } catch (error) { + console.error('Error getting API user ID:', error); + return null; + } + } + /** * Assign mission users to the Leantime project * @param projectId The Leantime project ID @@ -245,6 +285,17 @@ export class LeantimeService { if (!missionUsers || missionUsers.length === 0) { console.log('No users to assign to the project.'); + + // Ensure at least the API user is assigned to prevent "Current team member is New API Access" issue + console.log('Ensuring API user is assigned to the project to prevent orphaned projects.'); + const apiUserId = await this.getApiUserId(); + if (apiUserId) { + try { + await this.assignApiUserToProject(projectId, apiUserId); + } catch (apiUserError) { + console.error('Error assigning API user to project:', apiUserError); + } + } return; } @@ -278,6 +329,20 @@ export class LeantimeService { } } + // If no users were successfully assigned, assign the API user as a fallback + if (successCount === 0) { + console.log('No users successfully assigned. Adding API user as a fallback.'); + const apiUserId = await this.getApiUserId(); + if (apiUserId) { + try { + await this.assignApiUserToProject(projectId, apiUserId); + successCount++; + } catch (apiUserError) { + console.error('Error assigning API user to project:', apiUserError); + } + } + } + console.log(`User assignment complete: ${successCount} successful, ${errorCount} failed.`); if (errorCount > 0) { @@ -286,10 +351,125 @@ export class LeantimeService { } catch (error) { console.error('Error assigning users to project:', error); console.log(`⚠️ Users will need to be added manually at: ${projectUrl}`); - // Continue even if some user assignments fail + + // Always ensure the API user is assigned as a last resort + try { + const apiUserId = await this.getApiUserId(); + if (apiUserId) { + await this.assignApiUserToProject(projectId, apiUserId); + console.log('API user assigned as fallback to prevent orphaned project.'); + } + } catch (apiUserError) { + console.error('Error assigning API user as fallback:', apiUserError); + } } } + /** + * Special method to assign the API user to a project + * This uses different approaches to try to ensure success + */ + private async assignApiUserToProject(projectId: number, apiUserId: string): Promise { + console.log(`Assigning API user ${apiUserId} to project ${projectId} as admin`); + + // Try various methods to assign the API user + + // Method 1: Direct API assignment + try { + const response = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Projects.Projects.addUser', + jsonrpc: '2.0', + id: 1, + params: { + projectId: projectId, + userId: apiUserId, + role: 'admin' // API user gets admin to ensure full control + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (response.data && response.data.result) { + console.log(`Successfully assigned API user to project ${projectId}`); + return; + } + } catch (error) { + console.log('API user assignment method 1 failed, trying next method...'); + } + + // Method 2: editUserProjectRelations + try { + const response = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Projects.editUserProjectRelations', + jsonrpc: '2.0', + id: 1, + params: { + action: 'add', + userId: apiUserId, + projects: [{ + id: projectId, + role: 'admin' + }] + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (response.data && response.data.result) { + console.log(`Successfully assigned API user to project ${projectId} (method 2)`); + return; + } + } catch (error) { + console.log('API user assignment method 2 failed, trying next method...'); + } + + // Method 3: addProjectUser + try { + const response = await axios.post( + this.getApiEndpoint(), + { + method: 'leantime.rpc.Projects.addProjectUser', + jsonrpc: '2.0', + id: 1, + params: { + projectId: projectId, + userId: apiUserId, + role: 'admin' + } + }, + { + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': this.apiToken + } + } + ); + + if (response.data && response.data.result) { + console.log(`Successfully assigned API user to project ${projectId} (method 3)`); + return; + } + } catch (error) { + console.log('API user assignment method 3 failed.'); + } + + console.warn(`⚠️ Could not assign API user to project ${projectId}. The project may appear with "New API Access" as the only team member.`); + } + /** * Assign a user to a project with the specified role * @param projectId The Leantime project ID