2021-06-21 22:05:08 +00:00
|
|
|
import { KeyValueStorage } from "./basic.ts";
|
|
|
|
|
|
|
|
function protectKey(key: string): string {
|
|
|
|
return encodeURIComponent(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
type RestClient = (
|
|
|
|
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
|
|
uri: string,
|
|
|
|
body?: string,
|
|
|
|
) => Promise<string | null>;
|
|
|
|
export const HEADER_REQUESTER = "X-TK-Storage-App";
|
|
|
|
export const HEADER_REPLYIER = "X-TK-Storage-Control";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Storage on a remote server, directly usable using HTTP REST verbs
|
|
|
|
*/
|
|
|
|
export class RestRemoteStorage implements KeyValueStorage {
|
|
|
|
readonly appname: string;
|
|
|
|
readonly base_url: string;
|
|
|
|
readonly client: RestClient;
|
|
|
|
|
|
|
|
constructor(appname: string, url: string) {
|
|
|
|
this.appname = appname;
|
|
|
|
this.base_url = (url[url.length - 1] != "/") ? url + "/" : url;
|
|
|
|
this.client = this.createClient();
|
|
|
|
}
|
|
|
|
|
|
|
|
async get(key: string): Promise<string | null> {
|
|
|
|
key = protectKey(key);
|
|
|
|
return await this.client("GET", key);
|
|
|
|
}
|
|
|
|
|
|
|
|
async set(key: string, value: string | null): Promise<void> {
|
|
|
|
key = protectKey(key);
|
|
|
|
if (value === null) {
|
|
|
|
await this.client("DELETE", key);
|
|
|
|
} else {
|
|
|
|
await this.client("PUT", key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private createClient(): RestClient {
|
|
|
|
const client: RestClient = async (method, path, body?) => {
|
|
|
|
const response = await fetch(`${this.base_url}${path}`, {
|
|
|
|
method,
|
|
|
|
body,
|
|
|
|
headers: {
|
|
|
|
[HEADER_REQUESTER]: this.appname,
|
|
|
|
},
|
|
|
|
});
|
2021-06-22 20:07:26 +00:00
|
|
|
// the body is consumed here to not leak resource, but it would
|
|
|
|
// be better to consume it only when needed, once
|
|
|
|
// https://github.com/denoland/deno/issues/4735 is fixed
|
|
|
|
const text = await response.text();
|
2021-06-21 22:05:08 +00:00
|
|
|
if (response.headers.get(HEADER_REPLYIER) != "ok") {
|
|
|
|
throw new Error("storage not compatible with tk-storage");
|
|
|
|
} else if (response.status == 200) {
|
2021-06-22 20:07:26 +00:00
|
|
|
return text;
|
2021-06-21 22:05:08 +00:00
|
|
|
} else if (response.status == 404) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
throw new Error(
|
|
|
|
`http error ${response.status}: ${response.statusText}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return client;
|
|
|
|
}
|
|
|
|
}
|