@openstream/client
Version:
Openstream Radio Server Client
587 lines (586 loc) • 22.7 kB
JavaScript
import StatusCode from "http-status-codes";
import qs from "qs";
import { ACCESS_TOKEN_HEADER, FORWARD_IP_HEADER } from "./defs/constants.js";
import { ClientError } from "./error.js";
// import type { Logger } from "./logger.js";
import node_fetch, { Headers } from "node-fetch";
export { ClientError };
// import http from "http";
// import https from "https";
// const http_agent = new http.Agent({ keepAlive: false });
// const https_agent = new https.Agent({ keepAlive: false });
const qss = (v) => {
return qs.stringify(v, { addQueryPrefix: true, skipNulls: true });
};
export class Client {
base_url;
node_fetch;
// logger: Logger;
me;
auth;
plans;
admins;
users;
accounts;
stations;
analytics;
app_analytics;
invitations;
payment_methods;
stream_connections;
stream_connections_lite;
constructor(base_url, { /*ogger,*/ fetch = node_fetch } = {}) {
this.base_url = base_url.trim().replace(/\/+$/g, "");
// this.logger = logger.scoped("client");
this.node_fetch = fetch;
this.me = new Me(this);
this.auth = new Auth(this);
this.plans = new Plans(this);
this.admins = new Admins(this);
this.users = new Users(this);
this.accounts = new Accounts(this);
this.stations = new Stations(this);
this.analytics = new Analytics(this);
this.app_analytics = new AppAnalytics(this);
this.invitations = new AccountInvitations(this);
this.payment_methods = new PaymentMethods(this);
this.stream_connections = new StreamConnections(this);
this.stream_connections_lite = new StreamConnectionsLite(this);
}
async fetch(_url, init = {}) {
const url = `${this.base_url}${_url}`;
// const method = init.method ?? "GET";
// this.logger.debug(`fetch: ${method} ${url}`);
return await this.node_fetch(url, { ...init }).catch(e => {
// this.logger.warn(`fetch error: ${e} | cause=${e.cause}`)
console.warn("client fetch failed", e, e.cause ?? null);
throw new ClientError(StatusCode.BAD_GATEWAY, "CLIENT_GATEWAY_FETCH", "Gateway unavailable");
});
}
async get_json_body(res) {
const body = await res.json().catch(e => {
// this.logger.warn(`json error: ${e} cause=${e.cause}`)
throw new ClientError(StatusCode.BAD_GATEWAY, "CLIENT_GATEWAY_JSON", "Gateway error");
});
if (body?.error) {
let message = typeof body.error.message ? body.error.message : "Internal server error";
let code = typeof body.error?.code === "string" ?
body.error.code :
"CLIENT_GATEWAY_MISSING_CODE";
throw new ClientError(res.status, code, message);
}
return body;
}
json_headers({ ip, ua, token, wpayload }) {
const headers = new Headers();
if (ip)
headers.append(FORWARD_IP_HEADER, ip);
// remove default user agent
headers.append("user-agent", ua || "openstream-unknown");
if (token)
headers.append(ACCESS_TOKEN_HEADER, token);
if (wpayload)
headers.append("content-type", "application/json");
return headers;
}
async json_request(url, init) {
const res = await this.fetch(url, init);
const body = await this.get_json_body(res);
return body;
}
async get(ip, ua, token, url) {
return await this.json_request(url, {
headers: this.json_headers({ ip, ua, token, wpayload: false }),
});
}
async delete(ip, ua, token, url) {
return await this.json_request(url, {
method: "DELETE",
headers: this.json_headers({ ip, ua, token, wpayload: false }),
});
}
async post(ip, ua, token, url, payload) {
return await this.json_request(url, {
method: "POST",
headers: this.json_headers({ ip, ua, token, wpayload: true }),
body: JSON.stringify(payload)
});
}
async put(ip, ua, token, url, payload) {
return await this.json_request(url, {
method: "PUT",
headers: this.json_headers({ ip, ua, token, wpayload: true }),
body: JSON.stringify(payload)
});
}
async patch(ip, ua, token, url, payload) {
return await this.json_request(url, {
method: "PATCH",
headers: this.json_headers({ ip, ua, token, wpayload: true }),
body: JSON.stringify(payload)
});
}
async get_stream_stats(ip, ua, token) {
return await this.get(ip, ua, token, `/stream-stats`);
}
async get_stream_stats_item_now(ip, ua, token) {
return await this.get(ip, ua, token, `/stream-stats/now`);
}
async get_stream_stats_item_now_count(ip, ua, token) {
return await this.get(ip, ua, token, `/stream-stats/now/count`);
}
async get_stream_stats_now_count_by_station(ip, ua, token) {
return await this.get(ip, ua, token, `/stream-stats/now/count-by-station`);
}
async get_stream_stats_item_since(ip, ua, token, num, unit) {
return await this.get(ip, ua, token, `/stream-stats/last-${num}${unit}`);
}
async get_stream_stats_item_since_count(ip, ua, token, num, unit) {
return await this.get(ip, ua, token, `/stream-stats/last-${num}${unit}/count`);
}
}
export class Me {
client;
devices;
api_keys;
constructor(client) {
this.client = client;
this.devices = new MeDevices(client);
this.api_keys = new MeApiKeys(client);
}
async me(ip, ua, token) {
return await this.client.get(ip, ua, token, `/me`);
}
}
export class MeDevices {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/me/devices${qss(query)}`);
}
async delete(ip, ua, token, id) {
return await this.client.delete(ip, ua, token, `/me/devices/${id}`);
}
}
export class MeApiKeys {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/me/api-keys${qss(query)}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/me/api-keys`, payload);
}
async patch(ip, ua, token, id, payload) {
return await this.client.patch(ip, ua, token, `/me/api-keys/${id}`, payload);
}
async delete(ip, ua, token, id) {
return await this.client.delete(ip, ua, token, `/me/api-keys/${id}`);
}
}
export class Auth {
client;
user;
admin;
constructor(client) {
this.client = client;
this.user = new AuthUser(client);
this.admin = new AuthAdmin(client);
}
async send_email_verification_code(ip, ua, payload) {
return await this.client.post(ip, ua, null, `/auth/email-verification/send-code`, payload);
}
}
export class AuthAdmin {
client;
constructor(client) {
this.client = client;
}
async login(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, "/auth/admin/login", payload);
}
async logout(ip, ua, token) {
return await this.client.post(ip, ua, token, "/auth/admin/logout", void 0);
}
async delegate(ip, ua, token, user_id, payload) {
return await this.client.post(ip, ua, token, `/auth/admin/delegate/${user_id}`, payload);
}
}
export class AuthUser {
client;
recovery_token;
constructor(client) {
this.client = client;
this.recovery_token = new AuthUserRecoveryToken(this.client);
}
async email_exists(ip, ua, token, email) {
return await this.client.get(ip, ua, token, `/auth/user/email-exists/${email}`);
}
async login(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, "/auth/user/login", payload);
}
async logout(ip, ua, token) {
return await this.client.post(ip, ua, token, "/auth/user/logout", void 0);
}
async register(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, "/auth/user/register", payload);
}
async recover(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, "/auth/user/recover", payload);
}
}
export class AuthUserRecoveryToken {
client;
constructor(client) {
this.client = client;
}
async get(ip, ua, token, key) {
return await this.client.get(ip, ua, token, `/auth/user/recovery-token/${key}`);
}
async set_password(ip, ua, token, key, payload) {
return await this.client.post(ip, ua, token, `/auth/user/recovery-token/${key}/set-password`, payload);
}
}
export class Plans {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/plans${qss(query)}`);
}
async get(ip, ua, token, plan_id) {
return await this.client.get(ip, ua, token, `/plans/${plan_id}`);
}
async get_by_slug(ip, ua, token, plan_slug) {
return await this.client.get(ip, ua, token, `/plans/by-slug/${plan_slug}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/plans`, payload);
}
async patch(ip, ua, token, plan_id, payload) {
return await this.client.patch(ip, ua, token, `/plans/${plan_id}`, payload);
}
async delete(ip, ua, token, plan_id) {
return await this.client.delete(ip, ua, token, `/plans/${plan_id}`);
}
}
export class Accounts {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/accounts${qss(query)}`);
}
async get(ip, ua, token, id) {
return await this.client.get(ip, ua, token, `/accounts/${id}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/accounts`, payload);
}
async patch(ip, ua, token, account_id, payload) {
return await this.client.patch(ip, ua, token, `/accounts/${account_id}`, payload);
}
async delete(ip, ua, token, account_id) {
return await this.client.delete(ip, ua, token, `/accounts/${account_id}`);
}
async list_members(ip, ua, token, account_id) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/members`);
}
async delete_member(ip, ua, token, account_id, member_id) {
return await this.client.delete(ip, ua, token, `/accounts/${account_id}/members/${member_id}`);
}
async set_member_role(ip, ua, token, account_id, member_id, payload) {
return await this.client.post(ip, ua, token, `/accounts/${account_id}/members/${member_id}/set-role`, payload);
}
async get_stream_stats(ip, ua, token, account_id) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/stream-stats`);
}
async get_stream_stats_item_now(ip, ua, token, account_id) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/stream-stats/now`);
}
async get_stream_stats_item_now_count(ip, ua, token, account_id) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/stream-stats/now/count`);
}
async get_stream_stats_now_count_by_station(ip, ua, token, account_id) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/stream-stats/now/count-by-station`);
}
async get_stream_stats_item_since(ip, ua, token, account_id, num, unit) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/stream-stats/last-${num}${unit}`);
}
async get_stream_stats_item_since_count(ip, ua, token, account_id, num, unit) {
return await this.client.get(ip, ua, token, `/accounts/${account_id}/stream-stats/last-${num}${unit}/count`);
}
}
export class Stations {
client;
files;
pictures;
constructor(client) {
this.client = client;
this.files = new StationFiles(client);
this.pictures = new StationPictures(client);
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/stations${qss(query)}`);
}
async get(ip, ua, token, id) {
return await this.client.get(ip, ua, token, `/stations/${id}`);
}
async delete(ip, ua, token, id) {
return await this.client.delete(ip, ua, token, `/stations/${id}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/stations`, payload);
}
async is_slug_available(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/stations/is-slug-available${qss(query)}`);
}
async transfer(ip, ua, token, id, payload) {
return await this.client.post(ip, ua, token, `/stations/${id}/transfer`, payload);
}
async patch(ip, ua, token, id, payload) {
return await this.client.patch(ip, ua, token, `/stations/${id}`, payload);
}
async get_stream_stats(ip, ua, token, id) {
return await this.client.get(ip, ua, token, `/stations/${id}/stream-stats`);
}
async get_stream_stats_item_now(ip, ua, token, id) {
return await this.client.get(ip, ua, token, `/stations/${id}/stream-stats/now`);
}
async get_stream_stats_item_now_count(ip, ua, token, id) {
return await this.client.get(ip, ua, token, `/stations/${id}/stream-stats/now/count`);
}
async get_stream_stats_item_since(ip, ua, token, id, num, unit) {
return await this.client.get(ip, ua, token, `/stations/${id}/stream-stats/last-${num}${unit}`);
}
async get_stream_stats_item_since_count(ip, ua, token, id, num, unit) {
return await this.client.get(ip, ua, token, `/stations/${id}/stream-stats/last-${num}${unit}/count`);
}
async get_now_playing(ip, ua, token, id) {
return await this.client.get(ip, ua, token, `/stations/${id}/now-playing`);
}
// async get_dashboard_stats(ip: string | null, ua: string | null, token: string, id: string): Promise<import("./defs/api/stations/[station]/dashboard-stats/GET/Output.js").Output> {
// return await this.client.get(ip, ua, token, `/stations/${id}/dashboard-stats`);
// }
async restart_playlist(ip, ua, token, id) {
return await this.client.post(ip, ua, token, `/stations/${id}/restart-playlist`, undefined);
}
async reset_source_password(ip, ua, token, id) {
return await this.client.post(ip, ua, token, `/stations/${id}/reset-source-password`, undefined);
}
}
export class StationPictures {
client;
constructor(client) {
this.client = client;
}
async post(ip, ua, token, query, data) {
const headers = new Headers();
if (ip)
headers.append(FORWARD_IP_HEADER, ip);
if (ua)
headers.append("user-agent", ua);
headers.append(ACCESS_TOKEN_HEADER, token);
headers.append("content-type", "application/octet-stream");
let res = await this.client.fetch(`/station-pictures${qss(query)}`, {
method: "POST",
headers,
body: data
});
return await this.client.get_json_body(res);
}
}
export class Admins {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/admins${qss(query)}`);
}
async get(ip, ua, token, admin_id) {
return await this.client.get(ip, ua, token, `/admins/${admin_id}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/admins`, payload);
}
async patch(ip, ua, token, admin_id, payload) {
return await this.client.patch(ip, ua, token, `/admins/${admin_id}`, payload);
}
async change_password(ip, ua, token, admin_id, payload) {
return await this.client.post(ip, ua, token, `/admins/${admin_id}/change-password`, payload);
}
}
export class Users {
client;
stations;
constructor(client) {
this.client = client;
this.stations = new UserStations(client);
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/users${qss(query)}`);
}
async get(ip, ua, token, user_id) {
return await this.client.get(ip, ua, token, `/users/${user_id}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/users`, payload);
}
async patch(ip, ua, token, user_id, payload) {
return await this.client.patch(ip, ua, token, `/users/${user_id}`, payload);
}
async delete(ip, ua, token, user_id) {
return await this.client.delete(ip, ua, token, `/users/${user_id}`);
}
async change_password(ip, ua, token, user_id, payload) {
return await this.client.post(ip, ua, token, `/users/${user_id}/change-password`, payload);
}
}
export class UserStations {
client;
constructor(client) {
this.client = client;
}
}
export class StationFiles {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, station_id, query) {
return await this.client.get(ip, ua, token, `/stations/${station_id}/files${qss(query)}`);
}
async get(ip, ua, token, station_id, file_id) {
return await this.client.get(ip, ua, token, `/stations/${station_id}/files/${file_id}`);
}
async delete(ip, ua, token, station_id, file_id) {
return await this.client.delete(ip, ua, token, `/stations/${station_id}/files/${file_id}`);
}
async post(ip, ua, token, station_id, content_type, content_length, query, data) {
const headers = new Headers();
if (ip)
headers.append(FORWARD_IP_HEADER, ip);
if (ua)
headers.append("user-agent", ua);
headers.append(ACCESS_TOKEN_HEADER, token);
headers.append("content-type", content_type);
headers.append("content-length", String(content_length));
let res = await this.client.fetch(`/stations/${station_id}/files${qss(query)}`, {
method: "POST",
headers,
body: data
});
return await this.client.get_json_body(res);
}
async put_metadata(ip, ua, token, station_id, file_id, payload) {
return await this.client.put(ip, ua, token, `/stations/${station_id}/files/${file_id}/metadata`, payload);
}
async shuffle(ip, ua, token, station_id) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/shuffle`, undefined);
}
async unshuffle(ip, ua, token, station_id) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/unshuffle`, undefined);
}
async swap_order(ip, ua, token, station_id, file_id, payload) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/${file_id}/order/swap`, payload);
}
async move_to_first(ip, ua, token, station_id, file_id) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/${file_id}/order/move-to-first`, undefined);
}
async move_to_last(ip, ua, token, station_id, file_id) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/${file_id}/order/move-to-last`, undefined);
}
async move_before(ip, ua, token, station_id, file_id, payload) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/${file_id}/order/move-before`, payload);
}
async move_after(ip, ua, token, station_id, file_id, payload) {
return await this.client.post(ip, ua, token, `/stations/${station_id}/files/${file_id}/order/move-after`, payload);
}
}
export class Analytics {
client;
constructor(client) {
this.client = client;
}
async get(ip, ua, token, query) {
const url = `/analytics${qss(query)}`;
return await this.client.get(ip, ua, token, url);
}
}
export class AppAnalytics {
client;
constructor(client) {
this.client = client;
}
async get(ip, ua, token, query) {
const url = `/app-analytics${qss(query)}`;
return await this.client.get(ip, ua, token, url);
}
}
export class PaymentMethods {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/payment-methods${qss(query)}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/payment-methods`, payload);
}
async get(ip, ua, token, payment_method_id) {
return await this.client.get(ip, ua, token, `/payment-methods/${payment_method_id}`);
}
}
export class StreamConnections {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/stream-connections${qss(query)}`);
}
}
export class StreamConnectionsLite {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/stream-connections-lite${qss(query)}`);
}
}
export class AccountInvitations {
client;
constructor(client) {
this.client = client;
}
async list(ip, ua, token, query) {
return await this.client.get(ip, ua, token, `/invitations${qss(query)}`);
}
async post(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/invitations`, payload);
}
async get(ip, ua, token, invitation_id) {
return await this.client.get(ip, ua, token, `/invitations/${invitation_id}`);
}
async delete(ip, ua, token, invitation_id) {
return await this.client.delete(ip, ua, token, `/invitations/${invitation_id}`);
}
async get_by_token(ip, ua, token, invitation_token) {
return await this.client.get(ip, ua, token, `/invitations/get-by-token/${invitation_token}`);
}
async accept(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/invitations/accept`, payload);
}
async reject(ip, ua, token, payload) {
return await this.client.post(ip, ua, token, `/invitations/reject`, payload);
}
}