2018-02-01 15:27:01 +00:00
|
|
|
import * as rsa from "node-rsa";
|
2018-02-25 11:20:30 +00:00
|
|
|
import "isomorphic-fetch";
|
|
|
|
import * as btb from "blob-to-buffer"
|
|
|
|
|
2018-02-01 15:27:01 +00:00
|
|
|
|
|
|
|
export interface File {
|
|
|
|
_id: string;
|
|
|
|
type: "binary" | "text";
|
|
|
|
name: string;
|
|
|
|
time: string;
|
|
|
|
preview: string;
|
|
|
|
version: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface History {
|
|
|
|
file: File;
|
|
|
|
history: File[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class SecureFile {
|
|
|
|
private Server: string;
|
|
|
|
private Username: string;
|
|
|
|
private PrivateKey: string;
|
|
|
|
constructor(server: string, username: string, private_key: string) {
|
|
|
|
this.Server = server;
|
2018-02-25 11:24:52 +00:00
|
|
|
if (this.Server.endsWith("/")) {
|
|
|
|
this.Server += "api";
|
|
|
|
} else {
|
|
|
|
this.Server += "/api";
|
|
|
|
}
|
2018-02-01 15:27:01 +00:00
|
|
|
this.Username = username;
|
|
|
|
this.PrivateKey = private_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async getCode() {
|
2018-03-04 00:39:30 +00:00
|
|
|
var myHeaders = new Headers();
|
|
|
|
myHeaders.append('pragma', 'no-cache');
|
|
|
|
myHeaders.append('cache-control', 'no-cache');
|
|
|
|
|
|
|
|
var myInit = {
|
|
|
|
method: 'GET',
|
|
|
|
headers: myHeaders,
|
|
|
|
};
|
2018-03-10 17:33:47 +00:00
|
|
|
try {
|
|
|
|
var code_res = await fetch(this.Server + "/code?username=" + this.Username, myInit);
|
|
|
|
} catch (err) {
|
|
|
|
//TODO probably better fail check
|
|
|
|
throw new NoConnection();
|
|
|
|
}
|
2018-06-10 19:02:27 +00:00
|
|
|
if (code_res.status == 403) throw new Error("Unauthorized");
|
2018-03-02 11:23:25 +00:00
|
|
|
statusParser(code_res);
|
2018-02-01 15:27:01 +00:00
|
|
|
let code = (await code_res.json()).code;
|
|
|
|
let r = new rsa(this.PrivateKey, "pkcs1-pem");
|
|
|
|
return { code: code, signature: r.sign(code).toString("base64") };
|
|
|
|
}
|
|
|
|
|
2018-03-09 13:24:18 +00:00
|
|
|
public async makeRequest(endpoint: string, method: "POST" | "GET" | "PUT" | "DELETE", query: any, body?: Buffer) {
|
2018-02-01 15:27:01 +00:00
|
|
|
let code = await this.getCode();
|
|
|
|
query.code = code.code;
|
|
|
|
query.signature = code.signature;
|
|
|
|
let query_str = "?";
|
|
|
|
let first = true;
|
|
|
|
for (let key in query) {
|
|
|
|
if (!first) query_str += "&";
|
|
|
|
query_str += encodeURIComponent(key) + "=" + encodeURIComponent(query[key]);
|
|
|
|
first = false;
|
|
|
|
}
|
2018-03-04 00:39:30 +00:00
|
|
|
var myHeaders = new Headers();
|
|
|
|
myHeaders.append('pragma', 'no-cache');
|
|
|
|
myHeaders.append('cache-control', 'no-cache');
|
2018-03-10 17:33:47 +00:00
|
|
|
try {
|
|
|
|
let res = await fetch(this.Server + endpoint + query_str, { method: method, body: body, headers: myHeaders });
|
|
|
|
return res;
|
|
|
|
} catch (err) {
|
2018-06-11 20:10:14 +00:00
|
|
|
if (err instanceof TypeError || err.errno === "ECONNREFUSED")
|
|
|
|
throw new NoConnection();
|
|
|
|
console.log(err);
|
|
|
|
throw err;
|
2018-03-10 17:33:47 +00:00
|
|
|
}
|
2018-02-01 15:27:01 +00:00
|
|
|
}
|
|
|
|
|
2018-02-25 10:39:01 +00:00
|
|
|
async test(): Promise<{ user: string, test: true }> {
|
|
|
|
let res = await this.makeRequest("/test", "GET", {}, undefined);
|
|
|
|
statusParser(res);
|
|
|
|
return await res.json();
|
|
|
|
}
|
|
|
|
|
2018-02-05 09:32:48 +00:00
|
|
|
async list(folder?: string): Promise<File[]> {
|
|
|
|
if (!folder) folder = "root";
|
|
|
|
let res = await this.makeRequest("/files", "GET", { folder: folder }, undefined)
|
2018-02-01 15:27:01 +00:00
|
|
|
statusParser(res);
|
|
|
|
return await res.json();
|
|
|
|
}
|
|
|
|
|
2018-02-18 13:03:23 +00:00
|
|
|
async create(name: string, data: Buffer, type: "text" | "binary", folder?: string, encrypt: boolean = true, preview?: Buffer): Promise<File> {
|
|
|
|
let params: any = { type: type, name: name, no_encryption: !encrypt };
|
|
|
|
if (preview) {
|
|
|
|
params.preview = preview;
|
|
|
|
}
|
|
|
|
if (folder) {
|
|
|
|
params.folder = folder;
|
|
|
|
}
|
|
|
|
if (!encrypt) {
|
|
|
|
params.no_encryption = true;
|
|
|
|
}
|
|
|
|
let res = await this.makeRequest("/files", "POST", params, data)
|
2018-02-01 15:27:01 +00:00
|
|
|
statusParser(res);
|
|
|
|
return res.json();
|
|
|
|
}
|
|
|
|
|
|
|
|
async get(id: string, version?: string): Promise<Buffer> {
|
|
|
|
let res: Response;
|
|
|
|
if (typeof version === "string") {
|
|
|
|
res = await this.makeRequest(`/files/${id}/history/${version}`, "GET", {}, undefined);
|
|
|
|
} else {
|
|
|
|
res = await this.makeRequest("/files/" + id, "GET", {}, undefined);
|
|
|
|
}
|
|
|
|
statusParser(res);
|
2018-02-25 11:20:30 +00:00
|
|
|
if ((<any>res).buffer) {
|
|
|
|
return (<any>res).buffer();
|
|
|
|
} else {
|
|
|
|
return new Promise<Buffer>(async (resolve, reject) => {
|
|
|
|
btb(await res.blob(), (err, buffer) => {
|
|
|
|
if (err) reject(err);
|
|
|
|
else resolve(buffer);
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2018-02-01 15:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async update(id: string, data: Buffer, preview?: Buffer): Promise<File> {
|
|
|
|
let put: any = {};
|
|
|
|
if (preview) put.preview = preview;
|
|
|
|
let res = await this.makeRequest("/files/" + id, "PUT", put, data);
|
|
|
|
statusParser(res);
|
|
|
|
return res.json();
|
|
|
|
}
|
|
|
|
|
|
|
|
async delete(id: string): Promise<boolean> {
|
|
|
|
let res = await this.makeRequest("/files/" + id, "DELETE", {}, undefined);
|
|
|
|
statusParser(res);
|
|
|
|
return res.json();
|
|
|
|
}
|
|
|
|
|
|
|
|
async history(id: string): Promise<History> {
|
|
|
|
let res = await this.makeRequest(`/files/${id}/history`, "GET", {}, undefined);
|
|
|
|
statusParser(res);
|
|
|
|
return res.json();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-10 17:33:47 +00:00
|
|
|
export class NoConnection extends Error {
|
|
|
|
type: string;
|
|
|
|
constructor() {
|
|
|
|
super("No connection");
|
|
|
|
this.type = "noconnection"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-01 15:27:01 +00:00
|
|
|
export class Unauthorized extends Error {
|
2018-03-10 15:40:59 +00:00
|
|
|
type: string;
|
2018-02-01 15:27:01 +00:00
|
|
|
constructor() {
|
|
|
|
super("Not authorized");
|
2018-03-10 15:40:59 +00:00
|
|
|
this.type = "unauthorized"
|
2018-02-01 15:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class NotFound extends Error {
|
2018-03-10 15:40:59 +00:00
|
|
|
type: string;
|
2018-02-01 15:27:01 +00:00
|
|
|
constructor() {
|
|
|
|
super("Not found");
|
2018-03-10 15:40:59 +00:00
|
|
|
this.type = "notfound"
|
2018-02-01 15:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class BadRequest extends Error {
|
2018-03-10 15:40:59 +00:00
|
|
|
type: string;
|
2018-02-01 15:27:01 +00:00
|
|
|
constructor() {
|
|
|
|
super("Bad request");
|
2018-03-10 15:40:59 +00:00
|
|
|
this.type = "badrequest"
|
2018-02-01 15:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function statusParser(res: Response) {
|
|
|
|
if (res.status !== 200) {
|
|
|
|
switch (res.status) {
|
|
|
|
case 400:
|
|
|
|
throw new BadRequest();
|
|
|
|
case 404:
|
|
|
|
throw new NotFound();
|
|
|
|
case 403:
|
|
|
|
throw new Unauthorized();
|
|
|
|
default:
|
|
|
|
throw new Error(res.statusText);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|