Compare commits

...

2 Commits

Author SHA1 Message Date
7491224d0f hide shared page branding in EE (#1193)
* hide shared page branding in EE

* Hide branding in business plan
2025-05-17 19:17:34 +01:00
4a0b4040ed Add second plan (#1187) 2025-05-17 19:03:01 +01:00
9 changed files with 49 additions and 18 deletions

View File

@ -1,5 +1,6 @@
export enum BillingPlan { export enum BillingPlan {
STANDARD = "standard", STANDARD = "standard",
BUSINESS = "business",
} }
export interface IBilling { export interface IBilling {

View File

@ -2,14 +2,18 @@ import { useAtom } from "jotai";
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
import { BillingPlan } from "@/ee/billing/types/billing.types.ts"; import { BillingPlan } from "@/ee/billing/types/billing.types.ts";
export const usePlan = () => { const usePlan = () => {
const [workspace] = useAtom(workspaceAtom); const [workspace] = useAtom(workspaceAtom);
const isStandard = const isStandard =
typeof workspace?.plan === "string" && typeof workspace?.plan === "string" &&
workspace?.plan.toLowerCase() === BillingPlan.STANDARD.toLowerCase(); workspace?.plan.toLowerCase() === BillingPlan.STANDARD.toLowerCase();
return { isStandard }; const isBusiness =
typeof workspace?.plan === "string" &&
workspace?.plan.toLowerCase() === BillingPlan.BUSINESS.toLowerCase();
return { isStandard, isBusiness };
}; };
export default usePlan; export default usePlan;

View File

@ -10,11 +10,13 @@ import EnforceSso from "@/ee/security/components/enforce-sso.tsx";
import AllowedDomains from "@/ee/security/components/allowed-domains.tsx"; import AllowedDomains from "@/ee/security/components/allowed-domains.tsx";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import useLicense from "@/ee/hooks/use-license.tsx"; import useLicense from "@/ee/hooks/use-license.tsx";
import usePlan from "@/ee/hooks/use-plan.tsx";
export default function Security() { export default function Security() {
const { t } = useTranslation(); const { t } = useTranslation();
const { isAdmin } = useUserRole(); const { isAdmin } = useUserRole();
const { hasLicenseKey } = useLicense(); const { hasLicenseKey } = useLicense();
const { isBusiness } = usePlan();
if (!isAdmin) { if (!isAdmin) {
return null; return null;
@ -35,8 +37,7 @@ export default function Security() {
Single sign-on (SSO) Single sign-on (SSO)
</Title> </Title>
{/*TODO: revisit when we add a second plan */} {(isCloud() && isBusiness) || (!isCloud() && hasLicenseKey) ? (
{!isCloud() && hasLicenseKey ? (
<> <>
<EnforceSso /> <EnforceSso />
<Divider my="lg" /> <Divider my="lg" />

View File

@ -0,0 +1,16 @@
import { Affix, Button } from "@mantine/core";
export default function ShareBranding() {
return (
<Affix position={{ bottom: 20, right: 20 }}>
<Button
variant="default"
component="a"
target="_blank"
href="https://docmost.com?ref=public-share"
>
Powered by Docmost
</Button>
</Affix>
);
}

View File

@ -36,6 +36,7 @@ import {
} from "@/features/search/components/search-control.tsx"; } from "@/features/search/components/search-control.tsx";
import { ShareSearchSpotlight } from "@/features/search/share-search-spotlight"; import { ShareSearchSpotlight } from "@/features/search/share-search-spotlight";
import { shareSearchSpotlight } from "@/features/search/constants"; import { shareSearchSpotlight } from "@/features/search/constants";
import ShareBranding from '@/features/share/components/share-branding.tsx';
const MemoizedSharedTree = React.memo(SharedTree); const MemoizedSharedTree = React.memo(SharedTree);
@ -163,16 +164,7 @@ export default function ShareShell({
<AppShell.Main> <AppShell.Main>
{children} {children}
<Affix position={{ bottom: 20, right: 20 }}> {data && shareId && !data.hasLicenseKey && <ShareBranding />}
<Button
variant="default"
component="a"
target="_blank"
href="https://docmost.com?ref=public-share"
>
Powered by Docmost
</Button>
</Affix>
</AppShell.Main> </AppShell.Main>
<AppShell.Aside <AppShell.Aside

View File

@ -41,6 +41,7 @@ export interface ISharedPage extends IShare {
level: number; level: number;
sharedPage: { id: string; slugId: string; title: string; icon: string }; sharedPage: { id: string; slugId: string; title: string; icon: string };
}; };
hasLicenseKey: boolean;
} }
export interface IShareForPage extends IShare { export interface IShareForPage extends IShare {
@ -70,4 +71,5 @@ export interface IShareInfoInput {
export interface ISharedPageTree { export interface ISharedPageTree {
share: IShare; share: IShare;
pageTree: Partial<IPage[]>; pageTree: Partial<IPage[]>;
hasLicenseKey: boolean;
} }

View File

@ -7,8 +7,9 @@ import React, { useEffect } from "react";
import ReadonlyPageEditor from "@/features/editor/readonly-page-editor.tsx"; import ReadonlyPageEditor from "@/features/editor/readonly-page-editor.tsx";
import { extractPageSlugId } from "@/lib"; import { extractPageSlugId } from "@/lib";
import { Error404 } from "@/components/ui/error-404.tsx"; import { Error404 } from "@/components/ui/error-404.tsx";
import ShareBranding from "@/features/share/components/share-branding.tsx";
export default function SingleSharedPage() { export default function SharedPage() {
const { t } = useTranslation(); const { t } = useTranslation();
const { pageSlug } = useParams(); const { pageSlug } = useParams();
const { shareId } = useParams(); const { shareId } = useParams();
@ -53,6 +54,8 @@ export default function SingleSharedPage() {
content={data.page.content} content={data.page.content}
/> />
</Container> </Container>
{data && !shareId && !data.hasLicenseKey && <ShareBranding />}
</div> </div>
); );
} }

View File

@ -30,6 +30,7 @@ import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { Public } from '../../common/decorators/public.decorator'; import { Public } from '../../common/decorators/public.decorator';
import { ShareRepo } from '@docmost/db/repos/share/share.repo'; import { ShareRepo } from '@docmost/db/repos/share/share.repo';
import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
import { EnvironmentService } from '../../integrations/environment/environment.service';
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Controller('shares') @Controller('shares')
@ -39,6 +40,7 @@ export class ShareController {
private readonly spaceAbility: SpaceAbilityFactory, private readonly spaceAbility: SpaceAbilityFactory,
private readonly shareRepo: ShareRepo, private readonly shareRepo: ShareRepo,
private readonly pageRepo: PageRepo, private readonly pageRepo: PageRepo,
private readonly environmentService: EnvironmentService,
) {} ) {}
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@ -61,7 +63,12 @@ export class ShareController {
throw new BadRequestException(); throw new BadRequestException();
} }
return this.shareService.getSharedPage(dto, workspace.id); return {
...(await this.shareService.getSharedPage(dto, workspace.id)),
hasLicenseKey:
Boolean(workspace.licenseKey) ||
(this.environmentService.isCloud() && workspace.plan === 'business'),
};
} }
@Public() @Public()
@ -166,6 +173,11 @@ export class ShareController {
@Body() dto: ShareIdDto, @Body() dto: ShareIdDto,
@AuthWorkspace() workspace: Workspace, @AuthWorkspace() workspace: Workspace,
) { ) {
return this.shareService.getShareTree(dto.shareId, workspace.id); return {
...(await this.shareService.getShareTree(dto.shareId, workspace.id)),
hasLicenseKey:
Boolean(workspace.licenseKey) ||
(this.environmentService.isCloud() && workspace.plan === 'business'),
};
} }
} }