request-upload.ts
1 import type { FormDataFile } from "@literate.ink/utilities"; 2 import { UploadFailedError, type SessionHandle } from "~/models"; 3 import { aesKeys } from "~/api/private/keys"; 4 import { AES } from "~/api/private/aes"; 5 import { apiProperties } from "~/api/private/api-properties"; 6 import { USER_AGENT } from "~/api/private/user-agent"; 7 8 export class RequestUpload { 9 public order: string; 10 public id = `selecfile_1_${Date.now()}`; 11 12 private url: string; 13 private form: FormData; 14 private headers: Record<string, string> = { 15 "User-Agent": USER_AGENT 16 }; 17 18 public constructor ( 19 private session: SessionHandle, 20 public functionName: string, 21 file: FormDataFile, 22 public fileName: string 23 ) { 24 session.information.order++; 25 26 const { iv, key } = aesKeys(session); 27 this.order = AES.encrypt(session.information.order.toString(), key, iv); 28 29 const properties = apiProperties(this.session); 30 31 const form = new FormData(); 32 form.append(properties.fileUploadOrderNumber, this.order); 33 form.append(properties.fileUploadSession, session.information.id.toString()); 34 form.append(properties.fileUploadRequestId, functionName); 35 form.append(properties.fileUploadFileId, this.id); 36 form.append(properties.fileUploadMd5, ""); 37 // @ts-expect-error : trust me. 38 form.append("files[]", file, fileName); 39 40 this.form = form; 41 this.url = session.information.url + `/uploadfilesession/${session.information.accountKind}/${session.information.id}`; 42 this.headers["Content-Disposition"] = `attachment; filename="${encodeURI(fileName)}"`; 43 } 44 45 public async send (): Promise<void> { 46 // Please, see the following comment to understand. 47 // https://github.com/oven-sh/bun/issues/7917#issuecomment-1872454367 48 const textBody = await new Response(this.form).text(); 49 this.headers["Content-Type"] = `multipart/form-data; boundary=${textBody 50 .split("\n")[0] 51 .slice(2)}`; 52 53 let state = 3; // Set to UPLOADING by default. 54 55 while (state === 3) { // UPLOADING 56 const response = await this.session.fetcher({ 57 url: new URL(this.url), 58 method: "POST", 59 content: textBody, 60 headers: this.headers 61 }); 62 63 const json = JSON.parse(response.content); 64 state = json.etat; 65 } 66 67 // Even if there's an error, it bumped. 68 this.session.information.order++; 69 70 if (state === 0 || state === 2) { // UNKNOWN or ERROR 71 throw new UploadFailedError(); 72 } 73 } 74 }