mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 11:00:47 +02:00
Co-authored-by: ConnorYoh <[email protected]> Co-authored-by: Connor Yoh <[email protected]> Co-authored-by: EthanHealy01 <[email protected]> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
126 lines
4.5 KiB
TypeScript
126 lines
4.5 KiB
TypeScript
import apiClient from '@app/services/apiClient';
|
|
import { fileStorage } from '@app/services/fileStorage';
|
|
import { buildHistoryBundle, buildSharePackage } from '@app/services/serverStorageBundle';
|
|
import type { FileId } from '@app/types/file';
|
|
import type { StirlingFileStub } from '@app/types/fileContext';
|
|
|
|
function resolveUpdatedAt(value: unknown): number {
|
|
if (!value) {
|
|
return Date.now();
|
|
}
|
|
if (typeof value === 'number') {
|
|
return Number.isFinite(value) ? value : Date.now();
|
|
}
|
|
const parsed = new Date(String(value)).getTime();
|
|
return Number.isFinite(parsed) ? parsed : Date.now();
|
|
}
|
|
|
|
export async function uploadHistoryChain(
|
|
originalFileId: FileId,
|
|
existingRemoteId?: number
|
|
): Promise<{ remoteId: number; updatedAt: number; chain: StirlingFileStub[] }> {
|
|
const chain = await fileStorage.getHistoryChainStubs(originalFileId);
|
|
if (chain.length === 0) {
|
|
throw new Error('No history chain found.');
|
|
}
|
|
|
|
const finalStub =
|
|
chain.slice().reverse().find((stub) => stub.isLeaf !== false) || chain[chain.length - 1];
|
|
const finalFile = await fileStorage.getStirlingFile(finalStub.id);
|
|
if (!finalFile) {
|
|
throw new Error('Missing final file data for sharing.');
|
|
}
|
|
|
|
const { bundleFile, manifest } = await buildHistoryBundle(originalFileId);
|
|
const auditLog = new File([JSON.stringify(manifest, null, 2)], 'audit-log.json', {
|
|
type: 'application/json',
|
|
lastModified: Date.now(),
|
|
});
|
|
const formData = new FormData();
|
|
formData.append('file', finalFile, finalFile.name);
|
|
formData.append('historyBundle', bundleFile, bundleFile.name);
|
|
formData.append('auditLog', auditLog, auditLog.name);
|
|
|
|
if (existingRemoteId) {
|
|
const response = await apiClient.put(`/api/v1/storage/files/${existingRemoteId}`, formData);
|
|
const updatedAt = resolveUpdatedAt(response.data?.updatedAt);
|
|
return { remoteId: existingRemoteId, updatedAt, chain };
|
|
}
|
|
|
|
const response = await apiClient.post('/api/v1/storage/files', formData);
|
|
const remoteId = response.data?.id as number | undefined;
|
|
if (!remoteId) {
|
|
throw new Error('Missing stored file ID in response.');
|
|
}
|
|
|
|
const updatedAt = resolveUpdatedAt(response.data?.updatedAt);
|
|
return { remoteId, updatedAt, chain };
|
|
}
|
|
|
|
export async function uploadHistoryChains(
|
|
originalFileIds: FileId[],
|
|
existingRemoteId?: number
|
|
): Promise<{ remoteId: number; updatedAt: number; chain: StirlingFileStub[] }> {
|
|
const uniqueRoots = Array.from(new Set(originalFileIds));
|
|
const chainMap = new Map<FileId, StirlingFileStub[]>();
|
|
const combinedChain: StirlingFileStub[] = [];
|
|
const seenIds = new Set<FileId>();
|
|
const leafStubs: StirlingFileStub[] = [];
|
|
|
|
for (const rootId of uniqueRoots) {
|
|
const chain = await fileStorage.getHistoryChainStubs(rootId);
|
|
if (chain.length === 0) {
|
|
throw new Error('No history chain found.');
|
|
}
|
|
chainMap.set(rootId, chain);
|
|
const finalStub =
|
|
chain.slice().reverse().find((stub) => stub.isLeaf !== false) || chain[chain.length - 1];
|
|
if (finalStub) {
|
|
leafStubs.push(finalStub);
|
|
}
|
|
for (const stub of chain) {
|
|
if (!seenIds.has(stub.id as FileId)) {
|
|
seenIds.add(stub.id as FileId);
|
|
combinedChain.push(stub);
|
|
}
|
|
}
|
|
}
|
|
|
|
let shareFile: File;
|
|
if (leafStubs.length === 1) {
|
|
const finalFile = await fileStorage.getStirlingFile(leafStubs[0].id);
|
|
if (!finalFile) {
|
|
throw new Error('Missing final file data for sharing.');
|
|
}
|
|
shareFile = finalFile;
|
|
} else {
|
|
const { bundleFile } = await buildSharePackage(leafStubs);
|
|
shareFile = bundleFile;
|
|
}
|
|
|
|
const { bundleFile, manifest } = await buildHistoryBundle(uniqueRoots);
|
|
const auditLog = new File([JSON.stringify(manifest, null, 2)], 'audit-log.json', {
|
|
type: 'application/json',
|
|
lastModified: Date.now(),
|
|
});
|
|
const formData = new FormData();
|
|
formData.append('file', shareFile, shareFile.name);
|
|
formData.append('historyBundle', bundleFile, bundleFile.name);
|
|
formData.append('auditLog', auditLog, auditLog.name);
|
|
|
|
if (existingRemoteId) {
|
|
const response = await apiClient.put(`/api/v1/storage/files/${existingRemoteId}`, formData);
|
|
const updatedAt = resolveUpdatedAt(response.data?.updatedAt);
|
|
return { remoteId: existingRemoteId, updatedAt, chain: combinedChain };
|
|
}
|
|
|
|
const response = await apiClient.post('/api/v1/storage/files', formData);
|
|
const remoteId = response.data?.id as number | undefined;
|
|
if (!remoteId) {
|
|
throw new Error('Missing stored file ID in response.');
|
|
}
|
|
|
|
const updatedAt = resolveUpdatedAt(response.data?.updatedAt);
|
|
return { remoteId, updatedAt, chain: combinedChain };
|
|
}
|