UNPKG

reduct-js

Version:

ReductStore Client SDK for Javascript/NodeJS/Typescript

230 lines (229 loc) 7.84 kB
/** * Represents HTTP Client for ReductStore API * @class */ import { ServerInfo } from "./messages/ServerInfo"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import axios, { AxiosError } from "axios"; import { APIError } from "./APIError"; import { BucketInfo } from "./messages/BucketInfo"; import { BucketSettings } from "./messages/BucketSettings"; import { Bucket } from "./Bucket"; import { Token, TokenPermissions } from "./messages/Token"; import { Readable } from "stream"; import { Buffer } from "buffer"; import { FullReplicationInfo, ReplicationInfo, } from "./messages/ReplicationInfo"; import { ReplicationSettings } from "./messages/ReplicationSettings"; import * as https from "https"; export class Client { /** * HTTP Client for ReductStore * @param url URL to the storage * @param options */ constructor(url, options = {}) { // eslint-disable-next-line @typescript-eslint/no-var-requires const bigJson = require("json-bigint")({ alwaysParseAsBig: false, useNativeBigInt: true, }); // http client with big int support in JSON const axiosConfig = { baseURL: `${url}/api/v1`, timeout: options.timeout, headers: { Authorization: `Bearer ${options.apiToken}`, }, maxContentLength: Infinity, maxBodyLength: Infinity, transformRequest: [ (data) => { // very ugly hack to support big int in JSON if (typeof data !== "object" || data instanceof Readable || data instanceof Buffer) { return data; } return bigJson.stringify(data); }, ], transformResponse: [ (data) => { // very ugly hack to support big int in JSON if (typeof data !== "string") { return data; } if (data.length == 0) { return {}; } return bigJson.parse(data); }, ], }; if (typeof window === "undefined") { axiosConfig.httpsAgent = new https.Agent({ rejectUnauthorized: options.verifySSL !== false, }); } this.httpClient = axios.create(axiosConfig); this.httpClient.interceptors.response.use((response) => response, async (error) => { if (error instanceof AxiosError) { throw APIError.from(error); } throw error; }); } /** * Get server information * @async * @return {Promise<ServerInfo>} the data about the server */ async getInfo() { const { data } = await this.httpClient.get("/info"); return ServerInfo.parse(data); } /** * Get list of buckets * @async * @return {BucketInfo[]} * @see BucketInfo */ async getBucketList() { const { data } = await this.httpClient.get("/list"); return data.buckets.map((bucket) => BucketInfo.parse(bucket)); } /** * Create a new bucket * @param name name of the bucket * @param settings optional settings * @return {Promise<Bucket>} */ async createBucket(name, settings) { await this.httpClient.post(`/b/${name}`, settings ? BucketSettings.serialize(settings) : undefined); return new Bucket(name, this.httpClient); } /** * Get a bucket by name * @param name name of the bucket * @return {Promise<Bucket>} */ async getBucket(name) { await this.httpClient.get(`/b/${name}`); return new Bucket(name, this.httpClient); } /** * Try to create a bucket and get it if it already exists * @param name name of the bucket * @param settings optional settings * @return {Promise<Bucket>} */ async getOrCreateBucket(name, settings) { try { return await this.createBucket(name, settings); } catch (error) { if (error instanceof APIError && error.status === 409) { return await this.getBucket(name); } throw error; // pass exception forward } } /** * Create a new access token * @param name name of the token * @param permissions permissions for the token * @return {Promise<string>} the token * * @example * const token = await client.createToken("my-token", {fullAccess: true}); * const client = new Client("https://play.storage-reduct.dev", {apiToken: token}); */ async createToken(name, permissions) { const { data } = await this.httpClient.post(`/tokens/${name}`, TokenPermissions.serialize(permissions)); return data.value; } /** * Get a token by name * @param name name of the token * @return {Promise<Token>} the token */ async getToken(name) { const { data } = await this.httpClient.get(`/tokens/${name}`); return Token.parse(data); } /** * List all tokens * @return {Promise<Token[]>} the list of tokens */ async getTokenList() { const { data } = await this.httpClient.get("/tokens"); return data.tokens.map((token) => Token.parse(token)); } /** * Delete a token by name * @param name name of the token */ async deleteToken(name) { await this.httpClient.delete(`/tokens/${name}`); } /** * Get current API token and its permissions * @return {Promise<Token>} the token */ async me() { const { data } = await this.httpClient.get("/me"); return Token.parse(data); } /** * Get the list of replications * @return {Promise<ReplicationInfo[]>} the list of replications */ async getReplicationList() { const { data } = (await this.httpClient.get("/replications")); return data.replications.map((replication) => ReplicationInfo.parse(replication)); } /** * Get full information about a replication * @param name name of the replication * @return {Promise<FullReplicationInfo>} the replication */ async getReplication(name) { const { data } = await this.httpClient.get(`/replications/${name}`); return FullReplicationInfo.parse(data); } /** * Create a new replication * @param name name of the replication * @param settings settings of the replication * @return {Promise<void>} */ async createReplication(name, settings) { await this.httpClient.post(`/replications/${name}`, ReplicationSettings.serialize(settings)); } /** * Update a replication * @param name name of the replication * @param settings settings of the replication * @return {Promise<void>} */ async updateReplication(name, settings) { await this.httpClient.put(`/replications/${name}`, ReplicationSettings.serialize(settings)); } /** * Delete a replication * @param name name of the replication * @return {Promise<void>} */ async deleteReplication(name) { await this.httpClient.delete(`/replications/${name}`); } } export const isCompatibale = (min_version, current_version) => { if (min_version === undefined || current_version === undefined) { return false; } const [a_major, a_minor] = min_version.split(".").map((v) => parseInt(v)); const [b_major, b_minor] = current_version.split(".").map((v) => parseInt(v)); return a_major === b_major && a_minor <= b_minor; };