214 lines
5.4 KiB
TypeScript
214 lines
5.4 KiB
TypeScript
import { Serializable, AsBytes } from "./encoding";
|
|
import { SimpleContainer } from "./container";
|
|
import { VaultID } from "./vault";
|
|
import { AESKeyParams } from "./crypto";
|
|
import { getCryptoProvider as getProvider } from "./platform";
|
|
import { Err, ErrorCode } from "./error";
|
|
import { RequestProgress } from "./transport";
|
|
|
|
export async function readFileAsUint8Array(blob: File): Promise<Uint8Array> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = () => {
|
|
const result = new Uint8Array(reader.result as ArrayBuffer);
|
|
resolve(result);
|
|
};
|
|
|
|
reader.onerror = (error) => {
|
|
reader.abort();
|
|
reject(error);
|
|
};
|
|
|
|
reader.readAsArrayBuffer(blob);
|
|
});
|
|
}
|
|
|
|
export async function readFileAsArrayBuffer(blob: File): Promise<ArrayBuffer> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = () => {
|
|
resolve(reader.result as ArrayBuffer);
|
|
};
|
|
|
|
reader.onerror = (error) => {
|
|
reader.abort();
|
|
reject(error);
|
|
};
|
|
|
|
reader.readAsArrayBuffer(blob);
|
|
});
|
|
}
|
|
|
|
export async function readFileAsText(blob: File): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = () => {
|
|
resolve(reader.result as string);
|
|
};
|
|
|
|
reader.onerror = (error) => {
|
|
reader.abort();
|
|
reject(error);
|
|
};
|
|
|
|
reader.readAsText(blob);
|
|
});
|
|
}
|
|
|
|
function readFileAsDataURL(blob: File): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = () => {
|
|
resolve(reader.result as string);
|
|
};
|
|
|
|
reader.onerror = (e) => {
|
|
reader.abort();
|
|
reject(e);
|
|
};
|
|
|
|
reader.readAsDataURL(blob);
|
|
});
|
|
}
|
|
|
|
export type AttachmentID = string;
|
|
|
|
export class AttachmentInfo extends Serializable {
|
|
constructor(vals: Partial<AttachmentInfo> = {}) {
|
|
super();
|
|
Object.assign(this, vals);
|
|
}
|
|
|
|
id: AttachmentID = "";
|
|
vault: VaultID = "";
|
|
name: string = "";
|
|
size: number = 0;
|
|
type: string = "";
|
|
|
|
@AsBytes()
|
|
key!: Uint8Array;
|
|
}
|
|
|
|
export class Attachment extends SimpleContainer {
|
|
id: AttachmentID = "";
|
|
vault: VaultID = "";
|
|
name: string = "";
|
|
size: number = 0;
|
|
type: string = "";
|
|
uploadProgress?: RequestProgress;
|
|
downloadProgress?: RequestProgress;
|
|
|
|
constructor({ key, ...info }: Partial<AttachmentInfo> = {}) {
|
|
super();
|
|
Object.assign(this, {
|
|
_key: key,
|
|
...info,
|
|
});
|
|
}
|
|
|
|
get info(): AttachmentInfo {
|
|
return new AttachmentInfo({
|
|
id: this.id,
|
|
vault: this.vault,
|
|
name: this.name,
|
|
type: this.type,
|
|
size: this.size,
|
|
key: this._key,
|
|
});
|
|
}
|
|
|
|
get loaded(): boolean {
|
|
return !!this.encryptedData;
|
|
}
|
|
|
|
async fromFile(file: File) {
|
|
this.type = file.type;
|
|
this.size = file.size;
|
|
this.name = file.name;
|
|
|
|
const data = await readFileAsUint8Array(file);
|
|
|
|
this._key = await getProvider().generateKey({
|
|
algorithm: "AES",
|
|
keySize: this.encryptionParams.keySize,
|
|
} as AESKeyParams);
|
|
|
|
await this.setData(data);
|
|
return this;
|
|
}
|
|
|
|
async toFile(): Promise<File> {
|
|
const data = await this.getData();
|
|
return new File([data], this.name, { type: this.type });
|
|
}
|
|
|
|
async toDataURL(): Promise<string> {
|
|
const file = await this.toFile();
|
|
return readFileAsDataURL(file);
|
|
}
|
|
|
|
async toObjectURL(): Promise<string> {
|
|
const file = await this.toFile();
|
|
return URL.createObjectURL(file);
|
|
}
|
|
|
|
async toText(): Promise<string> {
|
|
const file = await this.toFile();
|
|
return readFileAsText(file);
|
|
}
|
|
|
|
validate() {
|
|
return typeof this.id === "string" && typeof this.vault === "string" && typeof this.size === "number";
|
|
}
|
|
}
|
|
|
|
export interface AttachmentStorage {
|
|
put(a: Attachment): Promise<void>;
|
|
get(vault: VaultID, id: AttachmentID): Promise<Attachment>;
|
|
delete(vault: VaultID, id: AttachmentID): Promise<void>;
|
|
deleteAll(vault: VaultID): Promise<void>;
|
|
getUsage(vault: VaultID): Promise<number>;
|
|
}
|
|
|
|
export class MemoryAttachmentStorage implements AttachmentStorage {
|
|
private _storage = new Map<string, Attachment>();
|
|
|
|
async put(a: Attachment): Promise<void> {
|
|
this._storage.set(`${a.vault}_${a.id}`, a);
|
|
}
|
|
|
|
async get(vault: VaultID, id: AttachmentID): Promise<Attachment> {
|
|
const att = this._storage.get(`${vault}_${id}`);
|
|
if (!att) {
|
|
throw new Err(ErrorCode.NOT_FOUND);
|
|
}
|
|
return att;
|
|
}
|
|
|
|
async delete(vault: VaultID, id: AttachmentID): Promise<void> {
|
|
this._storage.delete(`${vault}_${id}`);
|
|
}
|
|
|
|
async deleteAll(vault: VaultID): Promise<void> {
|
|
for (const key of this._storage.keys()) {
|
|
if (key.startsWith(vault)) {
|
|
this._storage.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
async getUsage(vault: VaultID): Promise<number> {
|
|
let size = 0;
|
|
for (const [key, att] of this._storage.entries()) {
|
|
if (key.startsWith(vault)) {
|
|
size += att.size;
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
}
|