mirror of
https://github.com/docmost/docmost.git
synced 2026-08-29 01:37:05 +08:00
feat: cloud and ee (#805)
* stripe init git submodules for enterprise modules * * Cloud billing UI - WIP * Proxy websockets in dev mode * Separate workspace login and creation for cloud * Other fixes * feat: billing (cloud) * * add domain service * prepare links from workspace hostname * WIP * Add exchange token generation * Validate JWT token type during verification * domain service * add SkipTransform decorator * * updates (server) * add new packages * new sso migration file * WIP * Fix hostname generation * WIP * WIP * Reduce input error font-size * set max password length * jwt package * license page - WIP * * License management UI * Move license key store to db * add reflector * SSO enforcement * * Add default plan * Add usePlan hook * * Fix auth container margin in mobile * Redirect login and home to select page in cloud * update .gitignore * Default to yearly * * Trial messaging * Handle ended trials * Don't set to readonly on collab disconnect (Cloud) * Refine trial (UI) * Fix bug caused by using jotai optics atom in AppHeader component * configurable database maximum pool * Close SSO form on save * wip * sync * Only show sign-in in cloud * exclude base api part from workspaceId check * close db connection beforeApplicationShutdown * Add health/live endpoint * clear cookie on hostname change * reset currentUser atom * Change text * return 401 if workspace does not match * feat: show user workspace list in cloud login page * sync * Add home path * Prefetch to speed up queries * * Add robots.txt * Disallow login and forgot password routes * wildcard user-agent * Fix space query cache * fix * fix * use space uuid for recent pages * prefetch billing plans * enhance license page * sync
This commit is contained in:
@@ -22,13 +22,13 @@ import { ForgotPasswordDto } from '../dto/forgot-password.dto';
|
||||
import ForgotPasswordEmail from '@docmost/transactional/emails/forgot-password-email';
|
||||
import { UserTokenRepo } from '@docmost/db/repos/user-token/user-token.repo';
|
||||
import { PasswordResetDto } from '../dto/password-reset.dto';
|
||||
import { UserToken } from '@docmost/db/types/entity.types';
|
||||
import { UserToken, Workspace } from '@docmost/db/types/entity.types';
|
||||
import { UserTokenType } from '../auth.constants';
|
||||
import { KyselyDB } from '@docmost/db/types/kysely.types';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { executeTx } from '@docmost/db/utils';
|
||||
import { VerifyUserTokenDto } from '../dto/verify-user-token.dto';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { DomainService } from '../../../integrations/environment/domain.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
@@ -38,7 +38,7 @@ export class AuthService {
|
||||
private userRepo: UserRepo,
|
||||
private userTokenRepo: UserTokenRepo,
|
||||
private mailService: MailService,
|
||||
private environmentService: EnvironmentService,
|
||||
private domainService: DomainService,
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
) {}
|
||||
|
||||
@@ -46,7 +46,9 @@ export class AuthService {
|
||||
const user = await this.userRepo.findByEmail(
|
||||
loginDto.email,
|
||||
workspaceId,
|
||||
true,
|
||||
{
|
||||
includePassword: true
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -68,8 +70,11 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async setup(createAdminUserDto: CreateAdminUserDto) {
|
||||
const user = await this.signupService.initialSetup(createAdminUserDto);
|
||||
return this.tokenService.generateAccessToken(user);
|
||||
const { workspace, user } =
|
||||
await this.signupService.initialSetup(createAdminUserDto);
|
||||
|
||||
const authToken = await this.tokenService.generateAccessToken(user);
|
||||
return { workspace, authToken };
|
||||
}
|
||||
|
||||
async changePassword(
|
||||
@@ -113,11 +118,11 @@ export class AuthService {
|
||||
|
||||
async forgotPassword(
|
||||
forgotPasswordDto: ForgotPasswordDto,
|
||||
workspaceId: string,
|
||||
workspace: Workspace,
|
||||
): Promise<void> {
|
||||
const user = await this.userRepo.findByEmail(
|
||||
forgotPasswordDto.email,
|
||||
workspaceId,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
@@ -125,7 +130,8 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const token = nanoIdGen(16);
|
||||
const resetLink = `${this.environmentService.getAppUrl()}/password-reset?token=${token}`;
|
||||
|
||||
const resetLink = `${this.domainService.getUrl(workspace.hostname)}/password-reset?token=${token}`;
|
||||
|
||||
await this.userTokenRepo.insertUserToken({
|
||||
token: token,
|
||||
@@ -199,7 +205,7 @@ export class AuthService {
|
||||
userTokenDto: VerifyUserTokenDto,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const userToken = await this.userTokenRepo.findById(
|
||||
const userToken: UserToken = await this.userTokenRepo.findById(
|
||||
userTokenDto.token,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { UserRepo } from '@docmost/db/repos/user/user.repo';
|
||||
import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types';
|
||||
import { executeTx } from '@docmost/db/utils';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { User } from '@docmost/db/types/entity.types';
|
||||
import { User, Workspace } from '@docmost/db/types/entity.types';
|
||||
import { GroupUserRepo } from '@docmost/db/repos/group/group-user.repo';
|
||||
import { UserRole } from '../../../common/helpers/types/permission';
|
||||
|
||||
@@ -32,7 +32,7 @@ export class SignupService {
|
||||
|
||||
if (userCheck) {
|
||||
throw new BadRequestException(
|
||||
'You already have an account on this workspace',
|
||||
'An account with this email already exists in this workspace',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,11 +72,14 @@ export class SignupService {
|
||||
createAdminUserDto: CreateAdminUserDto,
|
||||
trx?: KyselyTransaction,
|
||||
) {
|
||||
return await executeTx(
|
||||
let user: User,
|
||||
workspace: Workspace = null;
|
||||
|
||||
await executeTx(
|
||||
this.db,
|
||||
async (trx) => {
|
||||
// create user
|
||||
const user = await this.userRepo.insertUser(
|
||||
user = await this.userRepo.insertUser(
|
||||
{
|
||||
name: createAdminUserDto.name,
|
||||
email: createAdminUserDto.email,
|
||||
@@ -92,7 +95,7 @@ export class SignupService {
|
||||
name: createAdminUserDto.workspaceName,
|
||||
};
|
||||
|
||||
const workspace = await this.workspaceService.create(
|
||||
workspace = await this.workspaceService.create(
|
||||
user,
|
||||
workspaceData,
|
||||
trx,
|
||||
@@ -103,5 +106,7 @@ export class SignupService {
|
||||
},
|
||||
trx,
|
||||
);
|
||||
|
||||
return { user, workspace };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { EnvironmentService } from '../../../integrations/environment/environment.service';
|
||||
import { JwtCollabPayload, JwtPayload, JwtType } from '../dto/jwt-payload';
|
||||
import {
|
||||
JwtCollabPayload,
|
||||
JwtExchangePayload,
|
||||
JwtPayload,
|
||||
JwtType,
|
||||
} from '../dto/jwt-payload';
|
||||
import { User } from '@docmost/db/types/entity.types';
|
||||
|
||||
@Injectable()
|
||||
@@ -34,9 +39,29 @@ export class TokenService {
|
||||
return this.jwtService.sign(payload, { expiresIn });
|
||||
}
|
||||
|
||||
async verifyJwt(token: string) {
|
||||
return this.jwtService.verifyAsync(token, {
|
||||
async generateExchangeToken(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
): Promise<string> {
|
||||
const payload: JwtExchangePayload = {
|
||||
sub: userId,
|
||||
workspaceId: workspaceId,
|
||||
type: JwtType.EXCHANGE,
|
||||
};
|
||||
return this.jwtService.sign(payload, { expiresIn: '10s' });
|
||||
}
|
||||
|
||||
async verifyJwt(token: string, tokenType: string) {
|
||||
const payload = await this.jwtService.verifyAsync(token, {
|
||||
secret: this.environmentService.getAppSecret(),
|
||||
});
|
||||
|
||||
if (payload.type !== tokenType) {
|
||||
throw new UnauthorizedException(
|
||||
'Invalid JWT token. Token type does not match.',
|
||||
);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user