mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62a2eb61ea |
@@ -82,6 +82,7 @@
|
|||||||
"sanitize-filename-ts": "^1.0.2",
|
"sanitize-filename-ts": "^1.0.2",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"stripe": "^17.5.0",
|
"stripe": "^17.5.0",
|
||||||
|
"tld-extract": "^2.1.0",
|
||||||
"tmp-promise": "^3.0.3",
|
"tmp-promise": "^3.0.3",
|
||||||
"ws": "^8.18.2",
|
"ws": "^8.18.2",
|
||||||
"yauzl": "^3.2.0"
|
"yauzl": "^3.2.0"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, NestMiddleware, NotFoundException } from '@nestjs/common';
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||||
import { EnvironmentService } from '../../integrations/environment/environment.service';
|
import { EnvironmentService } from '../../integrations/environment/environment.service';
|
||||||
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
|
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
|
||||||
@@ -27,8 +27,19 @@ export class DomainMiddleware implements NestMiddleware {
|
|||||||
(req as any).workspace = workspace;
|
(req as any).workspace = workspace;
|
||||||
} else if (this.environmentService.isCloud()) {
|
} else if (this.environmentService.isCloud()) {
|
||||||
const header = req.headers.host;
|
const header = req.headers.host;
|
||||||
const subdomain = header.split('.')[0];
|
|
||||||
|
|
||||||
|
// First, try to find workspace by custom domain
|
||||||
|
const workspaceByCustomDomain =
|
||||||
|
await this.workspaceRepo.findByCustomDomain(header);
|
||||||
|
|
||||||
|
if (workspaceByCustomDomain) {
|
||||||
|
(req as any).workspaceId = workspaceByCustomDomain.id;
|
||||||
|
(req as any).workspace = workspaceByCustomDomain;
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to subdomain logic
|
||||||
|
const subdomain = header.split('.')[0];
|
||||||
const workspace = await this.workspaceRepo.findByHostname(subdomain);
|
const workspace = await this.workspaceRepo.findByHostname(subdomain);
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export class AuthService {
|
|||||||
|
|
||||||
const token = nanoIdGen(16);
|
const token = nanoIdGen(16);
|
||||||
|
|
||||||
const resetLink = `${this.domainService.getUrl(workspace.hostname)}/password-reset?token=${token}`;
|
const resetLink = `${this.domainService.getUrl(workspace.hostname, workspace.customDomain)}/password-reset?token=${token}`;
|
||||||
|
|
||||||
await this.userTokenRepo.insertUserToken({
|
await this.userTokenRepo.insertUserToken({
|
||||||
token: token,
|
token: token,
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export class WorkspaceInvitationService {
|
|||||||
invitation.email,
|
invitation.email,
|
||||||
invitation.token,
|
invitation.token,
|
||||||
authUser.name,
|
authUser.name,
|
||||||
workspace.hostname,
|
workspace,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -317,7 +317,7 @@ export class WorkspaceInvitationService {
|
|||||||
invitation.email,
|
invitation.email,
|
||||||
invitation.token,
|
invitation.token,
|
||||||
invitedByUser.name,
|
invitedByUser.name,
|
||||||
workspace.hostname,
|
workspace,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,17 +340,17 @@ export class WorkspaceInvitationService {
|
|||||||
return this.buildInviteLink({
|
return this.buildInviteLink({
|
||||||
invitationId,
|
invitationId,
|
||||||
inviteToken: token.token,
|
inviteToken: token.token,
|
||||||
hostname: workspace.hostname,
|
workspace: workspace,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildInviteLink(opts: {
|
async buildInviteLink(opts: {
|
||||||
invitationId: string;
|
invitationId: string;
|
||||||
inviteToken: string;
|
inviteToken: string;
|
||||||
hostname?: string;
|
workspace: Workspace;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const { invitationId, inviteToken, hostname } = opts;
|
const { invitationId, inviteToken, workspace } = opts;
|
||||||
return `${this.domainService.getUrl(hostname)}/invites/${invitationId}?token=${inviteToken}`;
|
return `${this.domainService.getUrl(workspace.hostname, workspace.customDomain)}/invites/${invitationId}?token=${inviteToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendInvitationMail(
|
async sendInvitationMail(
|
||||||
@@ -358,12 +358,12 @@ export class WorkspaceInvitationService {
|
|||||||
inviteeEmail: string,
|
inviteeEmail: string,
|
||||||
inviteToken: string,
|
inviteToken: string,
|
||||||
invitedByName: string,
|
invitedByName: string,
|
||||||
hostname?: string,
|
workspace: Workspace,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const inviteLink = await this.buildInviteLink({
|
const inviteLink = await this.buildInviteLink({
|
||||||
invitationId,
|
invitationId,
|
||||||
inviteToken,
|
inviteToken,
|
||||||
hostname,
|
workspace,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emailTemplate = InvitationEmail({
|
const emailTemplate = InvitationEmail({
|
||||||
|
|||||||
@@ -83,6 +83,14 @@ export class WorkspaceRepo {
|
|||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findByCustomDomain(domain: string): Promise<Workspace> {
|
||||||
|
return await this.db
|
||||||
|
.selectFrom('workspaces')
|
||||||
|
.selectAll()
|
||||||
|
.where(sql`LOWER(custom_domain)`, '=', sql`LOWER(${domain})`)
|
||||||
|
.executeTakeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
async hostnameExists(
|
async hostnameExists(
|
||||||
hostname: string,
|
hostname: string,
|
||||||
trx?: KyselyTransaction,
|
trx?: KyselyTransaction,
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: 19197d2610...1e127cec1d
@@ -5,10 +5,13 @@ import { EnvironmentService } from './environment.service';
|
|||||||
export class DomainService {
|
export class DomainService {
|
||||||
constructor(private environmentService: EnvironmentService) {}
|
constructor(private environmentService: EnvironmentService) {}
|
||||||
|
|
||||||
getUrl(hostname?: string): string {
|
getUrl(hostname?: string, customDomain?: string): string {
|
||||||
if (!this.environmentService.isCloud()) {
|
if (!this.environmentService.isCloud()) {
|
||||||
return this.environmentService.getAppUrl();
|
return this.environmentService.getAppUrl();
|
||||||
}
|
}
|
||||||
|
if (customDomain) {
|
||||||
|
return customDomain;
|
||||||
|
}
|
||||||
|
|
||||||
const domain = this.environmentService.getSubdomainHost();
|
const domain = this.environmentService.getSubdomainHost();
|
||||||
if (!hostname || !domain) {
|
if (!hostname || !domain) {
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ export class EnvironmentVariables {
|
|||||||
)
|
)
|
||||||
@ValidateIf((obj) => obj.CLOUD === 'true'.toLowerCase())
|
@ValidateIf((obj) => obj.CLOUD === 'true'.toLowerCase())
|
||||||
SUBDOMAIN_HOST: string;
|
SUBDOMAIN_HOST: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ValidateIf((obj) => obj.CLOUD === 'true'.toLowerCase())
|
||||||
|
APP_IP: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validate(config: Record<string, any>) {
|
export function validate(config: Record<string, any>) {
|
||||||
|
|||||||
Generated
+8
@@ -567,6 +567,9 @@ importers:
|
|||||||
stripe:
|
stripe:
|
||||||
specifier: ^17.5.0
|
specifier: ^17.5.0
|
||||||
version: 17.5.0
|
version: 17.5.0
|
||||||
|
tld-extract:
|
||||||
|
specifier: ^2.1.0
|
||||||
|
version: 2.1.0
|
||||||
tmp-promise:
|
tmp-promise:
|
||||||
specifier: ^3.0.3
|
specifier: ^3.0.3
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
@@ -8864,6 +8867,9 @@ packages:
|
|||||||
tiptap-extension-global-drag-handle@0.1.18:
|
tiptap-extension-global-drag-handle@0.1.18:
|
||||||
resolution: {integrity: sha512-jwFuy1K8DP3a4bFy76Hpc63w1Sil0B7uZ3mvhQomVvUFCU787Lg2FowNhn7NFzeyok761qY2VG+PZ/FDthWUdg==}
|
resolution: {integrity: sha512-jwFuy1K8DP3a4bFy76Hpc63w1Sil0B7uZ3mvhQomVvUFCU787Lg2FowNhn7NFzeyok761qY2VG+PZ/FDthWUdg==}
|
||||||
|
|
||||||
|
tld-extract@2.1.0:
|
||||||
|
resolution: {integrity: sha512-Y9QHWIoDQPJJVm3/pOC7kOfOj7vsNSVZl4JGoEHb605FiwZgIfzSMyU0HC0wYw5Cx8435vaG1yGZtIm1yiQGOw==}
|
||||||
|
|
||||||
tldts-core@6.1.72:
|
tldts-core@6.1.72:
|
||||||
resolution: {integrity: sha512-FW3H9aCaGTJ8l8RVCR3EX8GxsxDbQXuwetwwgXA2chYdsX+NY1ytCBl61narjjehWmCw92tc1AxlcY3668CU8g==}
|
resolution: {integrity: sha512-FW3H9aCaGTJ8l8RVCR3EX8GxsxDbQXuwetwwgXA2chYdsX+NY1ytCBl61narjjehWmCw92tc1AxlcY3668CU8g==}
|
||||||
|
|
||||||
@@ -19538,6 +19544,8 @@ snapshots:
|
|||||||
|
|
||||||
tiptap-extension-global-drag-handle@0.1.18: {}
|
tiptap-extension-global-drag-handle@0.1.18: {}
|
||||||
|
|
||||||
|
tld-extract@2.1.0: {}
|
||||||
|
|
||||||
tldts-core@6.1.72: {}
|
tldts-core@6.1.72: {}
|
||||||
|
|
||||||
tldts@6.1.72:
|
tldts@6.1.72:
|
||||||
|
|||||||
Reference in New Issue
Block a user