import { Injectable } from '@nestjs/common';
import { PutObjectCommand,S3Client } from '@aws-sdk/client-s3';
import { createHash,randomUUID } from 'node:crypto';
import { DatabaseService } from '../../../infrastructure/database/database.service';
@Injectable()
export class AccountingArtifactStorageService {
private readonly bucket=process.env.OBJECT_STORAGE_BUCKET ?? 'gastro-os';
private readonly s3=new S3Client({
region:process.env.OBJECT_STORAGE_REGION ?? 'local',
endpoint:process.env.OBJECT_STORAGE_ENDPOINT,
forcePathStyle:true,
credentials:{
accessKeyId:process.env.OBJECT_STORAGE_ACCESS_KEY ?? 'minio',
secretAccessKey:process.env.OBJECT_STORAGE_SECRET_KEY ?? 'change-me',
},
});
constructor(private readonly database:DatabaseService) {}
async store(input:{
organizationId:string;
locationId?:string|null;
sourceType:string;
sourceId:string;
artifactType:string;
profile?:string|null;
fileName:string;
mimeType:string;
content:Buffer;
userId?:string;
}) {
const assetId=randomUUID();
const ext=input.fileName.split('.').pop()??'bin';
const key=`org/${input.organizationId}/accounting/${input.sourceType}/${input.sourceId}/${randomUUID()}.${ext}`;
const sha=createHash('sha256').update(input.content).digest('hex');
await this.s3.send(new PutObjectCommand({
Bucket:this.bucket,
Key:key,
Body:input.content,
ContentType:input.mimeType,
}));
await this.database.transaction(async(tx)=>{
await tx.query(
`insert into file_assets(
id,organization_id,location_id,owner_entity_type,owner_entity_id,purpose,
storage_key,original_file_name,mime_type,size_bytes,checksum_sha256,status,
scan_status,created_by_user_id
) values($1,$2,$3,$4,$5,'invoice_document',$6,$7,$8,$9,$10,'available','not_required',$11)`,
[
assetId,input.organizationId,input.locationId??null,input.sourceType,input.sourceId,
key,input.fileName,input.mimeType,input.content.length,sha,input.userId??null,
],
);
await tx.query(
`insert into accounting_generated_artifacts(
id,organization_id,location_id,source_type,source_id,artifact_type,
file_asset_id,profile,checksum_sha256
) values($1,$2,$3,$4,$5,$6,$7,$8,$9)
on conflict(source_type,source_id,artifact_type,profile)
do update set file_asset_id=excluded.file_asset_id,checksum_sha256=excluded.checksum_sha256,created_at=now()`,
[
randomUUID(),input.organizationId,input.locationId??null,input.sourceType,input.sourceId,
input.artifactType,assetId,input.profile??'',sha,
],
);
});
return {fileAssetId:assetId,checksumSha256:sha};
}
}
