mirror of
https://github.com/Shadowfita/docmost.git
synced 2025-11-10 04:22:00 +10:00
Merge branch 'main' of https://github.com/Shadowfita/docmost
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "client",
|
||||
"private": true,
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
@ -14,26 +14,26 @@
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@excalidraw/excalidraw": "^0.17.6",
|
||||
"@mantine/core": "^7.11.0",
|
||||
"@mantine/form": "^7.11.0",
|
||||
"@mantine/hooks": "^7.11.0",
|
||||
"@mantine/modals": "^7.11.0",
|
||||
"@mantine/notifications": "^7.11.0",
|
||||
"@mantine/spotlight": "^7.11.0",
|
||||
"@tabler/icons-react": "^3.7.0",
|
||||
"@tanstack/react-query": "^5.48.0",
|
||||
"axios": "^1.7.2",
|
||||
"@mantine/core": "^7.12.2",
|
||||
"@mantine/form": "^7.12.2",
|
||||
"@mantine/hooks": "^7.12.2",
|
||||
"@mantine/modals": "^7.12.2",
|
||||
"@mantine/notifications": "^7.12.2",
|
||||
"@mantine/spotlight": "^7.12.2",
|
||||
"@tabler/icons-react": "^3.14.0",
|
||||
"@tanstack/react-query": "^5.53.2",
|
||||
"axios": "^1.7.7",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"emoji-mart": "^5.6.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"jotai": "^2.8.3",
|
||||
"jotai": "^2.9.3",
|
||||
"jotai-optics": "^0.4.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"katex": "^0.16.10",
|
||||
"katex": "^0.16.11",
|
||||
"lowlight": "^3.1.0",
|
||||
"mermaid": "^11.0.1",
|
||||
"mermaid": "^11.0.2",
|
||||
"react": "^18.3.1",
|
||||
"react-arborist": "^3.4.0",
|
||||
"react-clear-modal": "^2.0.9",
|
||||
@ -42,32 +42,32 @@
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-helmet-async": "^2.0.5",
|
||||
"react-moveable": "^0.56.0",
|
||||
"react-router-dom": "^6.24.0",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tippy.js": "^6.3.7",
|
||||
"tiptap-extension-global-drag-handle": "^0.1.10",
|
||||
"tiptap-extension-global-drag-handle": "^0.1.12",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tanstack/eslint-plugin-query": "^5.47.0",
|
||||
"@tanstack/eslint-plugin-query": "^5.53.0",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/node": "20.14.9",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/node": "22.5.2",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
||||
"@typescript-eslint/parser": "^7.14.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
||||
"@typescript-eslint/parser": "^8.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"eslint": "^9.5.0",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"eslint-plugin-react-refresh": "^0.4.11",
|
||||
"optics-ts": "^2.4.1",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-preset-mantine": "^1.15.0",
|
||||
"postcss": "^8.4.43",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "^3.3.2",
|
||||
"typescript": "^5.5.2",
|
||||
"vite": "^5.3.1"
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Group, Text, ScrollArea, ActionIcon, rem } from '@mantine/core';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Group, Text, ScrollArea, ActionIcon, rem } from "@mantine/core";
|
||||
import {
|
||||
IconUser,
|
||||
IconSettings,
|
||||
@ -8,9 +8,9 @@ import {
|
||||
IconUsersGroup,
|
||||
IconSpaces,
|
||||
IconBrush,
|
||||
} from '@tabler/icons-react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import classes from './settings.module.css';
|
||||
} from "@tabler/icons-react";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import classes from "./settings.module.css";
|
||||
|
||||
interface DataItem {
|
||||
label: string;
|
||||
@ -25,27 +25,27 @@ interface DataGroup {
|
||||
|
||||
const groupedData: DataGroup[] = [
|
||||
{
|
||||
heading: 'Account',
|
||||
heading: "Account",
|
||||
items: [
|
||||
{ label: 'Profile', icon: IconUser, path: '/settings/account/profile' },
|
||||
{ label: "Profile", icon: IconUser, path: "/settings/account/profile" },
|
||||
{
|
||||
label: 'Preferences',
|
||||
label: "Preferences",
|
||||
icon: IconBrush,
|
||||
path: '/settings/account/preferences',
|
||||
path: "/settings/account/preferences",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: 'Workspace',
|
||||
heading: "Workspace",
|
||||
items: [
|
||||
{ label: 'General', icon: IconSettings, path: '/settings/workspace' },
|
||||
{ label: "General", icon: IconSettings, path: "/settings/workspace" },
|
||||
{
|
||||
label: 'Members',
|
||||
label: "Members",
|
||||
icon: IconUsers,
|
||||
path: '/settings/members',
|
||||
path: "/settings/members",
|
||||
},
|
||||
{ label: 'Groups', icon: IconUsersGroup, path: '/settings/groups' },
|
||||
{ label: 'Spaces', icon: IconSpaces, path: '/settings/spaces' },
|
||||
{ label: "Groups", icon: IconUsersGroup, path: "/settings/groups" },
|
||||
{ label: "Spaces", icon: IconSpaces, path: "/settings/spaces" },
|
||||
],
|
||||
},
|
||||
];
|
||||
@ -96,6 +96,7 @@ export default function SettingsSidebar() {
|
||||
<div className={classes.version}>
|
||||
<Text
|
||||
className={classes.version}
|
||||
size="sm"
|
||||
c="dimmed"
|
||||
component="a"
|
||||
href="https://github.com/docmost/docmost/releases"
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import data from "@emoji-mart/data";
|
||||
import Picker from "@emoji-mart/react";
|
||||
import React, { ReactNode } from 'react';
|
||||
import {
|
||||
ActionIcon,
|
||||
Popover,
|
||||
Button,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const Picker = React.lazy(() => import('@emoji-mart/react'));
|
||||
|
||||
export interface EmojiPickerInterface {
|
||||
onEmojiSelect: (emoji: any) => void;
|
||||
@ -48,23 +49,25 @@ function EmojiPicker({
|
||||
{icon}
|
||||
</ActionIcon>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown bg="000" style={{ border: "none" }}>
|
||||
<Picker
|
||||
data={data}
|
||||
onEmojiSelect={handleEmojiSelect}
|
||||
perLine={8}
|
||||
skinTonePosition="search"
|
||||
theme={colorScheme}
|
||||
/>
|
||||
<Popover.Dropdown bg="000" style={{ border: 'none' }}>
|
||||
<Suspense fallback={null}>
|
||||
<Picker
|
||||
data={async () => (await import('@emoji-mart/data')).default}
|
||||
onEmojiSelect={handleEmojiSelect}
|
||||
perLine={8}
|
||||
skinTonePosition="search"
|
||||
theme={colorScheme}
|
||||
/>
|
||||
</Suspense>
|
||||
<Button
|
||||
variant="default"
|
||||
c="gray"
|
||||
size="xs"
|
||||
style={{
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
zIndex: 2,
|
||||
bottom: "1rem",
|
||||
right: "1rem",
|
||||
bottom: '1rem',
|
||||
right: '1rem',
|
||||
}}
|
||||
onClick={handleRemoveEmoji}
|
||||
>
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from "@tiptap/react";
|
||||
import { ActionIcon, CopyButton, Group, Select, Tooltip } from "@mantine/core";
|
||||
import { useEffect, useState } from "react";
|
||||
import { IconCheck, IconCopy } from "@tabler/icons-react";
|
||||
import MermaidView from "@/features/editor/components/code-block/mermaid-view.tsx";
|
||||
import classes from "./code-block.module.css";
|
||||
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from '@tiptap/react';
|
||||
import { ActionIcon, CopyButton, Group, Select, Tooltip } from '@mantine/core';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IconCheck, IconCopy } from '@tabler/icons-react';
|
||||
//import MermaidView from "@/features/editor/components/code-block/mermaid-view.tsx";
|
||||
import classes from './code-block.module.css';
|
||||
import React from 'react';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const MermaidView = React.lazy(
|
||||
() => import('@/features/editor/components/code-block/mermaid-view.tsx')
|
||||
);
|
||||
|
||||
export default function CodeBlockView(props: NodeViewProps) {
|
||||
const { node, updateAttributes, extension, editor, getPos } = props;
|
||||
const { language } = node.attrs;
|
||||
const [languageValue, setLanguageValue] = useState<string | null>(
|
||||
language || null,
|
||||
language || null
|
||||
);
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
|
||||
@ -24,9 +30,9 @@ export default function CodeBlockView(props: NodeViewProps) {
|
||||
setIsSelected(isNodeSelected);
|
||||
};
|
||||
|
||||
editor.on("selectionUpdate", updateSelection);
|
||||
editor.on('selectionUpdate', updateSelection);
|
||||
return () => {
|
||||
editor.off("selectionUpdate", updateSelection);
|
||||
editor.off('selectionUpdate', updateSelection);
|
||||
};
|
||||
}, [editor, getPos(), node.nodeSize]);
|
||||
|
||||
@ -47,7 +53,7 @@ export default function CodeBlockView(props: NodeViewProps) {
|
||||
value={languageValue}
|
||||
onChange={changeLanguage}
|
||||
searchable
|
||||
style={{ maxWidth: "130px" }}
|
||||
style={{ maxWidth: '130px' }}
|
||||
classNames={{ input: classes.selectInput }}
|
||||
disabled={!editor.isEditable}
|
||||
/>
|
||||
@ -55,12 +61,12 @@ export default function CodeBlockView(props: NodeViewProps) {
|
||||
<CopyButton value={node?.textContent} timeout={2000}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip
|
||||
label={copied ? "Copied" : "Copy"}
|
||||
label={copied ? 'Copied' : 'Copy'}
|
||||
withArrow
|
||||
position="right"
|
||||
>
|
||||
<ActionIcon
|
||||
color={copied ? "teal" : "gray"}
|
||||
color={copied ? 'teal' : 'gray'}
|
||||
variant="subtle"
|
||||
onClick={copy}
|
||||
>
|
||||
@ -74,15 +80,19 @@ export default function CodeBlockView(props: NodeViewProps) {
|
||||
<pre
|
||||
spellCheck="false"
|
||||
hidden={
|
||||
((language === "mermaid" && !editor.isEditable) ||
|
||||
(language === "mermaid" && !isSelected)) &&
|
||||
((language === 'mermaid' && !editor.isEditable) ||
|
||||
(language === 'mermaid' && !isSelected)) &&
|
||||
node.textContent.length > 0
|
||||
}
|
||||
>
|
||||
<NodeViewContent as="code" className={`language-${language}`} />
|
||||
</pre>
|
||||
|
||||
{language === "mermaid" && <MermaidView props={props} />}
|
||||
{language === 'mermaid' && (
|
||||
<Suspense fallback={null}>
|
||||
<MermaidView props={props} />
|
||||
</Suspense>
|
||||
)}
|
||||
</NodeViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
useComputedColorScheme,
|
||||
} from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { Excalidraw, exportToSvg, loadFromBlob } from '@excalidraw/excalidraw';
|
||||
import { uploadFile } from '@/features/page/services/page-service.ts';
|
||||
import { svgStringToFile } from '@/lib';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
@ -19,6 +18,14 @@ import { IAttachment } from '@/lib/types';
|
||||
import ReactClearModal from 'react-clear-modal';
|
||||
import clsx from 'clsx';
|
||||
import { IconEdit } from '@tabler/icons-react';
|
||||
import { lazy } from 'react';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const Excalidraw = lazy(() =>
|
||||
import('@excalidraw/excalidraw').then((module) => ({
|
||||
default: module.Excalidraw,
|
||||
}))
|
||||
);
|
||||
|
||||
export default function ExcalidrawView(props: NodeViewProps) {
|
||||
const { node, updateAttributes, editor, selected } = props;
|
||||
@ -43,6 +50,8 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
cache: 'no-store',
|
||||
});
|
||||
|
||||
const { loadFromBlob } = await import('@excalidraw/excalidraw');
|
||||
|
||||
const data = await loadFromBlob(await request.blob(), null, null);
|
||||
setExcalidrawData(data);
|
||||
}
|
||||
@ -58,6 +67,8 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { exportToSvg } = await import('@excalidraw/excalidraw');
|
||||
|
||||
const svg = await exportToSvg({
|
||||
elements: excalidrawAPI?.getSceneElements(),
|
||||
appState: {
|
||||
@ -129,13 +140,15 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
</Button>
|
||||
</Group>
|
||||
<div style={{ height: '90vh' }}>
|
||||
<Excalidraw
|
||||
excalidrawAPI={(api) => setExcalidrawAPI(api)}
|
||||
initialData={{
|
||||
...excalidrawData,
|
||||
scrollToContent: true,
|
||||
}}
|
||||
/>
|
||||
<Suspense fallback={null}>
|
||||
<Excalidraw
|
||||
excalidrawAPI={(api) => setExcalidrawAPI(api)}
|
||||
initialData={{
|
||||
...excalidrawData,
|
||||
scrollToContent: true,
|
||||
}}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
</ReactClearModal>
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
IconH2,
|
||||
IconH3,
|
||||
IconInfoCircle,
|
||||
IconLetterY,
|
||||
IconList,
|
||||
IconListNumbers,
|
||||
IconListTree,
|
||||
@ -18,6 +17,7 @@ import {
|
||||
IconPhoto,
|
||||
IconTable,
|
||||
IconTypography,
|
||||
IconMenu4
|
||||
} from "@tabler/icons-react";
|
||||
import {
|
||||
CommandProps,
|
||||
@ -139,12 +139,20 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
},
|
||||
{
|
||||
title: "Code",
|
||||
description: "Capture a code snippet.",
|
||||
description: "Insert code snippet.",
|
||||
searchTerms: ["codeblock"],
|
||||
icon: IconCode,
|
||||
command: ({ editor, range }: CommandProps) =>
|
||||
editor.chain().focus().deleteRange(range).toggleCodeBlock().run(),
|
||||
},
|
||||
{
|
||||
title: "Divider",
|
||||
description: "Insert horizontal rule divider",
|
||||
searchTerms: ["horizontal rule", "hr"],
|
||||
icon: IconMenu4,
|
||||
command: ({ editor, range }: CommandProps) =>
|
||||
editor.chain().focus().deleteRange(range).setHorizontalRule().run(),
|
||||
},
|
||||
{
|
||||
title: "Image",
|
||||
description: "Upload any image from your device.",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.0",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
@ -28,40 +28,38 @@
|
||||
"test:e2e": "jest --config test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.600.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.600.0",
|
||||
"@aws-sdk/client-s3": "^3.637.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.637.0",
|
||||
"@casl/ability": "^6.7.1",
|
||||
"@fastify/cookie": "^9.3.1",
|
||||
"@fastify/cookie": "^9.4.0",
|
||||
"@fastify/multipart": "^8.3.0",
|
||||
"@fastify/static": "^7.0.4",
|
||||
"@nestjs/bullmq": "^10.1.1",
|
||||
"@nestjs/common": "^10.3.9",
|
||||
"@nestjs/config": "^3.2.2",
|
||||
"@nestjs/core": "^10.3.9",
|
||||
"@nestjs/bullmq": "^10.2.1",
|
||||
"@nestjs/common": "^10.4.1",
|
||||
"@nestjs/config": "^3.2.3",
|
||||
"@nestjs/core": "^10.4.1",
|
||||
"@nestjs/event-emitter": "^2.0.4",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/mapped-types": "^2.0.5",
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-fastify": "^10.3.9",
|
||||
"@nestjs/platform-socket.io": "^10.3.9",
|
||||
"@nestjs/platform-fastify": "^10.4.1",
|
||||
"@nestjs/platform-socket.io": "^10.4.1",
|
||||
"@nestjs/terminus": "^10.2.3",
|
||||
"@nestjs/websockets": "^10.3.9",
|
||||
"@react-email/components": "0.0.19",
|
||||
"@react-email/render": "^0.0.15",
|
||||
"@nestjs/websockets": "^10.4.1",
|
||||
"@react-email/components": "0.0.24",
|
||||
"@react-email/render": "^1.0.1",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"@types/pg": "^8.11.6",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bullmq": "^5.8.2",
|
||||
"bullmq": "^5.12.12",
|
||||
"bytes": "^3.1.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"fastify": "^4.28.0",
|
||||
"fix-esm": "^1.0.1",
|
||||
"fs-extra": "^11.2.0",
|
||||
"happy-dom": "^14.12.3",
|
||||
"kysely": "^0.27.3",
|
||||
"happy-dom": "^15.7.3",
|
||||
"kysely": "^0.27.4",
|
||||
"kysely-migration-cli": "^0.4.2",
|
||||
"marked": "^13.0.2",
|
||||
"marked": "^13.0.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"nanoid": "^5.0.7",
|
||||
"nestjs-kysely": "^1.0.0",
|
||||
@ -69,46 +67,47 @@
|
||||
"passport-jwt": "^4.0.1",
|
||||
"pg": "^8.12.0",
|
||||
"pg-tsquery": "^8.4.2",
|
||||
"postmark": "^4.0.4",
|
||||
"postmark": "^4.0.5",
|
||||
"react": "^18.3.1",
|
||||
"redis": "^4.6.14",
|
||||
"redis": "^4.7.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"sanitize-filename-ts": "^1.0.2",
|
||||
"socket.io": "^4.7.5",
|
||||
"ws": "^8.17.1"
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.3.2",
|
||||
"@nestjs/schematics": "^10.1.1",
|
||||
"@nestjs/testing": "^10.3.9",
|
||||
"@nestjs/cli": "^10.4.5",
|
||||
"@nestjs/schematics": "^10.1.4",
|
||||
"@nestjs/testing": "^10.4.1",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/bytes": "^3.1.4",
|
||||
"@types/debounce": "^1.2.4",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/node": "^20.14.9",
|
||||
"@types/node": "^22.5.2",
|
||||
"@types/nodemailer": "^6.4.15",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/pg": "^8.11.8",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
||||
"@typescript-eslint/parser": "^7.14.1",
|
||||
"eslint": "^9.5.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
||||
"@typescript-eslint/parser": "^8.3.0",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"jest": "^29.7.0",
|
||||
"kysely-codegen": "^0.15.0",
|
||||
"prettier": "^3.3.2",
|
||||
"react-email": "^2.1.4",
|
||||
"kysely-codegen": "^0.16.3",
|
||||
"prettier": "^3.3.3",
|
||||
"react-email": "^3.0.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^7.0.0",
|
||||
"ts-jest": "^29.1.5",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.5.2"
|
||||
"typescript": "^5.5.4"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
|
||||
@ -10,10 +10,7 @@ export function generateJSON(
|
||||
const schema = getSchema(extensions);
|
||||
|
||||
const window = new Window();
|
||||
const dom = new HappyDomParser(window).parseFromString(
|
||||
html,
|
||||
'text/html',
|
||||
).body;
|
||||
const dom = new HappyDomParser().parseFromString(html, 'text/html').body;
|
||||
|
||||
// @ts-ignore
|
||||
return DOMParser.fromSchema(schema).parse(dom, options).toJSON();
|
||||
|
||||
@ -5,10 +5,10 @@ export enum AttachmentType {
|
||||
File = 'file',
|
||||
}
|
||||
|
||||
export const validImageExtensions = ['.jpg', '.png', '.jpeg', 'gif'];
|
||||
export const validImageExtensions = ['.jpg', '.png', '.jpeg'];
|
||||
export const MAX_AVATAR_SIZE = '5MB';
|
||||
|
||||
export const InlineFileExtensions = [
|
||||
export const inlineFileExtensions = [
|
||||
'.jpg',
|
||||
'.png',
|
||||
'.jpeg',
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
import { getMimeType } from '../../common/helpers';
|
||||
import {
|
||||
AttachmentType,
|
||||
inlineFileExtensions,
|
||||
MAX_AVATAR_SIZE,
|
||||
MAX_FILE_SIZE,
|
||||
} from './attachment.constants';
|
||||
@ -177,6 +178,14 @@ export class AttachmentController {
|
||||
'Content-Type': attachment.mimeType,
|
||||
'Cache-Control': 'public, max-age=3600',
|
||||
});
|
||||
|
||||
if (!inlineFileExtensions.includes(attachment.fileExt)) {
|
||||
res.header(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="${attachment.fileName}"`,
|
||||
);
|
||||
}
|
||||
|
||||
return res.send(fileStream);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
|
||||
@ -19,8 +19,8 @@ export class MailService {
|
||||
async sendEmail(message: MailMessage): Promise<void> {
|
||||
if (message.template) {
|
||||
// in case this method is used directly. we do not send the tsx template from queue
|
||||
message.html = render(message.template, { pretty: true });
|
||||
message.text = render(message.template, { plainText: true });
|
||||
message.html = await render(message.template, { pretty: true });
|
||||
message.text = await render(message.template, { plainText: true });
|
||||
}
|
||||
|
||||
const sender = `${this.environmentService.getMailFromName()} <${this.environmentService.getMailFromAddress()}> `;
|
||||
@ -30,8 +30,8 @@ export class MailService {
|
||||
async sendToQueue(message: MailMessage): Promise<void> {
|
||||
if (message.template) {
|
||||
// transform the React object because it gets lost when sent via the queue
|
||||
message.html = render(message.template, { pretty: true });
|
||||
message.text = render(message.template, {
|
||||
message.html = await render(message.template, { pretty: true });
|
||||
message.text = await render(message.template, {
|
||||
plainText: true,
|
||||
});
|
||||
delete message.template;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "docmost",
|
||||
"homepage": "https://docmost.com",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nx run-many -t build",
|
||||
|
||||
4812
pnpm-lock.yaml
generated
4812
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user