mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
[](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@aws-sdk/client-s3](https://togithub.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3) ([source](https://togithub.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3)) | [`3.484.0` -> `3.485.0`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.484.0/3.485.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@napi-rs/cli](https://togithub.com/napi-rs/napi-rs) | [`3.0.0-alpha.29` -> `3.0.0-alpha.30`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.0.0-alpha.29/3.0.0-alpha.30) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@node-rs/jsonwebtoken](https://togithub.com/napi-rs/node-rs) | [`^0.2.3` -> `^0.3.0`](https://renovatebot.com/diffs/npm/@node-rs%2fjsonwebtoken/0.2.3/0.3.1) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@opentelemetry/instrumentation-socket.io](https://togithub.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/instrumentation-socket.io#readme) ([source](https://togithub.com/open-telemetry/opentelemetry-js-contrib)) | [`^0.34.4` -> `^0.35.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-socket.io/0.34.4/0.35.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/test-runner](https://togithub.com/storybookjs/test-runner) | [`^0.15.2` -> `^0.16.0`](https://renovatebot.com/diffs/npm/@storybook%2ftest-runner/0.15.2/0.16.0) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@vitest/coverage-istanbul](https://togithub.com/vitest-dev/vitest/tree/main/packages/coverage-istanbul#readme) ([source](https://togithub.com/vitest-dev/vitest/tree/HEAD/packages/coverage-istanbul)) | [`1.1.1` -> `1.1.2`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-istanbul/1.1.1/1.1.2) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [@vitest/ui](https://togithub.com/vitest-dev/vitest/tree/main/packages/ui#readme) ([source](https://togithub.com/vitest-dev/vitest/tree/HEAD/packages/ui)) | [`1.1.1` -> `1.1.2`](https://renovatebot.com/diffs/npm/@vitest%2fui/1.1.1/1.1.2) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | | [vitest](https://togithub.com/vitest-dev/vitest) ([source](https://togithub.com/vitest-dev/vitest/tree/HEAD/packages/vitest)) | [`1.1.1` -> `1.1.2`](https://renovatebot.com/diffs/npm/vitest/1.1.1/1.1.2) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>aws/aws-sdk-js-v3 (@​aws-sdk/client-s3)</summary> ### [`v3.485.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#34850-2024-01-03) [Compare Source](https://togithub.com/aws/aws-sdk-js-v3/compare/v3.484.0...v3.485.0) ##### Features - **credential-providers:** add credentialScope field ([#​5606](https://togithub.com/aws/aws-sdk-js-v3/issues/5606)) ([04c1459](04c1459289)) </details> <details> <summary>napi-rs/napi-rs (@​napi-rs/cli)</summary> ### [`v3.0.0-alpha.30`](https://togithub.com/napi-rs/napi-rs/releases/tag/%40napi-rs/cli%403.0.0-alpha.30) [Compare Source](https://togithub.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.0.0-alpha.29...@napi-rs/cli@3.0.0-alpha.30) ##### What's Changed - chore(deps): lock file maintenance by [@​renovate](https://togithub.com/renovate) in [https://github.com/napi-rs/napi-rs/pull/1882](https://togithub.com/napi-rs/napi-rs/pull/1882) - fix(cli): wasi fallback package load logic by [@​Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1887](https://togithub.com/napi-rs/napi-rs/pull/1887) - fix(cli): upload to github releases issue by [@​Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1888](https://togithub.com/napi-rs/napi-rs/pull/1888) **Full Changelog**: https://github.com/napi-rs/napi-rs/compare/[@​napi-rs/cli](https://togithub.com/napi-rs/cli)[@​3](https://togithub.com/3).0.0-alpha.29...[@​napi-rs/cli](https://togithub.com/napi-rs/cli)[@​3](https://togithub.com/3).0.0-alpha.30 </details> <details> <summary>napi-rs/node-rs (@​node-rs/jsonwebtoken)</summary> ### [`v0.3.1`](https://togithub.com/napi-rs/node-rs/compare/@node-rs/jsonwebtoken@0.3.0...@node-rs/jsonwebtoken@0.3.1) [Compare Source](https://togithub.com/napi-rs/node-rs/compare/@node-rs/jsonwebtoken@0.3.0...@node-rs/jsonwebtoken@0.3.1) ### [`v0.3.0`](https://togithub.com/napi-rs/node-rs/compare/@node-rs/jsonwebtoken@0.2.3...@node-rs/jsonwebtoken@0.3.0) [Compare Source](https://togithub.com/napi-rs/node-rs/compare/@node-rs/jsonwebtoken@0.2.3...@node-rs/jsonwebtoken@0.3.0) </details> <details> <summary>open-telemetry/opentelemetry-js-contrib (@​opentelemetry/instrumentation-socket.io)</summary> ### [`v0.35.0`](efdfc727a4...f81f8a76a8) [Compare Source](a757b5e443...9092823125) </details> <details> <summary>storybookjs/test-runner (@​storybook/test-runner)</summary> ### [`v0.16.0`](https://togithub.com/storybookjs/test-runner/releases/tag/v0.16.0) [Compare Source](https://togithub.com/storybookjs/test-runner/compare/v0.15.2...v0.16.0) ##### 🚀 Enhancement - Introduce logLevel configuration [#​406](https://togithub.com/storybookjs/test-runner/pull/406) ([@​yannbf](https://togithub.com/yannbf)) ##### 🐛 Bug Fix - Filter duplicated error messages in browser logs [#​405](https://togithub.com/storybookjs/test-runner/pull/405) ([@​yannbf](https://togithub.com/yannbf)) - Fix sync issues between tests [#​404](https://togithub.com/storybookjs/test-runner/pull/404) ([@​yannbf](https://togithub.com/yannbf)) - Refactor: Extract the setup page scripts into a separate file [#​403](https://togithub.com/storybookjs/test-runner/pull/403) ([@​yannbf](https://togithub.com/yannbf)) - Docs: Adds feedback form to migration documentation [#​402](https://togithub.com/storybookjs/test-runner/pull/402) ([@​jonniebigodes](https://togithub.com/jonniebigodes)) - Bump `jest-playwright-preset` from `v3.0.1` to `v4.0.0` [#​400](https://togithub.com/storybookjs/test-runner/pull/400) ([@​kemuridama](https://togithub.com/kemuridama)) - Improve type safety and code quality [#​383](https://togithub.com/storybookjs/test-runner/pull/383) ([@​bryanjtc](https://togithub.com/bryanjtc) [@​yannbf](https://togithub.com/yannbf)) - Refactor: Improve internal code [#​378](https://togithub.com/storybookjs/test-runner/pull/378) ([@​bryanjtc](https://togithub.com/bryanjtc) [@​yannbf](https://togithub.com/yannbf)) ##### Authors: 4 - [@​jonniebigodes](https://togithub.com/jonniebigodes) - Bryan Thomas ([@​bryanjtc](https://togithub.com/bryanjtc)) - Ryo Ochiai ([@​kemuridama](https://togithub.com/kemuridama)) - Yann Braga ([@​yannbf](https://togithub.com/yannbf)) </details> <details> <summary>vitest-dev/vitest (@​vitest/coverage-istanbul)</summary> ### [`v1.1.2`](https://togithub.com/vitest-dev/vitest/releases/tag/v1.1.2) [Compare Source](https://togithub.com/vitest-dev/vitest/compare/v1.1.1...v1.1.2) ##### 🐞 Bug Fixes - Remove internal flag from UI option in the config - by [@​sheremet-va](https://togithub.com/sheremet-va) [<samp>(7b4a2)</samp>](https://togithub.com/vitest-dev/vitest/commit/7b4a2fce) - **browser**: - Avoid safaridriver collision - by [@​mbland](https://togithub.com/mbland) in [https://github.com/vitest-dev/vitest/issues/4863](https://togithub.com/vitest-dev/vitest/issues/4863) [<samp>(345a2)</samp>](https://togithub.com/vitest-dev/vitest/commit/345a25d6) - Resolved failure to find arbitrarily-named snapshot files when using `expect(...).toMatchFileSnapshot()` matcher. - by [@​zmullett](https://togithub.com/zmullett), **Zac Mullett** and [@​sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/4839](https://togithub.com/vitest-dev/vitest/issues/4839) [<samp>(b8140)</samp>](https://togithub.com/vitest-dev/vitest/commit/b8140fca) - Handle config.base - by [@​mbland](https://togithub.com/mbland) and [@​sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/4686](https://togithub.com/vitest-dev/vitest/issues/4686) and [https://github.com/vitest-dev/vitest/issues/4692](https://togithub.com/vitest-dev/vitest/issues/4692) [<samp>(9e345)</samp>](https://togithub.com/vitest-dev/vitest/commit/9e34557e) - **deps**: - Update dependency acorn-walk to ^8.3.1 - by [@​renovate](https://togithub.com/renovate)\[bot] in[https://github.com/vitest-dev/vitest/issues/4837](https://togithub.com/vitest-dev/vitest/issues/4837)7 [<samp>(47bc2)</samp>](https://togithub.com/vitest-dev/vitest/commit/47bc233d) - Update dependency sirv to ^2.0.4 - by [@​renovate](https://togithub.com/renovate)\[bot] in[https://github.com/vitest-dev/vitest/issues/4838](https://togithub.com/vitest-dev/vitest/issues/4838)8 [<samp>(df261)</samp>](https://togithub.com/vitest-dev/vitest/commit/df261ae1) - **runner**: - Fix fixture cleanup for concurrent tests - by [@​hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/4827](https://togithub.com/vitest-dev/vitest/issues/4827) [<samp>(1fee6)</samp>](https://togithub.com/vitest-dev/vitest/commit/1fee63f2) - **spy**: - Don't allow `Promise` in `mockImplementation` if it's not in the function signature - by [@​sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/4859](https://togithub.com/vitest-dev/vitest/issues/4859) [<samp>(072e0)</samp>](https://togithub.com/vitest-dev/vitest/commit/072e02bf) - **vite-node**: - Correctly return cached result - by [@​sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/4870](https://togithub.com/vitest-dev/vitest/issues/4870) [<samp>(15bbb)</samp>](https://togithub.com/vitest-dev/vitest/commit/15bbbf81) - **vitest**: - Throw an error if mock was already loaded when `vi.mock` is called - by [@​sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/4862](https://togithub.com/vitest-dev/vitest/issues/4862) [<samp>(e12a5)</samp>](https://togithub.com/vitest-dev/vitest/commit/e12a5a36) - Correctly rerun test files on change if server was restarted - by [@​sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/4871](https://togithub.com/vitest-dev/vitest/issues/4871) [<samp>(6088b)</samp>](https://togithub.com/vitest-dev/vitest/commit/6088b372) - **vm-threads**: - Don't crash on percentage based `memoryLimit` - by [@​inottn](https://togithub.com/inottn) and [@​AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/4802](https://togithub.com/vitest-dev/vitest/issues/4802) [<samp>(70e8a)</samp>](https://togithub.com/vitest-dev/vitest/commit/70e8a389) ##### [View changes on GitHub](https://togithub.com/vitest-dev/vitest/compare/v1.1.1...v1.1.2) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4xMDMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjEyMS4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
323 lines
7.9 KiB
TypeScript
323 lines
7.9 KiB
TypeScript
import { randomUUID } from 'node:crypto';
|
|
|
|
import {
|
|
BadRequestException,
|
|
Injectable,
|
|
InternalServerErrorException,
|
|
UnauthorizedException,
|
|
} from '@nestjs/common';
|
|
import { hash, verify } from '@node-rs/argon2';
|
|
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
|
|
import type { User } from '@prisma/client';
|
|
import { nanoid } from 'nanoid';
|
|
|
|
import { Config } from '../../config';
|
|
import { PrismaService } from '../../prisma';
|
|
import { verifyChallengeResponse } from '../../storage';
|
|
import { Quota_FreePlanV1 } from '../quota';
|
|
import { MailService } from './mailer';
|
|
|
|
export type UserClaim = Pick<
|
|
User,
|
|
'id' | 'name' | 'email' | 'emailVerified' | 'createdAt' | 'avatarUrl'
|
|
> & {
|
|
hasPassword?: boolean;
|
|
};
|
|
|
|
export const getUtcTimestamp = () => Math.floor(Date.now() / 1000);
|
|
|
|
@Injectable()
|
|
export class AuthService {
|
|
constructor(
|
|
private readonly config: Config,
|
|
private readonly prisma: PrismaService,
|
|
private readonly mailer: MailService
|
|
) {}
|
|
|
|
sign(user: UserClaim) {
|
|
const now = getUtcTimestamp();
|
|
return sign(
|
|
{
|
|
data: {
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
emailVerified: user.emailVerified?.toISOString(),
|
|
image: user.avatarUrl,
|
|
hasPassword: Boolean(user.hasPassword),
|
|
createdAt: user.createdAt.toISOString(),
|
|
},
|
|
iat: now,
|
|
exp: now + this.config.auth.accessTokenExpiresIn,
|
|
iss: this.config.serverId,
|
|
sub: user.id,
|
|
aud: 'https://affine.pro',
|
|
jti: randomUUID({
|
|
disableEntropyCache: true,
|
|
}),
|
|
},
|
|
this.config.auth.privateKey,
|
|
{
|
|
algorithm: Algorithm.ES256,
|
|
}
|
|
);
|
|
}
|
|
|
|
refresh(user: UserClaim) {
|
|
const now = getUtcTimestamp();
|
|
return sign(
|
|
{
|
|
data: {
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
emailVerified: user.emailVerified?.toISOString(),
|
|
image: user.avatarUrl,
|
|
hasPassword: Boolean(user.hasPassword),
|
|
createdAt: user.createdAt.toISOString(),
|
|
},
|
|
exp: now + this.config.auth.refreshTokenExpiresIn,
|
|
iat: now,
|
|
iss: this.config.serverId,
|
|
sub: user.id,
|
|
aud: 'https://affine.pro',
|
|
jti: randomUUID({
|
|
disableEntropyCache: true,
|
|
}),
|
|
},
|
|
this.config.auth.privateKey,
|
|
{
|
|
algorithm: Algorithm.ES256,
|
|
}
|
|
);
|
|
}
|
|
|
|
async verify(token: string) {
|
|
try {
|
|
const data = (
|
|
await jwtVerify(token, this.config.auth.publicKey, {
|
|
algorithms: [Algorithm.ES256],
|
|
iss: [this.config.serverId],
|
|
leeway: this.config.auth.leeway,
|
|
requiredSpecClaims: ['exp', 'iat', 'iss', 'sub'],
|
|
aud: ['https://affine.pro'],
|
|
})
|
|
).data as UserClaim;
|
|
|
|
return {
|
|
...data,
|
|
emailVerified: data.emailVerified ? new Date(data.emailVerified) : null,
|
|
createdAt: new Date(data.createdAt),
|
|
};
|
|
} catch (e) {
|
|
throw new UnauthorizedException('Invalid token');
|
|
}
|
|
}
|
|
|
|
async verifyCaptchaToken(token: any, ip: string) {
|
|
if (typeof token !== 'string' || !token) return false;
|
|
|
|
const formData = new FormData();
|
|
formData.append('secret', this.config.auth.captcha.turnstile.secret);
|
|
formData.append('response', token);
|
|
formData.append('remoteip', ip);
|
|
// prevent replay attack
|
|
formData.append('idempotency_key', nanoid());
|
|
|
|
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
|
|
const result = await fetch(url, {
|
|
body: formData,
|
|
method: 'POST',
|
|
});
|
|
const outcome = await result.json();
|
|
|
|
return (
|
|
!!outcome.success &&
|
|
// skip hostname check in dev mode
|
|
(this.config.affineEnv === 'dev' || outcome.hostname === this.config.host)
|
|
);
|
|
}
|
|
|
|
async verifyChallengeResponse(response: any, resource: string) {
|
|
return verifyChallengeResponse(
|
|
response,
|
|
this.config.auth.captcha.challenge.bits,
|
|
resource
|
|
);
|
|
}
|
|
|
|
async signIn(email: string, password: string): Promise<User> {
|
|
const user = await this.prisma.user.findFirst({
|
|
where: {
|
|
email,
|
|
},
|
|
});
|
|
|
|
if (!user) {
|
|
throw new BadRequestException('Invalid email');
|
|
}
|
|
|
|
if (!user.password) {
|
|
throw new BadRequestException('User has no password');
|
|
}
|
|
let equal = false;
|
|
try {
|
|
equal = await verify(user.password, password);
|
|
} catch (e) {
|
|
console.error(e);
|
|
throw new InternalServerErrorException(e, 'Verify password failed');
|
|
}
|
|
if (!equal) {
|
|
throw new UnauthorizedException('Invalid password');
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
async signUp(name: string, email: string, password: string): Promise<User> {
|
|
const user = await this.prisma.user.findFirst({
|
|
where: {
|
|
email,
|
|
},
|
|
});
|
|
|
|
if (user) {
|
|
throw new BadRequestException('Email already exists');
|
|
}
|
|
|
|
const hashedPassword = await hash(password);
|
|
|
|
return this.prisma.user.create({
|
|
data: {
|
|
name,
|
|
email,
|
|
password: hashedPassword,
|
|
features: {
|
|
create: {
|
|
reason: 'created by api sign up',
|
|
activated: true,
|
|
feature: {
|
|
connect: {
|
|
feature_version: Quota_FreePlanV1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
async createAnonymousUser(email: string): Promise<User> {
|
|
const user = await this.prisma.user.findFirst({
|
|
where: {
|
|
email,
|
|
},
|
|
});
|
|
|
|
if (user) {
|
|
throw new BadRequestException('Email already exists');
|
|
}
|
|
|
|
return this.prisma.user.create({
|
|
data: {
|
|
name: 'Unnamed',
|
|
email,
|
|
features: {
|
|
create: {
|
|
reason: 'created by invite sign up',
|
|
activated: true,
|
|
feature: {
|
|
connect: {
|
|
feature_version: Quota_FreePlanV1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
async getUserByEmail(email: string): Promise<User | null> {
|
|
return this.prisma.user.findUnique({
|
|
where: {
|
|
email,
|
|
},
|
|
});
|
|
}
|
|
|
|
async isUserHasPassword(email: string): Promise<boolean> {
|
|
const user = await this.prisma.user.findFirst({
|
|
where: {
|
|
email,
|
|
},
|
|
});
|
|
if (!user) {
|
|
throw new BadRequestException('Invalid email');
|
|
}
|
|
return Boolean(user.password);
|
|
}
|
|
|
|
async changePassword(email: string, newPassword: string): Promise<User> {
|
|
const user = await this.prisma.user.findUnique({
|
|
where: {
|
|
email,
|
|
emailVerified: {
|
|
not: null,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!user) {
|
|
throw new BadRequestException('Invalid email');
|
|
}
|
|
|
|
const hashedPassword = await hash(newPassword);
|
|
|
|
return this.prisma.user.update({
|
|
where: {
|
|
id: user.id,
|
|
},
|
|
data: {
|
|
password: hashedPassword,
|
|
},
|
|
});
|
|
}
|
|
|
|
async changeEmail(id: string, newEmail: string): Promise<User> {
|
|
const user = await this.prisma.user.findUnique({
|
|
where: {
|
|
id,
|
|
},
|
|
});
|
|
|
|
if (!user) {
|
|
throw new BadRequestException('Invalid email');
|
|
}
|
|
|
|
return this.prisma.user.update({
|
|
where: {
|
|
id,
|
|
},
|
|
data: {
|
|
email: newEmail,
|
|
},
|
|
});
|
|
}
|
|
|
|
async sendChangePasswordEmail(email: string, callbackUrl: string) {
|
|
return this.mailer.sendChangePasswordEmail(email, callbackUrl);
|
|
}
|
|
async sendSetPasswordEmail(email: string, callbackUrl: string) {
|
|
return this.mailer.sendSetPasswordEmail(email, callbackUrl);
|
|
}
|
|
async sendChangeEmail(email: string, callbackUrl: string) {
|
|
return this.mailer.sendChangeEmail(email, callbackUrl);
|
|
}
|
|
async sendVerifyChangeEmail(email: string, callbackUrl: string) {
|
|
return this.mailer.sendVerifyChangeEmail(email, callbackUrl);
|
|
}
|
|
async sendNotificationChangeEmail(email: string) {
|
|
return this.mailer.sendNotificationChangeEmail(email);
|
|
}
|
|
}
|