| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- import {
- accountBillingPortalUrl,
- accountBillingSubscriptionUrl,
- accountPasswordUrl,
- accountReservationSingleUrl,
- accountReservationUrl,
- accountSettingsUrl,
- accountSubscriptionSingleUrl,
- accountSubscriptionUrl,
- accountTokenUrl,
- accountUrl,
- tiersUrl,
- withBasicAuth,
- withBearerAuth
- } from "./utils";
- import session from "./Session";
- import subscriptionManager from "./SubscriptionManager";
- import i18n from "i18next";
- import prefs from "./Prefs";
- import routes from "../components/routes";
- import {fetchOrThrow, throwAppError, UnauthorizedError} from "./errors";
- const delayMillis = 45000; // 45 seconds
- const intervalMillis = 900000; // 15 minutes
- class AccountApi {
- constructor() {
- this.timer = null;
- this.listener = null; // Fired when account is fetched from remote
- }
- registerListener(listener) {
- this.listener = listener;
- }
- resetListener() {
- this.listener = null;
- }
- async login(user) {
- const url = accountTokenUrl(config.base_url);
- console.log(`[AccountApi] Checking auth for ${url}`);
- const response = await fetchOrThrow(url, {
- method: "POST",
- headers: withBasicAuth({}, user.username, user.password)
- });
- const json = await response.json(); // May throw SyntaxError
- if (!json.token) {
- throw new Error(`Unexpected server response: Cannot find token`);
- }
- return json.token;
- }
- async logout() {
- const url = accountTokenUrl(config.base_url);
- console.log(`[AccountApi] Logging out from ${url} using token ${session.token()}`);
- await fetchOrThrow(url, {
- method: "DELETE",
- headers: withBearerAuth({}, session.token())
- });
- }
- async create(username, password) {
- const url = accountUrl(config.base_url);
- const body = JSON.stringify({
- username: username,
- password: password
- });
- console.log(`[AccountApi] Creating user account ${url}`);
- await fetchOrThrow(url, {
- method: "POST",
- body: body
- });
- }
- async get() {
- const url = accountUrl(config.base_url);
- console.log(`[AccountApi] Fetching user account ${url}`);
- const response = await fetchOrThrow(url, {
- headers: withBearerAuth({}, session.token())
- });
- const account = await response.json(); // May throw SyntaxError
- console.log(`[AccountApi] Account`, account);
- if (this.listener) {
- this.listener(account);
- }
- return account;
- }
- async delete(password) {
- const url = accountUrl(config.base_url);
- console.log(`[AccountApi] Deleting user account ${url}`);
- await fetchOrThrow(url, {
- method: "DELETE",
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify({
- password: password
- })
- });
- }
- async changePassword(currentPassword, newPassword) {
- const url = accountPasswordUrl(config.base_url);
- console.log(`[AccountApi] Changing account password ${url}`);
- await fetchOrThrow(url, {
- method: "POST",
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify({
- password: currentPassword,
- new_password: newPassword
- })
- });
- }
- async createToken(label, expires) {
- const url = accountTokenUrl(config.base_url);
- const body = {
- label: label,
- expires: (expires > 0) ? Math.floor(Date.now() / 1000) + expires : 0
- };
- console.log(`[AccountApi] Creating user access token ${url}`);
- await fetchOrThrow(url, {
- method: "POST",
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify(body)
- });
- }
- async updateToken(token, label, expires) {
- const url = accountTokenUrl(config.base_url);
- const body = {
- token: token,
- label: label
- };
- if (expires > 0) {
- body.expires = Math.floor(Date.now() / 1000) + expires;
- }
- console.log(`[AccountApi] Creating user access token ${url}`);
- await fetchOrThrow(url, {
- method: "PATCH",
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify(body)
- });
- }
- async extendToken() {
- const url = accountTokenUrl(config.base_url);
- console.log(`[AccountApi] Extending user access token ${url}`);
- await fetchOrThrow(url, {
- method: "PATCH",
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify({
- token: session.token(),
- expires: Math.floor(Date.now() / 1000) + 6220800 // FIXME
- })
- });
- }
- async deleteToken(token) {
- const url = accountTokenUrl(config.base_url);
- console.log(`[AccountApi] Deleting user access token ${url}`);
- await fetchOrThrow(url, {
- method: "DELETE",
- headers: withBearerAuth({"X-Token": token}, session.token())
- });
- }
- async updateSettings(payload) {
- const url = accountSettingsUrl(config.base_url);
- const body = JSON.stringify(payload);
- console.log(`[AccountApi] Updating user account ${url}: ${body}`);
- await fetchOrThrow(url, {
- method: "PATCH",
- headers: withBearerAuth({}, session.token()),
- body: body
- });
- }
- async addSubscription(payload) {
- const url = accountSubscriptionUrl(config.base_url);
- const body = JSON.stringify(payload);
- console.log(`[AccountApi] Adding user subscription ${url}: ${body}`);
- const response = await fetchOrThrow(url, {
- method: "POST",
- headers: withBearerAuth({}, session.token()),
- body: body
- });
- const subscription = await response.json(); // May throw SyntaxError
- console.log(`[AccountApi] Subscription`, subscription);
- return subscription;
- }
- async updateSubscription(remoteId, payload) {
- const url = accountSubscriptionSingleUrl(config.base_url, remoteId);
- const body = JSON.stringify(payload);
- console.log(`[AccountApi] Updating user subscription ${url}: ${body}`);
- const response = await fetchOrThrow(url, {
- method: "PATCH",
- headers: withBearerAuth({}, session.token()),
- body: body
- });
- const subscription = await response.json(); // May throw SyntaxError
- console.log(`[AccountApi] Subscription`, subscription);
- return subscription;
- }
- async deleteSubscription(remoteId) {
- const url = accountSubscriptionSingleUrl(config.base_url, remoteId);
- console.log(`[AccountApi] Removing user subscription ${url}`);
- await fetchOrThrow(url, {
- method: "DELETE",
- headers: withBearerAuth({}, session.token())
- });
- }
- async upsertReservation(topic, everyone) {
- const url = accountReservationUrl(config.base_url);
- console.log(`[AccountApi] Upserting user access to topic ${topic}, everyone=${everyone}`);
- await fetchOrThrow(url, {
- method: "POST",
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify({
- topic: topic,
- everyone: everyone
- })
- });
- }
- async deleteReservation(topic, deleteMessages) {
- const url = accountReservationSingleUrl(config.base_url, topic);
- console.log(`[AccountApi] Removing topic reservation ${url}`);
- const headers = {
- "X-Delete-Messages": deleteMessages ? "true" : "false"
- }
- await fetchOrThrow(url, {
- method: "DELETE",
- headers: withBearerAuth(headers, session.token())
- });
- }
- async billingTiers() {
- const url = tiersUrl(config.base_url);
- console.log(`[AccountApi] Fetching billing tiers`);
- const response = await fetchOrThrow(url); // No auth needed!
- return await response.json(); // May throw SyntaxError
- }
- async createBillingSubscription(tier) {
- console.log(`[AccountApi] Creating billing subscription with ${tier}`);
- return await this.upsertBillingSubscription("POST", tier)
- }
- async updateBillingSubscription(tier) {
- console.log(`[AccountApi] Updating billing subscription with ${tier}`);
- return await this.upsertBillingSubscription("PUT", tier)
- }
- async upsertBillingSubscription(method, tier) {
- const url = accountBillingSubscriptionUrl(config.base_url);
- const response = await fetchOrThrow(url, {
- method: method,
- headers: withBearerAuth({}, session.token()),
- body: JSON.stringify({
- tier: tier
- })
- });
- return await response.json(); // May throw SyntaxError
- }
- async deleteBillingSubscription() {
- const url = accountBillingSubscriptionUrl(config.base_url);
- console.log(`[AccountApi] Cancelling billing subscription`);
- await fetchOrThrow(url, {
- method: "DELETE",
- headers: withBearerAuth({}, session.token())
- });
- }
- async createBillingPortalSession() {
- const url = accountBillingPortalUrl(config.base_url);
- console.log(`[AccountApi] Creating billing portal session`);
- const response = await fetchOrThrow(url, {
- method: "POST",
- headers: withBearerAuth({}, session.token())
- });
- return await response.json(); // May throw SyntaxError
- }
- async sync() {
- try {
- if (!session.token()) {
- return null;
- }
- console.log(`[AccountApi] Syncing account`);
- const account = await this.get();
- if (account.language) {
- await i18n.changeLanguage(account.language);
- }
- if (account.notification) {
- if (account.notification.sound) {
- await prefs.setSound(account.notification.sound);
- }
- if (account.notification.delete_after) {
- await prefs.setDeleteAfter(account.notification.delete_after);
- }
- if (account.notification.min_priority) {
- await prefs.setMinPriority(account.notification.min_priority);
- }
- }
- if (account.subscriptions) {
- await subscriptionManager.syncFromRemote(account.subscriptions, account.reservations);
- }
- return account;
- } catch (e) {
- console.log(`[AccountApi] Error fetching account`, e);
- if (e instanceof UnauthorizedError) {
- session.resetAndRedirect(routes.login);
- }
- }
- }
- startWorker() {
- if (this.timer !== null) {
- return;
- }
- console.log(`[AccountApi] Starting worker`);
- this.timer = setInterval(() => this.runWorker(), intervalMillis);
- setTimeout(() => this.runWorker(), delayMillis);
- }
- async runWorker() {
- if (!session.token()) {
- return;
- }
- console.log(`[AccountApi] Extending user access token`);
- try {
- await this.extendToken();
- } catch (e) {
- console.log(`[AccountApi] Error extending user access token`, e);
- }
- }
- }
- // Maps to user.Role in user/types.go
- export const Role = {
- ADMIN: "admin",
- USER: "user"
- };
- // Maps to server.visitorLimitBasis in server/visitor.go
- export const LimitBasis = {
- IP: "ip",
- TIER: "tier"
- };
- // Maps to stripe.SubscriptionStatus
- export const SubscriptionStatus = {
- ACTIVE: "active",
- PAST_DUE: "past_due"
- };
- // Maps to user.Permission in user/types.go
- export const Permission = {
- READ_WRITE: "read-write",
- READ_ONLY: "read-only",
- WRITE_ONLY: "write-only",
- DENY_ALL: "deny-all"
- };
- const accountApi = new AccountApi();
- export default accountApi;
|