Compare commits

...

17 Commits

Author SHA1 Message Date
Philipinho 7383673636 v0.2.3 2024-07-05 00:49:08 +01:00
Philip Okugbe 3e7b2495c5 Merge pull request #51 from docmost/private-attachments
make page attachments private
2024-07-05 00:47:51 +01:00
Philip Okugbe 0fc8edeb52 Merge pull request #55 from docmost/fix/bug-fixes
Fix: missing tree, editor font and responsive recent pages
2024-07-05 00:45:32 +01:00
Philipinho bbf865b2f6 cleanup debug log 2024-07-05 00:41:30 +01:00
Philipinho 0c622a0dc1 use sane font-weight 2024-07-05 00:33:59 +01:00
Philipinho f52cd011a4 remove unused imports 2024-07-05 00:33:12 +01:00
Philipinho a4d53468c3 fix tree state 2024-07-05 00:30:56 +01:00
Philipinho cc93abfb7e make pages table responsive 2024-07-04 21:13:43 +01:00
Philipinho 13f26f9c31 make page attachments private 2024-07-04 16:01:35 +01:00
Philipinho a4ec2dac6c v0.2.2 2024-07-03 13:01:00 +01:00
Philip Okugbe 681d7c789c merge: fix/media-in-firefox
fix media visibility in firefox
2024-07-03 11:57:03 +01:00
Philipinho 9f583174a9 fix media visibility in firefox 2024-07-03 11:53:09 +01:00
Philip Okugbe 05633082c5 feat/fuzzy-match-menu
added fuzzy matching logic for menu search
2024-07-03 11:25:21 +01:00
Philip Okugbe 491fbad4ac feat/full-width
add full page width user preference
2024-07-03 11:24:32 +01:00
Philipinho e824aeced7 Add width option to page menu 2024-07-03 11:23:42 +01:00
Philipinho 8f056d1071 add full page width preference 2024-07-03 11:00:42 +01:00
SurajJadhav7 99cf6dab62 added fuzzy matching logic for menu search 2024-07-01 16:49:36 +05:30
28 changed files with 313 additions and 124 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "client",
"private": true,
"version": "0.2.1",
"version": "0.2.3",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
@@ -1,4 +1,11 @@
import { Text, Group, UnstyledButton, Badge, Table } from "@mantine/core";
import {
Text,
Group,
UnstyledButton,
Badge,
Table,
ScrollArea,
} from "@mantine/core";
import { Link } from "react-router-dom";
import PageListSkeleton from "@/components/ui/page-list-skeleton.tsx";
import { buildPageUrl } from "@/features/page/page.utils.ts";
@@ -22,46 +29,48 @@ export default function RecentChanges({ spaceId }: Props) {
}
return pages && pages.items.length > 0 ? (
<Table highlightOnHover verticalSpacing="sm">
<Table.Tbody>
{pages.items.map((page) => (
<Table.Tr key={page.id}>
<Table.Td>
<UnstyledButton
component={Link}
to={buildPageUrl(page?.space.slug, page.slugId, page.title)}
>
<Group wrap="nowrap">
{page.icon || <IconFileDescription size={18} />}
<Text fw={500} size="md" lineClamp={1}>
{page.title || "Untitled"}
</Text>
</Group>
</UnstyledButton>
</Table.Td>
{!spaceId && (
<ScrollArea>
<Table highlightOnHover verticalSpacing="sm">
<Table.Tbody>
{pages.items.map((page) => (
<Table.Tr key={page.id}>
<Table.Td>
<Badge
color="blue"
variant="light"
<UnstyledButton
component={Link}
to={getSpaceUrl(page?.space.slug)}
style={{ cursor: "pointer" }}
to={buildPageUrl(page?.space.slug, page.slugId, page.title)}
>
{page?.space.name}
</Badge>
<Group wrap="nowrap">
{page.icon || <IconFileDescription size={18} />}
<Text fw={500} size="md" lineClamp={1}>
{page.title || "Untitled"}
</Text>
</Group>
</UnstyledButton>
</Table.Td>
)}
<Table.Td>
<Text c="dimmed" size="xs" fw={500}>
{formattedDate(page.updatedAt)}
</Text>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
{!spaceId && (
<Table.Td>
<Badge
color="blue"
variant="light"
component={Link}
to={getSpaceUrl(page?.space.slug)}
style={{ cursor: "pointer" }}
>
{page?.space.name}
</Badge>
</Table.Td>
)}
<Table.Td>
<Text c="dimmed" size="xs" fw={500}>
{formattedDate(page.updatedAt)}
</Text>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</ScrollArea>
) : (
<Text size="md" ta="center">
No pages yet
@@ -1,5 +1,6 @@
import { Avatar, Group, Menu, rem, UnstyledButton, Text } from "@mantine/core";
import { Group, Menu, UnstyledButton, Text } from "@mantine/core";
import {
IconBrush,
IconChevronDown,
IconLogout,
IconSettings,
@@ -38,10 +39,7 @@ export default function TopMenu() {
<Text fw={500} size="sm" lh={1} mr={3}>
{workspace.name}
</Text>
<IconChevronDown
style={{ width: rem(12), height: rem(12) }}
stroke={1.5}
/>
<IconChevronDown size={16} />
</Group>
</UnstyledButton>
</Menu.Target>
@@ -51,12 +49,7 @@ export default function TopMenu() {
<Menu.Item
component={Link}
to={APP_ROUTE.SETTINGS.WORKSPACE.GENERAL}
leftSection={
<IconSettings
style={{ width: rem(16), height: rem(16) }}
stroke={1.5}
/>
}
leftSection={<IconSettings size={16} />}
>
Workspace settings
</Menu.Item>
@@ -64,12 +57,7 @@ export default function TopMenu() {
<Menu.Item
component={Link}
to={APP_ROUTE.SETTINGS.WORKSPACE.MEMBERS}
leftSection={
<IconUsers
style={{ width: rem(16), height: rem(16) }}
stroke={1.5}
/>
}
leftSection={<IconUsers size={16} />}
>
Manage members
</Menu.Item>
@@ -98,27 +86,22 @@ export default function TopMenu() {
<Menu.Item
component={Link}
to={APP_ROUTE.SETTINGS.ACCOUNT.PROFILE}
leftSection={
<IconUserCircle
style={{ width: rem(16), height: rem(16) }}
stroke={1.5}
/>
}
leftSection={<IconUserCircle size={16} />}
>
My profile
</Menu.Item>
<Menu.Item
component={Link}
to={APP_ROUTE.SETTINGS.ACCOUNT.PREFERENCES}
leftSection={<IconBrush size={16} />}
>
My preferences
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={logout}
leftSection={
<IconLogout
style={{ width: rem(16), height: rem(16) }}
stroke={1.5}
/>
}
>
<Menu.Item onClick={logout} leftSection={<IconLogout size={16} />}>
Logout
</Menu.Item>
</Menu.Dropdown>
@@ -2,32 +2,28 @@ import { NodeViewProps, NodeViewWrapper } from "@tiptap/react";
import { useMemo } from "react";
import { Image } from "@mantine/core";
import { getFileUrl } from "@/lib/config.ts";
import clsx from "clsx";
export default function ImageView(props: NodeViewProps) {
const { node, selected } = props;
const { src, width, align, title } = node.attrs;
const flexJustifyContent = useMemo(() => {
if (align === "center") return "center";
if (align === "right") return "flex-end";
return "flex-start";
const alignClass = useMemo(() => {
if (align === "left") return "alignLeft";
if (align === "right") return "alignRight";
if (align === "center") return "alignCenter";
return "alignCenter";
}, [align]);
return (
<NodeViewWrapper
style={{
position: "relative",
display: "flex",
justifyContent: flexJustifyContent,
}}
>
<NodeViewWrapper>
<Image
radius="md"
fit="contain"
w={width}
src={getFileUrl(src)}
alt={title}
className={selected ? "ProseMirror-selectednode" : ""}
className={clsx(selected ? "ProseMirror-selectednode" : "", alignClass)}
/>
</NodeViewWrapper>
);
@@ -264,10 +264,20 @@ export const getSuggestionItems = ({
const search = query.toLowerCase();
const filteredGroups: SlashMenuGroupedItemsType = {};
const fuzzyMatch = (query, target) => {
let queryIndex = 0;
target = target.toLowerCase();
for (let char of target) {
if (query[queryIndex] === char) queryIndex++;
if (queryIndex === query.length) return true;
}
return false;
};
for (const [group, items] of Object.entries(CommandGroups)) {
const filteredItems = items.filter((item) => {
return (
item.title.toLowerCase().includes(search) ||
fuzzyMatch(search, item.title) ||
item.description.toLowerCase().includes(search) ||
(item.searchTerms &&
item.searchTerms.some((term: string) => term.includes(search)))
@@ -1,31 +1,27 @@
import { NodeViewProps, NodeViewWrapper } from "@tiptap/react";
import { useMemo } from "react";
import { getFileUrl } from "@/lib/config.ts";
import clsx from "clsx";
export default function VideoView(props: NodeViewProps) {
const { node, selected } = props;
const { src, width, align } = node.attrs;
const flexJustifyContent = useMemo(() => {
if (align === "center") return "center";
if (align === "right") return "flex-end";
return "flex-start";
const alignClass = useMemo(() => {
if (align === "left") return "alignLeft";
if (align === "right") return "alignRight";
if (align === "center") return "alignCenter";
return "alignCenter";
}, [align]);
return (
<NodeViewWrapper
style={{
position: "relative",
display: "flex",
justifyContent: flexJustifyContent,
}}
>
<NodeViewWrapper>
<video
preload="metadata"
width={width}
controls
src={getFileUrl(src)}
className={selected ? "ProseMirror-selectednode" : ""}
className={clsx(selected ? "ProseMirror-selectednode" : "", alignClass)}
/>
</NodeViewWrapper>
);
@@ -2,6 +2,9 @@ import classes from "@/features/editor/styles/editor.module.css";
import React from "react";
import { TitleEditor } from "@/features/editor/title-editor";
import PageEditor from "@/features/editor/page-editor";
import { Container } from "@mantine/core";
import { useAtom } from "jotai/index";
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
const MemoizedTitleEditor = React.memo(TitleEditor);
const MemoizedPageEditor = React.memo(PageEditor);
@@ -21,8 +24,16 @@ export function FullEditor({
spaceSlug,
editable,
}: FullEditorProps) {
const [user] = useAtom(userAtom);
const fullPageWidth = user.settings?.preferences?.fullPageWidth;
return (
<div className={classes.editor}>
<Container
fluid={fullPageWidth}
{...(fullPageWidth && { mx: 80 })}
size={850}
className={classes.editor}
>
<MemoizedTitleEditor
pageId={pageId}
slugId={slugId}
@@ -31,6 +42,6 @@ export function FullEditor({
editable={editable}
/>
<MemoizedPageEditor pageId={pageId} editable={editable} />
</div>
</Container>
);
}
@@ -9,7 +9,7 @@
);
font-size: var(--mantine-font-size-md);
line-height: var(--mantine-line-height-xl);
font-weight: 415;
font-weight: 400;
width: 100%;
> * + * {
@@ -125,6 +125,21 @@
cursor: ew-resize;
cursor: col-resize;
}
.alignLeft {
margin-left: 0;
margin-right: auto;
}
.alignRight {
margin-right: 0;
margin-left: auto;
}
.alignCenter {
margin-left: auto;
margin-right: auto;
}
}
.ProseMirror-icon {
@@ -1,5 +1,4 @@
.editor {
max-width: 800px;
height: 100%;
padding: 8px 20px;
margin: 64px auto;
@@ -4,7 +4,7 @@
iframe {
display: block;
outline: 0px solid transparent;
outline: 0 solid transparent;
border-radius: var(--mantine-radius-md);
width: 100%;
}
@@ -1,5 +1,6 @@
import { ActionIcon, Menu, Tooltip } from "@mantine/core";
import { ActionIcon, Group, Menu, Tooltip } from "@mantine/core";
import {
IconArrowsHorizontal,
IconDots,
IconHistory,
IconLink,
@@ -19,6 +20,7 @@ import { getAppUrl } from "@/lib/config.ts";
import { extractPageSlugId } from "@/lib";
import { treeApiAtom } from "@/features/page/tree/atoms/tree-api-atom.ts";
import { useDeletePageModal } from "@/features/page/hooks/use-delete-page-modal.tsx";
import { PageWidthToggle } from "@/features/user/components/page-width-pref.tsx";
interface PageHeaderMenuProps {
readOnly?: boolean;
@@ -95,6 +97,13 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
Copy link
</Menu.Item>
<Menu.Divider />
<Menu.Item leftSection={<IconArrowsHorizontal size={16} stroke={2} />}>
<Group wrap="nowrap">
<PageWidthToggle label="Full width" />
</Group>
</Menu.Item>
<Menu.Item
leftSection={<IconHistory size={16} stroke={2} />}
onClick={openHistoryModal}
@@ -88,6 +88,7 @@ export default function SpaceTree({ spaceId, readOnly }: SpaceTreeProps) {
if (pagesData?.pages && !hasNextPage) {
const allItems = pagesData.pages.flatMap((page) => page.items);
const treeData = buildTree(allItems);
if (data.length < 1 || data?.[0].spaceId !== spaceId) {
//Thoughts
// don't reset if there is data in state
@@ -106,7 +107,7 @@ export default function SpaceTree({ spaceId, readOnly }: SpaceTreeProps) {
const fetchData = async () => {
if (isDataLoaded.current && currentPage) {
// check if pageId node is present in the tree
const node = dfs(treeApiRef.current.root, currentPage.id);
const node = dfs(treeApiRef.current?.root, currentPage.id);
if (node) {
// if node is found, no need to traverse its ancestors
return;
@@ -52,6 +52,8 @@ export function useTreeMutation<T>(spaceId: string) {
slugId: createdPage.slugId,
name: "",
position: createdPage.position,
spaceId: createdPage.spaceId,
parentPageId: createdPage.parentPageId,
children: [],
} as any;
@@ -1,17 +1,10 @@
import { Group, Center, Text } from "@mantine/core";
import { Spotlight } from "@mantine/spotlight";
import {
IconFileDescription,
IconHome,
IconSearch,
IconSettings,
} from "@tabler/icons-react";
import { IconFileDescription, IconSearch } from "@tabler/icons-react";
import React, { useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useDebouncedValue } from "@mantine/hooks";
import { usePageSearchQuery } from "@/features/search/queries/search-query";
import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts";
import { useRecentChangesQuery } from "@/features/page/queries/page-query.ts";
import { buildPageUrl } from "@/features/page/page.utils.ts";
interface SearchSpotlightProps {
@@ -1,5 +1,16 @@
import { atomWithStorage } from "jotai/utils";
import { ICurrentUser } from "@/features/user/types/user.types";
import { focusAtom } from "jotai-optics";
export const currentUserAtom = atomWithStorage<ICurrentUser | null>("currentUser", null);
export const currentUserAtom = atomWithStorage<ICurrentUser | null>(
"currentUser",
null,
);
export const userAtom = focusAtom(currentUserAtom, (optic) =>
optic.prop("user"),
);
export const workspaceAtom = focusAtom(currentUserAtom, (optic) =>
optic.prop("workspace"),
);
@@ -0,0 +1,49 @@
import { Group, Text, Switch, MantineSize } from "@mantine/core";
import { useAtom } from "jotai/index";
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
import { updateUser } from "@/features/user/services/user-service.ts";
import React, { useState } from "react";
export default function PageWidthPref() {
return (
<Group justify="space-between" wrap="nowrap" gap="xl">
<div>
<Text size="md">Full page width</Text>
<Text size="sm" c="dimmed">
Choose your preferred page width.
</Text>
</div>
<PageWidthToggle />
</Group>
);
}
interface PageWidthToggleProps {
size?: MantineSize;
label?: string;
}
export function PageWidthToggle({ size, label }: PageWidthToggleProps) {
const [user, setUser] = useAtom(userAtom);
const [checked, setChecked] = useState(
user.settings?.preferences?.fullPageWidth,
);
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.currentTarget.checked;
const updatedUser = await updateUser({ fullPageWidth: value });
setChecked(value);
setUser(updatedUser);
};
return (
<Switch
size={size}
label={label}
labelPosition="left"
defaultChecked={checked}
onChange={handleChange}
aria-label="Toggle full page width"
/>
);
}
@@ -7,7 +7,7 @@ export interface IUser {
emailVerifiedAt: Date;
avatarUrl: string;
timezone: string;
settings: any;
settings: IUserSettings;
invitedById: string;
lastLoginAt: string;
lastActiveAt: Date;
@@ -17,9 +17,16 @@ export interface IUser {
workspaceId: string;
deactivatedAt: Date;
deletedAt: Date;
fullPageWidth: boolean; // used for update
}
export interface ICurrentUser {
user: IUser;
workspace: IWorkspace;
}
export interface IUserSettings {
preferences: {
fullPageWidth: boolean;
};
}
+1
View File
@@ -5,6 +5,7 @@ import { getBackendUrl } from "@/lib/config.ts";
const api: AxiosInstance = axios.create({
baseURL: getBackendUrl(),
withCredentials: true,
});
api.interceptors.request.use(
@@ -1,11 +1,15 @@
import SettingsTitle from "@/components/settings/settings-title.tsx";
import AccountTheme from "@/features/user/components/account-theme.tsx";
import PageWidthPref from "@/features/user/components/page-width-pref.tsx";
import { Divider } from "@mantine/core";
export default function AccountPreferences() {
return (
<>
<SettingsTitle title="Preferences" />
<AccountTheme />
<Divider my={"md"} />
<PageWidthPref />
</>
);
}
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "server",
"version": "0.2.1",
"version": "0.2.3",
"description": "",
"author": "",
"private": true,
@@ -31,6 +31,7 @@
"@aws-sdk/client-s3": "^3.600.0",
"@aws-sdk/s3-request-presigner": "^3.600.0",
"@casl/ability": "^6.7.1",
"@fastify/cookie": "^9.3.1",
"@fastify/multipart": "^8.3.0",
"@fastify/static": "^7.0.4",
"@nestjs/bullmq": "^10.1.1",
@@ -45,7 +45,6 @@ import {
import WorkspaceAbilityFactory from '../casl/abilities/workspace-ability.factory';
import { PageRepo } from '@docmost/db/repos/page/page.repo';
import { AttachmentRepo } from '@docmost/db/repos/attachment/attachment.repo';
import { Public } from '../../common/decorators/public.decorator';
import { validate as isValidUUID } from 'uuid';
@Controller()
@@ -129,12 +128,11 @@ export class AttachmentController {
}
}
@Public()
@UseGuards(JwtAuthGuard)
@Get('/files/:fileId/:fileName')
async getFile(
@Res() res: FastifyReply,
//@AuthUser() user: User,
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
@Param('fileId') fileId: string,
@Param('fileName') fileName?: string,
@@ -144,18 +142,29 @@ export class AttachmentController {
}
const attachment = await this.attachmentRepo.findById(fileId);
if (attachment.workspaceId !== workspace.id) {
if (
!attachment ||
attachment.workspaceId !== workspace.id ||
!attachment.pageId ||
!attachment.spaceId
) {
throw new NotFoundException();
}
if (!attachment || !attachment.pageId) {
throw new NotFoundException('File record not found');
const spaceAbility = await this.spaceAbility.createForUser(
user,
attachment.spaceId,
);
if (spaceAbility.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
throw new ForbiddenException();
}
try {
const fileStream = await this.storageService.read(attachment.filePath);
res.headers({
'Content-Type': getMimeType(attachment.filePath),
'Cache-Control': 'public, max-age=3600',
});
return res.send(fileStream);
} catch (err) {
@@ -268,6 +277,7 @@ export class AttachmentController {
const fileStream = await this.storageService.read(filePath);
res.headers({
'Content-Type': getMimeType(filePath),
'Cache-Control': 'public, max-age=86400',
});
return res.send(fileStream);
} catch (err) {
@@ -4,11 +4,12 @@ import {
UnauthorizedException,
} from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { Strategy } from 'passport-jwt';
import { EnvironmentService } from '../../../integrations/environment/environment.service';
import { JwtPayload, JwtType } from '../dto/jwt-payload';
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
import { UserRepo } from '@docmost/db/repos/user/user.repo';
import { FastifyRequest } from 'fastify';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
@@ -18,7 +19,15 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
private readonly environmentService: EnvironmentService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
jwtFromRequest: (req: FastifyRequest) => {
let accessToken = null;
try {
accessToken = JSON.parse(req.cookies?.authTokens)?.accessToken;
} catch {}
return accessToken || this.extractTokenFromHeader(req);
},
ignoreExpiration: false,
secretOrKey: environmentService.getAppSecret(),
passReqToCallback: true,
@@ -50,4 +59,9 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
return { user, workspace };
}
private extractTokenFromHeader(request: FastifyRequest): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
@@ -1,6 +1,6 @@
import { OmitType, PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from '../../auth/dto/create-user.dto';
import { IsOptional, IsString } from 'class-validator';
import { IsBoolean, IsOptional, IsString } from 'class-validator';
export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ['password'] as const),
@@ -8,4 +8,8 @@ export class UpdateUserDto extends PartialType(
@IsOptional()
@IsString()
avatarUrl: string;
@IsOptional()
@IsBoolean()
fullPageWidth: boolean;
}
+17
View File
@@ -20,10 +20,19 @@ export class UserService {
workspaceId: string,
) {
const user = await this.userRepo.findById(userId, workspaceId);
if (!user) {
throw new NotFoundException('User not found');
}
// preference update
if (typeof updateUserDto.fullPageWidth !== 'undefined') {
return this.updateUserPageWidthPreference(
userId,
updateUserDto.fullPageWidth,
);
}
if (updateUserDto.name) {
user.name = updateUserDto.name;
}
@@ -42,4 +51,12 @@ export class UserService {
await this.userRepo.updateUser(updateUserDto, userId, workspaceId);
return user;
}
async updateUserPageWidthPreference(userId: string, fullPageWidth: boolean) {
return this.userRepo.updatePreference(
userId,
'fullPageWidth',
fullPageWidth,
);
}
}
@@ -15,6 +15,7 @@ import {
executeWithPagination,
PaginationResult,
} from '@docmost/db/pagination/pagination';
import { sql } from 'kysely';
@Injectable()
export class UserRepo {
@@ -157,6 +158,24 @@ export class UserRepo {
return result;
}
async updatePreference(
userId: string,
prefKey: string,
prefValue: string | boolean,
) {
return await this.db
.updateTable('users')
.set({
settings: sql`COALESCE(settings, '{}'::jsonb)
|| jsonb_build_object('preferences', COALESCE(settings->'preferences', '{}'::jsonb)
|| jsonb_build_object('${sql.raw(prefKey)}', ${sql.lit(prefValue)}))`,
updatedAt: new Date(),
})
.where('id', '=', userId)
.returning(this.baseFields)
.executeTakeFirst();
}
/*
async getSpaceIds(
workspaceId: string,
+12 -1
View File
@@ -9,6 +9,7 @@ import { TransformHttpResponseInterceptor } from './common/interceptors/http-res
import fastifyMultipart from '@fastify/multipart';
import { WsRedisIoAdapter } from './ws/adapter/ws-redis.adapter';
import { InternalLogFilter } from './common/logger/internal-log-filter';
import fastifyCookie from '@fastify/cookie';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
@@ -31,6 +32,7 @@ async function bootstrap() {
app.useWebSocketAdapter(redisIoAdapter);
await app.register(fastifyMultipart as any);
await app.register(fastifyCookie as any);
app
.getHttpAdapter()
@@ -56,7 +58,16 @@ async function bootstrap() {
transform: true,
}),
);
app.enableCors();
if (process.env.NODE_ENV !== 'production') {
// make development easy
app.enableCors({
origin: ['http://localhost:5173'],
credentials: true,
});
} else {
app.enableCors();
}
app.useGlobalInterceptors(new TransformHttpResponseInterceptor());
app.enableShutdownHooks();
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "docmost",
"homepage": "https://docmost.com",
"version": "0.2.1",
"version": "0.2.3",
"private": true,
"scripts": {
"build": "nx run-many -t build",
+17
View File
@@ -331,6 +331,9 @@ importers:
'@casl/ability':
specifier: ^6.7.1
version: 6.7.1
'@fastify/cookie':
specifier: ^9.3.1
version: 9.3.1
'@fastify/multipart':
specifier: ^8.3.0
version: 8.3.0
@@ -1875,6 +1878,9 @@ packages:
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
engines: {node: '>=14'}
'@fastify/cookie@9.3.1':
resolution: {integrity: sha512-h1NAEhB266+ZbZ0e9qUE6NnNR07i7DnNXWG9VbbZ8uC6O/hxHpl+Zoe5sw1yfdZ2U6XhToUGDnzQtWJdCaPwfg==}
'@fastify/cors@9.0.1':
resolution: {integrity: sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==}
@@ -4594,6 +4600,10 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-signature@1.2.1:
resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==}
engines: {node: '>=6.6.0'}
cookie@0.4.2:
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
engines: {node: '>= 0.6'}
@@ -9789,6 +9799,11 @@ snapshots:
'@fastify/busboy@2.1.1': {}
'@fastify/cookie@9.3.1':
dependencies:
cookie-signature: 1.2.1
fastify-plugin: 4.5.1
'@fastify/cors@9.0.1':
dependencies:
fastify-plugin: 4.5.1
@@ -12947,6 +12962,8 @@ snapshots:
convert-source-map@2.0.0: {}
cookie-signature@1.2.1: {}
cookie@0.4.2: {}
cookie@0.6.0: {}