import { Injectable, NotFoundException, ForbiddenException, BadRequestException } from '@nestjs/common'; import * as XLSX from 'xlsx'; import { UserService } from '../user/user.service'; import { TenantService } from '../tenant/tenant.service'; @Injectable() export class AdminService { constructor( private readonly userService: UserService, private readonly tenantService: TenantService, ) { } async getTenantUsers(tenantId?: string, page?: number, limit?: number) { if (!tenantId) { return this.userService.findAll(page, limit); } return this.userService.findByTenantId(tenantId, page, limit); } async exportUsers(tenantId?: string): Promise { const { data: users } = tenantId ? await this.userService.findByTenantId(tenantId) : await this.userService.findAll(); const worksheet = XLSX.utils.json_to_sheet(users.map(u => ({ Username: u.username, DisplayName: u.displayName || '', IsAdmin: u.isAdmin ? 'Yes' : 'No', CreatedAt: u.createdAt, Password: '', // Placeholder for new users }))); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, 'Users'); return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }); } async importUsers(tenantId?: string, file?: any) { if (!file) throw new BadRequestException('No file uploaded'); const workbook = XLSX.read(file.buffer, { type: 'buffer' }); const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; const data = XLSX.utils.sheet_to_json(worksheet) as any[]; const results = { success: 0, failed: 0, errors: [] as string[], }; for (const row of data) { try { const username = (row.Username || row.username)?.toString(); const displayName = (row.DisplayName || row.displayName || row.Name || row.name)?.toString(); const password = (row.Password || row.password)?.toString(); const isAdminStr = (row.IsAdmin || row.isAdmin || 'No').toString(); const isAdmin = isAdminStr.toLowerCase() === 'yes' || isAdminStr === 'true' || isAdminStr === '1'; if (!username) { throw new Error('Username is missing'); } const existingUser = await this.userService.findOneByUsername(username); if (existingUser) { await this.userService.updateUser(existingUser.id, { displayName: displayName || existingUser.displayName, password: password || undefined, // We avoid changing isAdmin status via import for security unless explicitly required }); } else { if (!password) { throw new Error(`Password missing for new user: ${username}`); } await this.userService.createUser( username, password, isAdmin, tenantId, displayName ); } results.success++; } catch (e: any) { results.failed++; results.errors.push(`${row.Username || 'Unknown'}: ${e.message}`); } } return results; } async getTenantSettings(tenantId: string) { return this.tenantService.getSettings(tenantId); } async updateTenantSettings(tenantId: string, data: any) { return this.tenantService.updateSettings(tenantId, data); } // Notebook sharing approval and model assignments would go here async getPendingShares(tenantId: string) { // Mock implementation for pending shares to satisfy UI. // Needs proper schema/entity support in the future. return []; } }