agent-ecosystem/src/features/agent-attachments/main/infrastructure/attachmentArtifactStore.ts

55 lines
1.7 KiB
TypeScript

import { getAppDataPath } from '@main/utils/pathDecoder';
import { assertSafeAttachmentStorageId } from '@features/agent-attachments/core/domain';
import * as fs from 'fs/promises';
import * as path from 'path';
export type AgentAttachmentArtifactFileName =
| 'original.png'
| 'original.jpg'
| 'optimized.png'
| 'optimized.jpg'
| 'thumb.jpg'
| 'meta.json';
export interface ResolveAgentAttachmentArtifactPathInput {
teamName: string;
messageId: string;
attachmentId: string;
fileName: AgentAttachmentArtifactFileName;
appDataPath?: string;
}
export function resolveAgentAttachmentArtifactPath(
input: ResolveAgentAttachmentArtifactPathInput
): string {
assertSafeAttachmentStorageId('teamName', input.teamName);
assertSafeAttachmentStorageId('messageId', input.messageId);
assertSafeAttachmentStorageId('attachmentId', input.attachmentId);
const root = input.appDataPath ?? getAppDataPath();
const base = path.resolve(
root,
'attachments',
input.teamName,
input.messageId,
input.attachmentId
);
const resolved = path.resolve(base, input.fileName);
if (!resolved.startsWith(base + path.sep)) {
throw new Error('Attachment artifact path escaped managed directory');
}
return resolved;
}
export async function writeFileAtomic(filePath: string, bytes: Buffer | string): Promise<void> {
const dir = path.dirname(filePath);
await fs.mkdir(dir, { recursive: true });
const tmpPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
try {
await fs.writeFile(tmpPath, bytes);
await fs.rename(tmpPath, filePath);
} catch (error) {
await fs.rm(tmpPath, { force: true }).catch(() => undefined);
throw error;
}
}