ngx-appwrite
Version:
A wrapper around the Appwrite WebSDK for easier implementation in Angular 16+ projects. The goal is to make the whole SDK accessible as well as provide some convenience functionality like RxJS streams where appropriate.
1,326 lines (1,320 loc) • 78.6 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, APP_INITIALIZER, Injectable, inject } from '@angular/core';
import { Client, Account as Account$1, ID, AuthenticatorType, Databases as Databases$1, Query, Avatars, Functions, Locale, Messaging, Storage, Teams } from 'appwrite';
export { ID } from 'appwrite';
import { Observable, of, shareReplay, switchMap, startWith, Subject, merge, debounceTime, map, distinctUntilChanged } from 'rxjs';
const watch = (client, channel, events) => {
const observable = new Observable((observer) => {
try {
client.subscribe(channel, (response) => {
if (!events) {
observer.next(response);
}
else if ((typeof events === 'string' && response.events.includes(events)) ||
intersection(response.events, events)) {
observer.next(response);
}
});
}
catch (error) {
console.error('Error while watching channel: ', channel);
if (error instanceof Error)
observer.error(error.message);
}
});
return observable;
};
const wait = (seconds) => {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000);
});
};
const deepEqual = (obj1, obj2) => {
return JSON.stringify(obj1) === JSON.stringify(obj2);
};
function intersection(...args) {
const arrays = args.map((arg) => Array.isArray(arg) ? arg : [arg]);
if (arrays.length === 0) {
return [];
}
const firstArray = arrays[0];
const uniqueValues = new Set(firstArray);
for (let i = 1; i < arrays.length; i++) {
const currentArray = arrays[i];
for (let j = 0; j < currentArray.length; j++) {
const value = currentArray[j];
if (!uniqueValues.has(value)) {
uniqueValues.delete(value);
}
}
}
return Array.from(uniqueValues);
}
let __client;
let __defaultDatabaseId;
const CLIENT = () => {
if (!__client) {
throw new Error('Appwrite client not initialized, did you call initializeAppwrite?');
}
return __client;
};
const DEFAULT_DATABASE_ID = () => {
if (!__defaultDatabaseId) {
console.warn('Appwrite default database id not initialized, can be passed inside provideAppwrite');
}
return __defaultDatabaseId;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ConfigToken = new InjectionToken('APPWRITE_USER_CONFIG');
const initializeAppwrite = (config) => {
return () => {
__client = new Client();
__client.setEndpoint(config.endpoint).setProject(config.project); //
if (config.defaultDatabase) {
console.log('Configured defaultDatabaseId: ', config.defaultDatabase);
__defaultDatabaseId = config.defaultDatabase;
}
};
};
const provideAppwrite = (config) => {
return [
{
provide: ConfigToken,
useValue: config,
},
{
provide: APP_INITIALIZER,
useFactory: initializeAppwrite,
multi: true,
deps: [ConfigToken],
},
];
};
class Account {
/* -------------------------------------------------------------------------- */
/* Setup */
/* -------------------------------------------------------------------------- */
_account;
_client$ = of(CLIENT()).pipe(shareReplay(1));
_watchAuthChannel$ = this._client$.pipe(switchMap((client) => watch(client, 'account').pipe(startWith(null))));
_triggerManualAuthCheck$ = new Subject();
_auth$;
/* -------------------------------------------------------------------------- */
/* Reactive */
/* -------------------------------------------------------------------------- */
constructor() {
this._account = new Account$1(CLIENT());
}
onAuth() {
try {
if (!this._auth$) {
this._auth$ = merge(this._watchAuthChannel$, this._triggerManualAuthCheck$).pipe(switchMap(() => this._checkIfAuthExists()), debounceTime(50), map((account) => {
if (!account)
return null;
return account;
}), distinctUntilChanged(deepEqual), shareReplay(1));
}
return this._auth$;
}
catch (error) {
console.error('Error in Account > onAuth');
throw error;
}
}
/* -------------------------------------------------------------------------- */
/* Default API - https://appwrite.io/docs/client/account?sdk=web-default */
/* -------------------------------------------------------------------------- */
/**
* Get Account
*
* Get currently logged in user data as JSON object.
*
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async get() {
try {
const account = await this._account.get();
return account;
}
catch (error) {
console.error('Error fetching account');
throw error;
}
}
/**
* Create Account
*
* Use this endpoint to allow a new user to register a new account in your project.
* After the user registration completes successfully, you can use the /account/verfication route
* to start verifying the user email address. To allow the new user to login to their new account,
* you need to create a new account session.
*
* @param {string} email
* @param {string} password
* @param {Models.Preferences} defaultPrefs
* @param {string} name
* @param {string} userId
* Defaults to ID.unique()
* @throws {AppwriteException}
* @returns {Promise<Models.User<T>>}
*/
async create(email, password, defaultPrefs = {}, name, userId = ID.unique()) {
const account = await this._account.create(userId, email, password, name);
this.triggerAuthCheck();
await this.updatePrefs(defaultPrefs);
return account;
}
/**
* Update Email
*
* Update currently logged in user account email address.
* After changing user address, the user confirmation status will get reset.
* A new confirmation email is not sent automatically however you can use the
* send confirmation email endpoint again to send the confirmation email.
* For security measures, user password is required to complete this request.
* This endpoint can also be used to convert an anonymous account to a normal one,
* by passing an email address and a new password.
*
*
* @param {string} email
* @param {string} password
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updateEmail(email, password) {
return this._account.updateEmail(email, password);
}
/**
* List Identities
*
* Get the list of identities for the currently logged in user.
*
*
* @param {string[]} queries
* @throws {AppwriteException}
* @returns {Promise<Models.IdentityList>}
*/
async listIdentities(queries = []) {
return this._account.listIdentities(queries);
}
/**
* Delete Identity
*
* Delete a user identity by id.
*
*
* @param {string} id
* @throws {AppwriteException}
* @returns {Promise<{}>}
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
async deleteIdentity(id) {
return this._account.deleteIdentity(id);
}
/**
* Create JWT
*
* Use this endpoint to create a JSON Web Token. You can use the resulting JWT
* to authenticate on behalf of the current user when working with the
* Appwrite server-side API and SDKs. The JWT secret is valid for 15 minutes
* from its creation and will be invalid if the user will logout in that time
* frame.
*
* @throws {AppwriteException}
* @returns {Promise<Models.Jwt>}
*/
async createJWT() {
return await this._account.createJWT();
}
/**
* List Logs
*
* Get the list of latest security activity logs for the currently logged in user.
* Each log returns user IP address, location and date and time of log.
*
* @param {string[]} queries
* @throws {AppwriteException}
* @returns {Promise<AppwriteLogListObject>}
*/
async listLogs(queries = []) {
return this._account.listLogs(queries);
}
/**
* Update MFA
*
* Enable or disable MFA on an account.
*
* @param {boolean} enableMFA
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updateMFA(enableMFA) {
return this._account.updateMFA(enableMFA);
}
/**
* Add Authenticator
*
* Add an authenticator app to be used as an MFA factor.
* Verify the authenticator using the verify authenticator method.
*
* @throws {AppwriteException}
* @returns {Promise<Models.MfaType>}
*/
async createMfaAuthenticator() {
return this._account.createMfaAuthenticator(AuthenticatorType.Totp);
}
/**
* Verify Authenticator
*
* Verify an authenticator app after adding it using the add authenticator method.
*
* @param {string} otp
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updateMfaAuthenticator(otp) {
return this._account.updateMfaAuthenticator(AuthenticatorType.Totp, // type
otp);
}
/**
* Delete Authenticator
*
* Delete an authenticator for a user.
* Verify the authenticator using the verify authenticator method.
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
async deleteMfaAuthenticator() {
return this._account.deleteMfaAuthenticator(AuthenticatorType.Totp);
}
/**
* Create 2FA Challenge
*
* Begin the process of MFA verification after sign-in.
* Finish the flow with updateMfaChallenge method.
*
* @param {AuthenticationFactor} factor
* @throws {AppwriteException}
* @returns {Promise<Models.MfaChallenge>}
*/
async createMfaChallenge(factor) {
return this._account.createMfaChallenge(factor);
}
/**
* Create MFA Challenge (confirmation)
*
* Complete the MFA challenge by providing the one-time password.
* Finish the process of MFA verification by providing the one-time password.
* To begin the flow, use createMfaChallenge method.
*
* @param {string} challengeId
* @param {string} otp
* @throws {AppwriteException}
* @returns {Promise<{}>}
*/
async updateMfaChallenge(challengeId, otp) {
return this._account.updateMfaChallenge(challengeId, otp);
}
/**
* List Factors
*
* List the factors available on the account to be used as a MFA challange.
*
* @throws {AppwriteException}
* @returns {Promise<Models.MfaFactors>}
*/
async listMfaFactors() {
return this._account.listMfaFactors();
}
/**
* Get MFA Recovery Codes
*
* Get recovery codes that can be used as backup for MFA flow.
* Before getting codes, they must be generated using createMfaRecoveryCodes method.
* An OTP challenge is required to read recovery codes.
*
* @throws {AppwriteException}
* @returns {Promise<Models.MfaRecoveryCodes>}
*/
async getMfaRecoveryCodes() {
return this._account.getMfaRecoveryCodes();
}
/**
* Create MFA Recovery Codes
*
* Generate recovery codes as backup for MFA flow.
* It's recommended to generate and show then immediately after user
* successfully adds their authehticator. Recovery codes can be used as a MFA
* verification type in createMfaChallenge method.
*
* @throws {AppwriteException}
* @returns {Promise<Models.MfaRecoveryCodes>}
*/
async createMfaRecoveryCodes() {
return this._account.createMfaRecoveryCodes();
}
/**
* Regenerate MFA Recovery Codes
*
* Regenerate recovery codes that can be used as backup for MFA flow.
* Before regenerating codes, they must be first generated using
* createMfaRecoveryCodes method. An OTP challenge is required to regenreate
* recovery codes.
*
* @throws {AppwriteException}
* @returns {Promise<Models.MfaRecoveryCodes>}
*/
async updateMfaRecoveryCodes() {
return this._account.updateMfaRecoveryCodes();
}
/**
* Update Name
*
* Update currently logged in user account name.
*
* @param {string} name
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updateName(name) {
return this._account.updateName(name);
}
/**
* Update Password
*
* Update currently logged in user password. For validation, user is required
* to pass in the new password, and the old password. For users created with
* OAuth, Team Invites and Magic URL, oldPassword is optional.
*
* @param {string} password
* @param {string} oldPassword
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updatePassword(password, oldPassword) {
return this._account.updatePassword(password, oldPassword);
}
/**
* Update Phone
*
* Update the currently logged in user's phone number. After updating the
* phone number, the phone verification status will be reset. A confirmation
* SMS is not sent automatically, however you can use the [POST
* /account/verification/phone](/docs/client/account#accountCreatePhoneVerification)
* endpoint to send a confirmation SMS.
*
* @param {string} phoneNumber
* @param {string} password
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updatePhone(phoneNumber, password) {
return this._account.updatePhone(phoneNumber, password);
}
/**
* Get Account Preferences
*
* Get the preferences as a key-value object for the currently logged in user.
*
* @throws {AppwriteException}
* @returns {Promise<TPrefs>}
*/
async getPrefs() {
return this._account.getPrefs();
}
/**
* Update Preferences
*
* Update currently logged in user account preferences. The object you pass is
* stored as is, and replaces any previous value. The maximum allowed prefs
* size is 64kB and throws error if exceeded.
*
* @param {object} prefs
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updatePrefs(prefs) {
return this._account.updatePrefs(prefs);
}
/**
* Create Password Recovery
*
* Sends the user an email with a temporary secret key for password reset.
* When the user clicks the confirmation link he is redirected back to your
* app password reset URL with the secret key and email address values
* attached to the URL query string. Use the query string params to submit a
* request to the [PUT
* /account/recovery](/docs/client/account#accountUpdateRecovery) endpoint to
* complete the process. The verification link sent to the user's email
* address is valid for 1 hour.
*
* @param {string} email
* @param {string} url
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async createRecovery(email, url) {
const result = await this._account.createRecovery(email, url);
this.triggerAuthCheck();
return result;
}
/**
* Create Password Recovery (confirmation)
*
* Use this endpoint to complete the user account password reset. Both the
* **userId** and **secret** arguments will be passed as query parameters to
* the redirect URL you have provided when sending your request to the [POST
* /account/recovery](/docs/client/account#accountCreateRecovery) endpoint.
*
* Please note that in order to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
* the only valid redirect URLs are the ones from domains you have set when
* adding your platforms in the console interface.
*
* @param {string} userId
* @param {string} secret
* @param {string} password
* @throws {AppwriteException}
* @returns {Promise<Models.Toke>}
*/
async updateRecovery(userId, secret, password) {
const result = await this._account.updateRecovery(userId, secret, password);
this.triggerAuthCheck();
return result;
}
/**
* List Sessions
*
* Get currently logged in user list of active sessions across different
* devices.
*
* @throws {AppwriteException}
* @returns {Promise<Models.SessionList>}
*/
async listSessions() {
return this._account.listSessions();
}
/**
* Delete Sessions
*
* Delete all sessions from the user account and remove any sessions cookies
* from the end client.
*
* @throws {AppwriteException}
* @returns {Promise<{}>}
*/
async deleteSessions() {
const result = await this._account.deleteSessions();
this.triggerAuthCheck();
return result;
}
/**
* Create Anonymous Session
*
* Use this endpoint to allow a new user to register an anonymous account in
* your project. This route will also create a new session for the user. To
* allow the new user to convert an anonymous account to a normal account, you
* need to update its [email and
* password](/docs/client/account#accountUpdateEmail) or create an [OAuth2
* session](/docs/client/account#accountCreateOAuth2Session).
*
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async createAnonymousSession() {
const session = await this._account.createAnonymousSession();
this.triggerAuthCheck();
return session;
}
/**
* Create email password session
*
* Allow the user to login into their account by providing a valid email and
* password combination. This route will create a new session for the user.
*
* A user is limited to 10 active sessions at a time by default. [Learn more
* about session limits](/docs/authentication#limits).
*
* @param {string} email
* @param {string} password
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async createEmailPasswordSession(email, password) {
const session = await this._account.createEmailPasswordSession(email, password);
this.triggerAuthCheck();
return session;
}
/**
* Create Magic URL session (confirmation)
*
* Use this endpoint to create a session from token.
* Provide the userId and secret parameters from the successful response
* of authentication flows initiated by token creation. For example,
* magic URL and phone login.
*
* @param {string} userId
* @param {string} secret
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async updateMagicURLSession(userId, secret) {
const session = await this._account.updateMagicURLSession(userId, secret);
this.triggerAuthCheck();
return session;
}
/**
* Create OAuth2 Session
*
* Allow the user to login to their account using the OAuth2 provider of their
* choice. Each OAuth2 provider should be enabled from the Appwrite console
* first. Use the success and failure arguments to provide a redirect URL's
* back to your app when login is completed.
*
* If there is already an active session, the new session will be attached to
* the logged-in account. If there are no active sessions, the server will
* attempt to look for a user with the same email address as the email
* received from the OAuth2 provider and attach the new session to the
* existing user. If no matching user is found - the server will create a new
* user.
*
* A user is limited to 10 active sessions at a time by default. [Learn more
* about session limits](/docs/authentication#limits).
*
*
* @param {OAuthProvider} provider
* @param {string} success
* @param {string} failure
* @param {string[]} scopes
* @throws {AppwriteException}
* @returns {void|string}
*/
async createOAuth2Session(provider, success, failure, scopes) {
const url = this._account.createOAuth2Session(provider, success, failure, scopes);
return url;
}
/**
* Update phone session
*
* Use this endpoint to create a session from token.
* Provide the userId and secret parameters from the successful
* response of authentication flows initiated by token creation.
* For example, magic URL and phone login.
*
* @param {string} userId
* @param {string} secret
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async updatePhoneSession(userId, secret) {
const session = await this._account.updatePhoneSession(userId, secret);
this.triggerAuthCheck();
return session;
}
/**
* Create session
*
* Use this endpoint to create a session from token.
* Provide the userId and secret parameters from the successful
* response of authentication flows initiated by token creation.
* For example, magic URL and phone login.
*
* @param {string} userId
* @param {string} secret
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async createSession(userId, secret) {
const session = await this._account.createSession(userId, secret);
this.triggerAuthCheck();
return session;
}
/**
* Get Session
*
* Use this endpoint to get a logged in user's session using a Session ID.
* Inputting 'current' will return the current session being used.
*
* @param {string} sessionId
* default is 'current' session
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async getSession(sessionId = 'current') {
try {
const session = await this._account.getSession(sessionId);
return session;
}
catch (error) {
throw new Error(`Could not retrieve Appwrite session for id: ${sessionId} `);
}
}
/**
* Update session
*
* Use this endpoint to extend a session's length.
* Extending a session is useful when session expiry is short.
* If the session was created using an OAuth provider,
* this endpoint refreshes the access token from the provider.
*
* @param {string} sessionId
* defaults to 'current'
* @throws {AppwriteException}
* @returns {Promise<Models.Session>}
*/
async updateSession(sessionId = 'current') {
const result = await this._account.updateSession(sessionId);
this.triggerAuthCheck();
return result;
}
/**
* Delete Session
*
* Logout the user. Use 'current' as the session ID to logout on this device,
* use a session ID to logout on another device.
* If you're looking to logout the user on all devices, use Delete Sessions instead.
*
*
* @param {string} sessionId
* defaults to 'current'
* @throws {AppwriteException}
* @returns {Promise<{}>}
*/
async deleteSession(sessionId = 'current') {
const result = await this._account.deleteSession(sessionId);
this.triggerAuthCheck();
return result;
}
/**
* Update status
*
* Block the currently logged in user account. Behind the scene, the user
* record is not deleted but permanently blocked from any access. To
* completely delete a user, use the Users API instead.
*
* @throws {AppwriteException}
* @returns {Promise<Models.User<TPrefs>>}
*/
async updateStatus() {
const account = await this._account.updateStatus();
this.triggerAuthCheck();
return account;
}
/**
* Create push target
*
* No description at this moment
*
* @param {string} targetId
* @param {string} identifier
* @param {string} providerId
* @throws {AppwriteException}
* @returns {Promise<Models.Target>}
*/
async createPushTarget(targetId, identifier, providerId) {
const account = await this._account.createPushTarget(targetId, identifier, providerId);
this.triggerAuthCheck();
return account;
}
/**
* Update push target
*
* No description at this moment
*
* @param {string} targetId
* @param {string} identifier
* @throws {AppwriteException}
* @returns {Promise<Models.Target>}
*/
async updatePushTarget(targetId, identifier) {
const account = await this._account.updatePushTarget(targetId, identifier);
this.triggerAuthCheck();
return account;
}
/**
* Delete push target
*
* No description at this moment
*
* @param {string} targetId
* @throws {AppwriteException}
* @returns {Promise<Models.Target>}
*/
async deletePushTarget(targetId) {
const account = await this._account.deletePushTarget(targetId);
this.triggerAuthCheck();
return account;
}
/**
* Create email token (OTP)
*
* Sends the user an email with a secret key for creating a session.
* If the provided user ID has not be registered, a new user will be created.
* Use the returned user ID and secret and submit a request to the
* POST /v1/account/sessions/token endpoint to complete the login process.
* The secret sent to the user's email is valid for 15 minutes.
*
* A user is limited to 10 active sessions at a time by default. Learn more about session limits.
*
* @param {string} userId
* @param {string} email
* @param {boolean} phrase
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async createEmailToken(userId, email, phrase = false) {
const account = await this._account.createEmailToken(userId, email, phrase);
this.triggerAuthCheck();
return account;
}
/**
* Create magic URL token
*
* Sends the user an email with a secret key for creating a session.
* If the provided user ID has not been registered, a new user will be created.
* When the user clicks the link in the email, the user is redirected back to
* the URL you provided with the secret key and userId values attached to
* the URL query string. Use the query string parameters to submit a request
* to the POST /v1/account/sessions/token endpoint to complete the login process.
*
* The link sent to the user's email address is valid for 1 hour.
* If you are on a mobile device you can leave the URL parameter empty,
* so that the login completion will be handled by your Appwrite instance
* by default.
*
* A user is limited to 10 active sessions at a time by default. Learn more about session limits.
*
* @param {string} userId
* @param {string} email
* @param {string} url
* Defaults to ID.unique()
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async createMagicURLToken(email, url, userId = ID.unique(), phrase = true) {
const session = await this._account.createMagicURLToken(userId, email, url, phrase);
this.triggerAuthCheck();
return session;
}
/**
* Create OAuth2 token
*
* Allow the user to login to their account using the OAuth2 provider of their choice.
* Each OAuth2 provider should be enabled from the Appwrite console first.
* Use the success and failure arguments to provide a redirect URL's back to
* your app when login is completed.
*
* If authentication succeeds, userId and secret of a token will be appended to the success URL as query parameters. These can be used to create a new session using the Create session endpoint.
*
* A user is limited to 10 active sessions at a time by default. Learn more about session limits.
*
*
* @param {OAuthProvider} provider
* @param {string} success
* @param {string} failure
* @param {string[]} scopes
* @throws {AppwriteException}
* @returns {void|string}
*/
async createOAuth2Token(provider, success, failure, scopes) {
const url = this._account.createOAuth2Token(provider, success, failure, scopes);
return url;
}
/**
* Create Phone token
*
* Sends the user an SMS with a secret key for creating a session.
* If the provided user ID has not be registered, a new user will be created.
* Use the returned user ID and secret and submit a request to the
* POST /v1/account/sessions/token endpoint to complete the login process.
* The secret sent to the user's phone is valid for 15 minutes.
*
* A user is limited to 10 active sessions at a time by default. Learn more about session limits.
*
* @param {string} userId
* @param {string} phone
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async createPhoneToken(userId, phone) {
const session = await this._account.createPhoneToken(userId, phone);
this.triggerAuthCheck();
return session;
}
/**
* Create Email Verification
*
* Use this endpoint to send a verification message to your user email address
* to confirm they are the valid owners of that address. Both the **userId**
* and **secret** arguments will be passed as query parameters to the URL you
* have provided to be attached to the verification email. The provided URL
* should redirect the user back to your app and allow you to complete the
* verification process by verifying both the **userId** and **secret**
* parameters. Learn more about how to [complete the verification
* process](/docs/client/account#accountUpdateEmailVerification). The
* verification link sent to the user's email address is valid for 7 days.
*
* Please note that in order to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md),
* the only valid redirect URLs are the ones from domains you have set when
* adding your platforms in the console interface.
*
*
* @param {string} url
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async createVerification(url) {
const result = await this._account.createVerification(url);
this.triggerAuthCheck();
return result;
}
/**
* Create Email Verification (confirmation)
*
* Use this endpoint to complete the user email verification process. Use both
* the **userId** and **secret** parameters that were attached to your app URL
* to verify the user email ownership. If confirmed this route will return a
* 200 status code.
*
* @param {string} userId
* @param {string} secret
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async updateVerification(userId, secret) {
const result = await this._account.updateVerification(userId, secret);
this.triggerAuthCheck();
return result;
}
/**
* Create Phone Verification
*
* Use this endpoint to send a verification SMS to the currently logged in
* user. This endpoint is meant for use after updating a user's phone number
* using the [accountUpdatePhone](/docs/client/account#accountUpdatePhone)
* endpoint. Learn more about how to [complete the verification
* process](/docs/client/account#accountUpdatePhoneVerification). The
* verification code sent to the user's phone number is valid for 15 minutes.
*
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async createPhoneVerification() {
const result = await this._account.createPhoneVerification();
this.triggerAuthCheck();
return result;
}
/**
* Create Phone Verification (confirmation)
*
* Use this endpoint to complete the user phone verification process. Use the
* **userId** and **secret** that were sent to your user's phone number to
* verify the user email ownership. If confirmed this route will return a 200
* status code.
*
* @param {string} userId
* @param {string} secret
* @throws {AppwriteException}
* @returns {Promise<Models.Token>}
*/
async updatePhoneVerification(userId, secret) {
const result = await this._account.updatePhoneVerification(userId, secret);
this.triggerAuthCheck();
return result;
}
/* -------------------------------------------------------------------------- */
/* Additional functionality */
/* -------------------------------------------------------------------------- */
/**
* Convert Anonymous account with password
*
* This endpoint is a shortcut in order to convert an anonymous account
* to a permanent one
*
* @param {string} email
* @param {string} password
* @param {ObjectSchema<TPrefs>} prefsSchema
* @throws {AppwriteException}
* @returns {Promise<Models.User<T>>}
*/
async convertAnonymousAccountWithEmailAndPassword(email, password) {
const account = await this._account.updateEmail(email, password);
this.triggerAuthCheck();
return account;
}
/**
* Logout - Shortcut for deletesession
*
* @throws {AppwriteException}
* @returns {Promise<{}> }
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
async logout() {
return this.deleteSession('current');
}
/**
* Triggering an auth-check
*
* Trigger a check of all account and
* session-related actions to enable
* reactive monitoring of authentication status
* @returns {void}
*/
triggerAuthCheck() {
this._triggerManualAuthCheck$.next(true);
}
async _checkIfAuthExists() {
try {
const account = await this._account.get();
return account;
}
catch (error) {
console.warn(error);
return null;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: Account, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: Account, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: Account, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
const DATABASE_ERROR = `No Database ID provided or database not initialized,
use >>alternateDatabaseId << param`;
class Databases {
accountService = inject(Account);
_databases = new Databases$1(CLIENT());
_client$ = of(CLIENT()).pipe(shareReplay(1));
/* -------------------------------------------------------------------------- */
/* Databases - CRUD - Appwrite API https://appwrite.io/docs/client/databases */
/* -------------------------------------------------------------------------- */
/**
* Create Document
*
* Create a new Document. Before using this route, you should create a new
* collection resource using either a [server
* integration](/docs/server/databases#databasesCreateCollection) API or
* directly from your database console.
*
* @param {string} collectionId
* @param {Record<string, unknown>} data
* @param {string[]} [permissions]
* @param {string} [documentId]
* defaults to ID.unique()
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {Promise}
*/
async createDocument(collectionId, data, permissions, documentId = ID.unique(), alternateDatabaseId) {
const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error(DATABASE_ERROR);
}
else {
this.accountService.triggerAuthCheck();
return this._databases.createDocument(databaseId, collectionId, documentId, this._cleanData(data), permissions);
}
}
/**
* Upsert Document
*
* Create a new Document if it can't be found (using $id), otherwise the document is updated.
*
* This will use incurr a read and a write to the database.
*
* Before using this route, you should create a new
* collection resource using either a [server
* integration](/docs/server/databases#databasesCreateCollection) API or
* directly from your database console.
*
* @param {string} collectionId
* @param {Record<string, unknown>} data
* @param {string[]} [permissions]
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {Promise}
*/
async upsertDocument(collectionId, data, permissions, alternateDatabaseId) {
const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error(DATABASE_ERROR);
}
else {
this.accountService.triggerAuthCheck();
const id = data.$id ?? ID.unique();
try {
// try to retrieve the document if it does exist update it
const doc = await this.getDocument(collectionId, id);
const merged = { ...doc, ...data };
return this.updateDocument(collectionId, id, merged);
}
catch (error) {
return this.createDocument(collectionId, this._cleanData(data), permissions, ID.unique(), databaseId);
}
}
}
/**
* Get Document
*
* Get a document by its unique ID. This endpoint response returns a JSON
* object with the document data.
*
* @param {string} collectionId
* @param {string} documentId
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<DocumentShape>}
*/
async getDocument(collectionId, documentId, alternateDatabaseId) {
const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error(DATABASE_ERROR);
}
else {
this.accountService.triggerAuthCheck();
return this._databases.getDocument(databaseId, collectionId, documentId);
}
}
/**
* List Documents
*
* Get a list of all the user's documents in a given collection. You can use
* the query params to filter your results.
*
* @param {string} collectionId
* @param {string[]} queries
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {PromisePromise<{total: number; documents: (Input<typeof AppwriteDocumentSchema> & Input<typeof validationSchema>)[]; }>}
*/
async listDocuments(collectionId, queries, alternateDatabaseId) {
const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error(DATABASE_ERROR);
}
else {
this.accountService.triggerAuthCheck();
return this._databases.listDocuments(databaseId, collectionId, queries);
}
}
/**
* Update Document
*
* Update a document by its unique ID. Using the patch method you can pass
* only specific fields that will get updated.
*
* @param {string} collectionId
* @param {string} documentId
* @param {unknown} data
* @param {string[]} [permissions]
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<DocumentShape>}
*/
async updateDocument(collectionId, documentId, data, permissions, alternateDatabaseId) {
const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error(DATABASE_ERROR);
}
else {
this.accountService.triggerAuthCheck();
return this._databases.updateDocument(databaseId, collectionId, documentId, this._cleanData(data), permissions);
}
}
/**
* Delete Document
*
* Delete a document by its unique ID.
*
* @param {string} collectionId
* @param {string} documentId
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<void>}
*/
async deleteDocument(collectionId, documentId, alternateDatabaseId) {
const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error(DATABASE_ERROR);
}
else {
this.accountService.triggerAuthCheck();
return this._databases.deleteDocument(databaseId, collectionId, documentId);
}
}
/* -------------------------------------------------------------------------- */
/* Databases Realtime */
/* -------------------------------------------------------------------------- */
/* --------------- https://appwrite.io/docs/realtime#channels --------------- */
/* -------------------------------------------------------------------------- */
/**
* Monitor Collection
*
* Monitors real-time changes in a collection. Uses the configured default database
* An alternate database can be provided if needed
*
* @param {string} collectionId
* @param {string[]} [queries]
* @param {string | string[]} [events]
* @param {string} [alternativeDatabaseId]
* @throws {AppwriteException}
* @returns {Observable<(Input<typeof AppwriteDocumentSchema> & Input<typeof validationSchema>)[]>}
*/
collection$(collectionId, queries = [], events, alternativeDatabaseId) {
// check if required data is present runtime
const { path } = this._generatePath(alternativeDatabaseId, collectionId);
return this._client$.pipe(switchMap((client) => watch(client, path, events)), startWith(true), switchMap(() => {
return this.listDocuments(collectionId, queries, alternativeDatabaseId);
}), distinctUntilChanged((a, b) => deepEqual(a, b)), shareReplay(1));
}
/**
* Monitor Docuemnt
*
* Monitors real-time changes in a document. Uses the configured default database
* An alternate database can be provided if needed
*
* @param {string} collectionId
* @param {string} documentId
* @param {string | string[]} [events]
* @param {string} [alternativeDatabaseId]
* @throws {AppwriteException}
* @returns {Observable<(Input<typeof AppwriteDocumentSchema> & Input<typeof validationSchema>) | null>}
*/
document$(collectionId, documentId, events, alternativeDatabaseId) {
return this.collection$(collectionId, [Query.equal('$id', documentId)], events, alternativeDatabaseId).pipe(map((res) => {
if (res.documents[0]) {
return res.documents[0];
}
else {
return null;
}
}), shareReplay(1));
}
// TODO: query listening is done manually for now
// watch this Issue
// https://github.com/appwrite/appwrite/issues/2490
// https://appwrite.io/docs/databases#querying-documents
// right now this is resolved by only watching ids of the original query list
/* ----------------------------- Private Helpers ---------------------------- */
_generatePath(alternativeDatabaseId, collectionId) {
const databaseId = alternativeDatabaseId ?? DEFAULT_DATABASE_ID();
if (!databaseId) {
throw new Error('No Database ID provided or database not initialized, use alternateDatabaseId argument');
}
// generate collection path
const path = `databases.${databaseId}.collections.${collectionId}.documents`;
return { path, databaseId };
}
// remove the document meta data
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_cleanData(data) {
delete data.$collectionId;
delete data.$permissions;
delete data.$databaseId;
delete data.$createdAt;
delete data.$updatedAt;
delete data.$id;
return data;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: Databases, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: Databases, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: Databases, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class AppwriteAdapter {
databases = inject(Databases);
/**
* Create Document
*
* Create a new Document. Before using this route, you should create a new
* collection resource using either a [server
* integration](/docs/server/databases#databasesCreateCollection) API or
* directly from your database console.
* @param {DocumentShape} awDocument
* @param {string[]} [permissions]
* @param {string} [alternativeDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<DocumentShape>}
*/
async create(awDocument, permissions = [], documentId = ID.unique(), alternativeDatabaseId) {
const data = await this.databases.createDocument(this.collectionId, awDocument, permissions, documentId, alternativeDatabaseId);
if (this.validationFn) {
return this.validationFn(data);
}
return data;
}
/**
* Update Document
*
* Updates a Document. Before using this route, you should create a new
* collection resource using either a [server
* integration](/docs/server/databases#databasesCreateCollection) API or
* directly from your database console.
* @param {Partial<DocumentShape>} awDocument
* @param {string[]} [permissions]
* @param {string} [alternativeDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<T & Models.Document>}
*/
async update(awDocument, permissions = [], alternativeDatabaseId) {
if (!awDocument.$id) {
throw new Error('Document must have an id');
}
const data = await this.databases.updateDocument(this.collectionId, awDocument.$id, awDocument, permissions, alternativeDatabaseId);
if (this.validationFn) {
return this.validationFn(data);
}
return data;
}
/**
* Upsert Document
*
* Upserts a Document. If an { $id: string } exists on the document, it is updated, otherwise a new document is created
* Before using this route, you should create a new
* collection resource using either a [server
* integration](/docs/server/databases#databasesCreateCollection) API or
* directly from your database console.
* @param {DocumentShape} awDocument
* @param {string[]} [permissions]
* @param {string} [alternativeDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<T & Models.Document>}
*/
async upsert(awDocument, permissions = [], alternativeDatabaseId) {
const data = await this.databases.upsertDocument(this.collectionId, awDocument, permissions, alternativeDatabaseId);
if (this.validationFn) {
return this.validationFn(data);
}
return data;
}
/**
* Delete Document
*
* Deletes a Document.
* Takes either a document id or a document object with an id
* @param {DocumentShape | string} awDocumentIdOrAwDocument
* @param {string} [alternateDatabaseId]
* @throws {AppwriteException}
* @returns {Promise<T & Models.Document>}
*/
async delete(awDocumentIdOrAwDocument, alternateDatabaseId) {
const id = typeof awDocumentIdOrAwDocument === 'string'
? awDocumentIdOrAwDocument
: awDocumentIdOrAwDocument.$id;
if (!id) {
throw new Error('Document must have an id to be deleted');
}
return this.databases.deleteDocument(this.collectionId, id, alternateDatabaseId);
}
async document(documentId, alternativeDatabaseId) {
const data = await this.databases.getDocument(this.colle