feat: implement QuestionBank CRUD with pagination and template query
- 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
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TikaService } from './tika.service';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
providers: [TikaService],
|
||||
exports: [TikaService],
|
||||
})
|
||||
export class TikaModule {}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as fs from 'fs';
|
||||
import { I18nService } from '../i18n/i18n.service';
|
||||
|
||||
@Injectable()
|
||||
export class TikaService {
|
||||
private readonly logger = new Logger(TikaService.name);
|
||||
private readonly tikaHost: string;
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private i18nService: I18nService,
|
||||
) {
|
||||
const tikaHost = this.configService.get<string>('TIKA_HOST');
|
||||
if (!tikaHost) {
|
||||
throw new Error(this.i18nService.getMessage('tikaHostRequired'));
|
||||
}
|
||||
this.tikaHost = tikaHost;
|
||||
}
|
||||
|
||||
async extractText(filePath: string): Promise<string> {
|
||||
try {
|
||||
// Use stream instead of reading entire file into memory
|
||||
const fileStream = fs.createReadStream(filePath);
|
||||
|
||||
// Node.js native fetch supports passing a stream as body, but often requires 'duplex: "half"'
|
||||
// and checking if we need to convert to Web Stream (Readable.toWeb) depends on Node version,
|
||||
// but usually standard stream works or fs.openAsBlob (Node 20).
|
||||
// Let's try passing the stream directly with duplex option.
|
||||
|
||||
const response = await fetch(`${this.tikaHost}/tika`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'text/plain',
|
||||
},
|
||||
// @ts-expect-error - duplex is present in Node's RequestInit but strictly typed definitions might miss it
|
||||
duplex: 'half',
|
||||
body: fileStream as any,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const statusText = response.statusText;
|
||||
// Ensure stream is closed if request failed
|
||||
fileStream.destroy();
|
||||
throw new Error(
|
||||
`Tika extraction failed: ${response.status} ${statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
return await response.text();
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to extract text from ${filePath}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user