Spaces:
Runtime error
Runtime error
File size: 3,571 Bytes
d44ff09 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import { Injectable, Inject, Logger, NotFoundException, ForbiddenException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { Export, Campaign } from '../../database/entities';
import { ExportPackagerService, ExportRequest } from './export-packager.service';
export const EXPORT_QUEUE = 'export';
@Injectable()
export class ExportService {
private readonly logger = new Logger('Export');
constructor(
@InjectRepository(Export) private readonly exportRepo: Repository<Export>,
@InjectRepository(Campaign) private readonly campaignRepo: Repository<Campaign>,
private readonly packager: ExportPackagerService,
@InjectQueue(EXPORT_QUEUE) private readonly queue: Queue,
) {}
async createExport(userId: string, campaignId: string, req: ExportRequest) {
// Ownership check
const campaign = await this.campaignRepo.findOne({ where: { id: campaignId } });
if (!campaign) throw new NotFoundException({ code: 'NOT_FOUND', message: 'Campaign not found' });
if (campaign.user_id !== userId) throw new ForbiddenException({ code: 'FORBIDDEN', message: 'Not your campaign' });
const exportRec = this.exportRepo.create({
campaign_id: campaignId,
format_selection: { formats: req.formats, bundle_as_zip: req.bundle_as_zip },
storage_path: '',
status: 'pending',
});
await this.exportRepo.save(exportRec);
// Enqueue async packaging job (Section 5: BullMQ for export jobs).
await this.queue.add('package', {
exportId: exportRec.id,
campaignId,
formats: req.formats,
bundle_as_zip: req.bundle_as_zip,
});
this.logger.log(`Export ${exportRec.id} queued (pending)`);
return { export_id: exportRec.id, status: 'pending' as const };
}
async listExports(userId: string, campaignId: string) {
const campaign = await this.campaignRepo.findOne({ where: { id: campaignId } });
if (!campaign) throw new NotFoundException({ code: 'NOT_FOUND', message: 'Campaign not found' });
if (campaign.user_id !== userId) throw new ForbiddenException({ code: 'FORBIDDEN', message: 'Not your campaign' });
const exports = await this.exportRepo.find({
where: { campaign_id: campaignId },
order: { created_at: 'DESC' },
});
return { exports };
}
async getDownload(userId: string, exportId: string) {
const exportRec = await this.exportRepo.findOne({ where: { id: exportId } });
if (!exportRec) throw new NotFoundException({ code: 'NOT_FOUND', message: 'Export not found' });
const campaign = await this.campaignRepo.findOne({ where: { id: exportRec.campaign_id } });
if (!campaign || campaign.user_id !== userId) {
throw new ForbiddenException({ code: 'FORBIDDEN', message: 'Not your export' });
}
if (exportRec.status !== 'completed') {
throw new NotFoundException({ code: 'NOT_READY', message: `Export is ${exportRec.status}` });
}
return exportRec;
}
/** Called by the BullMQ processor after packaging completes. */
async markCompleted(exportId: string, storagePath: string) {
await this.exportRepo.update(exportId, { storage_path: storagePath, status: 'completed' });
this.logger.log(`Export ${exportId} completed -> ${storagePath}`);
}
async markFailed(exportId: string, reason: string) {
await this.exportRepo.update(exportId, { status: 'failed' });
this.logger.error(`Export ${exportId} failed: ${reason}`);
}
}
|