feat(storage): persist siprouter data in smartdata and smartbucket
This commit is contained in:
+37
-41
@@ -1,6 +1,7 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import type { SiprouterStorage } from './storage.ts';
|
||||
import type {
|
||||
IFaxCompletedEvent,
|
||||
IFaxFailedEvent,
|
||||
@@ -16,6 +17,7 @@ export interface IFaxJob {
|
||||
status: 'dialing' | 'started' | 'completed' | 'failed';
|
||||
transport?: 'audio' | 't38';
|
||||
filePath?: string;
|
||||
objectKey?: string;
|
||||
codec?: string;
|
||||
remoteMedia?: string;
|
||||
success?: boolean;
|
||||
@@ -28,25 +30,21 @@ export interface IFaxJob {
|
||||
}
|
||||
|
||||
export class FaxJobManager {
|
||||
private readonly basePath: string;
|
||||
private readonly jobsPath: string;
|
||||
private jobs: IFaxJob[] = [];
|
||||
private readonly log: (msg: string) => void;
|
||||
private readonly storage: SiprouterStorage;
|
||||
|
||||
constructor(log: (msg: string) => void) {
|
||||
this.basePath = path.join(process.cwd(), '.nogit', 'fax');
|
||||
this.jobsPath = path.join(this.basePath, 'jobs.json');
|
||||
constructor(log: (msg: string) => void, storageArg: SiprouterStorage) {
|
||||
this.log = log;
|
||||
this.storage = storageArg;
|
||||
}
|
||||
|
||||
init(): void {
|
||||
fs.mkdirSync(this.basePath, { recursive: true });
|
||||
if (!fs.existsSync(this.jobsPath)) {
|
||||
this.writeJobs([]);
|
||||
}
|
||||
async init(): Promise<void> {
|
||||
this.jobs = await this.storage.getFaxJobs();
|
||||
}
|
||||
|
||||
noteDialing(callId: string, number: string, providerId: string): void {
|
||||
const jobs = this.loadJobs();
|
||||
async noteDialing(callId: string, number: string, providerId: string): Promise<void> {
|
||||
const jobs = this.jobs;
|
||||
const now = Date.now();
|
||||
const existing = jobs.find((job) => job.callId === callId);
|
||||
if (existing) {
|
||||
@@ -65,62 +63,61 @@ export class FaxJobManager {
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
this.writeJobs(jobs);
|
||||
await this.writeJobs();
|
||||
}
|
||||
|
||||
noteStarted(event: IFaxStartedEvent): void {
|
||||
const jobs = this.loadJobs();
|
||||
async noteStarted(event: IFaxStartedEvent): Promise<void> {
|
||||
const now = Date.now();
|
||||
const job = this.getOrCreateJob(jobs, event.call_id, event.direction, now);
|
||||
const job = this.getOrCreateJob(event.call_id, event.direction, now);
|
||||
job.status = 'started';
|
||||
job.transport = event.transport;
|
||||
job.filePath = event.file_path;
|
||||
await this.ensureOutboundFileObject(job, event.file_path);
|
||||
job.codec = event.codec;
|
||||
job.remoteMedia = event.remote_media;
|
||||
job.updatedAt = now;
|
||||
this.writeJobs(jobs);
|
||||
await this.writeJobs();
|
||||
}
|
||||
|
||||
noteCompleted(event: IFaxCompletedEvent): void {
|
||||
const jobs = this.loadJobs();
|
||||
async noteCompleted(event: IFaxCompletedEvent): Promise<void> {
|
||||
const now = Date.now();
|
||||
const job = this.getOrCreateJob(jobs, event.call_id, event.direction, now);
|
||||
const job = this.getOrCreateJob(event.call_id, event.direction, now);
|
||||
job.status = 'completed';
|
||||
job.transport = event.transport;
|
||||
job.filePath = event.file_path;
|
||||
await this.ensureOutboundFileObject(job, event.file_path);
|
||||
job.codec = event.codec;
|
||||
job.success = event.success;
|
||||
job.completionCode = event.completion_code ?? null;
|
||||
job.completionLabel = event.completion_label ?? null;
|
||||
job.stats = event.stats;
|
||||
job.updatedAt = now;
|
||||
this.writeJobs(jobs);
|
||||
await this.writeJobs();
|
||||
}
|
||||
|
||||
noteFailed(event: IFaxFailedEvent): void {
|
||||
const jobs = this.loadJobs();
|
||||
async noteFailed(event: IFaxFailedEvent): Promise<void> {
|
||||
const now = Date.now();
|
||||
const job = this.getOrCreateJob(jobs, event.call_id, event.direction, now);
|
||||
const job = this.getOrCreateJob(event.call_id, event.direction, now);
|
||||
job.status = 'failed';
|
||||
job.transport = event.transport;
|
||||
job.filePath = event.file_path;
|
||||
await this.ensureOutboundFileObject(job, event.file_path);
|
||||
job.error = event.error;
|
||||
job.success = false;
|
||||
job.updatedAt = now;
|
||||
this.writeJobs(jobs);
|
||||
await this.writeJobs();
|
||||
}
|
||||
|
||||
getJobs(): IFaxJob[] {
|
||||
return this.loadJobs();
|
||||
return [...this.jobs];
|
||||
}
|
||||
|
||||
private getOrCreateJob(
|
||||
jobs: IFaxJob[],
|
||||
callId: string,
|
||||
direction: 'outbound' | 'inbound',
|
||||
now: number,
|
||||
): IFaxJob {
|
||||
let job = jobs.find((entry) => entry.callId === callId);
|
||||
let job = this.jobs.find((entry) => entry.callId === callId);
|
||||
if (!job) {
|
||||
job = {
|
||||
id: callId,
|
||||
@@ -130,24 +127,23 @@ export class FaxJobManager {
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
jobs.unshift(job);
|
||||
this.jobs.unshift(job);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
private loadJobs(): IFaxJob[] {
|
||||
try {
|
||||
const content = fs.readFileSync(this.jobsPath, 'utf8');
|
||||
const parsed = JSON.parse(content);
|
||||
return Array.isArray(parsed) ? parsed as IFaxJob[] : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
private async ensureOutboundFileObject(jobArg: IFaxJob, filePathArg: string | undefined): Promise<void> {
|
||||
if (jobArg.direction !== 'outbound' || jobArg.objectKey || !filePathArg) return;
|
||||
|
||||
const localPath = path.isAbsolute(filePathArg) ? filePathArg : path.join(process.cwd(), filePathArg);
|
||||
if (!fs.existsSync(localPath)) return;
|
||||
|
||||
const extension = path.extname(localPath) || '.tif';
|
||||
jobArg.objectKey = await this.storage.putFileObject(`fax/outbound/${jobArg.callId}${extension}`, localPath);
|
||||
}
|
||||
|
||||
private writeJobs(jobs: IFaxJob[]): void {
|
||||
fs.mkdirSync(this.basePath, { recursive: true });
|
||||
fs.writeFileSync(this.jobsPath, JSON.stringify(jobs, null, 2));
|
||||
this.log(`[fax] persisted ${jobs.length} job(s)`);
|
||||
private async writeJobs(): Promise<void> {
|
||||
await this.storage.writeFaxJobs(this.jobs);
|
||||
this.log(`[fax] persisted ${this.jobs.length} job(s)`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user