添加快传7天离线传

This commit is contained in:
yoyuzh
2026-03-24 09:12:10 +08:00
parent e004e64009
commit b9ab1a7640
32 changed files with 1927 additions and 81 deletions

View File

@@ -0,0 +1,30 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { buildOfflineTransferDownloadUrl, toTransferFilePayload } from './transfer';
test('toTransferFilePayload keeps relative folder paths for transfer files', () => {
const report = new File(['hello'], 'report.pdf', {
type: 'application/pdf',
});
Object.defineProperty(report, 'webkitRelativePath', {
configurable: true,
value: '课程资料/第一周/report.pdf',
});
assert.deepEqual(toTransferFilePayload([report]), [
{
name: 'report.pdf',
relativePath: '课程资料/第一周/report.pdf',
size: 5,
contentType: 'application/pdf',
},
]);
});
test('buildOfflineTransferDownloadUrl points to the public offline download endpoint', () => {
assert.equal(
buildOfflineTransferDownloadUrl('session-1', 'file-1'),
'/api/transfer/sessions/session-1/files/file-1/download',
);
});

View File

@@ -1,4 +1,7 @@
import type { FileMetadata, TransferMode } from './types';
import { apiRequest } from './api';
import { apiUploadRequest } from './api';
import { getTransferFileRelativePath } from './transfer-protocol';
import type {
LookupTransferSessionResponse,
PollTransferSignalsResponse,
@@ -13,15 +16,17 @@ export const DEFAULT_TRANSFER_ICE_SERVERS: RTCIceServer[] = [
export function toTransferFilePayload(files: File[]) {
return files.map((file) => ({
name: file.name,
relativePath: getTransferFileRelativePath(file),
size: file.size,
contentType: file.type || 'application/octet-stream',
}));
}
export function createTransferSession(files: File[]) {
export function createTransferSession(files: File[], mode: TransferMode) {
return apiRequest<TransferSessionResponse>('/transfer/sessions', {
method: 'POST',
body: {
mode,
files: toTransferFilePayload(files),
},
});
@@ -39,6 +44,38 @@ export function joinTransferSession(sessionId: string) {
});
}
export function uploadOfflineTransferFile(
sessionId: string,
fileId: string,
file: File,
onProgress?: (progress: {loaded: number; total: number}) => void,
) {
const body = new FormData();
body.append('file', file);
return apiUploadRequest<void>(`/transfer/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(fileId)}/content`, {
body,
onProgress,
});
}
export function buildOfflineTransferDownloadUrl(sessionId: string, fileId: string) {
const apiBaseUrl = (import.meta.env?.VITE_API_BASE_URL || '/api').replace(/\/$/, '');
return `${apiBaseUrl}/transfer/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(fileId)}/download`;
}
export function importOfflineTransferFile(sessionId: string, fileId: string, path: string) {
return apiRequest<FileMetadata>(
`/transfer/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(fileId)}/import`,
{
method: 'POST',
body: {
path,
},
},
);
}
export function postTransferSignal(sessionId: string, role: 'sender' | 'receiver', type: string, payload: string) {
return apiRequest<void>(`/transfer/sessions/${encodeURIComponent(sessionId)}/signals?role=${role}`, {
method: 'POST',

View File

@@ -106,15 +106,21 @@ export interface FileShareDetailsResponse {
createdAt: string;
}
export type TransferMode = 'ONLINE' | 'OFFLINE';
export interface TransferFileItem {
id?: string | null;
name: string;
relativePath: string;
size: number;
contentType: string;
uploaded?: boolean | null;
}
export interface TransferSessionResponse {
sessionId: string;
pickupCode: string;
mode: TransferMode;
expiresAt: string;
files: TransferFileItem[];
}
@@ -122,6 +128,7 @@ export interface TransferSessionResponse {
export interface LookupTransferSessionResponse {
sessionId: string;
pickupCode: string;
mode: TransferMode;
expiresAt: string;
}