forked from Kieran/void.cat
Add headers to request
This commit is contained in:
parent
1babc8e4e0
commit
e0652dfbb8
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@void-cat/api",
|
"name": "@void-cat/api",
|
||||||
"version": "1.0.1",
|
"version": "1.0.4",
|
||||||
"description": "void.cat API package",
|
"description": "void.cat API package",
|
||||||
"main": "dist/index.js",
|
"main": "dist/lib.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"repository": "https://git.v0l.io/Kieran/void.cat",
|
"repository": "https://git.v0l.io/Kieran/void.cat",
|
||||||
"author": "Kieran",
|
"author": "Kieran",
|
||||||
|
@ -52,9 +52,9 @@ export class VoidApi {
|
|||||||
*/
|
*/
|
||||||
getUploader(
|
getUploader(
|
||||||
file: File | Blob,
|
file: File | Blob,
|
||||||
stateChange: StateChangeHandler,
|
stateChange?: StateChangeHandler,
|
||||||
progress: ProgressHandler,
|
progress?: ProgressHandler,
|
||||||
proxyChallenge: ProxyChallengeHandler,
|
proxyChallenge?: ProxyChallengeHandler,
|
||||||
chunkSize?: number
|
chunkSize?: number
|
||||||
): VoidUploader {
|
): VoidUploader {
|
||||||
if (StreamUploader.canUse()) {
|
if (StreamUploader.canUse()) {
|
||||||
|
@ -27,15 +27,15 @@ export class StreamUploader extends VoidUploader {
|
|||||||
return this.#encrypt?.getKey()
|
return this.#encrypt?.getKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(): Promise<VoidUploadResult> {
|
async upload(headers?: HeadersInit): Promise<VoidUploadResult> {
|
||||||
this.onStateChange(UploadState.Hashing);
|
this.onStateChange?.(UploadState.Hashing);
|
||||||
const hash = await this.digest(this.file);
|
const hash = await this.digest(this.file);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
|
|
||||||
const DefaultChunkSize = 1024 * 1024;
|
const DefaultChunkSize = 1024 * 1024;
|
||||||
const rsBase = new ReadableStream({
|
const rsBase = new ReadableStream({
|
||||||
start: async () => {
|
start: async () => {
|
||||||
this.onStateChange(UploadState.Uploading);
|
this.onStateChange?.(UploadState.Uploading);
|
||||||
},
|
},
|
||||||
pull: async (controller) => {
|
pull: async (controller) => {
|
||||||
const chunk = await this.readChunk(offset, controller.desiredSize ?? DefaultChunkSize);
|
const chunk = await this.readChunk(offset, controller.desiredSize ?? DefaultChunkSize);
|
||||||
@ -43,7 +43,7 @@ export class StreamUploader extends VoidUploader {
|
|||||||
controller.close();
|
controller.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.onProgress(offset + chunk.byteLength);
|
this.onProgress?.(offset + chunk.byteLength);
|
||||||
offset += chunk.byteLength
|
offset += chunk.byteLength
|
||||||
controller.enqueue(chunk);
|
controller.enqueue(chunk);
|
||||||
},
|
},
|
||||||
@ -55,23 +55,26 @@ export class StreamUploader extends VoidUploader {
|
|||||||
highWaterMark: DefaultChunkSize
|
highWaterMark: DefaultChunkSize
|
||||||
});
|
});
|
||||||
|
|
||||||
const headers = {
|
const reqHeaders = {
|
||||||
"Content-Type": "application/octet-stream",
|
"Content-Type": "application/octet-stream",
|
||||||
"V-Content-Type": !this.file.type ? "application/octet-stream" : this.file.type,
|
"V-Content-Type": !this.file.type ? "application/octet-stream" : this.file.type,
|
||||||
"V-Filename": "name" in this.file ? this.file.name : "",
|
"V-Filename": "name" in this.file ? this.file.name : "",
|
||||||
"V-Full-Digest": hash
|
"V-Full-Digest": hash,
|
||||||
} as Record<string, string>;
|
} as Record<string, string>;
|
||||||
if (this.#encrypt) {
|
if (this.#encrypt) {
|
||||||
headers["V-EncryptionParams"] = JSON.stringify(this.#encrypt!.getParams());
|
reqHeaders["V-EncryptionParams"] = JSON.stringify(this.#encrypt!.getParams());
|
||||||
}
|
}
|
||||||
if (this.auth) {
|
if (this.auth) {
|
||||||
headers["Authorization"] = `Bearer ${this.auth}`;
|
reqHeaders["Authorization"] = `Bearer ${this.auth}`;
|
||||||
}
|
}
|
||||||
const req = await fetch(`${this.uri}/upload`, {
|
const req = await fetch(`${this.uri}/upload`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
body: this.#encrypt ? rsBase.pipeThrough(this.#encrypt!.getEncryptionTransform()) : rsBase,
|
body: this.#encrypt ? rsBase.pipeThrough(this.#encrypt!.getEncryptionTransform()) : rsBase,
|
||||||
headers,
|
headers: {
|
||||||
|
...reqHeaders,
|
||||||
|
...headers
|
||||||
|
},
|
||||||
// @ts-ignore New stream spec
|
// @ts-ignore New stream spec
|
||||||
duplex: 'half'
|
duplex: 'half'
|
||||||
});
|
});
|
||||||
|
@ -27,16 +27,16 @@ export abstract class VoidUploader {
|
|||||||
protected file: File | Blob;
|
protected file: File | Blob;
|
||||||
protected auth?: string;
|
protected auth?: string;
|
||||||
protected maxChunkSize: number;
|
protected maxChunkSize: number;
|
||||||
protected onStateChange: StateChangeHandler;
|
protected onStateChange?: StateChangeHandler;
|
||||||
protected onProgress: ProgressHandler;
|
protected onProgress?: ProgressHandler;
|
||||||
protected onProxyChallenge: ProxyChallengeHandler;
|
protected onProxyChallenge?: ProxyChallengeHandler;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
uri: string,
|
uri: string,
|
||||||
file: File | Blob,
|
file: File | Blob,
|
||||||
stateChange: StateChangeHandler,
|
stateChange?: StateChangeHandler,
|
||||||
progress: ProgressHandler,
|
progress?: ProgressHandler,
|
||||||
proxyChallenge: ProxyChallengeHandler,
|
proxyChallenge?: ProxyChallengeHandler,
|
||||||
auth?: string,
|
auth?: string,
|
||||||
chunkSize?: number
|
chunkSize?: number
|
||||||
) {
|
) {
|
||||||
@ -65,12 +65,16 @@ export abstract class VoidUploader {
|
|||||||
const slice = file.slice(offset, offset + ChunkSize);
|
const slice = file.slice(offset, offset + ChunkSize);
|
||||||
const chunk = await slice.arrayBuffer();
|
const chunk = await slice.arrayBuffer();
|
||||||
sha.update(sjclcodec.toBits(new Uint8Array(chunk)));
|
sha.update(sjclcodec.toBits(new Uint8Array(chunk)));
|
||||||
this.onProgress(progress += chunk.byteLength);
|
this.onProgress?.(progress += chunk.byteLength);
|
||||||
}
|
}
|
||||||
return buf2hex(sjclcodec.fromBits(sha.finalize()));
|
return buf2hex(sjclcodec.fromBits(sha.finalize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract upload(): Promise<VoidUploadResult>;
|
/**
|
||||||
|
* Upload a file to the API
|
||||||
|
* @param headers any additional headers to send with the request
|
||||||
|
*/
|
||||||
|
abstract upload(headers?: HeadersInit): Promise<VoidUploadResult>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can we use local encryption
|
* Can we use local encryption
|
||||||
|
@ -14,23 +14,23 @@ export class XHRUploader extends VoidUploader {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(): Promise<VoidUploadResult> {
|
async upload(headers?: HeadersInit): Promise<VoidUploadResult> {
|
||||||
this.onStateChange(UploadState.Hashing);
|
this.onStateChange?.(UploadState.Hashing);
|
||||||
const hash = await this.digest(this.file);
|
const hash = await this.digest(this.file);
|
||||||
if (this.file.size > this.maxChunkSize) {
|
if (this.file.size > this.maxChunkSize) {
|
||||||
return await this.#doSplitXHRUpload(hash, this.maxChunkSize);
|
return await this.#doSplitXHRUpload(hash, this.maxChunkSize, headers);
|
||||||
} else {
|
} else {
|
||||||
return await this.#xhrSegment(this.file, hash);
|
return await this.#xhrSegment(this.file, hash, undefined, undefined, 1, 1, headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #doSplitXHRUpload(hash: string, splitSize: number) {
|
async #doSplitXHRUpload(hash: string, splitSize: number, headers?: HeadersInit) {
|
||||||
let xhr: VoidUploadResult | null = null;
|
let xhr: VoidUploadResult | null = null;
|
||||||
const segments = Math.ceil(this.file.size / splitSize);
|
const segments = Math.ceil(this.file.size / splitSize);
|
||||||
for (let s = 0; s < segments; s++) {
|
for (let s = 0; s < segments; s++) {
|
||||||
const offset = s * splitSize;
|
const offset = s * splitSize;
|
||||||
const slice = this.file.slice(offset, offset + splitSize, this.file.type);
|
const slice = this.file.slice(offset, offset + splitSize, this.file.type);
|
||||||
xhr = await this.#xhrSegment(slice, hash, xhr?.file?.id, xhr?.file?.metadata?.editSecret, s + 1, segments);
|
xhr = await this.#xhrSegment(slice, hash, xhr?.file?.id, xhr?.file?.metadata?.editSecret, s + 1, segments, headers);
|
||||||
if (!xhr.ok) {
|
if (!xhr.ok) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -46,9 +46,11 @@ export class XHRUploader extends VoidUploader {
|
|||||||
* @param editSecret
|
* @param editSecret
|
||||||
* @param part Segment number
|
* @param part Segment number
|
||||||
* @param partOf Total number of segments
|
* @param partOf Total number of segments
|
||||||
|
* @param headers
|
||||||
*/
|
*/
|
||||||
async #xhrSegment(segment: ArrayBuffer | Blob, fullDigest: string, id?: string, editSecret?: string, part?: number, partOf?: number) {
|
async #xhrSegment(segment: ArrayBuffer | Blob, fullDigest: string,
|
||||||
this.onStateChange(UploadState.Uploading);
|
id?: string, editSecret?: string, part?: number, partOf?: number, headers?: HeadersInit) {
|
||||||
|
this.onStateChange?.(UploadState.Uploading);
|
||||||
|
|
||||||
return await new Promise<VoidUploadResult>((resolve, reject) => {
|
return await new Promise<VoidUploadResult>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
@ -60,15 +62,15 @@ export class XHRUploader extends VoidUploader {
|
|||||||
} else if (req.readyState === XMLHttpRequest.DONE && req.status === 403) {
|
} else if (req.readyState === XMLHttpRequest.DONE && req.status === 403) {
|
||||||
const contentType = req.getResponseHeader("content-type");
|
const contentType = req.getResponseHeader("content-type");
|
||||||
if (contentType?.toLowerCase().trim().indexOf("text/html") === 0) {
|
if (contentType?.toLowerCase().trim().indexOf("text/html") === 0) {
|
||||||
this.onProxyChallenge(req.response);
|
this.onProxyChallenge?.(req.response);
|
||||||
this.onStateChange(UploadState.Challenge);
|
this.onStateChange?.(UploadState.Challenge);
|
||||||
reject(new Error("CF Challenge"));
|
reject(new Error("CF Challenge"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
req.upload.onprogress = (e) => {
|
req.upload.onprogress = (e) => {
|
||||||
if (e instanceof ProgressEvent) {
|
if (e instanceof ProgressEvent) {
|
||||||
this.onProgress(e.loaded);
|
this.onProgress?.(e.loaded);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
req.open("POST", id ? `${this.uri}/upload/${id}` : `${this.uri}/upload`);
|
req.open("POST", id ? `${this.uri}/upload/${id}` : `${this.uri}/upload`);
|
||||||
@ -84,6 +86,11 @@ export class XHRUploader extends VoidUploader {
|
|||||||
if (editSecret) {
|
if (editSecret) {
|
||||||
req.setRequestHeader("V-EditSecret", editSecret);
|
req.setRequestHeader("V-EditSecret", editSecret);
|
||||||
}
|
}
|
||||||
|
if (headers) {
|
||||||
|
for (const [k, v] of Object.entries(headers)) {
|
||||||
|
req.setRequestHeader(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
req.send(segment);
|
req.send(segment);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const isProduction = process.env.NODE_ENV == 'production';
|
const isProduction = process.env.NODE_ENV == 'production';
|
||||||
const config = {
|
const config = {
|
||||||
entry: './src/index.ts',
|
entry: {
|
||||||
|
lib: './src/index.ts',
|
||||||
|
},
|
||||||
devtool: isProduction ? "source-map" : "eval",
|
devtool: isProduction ? "source-map" : "eval",
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
Loading…
Reference in New Issue
Block a user