mirror of
https://github.com/stoatchat/javascript-client-sdk.git
synced 2026-07-21 04:25:27 -04:00
feat: implement push, account, mfa helpers
This commit is contained in:
+4
-10
@@ -1,7 +1,7 @@
|
||||
import { Accessor, Setter, createSignal } from "solid-js";
|
||||
|
||||
import EventEmitter from "eventemitter3";
|
||||
import { API, DataCreateAccount, Role } from "revolt-api";
|
||||
import { API, Role } from "revolt-api";
|
||||
import type { DataLogin, RevoltConfig } from "revolt-api";
|
||||
|
||||
import { Channel, Emoji, Message, Server, ServerMember, User } from "./classes";
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
SessionCollection,
|
||||
UserCollection,
|
||||
} from "./collections";
|
||||
import { AccountCollection } from "./collections/AccountCollection";
|
||||
import {
|
||||
ConnectionState,
|
||||
EventClient,
|
||||
@@ -143,6 +144,7 @@ export type ClientOptions = Partial<EventClientOptions> & {
|
||||
* Revolt.js Clients
|
||||
*/
|
||||
export class Client extends EventEmitter<Events> {
|
||||
readonly account;
|
||||
readonly bots;
|
||||
readonly channels;
|
||||
readonly channelUnreads;
|
||||
@@ -213,6 +215,7 @@ export class Client extends EventEmitter<Events> {
|
||||
this.connectionFailureCount = connectionFailureCount;
|
||||
this.#setConnectionFailureCount = setConnectionFailureCount;
|
||||
|
||||
this.account = new AccountCollection(this);
|
||||
this.bots = new BotCollection(this);
|
||||
this.channels = new ChannelCollection(this);
|
||||
this.channelUnreads = new ChannelUnreadCollection(this);
|
||||
@@ -334,15 +337,6 @@ export class Client extends EventEmitter<Events> {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register for a new account
|
||||
* @param data Registration data object
|
||||
* @returns A promise containing a registration response object
|
||||
*/
|
||||
register(data: DataCreateAccount) {
|
||||
return this.api.post("/auth/account/create", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a markdown-based message to be displayed to the user as plain text.
|
||||
* @param source Source markdown text
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import { SetStoreFunction, createStore } from "solid-js/store";
|
||||
|
||||
import {
|
||||
MFAResponse,
|
||||
MultiFactorStatus,
|
||||
MFATicket as TicketType,
|
||||
} from "revolt-api";
|
||||
|
||||
import { Client } from "..";
|
||||
|
||||
/**
|
||||
* Multi-Factor Authentication
|
||||
*/
|
||||
export class MFA {
|
||||
#client: Client;
|
||||
#store: [MultiFactorStatus, SetStoreFunction<MultiFactorStatus>];
|
||||
|
||||
/**
|
||||
* Construct MFA helper
|
||||
* @param client Client
|
||||
* @param state State
|
||||
*/
|
||||
constructor(client: Client, state: MultiFactorStatus) {
|
||||
this.#client = client;
|
||||
this.#store = createStore(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether authenticator app is enabled
|
||||
*/
|
||||
get authenticatorEnabled() {
|
||||
return this.#store[0].totp_mfa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether recovery codes are enabled
|
||||
*/
|
||||
get recoveryEnabled() {
|
||||
return this.#store[0].recovery_active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an MFA ticket
|
||||
* @param params
|
||||
* @returns Token
|
||||
*/
|
||||
createTicket(params: MFAResponse) {
|
||||
return this.#client.api
|
||||
.put("/auth/mfa/ticket", params)
|
||||
.then((ticket) => new MFATicket(this.#client, ticket));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable authenticator using token generated from secret found earlier
|
||||
* @param token Token
|
||||
*/
|
||||
enableAuthenticator(token: string) {
|
||||
return this.#client.api.put("/auth/mfa/totp", { totp_code: token });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MFA Ticket
|
||||
*/
|
||||
export class MFATicket {
|
||||
#client: Client;
|
||||
#ticket: TicketType;
|
||||
#used = false;
|
||||
|
||||
/**
|
||||
* Construct MFA Ticket
|
||||
* @param client Client
|
||||
* @param ticket Ticket
|
||||
*/
|
||||
constructor(client: Client, ticket: TicketType) {
|
||||
this.#client = client;
|
||||
this.#ticket = ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Token
|
||||
*/
|
||||
get token() {
|
||||
return this.#ticket.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the ticket
|
||||
*/
|
||||
#consume() {
|
||||
if (this.#used) throw "Already used this ticket!";
|
||||
this.#used = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch recovery codes
|
||||
* @returns List of codes
|
||||
*/
|
||||
fetchRecoveryCodes() {
|
||||
this.#consume();
|
||||
return this.#client.api.post("/auth/mfa/recovery", undefined, {
|
||||
headers: {
|
||||
"X-MFA-Ticket": this.token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new set of recovery codes
|
||||
* @returns List of codes
|
||||
*/
|
||||
generateRecoveryCodes() {
|
||||
this.#consume();
|
||||
return this.#client.api.patch("/auth/mfa/recovery", undefined, {
|
||||
headers: {
|
||||
"X-MFA-Ticket": this.token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new authenticator secret
|
||||
* @returns Secret
|
||||
*/
|
||||
generateAuthenticatorSecret() {
|
||||
this.#consume();
|
||||
return this.#client.api
|
||||
.post("/auth/mfa/totp", undefined, {
|
||||
headers: {
|
||||
"X-MFA-Ticket": this.token,
|
||||
},
|
||||
})
|
||||
.then((response) => response.secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable authenticator
|
||||
*/
|
||||
disableAuthenticator() {
|
||||
this.#consume();
|
||||
return this.#client.api.delete("/auth/mfa/totp", undefined, {
|
||||
headers: {
|
||||
"X-MFA-Ticket": this.token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable account
|
||||
*/
|
||||
disableAccount() {
|
||||
this.#consume();
|
||||
return this.#client.api.post("/auth/account/disable", undefined, {
|
||||
headers: {
|
||||
"X-MFA-Ticket": this.token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete account
|
||||
*/
|
||||
deleteAccount() {
|
||||
this.#consume();
|
||||
return this.#client.api.post("/auth/account/delete", undefined, {
|
||||
headers: {
|
||||
"X-MFA-Ticket": this.token,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { DataCreateAccount, WebPushSubscription } from "revolt-api";
|
||||
|
||||
import { Client } from "..";
|
||||
import { MFA } from "../classes/MFA";
|
||||
|
||||
/**
|
||||
* Utility functions for working with accounts
|
||||
*/
|
||||
export class AccountCollection {
|
||||
readonly client: Client;
|
||||
|
||||
/**
|
||||
* Create generic class collection
|
||||
* @param client Client
|
||||
*/
|
||||
constructor(client: Client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current account email
|
||||
* @returns Email
|
||||
*/
|
||||
fetchEmail() {
|
||||
return this.client.api
|
||||
.get("/auth/account/")
|
||||
.then((account) => account.email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MFA helper
|
||||
*/
|
||||
async mfa() {
|
||||
const state = await this.client.api.get("/auth/mfa/");
|
||||
return new MFA(this.client, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new account
|
||||
* @param data Account details
|
||||
*/
|
||||
create(data: DataCreateAccount) {
|
||||
return this.client.api.post("/auth/account/create", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend email verification
|
||||
* @param email Email
|
||||
* @param captcha Captcha if enabled
|
||||
*/
|
||||
reverify(email: string, captcha?: string) {
|
||||
return this.client.api.post("/auth/account/reverify", { email, captcha });
|
||||
}
|
||||
|
||||
/**
|
||||
* Send password reset email
|
||||
* @param email Email
|
||||
* @param captcha Captcha if enabled
|
||||
*/
|
||||
resetPassword(email: string, captcha?: string) {
|
||||
return this.client.api.post("/auth/account/reset_password", {
|
||||
email,
|
||||
captcha,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an account given the code
|
||||
* @param code Verification code
|
||||
*/
|
||||
verify(code: string) {
|
||||
return this.client.api.post(`/auth/account/verify/${code}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm account deletion
|
||||
* @param token Deletion token
|
||||
*/
|
||||
confirmDelete(token: string) {
|
||||
return this.client.api.put("/auth/account/delete", { token });
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm password reset
|
||||
* @param token Token
|
||||
* @param newPassword New password
|
||||
* @param removeSessions Whether to remove existing sessions
|
||||
*/
|
||||
confirmPasswordReset(
|
||||
token: string,
|
||||
newPassword: string,
|
||||
removeSessions: boolean
|
||||
) {
|
||||
return this.client.api.patch("/auth/account/reset_password", {
|
||||
token,
|
||||
password: newPassword,
|
||||
remove_sessions: removeSessions,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change account password
|
||||
* @param newPassword New password
|
||||
* @param currentPassword Current password
|
||||
*/
|
||||
changePassword(newPassword: string, currentPassword: string) {
|
||||
return this.client.api.patch("/auth/account/change/password", {
|
||||
password: newPassword,
|
||||
current_password: currentPassword,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change account email
|
||||
* @param newEmail New email
|
||||
* @param currentPassword Current password
|
||||
*/
|
||||
changeEmail(newEmail: string, currentPassword: string) {
|
||||
return this.client.api.patch("/auth/account/change/email", {
|
||||
email: newEmail,
|
||||
current_password: currentPassword,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch settings
|
||||
* @param keys Keys
|
||||
* @returns Settings
|
||||
*/
|
||||
fetchSettings(keys: string[]) {
|
||||
return this.client.api.post("/sync/settings/fetch", { keys }) as Promise<
|
||||
Record<string, [number, string]>
|
||||
>;
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/**
|
||||
* Set settings
|
||||
* @param settings Settings
|
||||
* @param timestamp Timestamp
|
||||
*/
|
||||
setSettings(settings: Record<string, any>, timestamp = +new Date()) {
|
||||
return this.client.api.post("/sync/settings/set", {
|
||||
...settings,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/**
|
||||
* Create a new Web Push subscription
|
||||
* @param subscription Subscription
|
||||
*/
|
||||
webPushSubscribe(subscription: WebPushSubscription) {
|
||||
return this.client.api.post("/push/subscribe", subscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove existing Web Push subscription
|
||||
*/
|
||||
webPushUnsubscribe() {
|
||||
return this.client.api.post("/push/unsubscribe");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,16 @@ export class SessionCollection extends ClassCollection<
|
||||
return data.map((session) => this.getOrCreate(session._id, session));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all sessions, optionally including self
|
||||
* @param revokeSelf Whether to remove current session too
|
||||
*/
|
||||
async deleteAll(revokeSelf = false) {
|
||||
await this.client.api.delete("/auth/session/all", {
|
||||
revoke_self: revokeSelf,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
|
||||
Reference in New Issue
Block a user