forked from hangshuo652/aurak
0a9588abb7
- Add pagination support to findAll (page, limit query params) - Add findByTemplateId method to service - Add GET /by-template/:templateId endpoint to controller - Service already includes CRUD for QuestionBank and QuestionBankItem
147 lines
4.3 KiB
TypeScript
147 lines
4.3 KiB
TypeScript
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';
|
|
import { I18nService } from '../i18n/i18n.service';
|
|
|
|
interface UserImportRow {
|
|
Username?: string | number;
|
|
username?: string | number;
|
|
DisplayName?: string | number;
|
|
displayName?: string | number;
|
|
Name?: string | number;
|
|
name?: string | number;
|
|
Password?: string | number;
|
|
password?: string | number;
|
|
IsAdmin?: string | number | boolean;
|
|
isAdmin?: string | number | boolean;
|
|
}
|
|
|
|
@Injectable()
|
|
export class AdminService {
|
|
constructor(
|
|
private readonly userService: UserService,
|
|
private readonly tenantService: TenantService,
|
|
private readonly i18nService: I18nService,
|
|
) {}
|
|
|
|
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<Buffer> {
|
|
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(
|
|
this.i18nService.getMessage('uploadNoFile'),
|
|
);
|
|
|
|
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<UserImportRow>(worksheet);
|
|
|
|
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(this.i18nService.getMessage('usernameRequired'));
|
|
}
|
|
|
|
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(
|
|
this.i18nService.formatMessage('passwordRequiredForNewUser', {
|
|
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 [];
|
|
}
|
|
}
|