2026-06-09 19:46:08 +07:00
|
|
|
import { createChildLogger } from "@bete/shared/logger";
|
2026-06-02 21:23:12 +07:00
|
|
|
import { retryWithBackoff } from "@bete/shared/utils";
|
2026-06-01 21:44:29 +07:00
|
|
|
|
2026-06-09 19:46:08 +07:00
|
|
|
const logger = createChildLogger("tele-upload");
|
|
|
|
|
|
2026-06-01 21:44:29 +07:00
|
|
|
export interface TeleUploadResponse {
|
|
|
|
|
download_url: string;
|
|
|
|
|
public_id?: string;
|
|
|
|
|
file_name?: string;
|
|
|
|
|
size_bytes?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TeleUploadResult {
|
|
|
|
|
url: string;
|
|
|
|
|
publicId?: string;
|
|
|
|
|
filename?: string;
|
|
|
|
|
sizeBytes?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseTeleUploadResponse(
|
|
|
|
|
response: TeleUploadResponse,
|
|
|
|
|
): TeleUploadResult {
|
|
|
|
|
if (!response.download_url) {
|
|
|
|
|
throw new Error("Missing download_url in response");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
url: response.download_url,
|
|
|
|
|
publicId: response.public_id,
|
|
|
|
|
filename: response.file_name,
|
|
|
|
|
sizeBytes: response.size_bytes,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function uploadToTele(input: {
|
|
|
|
|
buffer: Buffer;
|
|
|
|
|
filename: string;
|
|
|
|
|
contentType: string;
|
|
|
|
|
uploadUrl: string;
|
|
|
|
|
timeoutMs?: number;
|
|
|
|
|
retries: number;
|
|
|
|
|
}): Promise<TeleUploadResult> {
|
2026-06-04 18:52:56 +07:00
|
|
|
const { buffer, filename, contentType, uploadUrl, timeoutMs, retries } =
|
|
|
|
|
input;
|
2026-06-01 21:44:29 +07:00
|
|
|
|
2026-06-09 19:46:08 +07:00
|
|
|
logger.debug({ filename, uploadUrl }, "Starting tele upload");
|
|
|
|
|
|
2026-06-01 21:44:29 +07:00
|
|
|
const response = await retryWithBackoff(
|
|
|
|
|
async () => {
|
|
|
|
|
const fileBlob = new Blob([new Uint8Array(buffer)], {
|
|
|
|
|
type: contentType,
|
|
|
|
|
});
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append("file", fileBlob, filename);
|
|
|
|
|
formData.append("fileName", filename);
|
|
|
|
|
|
|
|
|
|
const res = await fetch(uploadUrl, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
accept: "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: formData,
|
|
|
|
|
...(timeoutMs ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
throw new Error(`Upload failed: Status ${res.status}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (await res.json()) as TeleUploadResponse;
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
retries,
|
2026-06-13 14:35:35 +07:00
|
|
|
minTimeout: 1000,
|
|
|
|
|
maxTimeout: 10000,
|
2026-06-01 21:44:29 +07:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return parseTeleUploadResponse(response);
|
|
|
|
|
}
|