mirror of
https://github.com/docmost/docmost.git
synced 2025-11-12 00:42:06 +10:00
Compare commits
7 Commits
v0.6.0
...
poc/server
| Author | SHA1 | Date | |
|---|---|---|---|
| 5506fe5e73 | |||
| 1302b1b602 | |||
| 89a3f4cfc2 | |||
| e48b1c0dae | |||
| 4a2a5a7a4d | |||
| 532001fd82 | |||
| e6bf4cdd6c |
@ -1,22 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: { browser: true, es2020: true },
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
'plugin:@tanstack/eslint-plugin-query/recommended',
|
|
||||||
],
|
|
||||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['react-refresh'],
|
|
||||||
rules: {
|
|
||||||
'react-refresh/only-export-components': [
|
|
||||||
'warn',
|
|
||||||
{ allowConstantExport: true },
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
36
apps/client/eslint.config.mjs
Normal file
36
apps/client/eslint.config.mjs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
|
import reactRefresh from "eslint-plugin-react-refresh";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import pluginQuery from "@tanstack/eslint-plugin-query";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ["dist"] },
|
||||||
|
{
|
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
|
files: ["**/*.{ts,tsx}"],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
"react-hooks": reactHooks,
|
||||||
|
"react-refresh": reactRefresh,
|
||||||
|
"@tanstack/query": pluginQuery,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
"react-refresh/only-export-components": [
|
||||||
|
"warn",
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"react-hooks/exhaustive-deps": "off",
|
||||||
|
"@typescript-eslint/no-unused-expressions": "off",
|
||||||
|
"no-useless-escape": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -6,6 +6,7 @@
|
|||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Docmost</title>
|
<title>Docmost</title>
|
||||||
|
<!--meta-tags-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"format": "prettier --write \"src/**/*.tsx\" \"src/**/*.ts\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@casl/ability": "^6.7.2",
|
"@casl/ability": "^6.7.2",
|
||||||
@ -49,25 +50,27 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tanstack/eslint-plugin-query": "^5.61.4",
|
"@eslint/js": "^9.16.0",
|
||||||
|
"@tanstack/eslint-plugin-query": "^5.62.1",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/katex": "^0.16.7",
|
"@types/katex": "^0.16.7",
|
||||||
"@types/node": "22.10.0",
|
"@types/node": "22.10.0",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
||||||
"@typescript-eslint/parser": "^8.16.0",
|
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"eslint": "^9.15.0",
|
"eslint": "^9.15.0",
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
"eslint-plugin-react": "^7.37.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.14",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.16",
|
||||||
|
"globals": "^15.13.0",
|
||||||
"optics-ts": "^2.4.1",
|
"optics-ts": "^2.4.1",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"postcss-preset-mantine": "^1.17.0",
|
"postcss-preset-mantine": "^1.17.0",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"prettier": "^3.4.1",
|
"prettier": "^3.4.1",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.17.0",
|
||||||
"vite": "^6.0.0"
|
"vite": "^6.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,13 @@ export default function App() {
|
|||||||
<>
|
<>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route index element={<Navigate to="/home" />} />
|
<Route index element={<Navigate to="/home" />} />
|
||||||
|
<Route path={"/share/:id"} element={
|
||||||
|
<ErrorBoundary
|
||||||
|
fallback={<>Failed to load home. An error occurred.</>}
|
||||||
|
>
|
||||||
|
<Home />
|
||||||
|
</ErrorBoundary>
|
||||||
|
}/>
|
||||||
<Route path={"/login"} element={<LoginPage />} />
|
<Route path={"/login"} element={<LoginPage />} />
|
||||||
<Route path={"/invites/:invitationId"} element={<InviteSignup />} />
|
<Route path={"/invites/:invitationId"} element={<InviteSignup />} />
|
||||||
<Route path={"/setup/register"} element={<SetupWorkspace />} />
|
<Route path={"/setup/register"} element={<SetupWorkspace />} />
|
||||||
|
|||||||
@ -25,7 +25,6 @@ export function useCommentsQuery(
|
|||||||
params: ICommentParams,
|
params: ICommentParams,
|
||||||
): UseQueryResult<IPagination<IComment>, Error> {
|
): UseQueryResult<IPagination<IComment>, Error> {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
// eslint-disable-next-line @tanstack/query/exhaustive-deps
|
|
||||||
queryKey: RQ_KEY(params.pageId),
|
queryKey: RQ_KEY(params.pageId),
|
||||||
queryFn: () => getPageComments(params),
|
queryFn: () => getPageComments(params),
|
||||||
enabled: !!params.pageId,
|
enabled: !!params.pageId,
|
||||||
|
|||||||
@ -40,7 +40,7 @@ export default function DrawioView(props: NodeViewProps) {
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
let base64data = (reader.result || '') as string;
|
const base64data = (reader.result || '') as string;
|
||||||
setInitialXML(base64data);
|
setInitialXML(base64data);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -456,7 +456,7 @@ export const getSuggestionItems = ({
|
|||||||
const fuzzyMatch = (query: string, target: string) => {
|
const fuzzyMatch = (query: string, target: string) => {
|
||||||
let queryIndex = 0;
|
let queryIndex = 0;
|
||||||
target = target.toLowerCase();
|
target = target.toLowerCase();
|
||||||
for (let char of target) {
|
for (const char of target) {
|
||||||
if (query[queryIndex] === char) queryIndex++;
|
if (query[queryIndex] === char) queryIndex++;
|
||||||
if (queryIndex === query.length) return true;
|
if (queryIndex === query.length) return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -119,7 +119,7 @@ function ImportFormatSelection({ spaceId, onClose }: ImportFormatSelection) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SimpleGrid cols={2}>
|
<SimpleGrid cols={2}>
|
||||||
<FileButton onChange={handleFileUpload} accept="text/markdown" multiple>
|
<FileButton onChange={handleFileUpload} accept=".md" multiple>
|
||||||
{(props) => (
|
{(props) => (
|
||||||
<Button
|
<Button
|
||||||
justify="start"
|
justify="start"
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export default function SpaceSettingsModal({
|
|||||||
const {data: space, isLoading} = useSpaceQuery(spaceId);
|
const {data: space, isLoading} = useSpaceQuery(spaceId);
|
||||||
|
|
||||||
const spaceRules = space?.membership?.permissions;
|
const spaceRules = space?.membership?.permissions;
|
||||||
const spaceAbility = useMemo(() => useSpaceAbility(spaceRules), [spaceRules]);
|
const spaceAbility = useSpaceAbility(spaceRules);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export function SpaceSidebar() {
|
|||||||
const { data: space, isLoading, isError } = useGetSpaceBySlugQuery(spaceSlug);
|
const { data: space, isLoading, isError } = useGetSpaceBySlugQuery(spaceSlug);
|
||||||
|
|
||||||
const spaceRules = space?.membership?.permissions;
|
const spaceRules = space?.membership?.permissions;
|
||||||
const spaceAbility = useMemo(() => useSpaceAbility(spaceRules), [spaceRules]);
|
const spaceAbility = useSpaceAbility(spaceRules);
|
||||||
|
|
||||||
if (!space) {
|
if (!space) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@ -141,7 +141,6 @@ export function useGetInvitationQuery(
|
|||||||
invitationId: string,
|
invitationId: string,
|
||||||
): UseQueryResult<any, Error> {
|
): UseQueryResult<any, Error> {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
// eslint-disable-next-line @tanstack/query/exhaustive-deps
|
|
||||||
queryKey: ["invitations", invitationId],
|
queryKey: ["invitations", invitationId],
|
||||||
queryFn: () => getInvitationById({ invitationId }),
|
queryFn: () => getInvitationById({ invitationId }),
|
||||||
enabled: !!invitationId,
|
enabled: !!invitationId,
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function Page() {
|
|||||||
const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug);
|
const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug);
|
||||||
|
|
||||||
const spaceRules = space?.membership?.permissions;
|
const spaceRules = space?.membership?.permissions;
|
||||||
const spaceAbility = useMemo(() => useSpaceAbility(spaceRules), [spaceRules]);
|
const spaceAbility = useSpaceAbility(spaceRules);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
project: 'tsconfig.json',
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
|
||||||
extends: [
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
],
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
ignorePatterns: ['.eslintrc.js'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/interface-name-prefix': 'off',
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
34
apps/server/eslint.config.mjs
Normal file
34
apps/server/eslint.config.mjs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import globals from 'globals';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||||
|
|
||||||
|
/** @type {import('eslint').Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
eslintConfigPrettier,
|
||||||
|
{
|
||||||
|
ignores: ['eslint.config.mjs'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.node, ...globals.jest },
|
||||||
|
sourceType: 'module',
|
||||||
|
parser: tseslint.parser,
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
|
'prefer-rest-params': 'off',
|
||||||
|
'no-useless-catch': 'off',
|
||||||
|
'no-useless-escape': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -76,6 +76,7 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.16.0",
|
||||||
"@nestjs/cli": "^10.4.8",
|
"@nestjs/cli": "^10.4.8",
|
||||||
"@nestjs/schematics": "^10.2.3",
|
"@nestjs/schematics": "^10.2.3",
|
||||||
"@nestjs/testing": "^10.4.9",
|
"@nestjs/testing": "^10.4.9",
|
||||||
@ -90,11 +91,9 @@
|
|||||||
"@types/pg": "^8.11.10",
|
"@types/pg": "^8.11.10",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@types/ws": "^8.5.13",
|
"@types/ws": "^8.5.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
||||||
"@typescript-eslint/parser": "^8.16.0",
|
|
||||||
"eslint": "^9.15.0",
|
"eslint": "^9.15.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"globals": "^15.13.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"kysely-codegen": "^0.17.0",
|
"kysely-codegen": "^0.17.0",
|
||||||
"prettier": "^3.4.1",
|
"prettier": "^3.4.1",
|
||||||
@ -105,7 +104,8 @@
|
|||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.17.0"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { Controller, Get } from '@nestjs/common';
|
import { Controller, Get, Param, Res } from '@nestjs/common';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
import { FastifyReply } from "fastify";
|
||||||
|
import { join } from 'path';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
@ -9,4 +12,27 @@ export class AppController {
|
|||||||
getHello(): string {
|
getHello(): string {
|
||||||
return this.appService.getHello();
|
return this.appService.getHello();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('/share/:id')
|
||||||
|
getShare(@Res({ passthrough: false}) res: FastifyReply, @Param() params: any): string {
|
||||||
|
const clientDistPath = join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'client/dist',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fs.existsSync(clientDistPath)) {
|
||||||
|
console.log('exists')
|
||||||
|
const indexFilePath = join(clientDistPath, 'index.html');
|
||||||
|
const stream = fs.createReadStream(indexFilePath);
|
||||||
|
|
||||||
|
console.log(params.id)
|
||||||
|
res.type('text/html').send(stream);
|
||||||
|
console.log('found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('end')
|
||||||
|
return this.appService.getHello();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
const { customAlphabet } = require('fix-esm').require('nanoid');
|
const { customAlphabet } = require('fix-esm').require('nanoid');
|
||||||
|
|
||||||
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
|
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export class AttachmentService {
|
|||||||
// passing attachmentId to allow for updating diagrams
|
// passing attachmentId to allow for updating diagrams
|
||||||
// instead of creating new files for each save
|
// instead of creating new files for each save
|
||||||
if (opts?.attachmentId) {
|
if (opts?.attachmentId) {
|
||||||
let existingAttachment = await this.attachmentRepo.findById(
|
const existingAttachment = await this.attachmentRepo.findById(
|
||||||
opts.attachmentId,
|
opts.attachmentId,
|
||||||
);
|
);
|
||||||
if (!existingAttachment) {
|
if (!existingAttachment) {
|
||||||
|
|||||||
@ -24,7 +24,9 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
accessToken = JSON.parse(req.cookies?.authTokens)?.accessToken;
|
accessToken = JSON.parse(req.cookies?.authTokens)?.accessToken;
|
||||||
} catch {}
|
} catch {
|
||||||
|
throw new BadRequestException('Failed to parse access token');
|
||||||
|
}
|
||||||
|
|
||||||
return accessToken || this.extractTokenFromHeader(req);
|
return accessToken || this.extractTokenFromHeader(req);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,7 +5,8 @@ import { InjectKysely } from 'nestjs-kysely';
|
|||||||
import { KyselyDB } from '@docmost/db/types/kysely.types';
|
import { KyselyDB } from '@docmost/db/types/kysely.types';
|
||||||
import { sql } from 'kysely';
|
import { sql } from 'kysely';
|
||||||
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
const tsquery = require('pg-tsquery')();
|
const tsquery = require('pg-tsquery')();
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@ -33,13 +33,13 @@ export async function executeWithPagination<O, DB, TB extends keyof DB>(
|
|||||||
.select((eb) => eb.ref(deferredJoinPrimaryKey).as('primaryKey'))
|
.select((eb) => eb.ref(deferredJoinPrimaryKey).as('primaryKey'))
|
||||||
.execute()
|
.execute()
|
||||||
// @ts-expect-error TODO: Fix the type here later
|
// @ts-expect-error TODO: Fix the type here later
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
||||||
.then((rows) => rows.map((row) => row.primaryKey));
|
.then((rows) => rows.map((row) => row.primaryKey));
|
||||||
|
|
||||||
qb = qb
|
qb = qb
|
||||||
.where((eb) =>
|
.where((eb) =>
|
||||||
primaryKeys.length > 0
|
primaryKeys.length > 0
|
||||||
? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
?
|
||||||
eb(deferredJoinPrimaryKey, 'in', primaryKeys as any)
|
eb(deferredJoinPrimaryKey, 'in', primaryKeys as any)
|
||||||
: eb(sql`1`, '=', 0),
|
: eb(sql`1`, '=', 0),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -11,14 +11,22 @@ import { plainToInstance } from 'class-transformer';
|
|||||||
export class EnvironmentVariables {
|
export class EnvironmentVariables {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsUrl(
|
@IsUrl(
|
||||||
{ protocols: ['postgres', 'postgresql'], require_tld: false },
|
{
|
||||||
|
protocols: ['postgres', 'postgresql'],
|
||||||
|
require_tld: false,
|
||||||
|
allow_underscores: true,
|
||||||
|
},
|
||||||
{ message: 'DATABASE_URL must be a valid postgres connection string' },
|
{ message: 'DATABASE_URL must be a valid postgres connection string' },
|
||||||
)
|
)
|
||||||
DATABASE_URL: string;
|
DATABASE_URL: string;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsUrl(
|
@IsUrl(
|
||||||
{ protocols: ['redis', 'rediss'], require_tld: false },
|
{
|
||||||
|
protocols: ['redis', 'rediss'],
|
||||||
|
require_tld: false,
|
||||||
|
allow_underscores: true,
|
||||||
|
},
|
||||||
{ message: 'REDIS_URL must be a valid redis connection string' },
|
{ message: 'REDIS_URL must be a valid redis connection string' },
|
||||||
)
|
)
|
||||||
REDIS_URL: string;
|
REDIS_URL: string;
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export class ExportService {
|
|||||||
content: [{ type: 'text', text: getPageTitle(page.title) }],
|
content: [{ type: 'text', text: getPageTitle(page.title) }],
|
||||||
};
|
};
|
||||||
|
|
||||||
let prosemirrorJson: any = getProsemirrorContent(page.content);
|
const prosemirrorJson: any = getProsemirrorContent(page.content);
|
||||||
|
|
||||||
if (page.title) {
|
if (page.title) {
|
||||||
prosemirrorJson.content.unshift(titleNode);
|
prosemirrorJson.content.unshift(titleNode);
|
||||||
|
|||||||
@ -117,7 +117,7 @@ function mathBlock(turndownService: TurndownService) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
replacement: function (content: any, node: HTMLInputElement) {
|
replacement: function (content: any, node: HTMLInputElement) {
|
||||||
return `\n$$${content}$$\n`;
|
return `\n$$\n${content}\n$$\n`;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import { calloutExtension } from './callout.marked';
|
import { calloutExtension } from './callout.marked';
|
||||||
|
import { mathBlockExtension } from './math-block.marked';
|
||||||
|
import { mathInlineExtension } from "./math-inline.marked";
|
||||||
|
|
||||||
marked.use({
|
marked.use({
|
||||||
renderer: {
|
renderer: {
|
||||||
@ -26,7 +28,7 @@ marked.use({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
marked.use({ extensions: [calloutExtension] });
|
marked.use({ extensions: [calloutExtension, mathBlockExtension, mathInlineExtension] });
|
||||||
|
|
||||||
export async function markdownToHtml(markdownInput: string): Promise<string> {
|
export async function markdownToHtml(markdownInput: string): Promise<string> {
|
||||||
const YAML_FONT_MATTER_REGEX = /^\s*---[\s\S]*?---\s*/;
|
const YAML_FONT_MATTER_REGEX = /^\s*---[\s\S]*?---\s*/;
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { Token, marked } from 'marked';
|
||||||
|
|
||||||
|
interface MathBlockToken {
|
||||||
|
type: 'mathBlock';
|
||||||
|
text: string;
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mathBlockExtension = {
|
||||||
|
name: 'mathBlock',
|
||||||
|
level: 'block',
|
||||||
|
start(src: string) {
|
||||||
|
return src.match(/\$\$/)?.index ?? -1;
|
||||||
|
},
|
||||||
|
tokenizer(src: string): MathBlockToken | undefined {
|
||||||
|
const rule = /^\$\$(?!(\$))([\s\S]+?)\$\$/;
|
||||||
|
const match = rule.exec(src);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
type: 'mathBlock',
|
||||||
|
raw: match[0],
|
||||||
|
text: match[2]?.trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token: Token) {
|
||||||
|
const mathBlockToken = token as MathBlockToken;
|
||||||
|
// parse to prevent escaping slashes
|
||||||
|
const latex = marked
|
||||||
|
.parse(mathBlockToken.text)
|
||||||
|
.toString()
|
||||||
|
.replace(/<(\/)?p>/g, '');
|
||||||
|
|
||||||
|
return `<div data-type="${mathBlockToken.type}" data-katex="true">${latex}</div>`;
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { Token, marked } from 'marked';
|
||||||
|
|
||||||
|
interface MathInlineToken {
|
||||||
|
type: 'mathInline';
|
||||||
|
text: string;
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inlineMathRegex = /^\$(?!\s)(.+?)(?<!\s)\$(?!\d)/;
|
||||||
|
|
||||||
|
export const mathInlineExtension = {
|
||||||
|
name: 'mathInline',
|
||||||
|
level: 'inline',
|
||||||
|
start(src: string) {
|
||||||
|
let index: number;
|
||||||
|
let indexSrc = src;
|
||||||
|
|
||||||
|
while (indexSrc) {
|
||||||
|
index = indexSrc.indexOf('$');
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const f = index === 0 || indexSrc.charAt(index - 1) === ' ';
|
||||||
|
if (f) {
|
||||||
|
const possibleKatex = indexSrc.substring(index);
|
||||||
|
if (possibleKatex.match(inlineMathRegex)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indexSrc = indexSrc.substring(index + 1).replace(/^\$+/, '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tokenizer(src: string): MathInlineToken | undefined {
|
||||||
|
const match = inlineMathRegex.exec(src);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
type: 'mathInline',
|
||||||
|
raw: match[0],
|
||||||
|
text: match[1]?.trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token: Token) {
|
||||||
|
const mathInlineToken = token as MathInlineToken;
|
||||||
|
// parse to prevent escaping slashes
|
||||||
|
const latex = marked
|
||||||
|
.parse(mathInlineToken.text)
|
||||||
|
.toString()
|
||||||
|
.replace(/<(\/)?p>/g, '');
|
||||||
|
|
||||||
|
return `<span data-type="${mathInlineToken.type}" data-katex="true">${latex}</span>`;
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -25,7 +25,7 @@ export const mailDriverConfigProvider = {
|
|||||||
const driver = environmentService.getMailDriver().toLocaleLowerCase();
|
const driver = environmentService.getMailDriver().toLocaleLowerCase();
|
||||||
|
|
||||||
switch (driver) {
|
switch (driver) {
|
||||||
case MailOption.SMTP:
|
case MailOption.SMTP: {
|
||||||
let auth = undefined;
|
let auth = undefined;
|
||||||
if (
|
if (
|
||||||
environmentService.getSmtpUsername() &&
|
environmentService.getSmtpUsername() &&
|
||||||
@ -44,9 +44,10 @@ export const mailDriverConfigProvider = {
|
|||||||
connectionTimeout: 30 * 1000, // 30 seconds
|
connectionTimeout: 30 * 1000, // 30 seconds
|
||||||
auth,
|
auth,
|
||||||
secure: environmentService.getSmtpSecure(),
|
secure: environmentService.getSmtpSecure(),
|
||||||
ignoreTLS: environmentService.getSmtpIgnoreTLS()
|
ignoreTLS: environmentService.getSmtpIgnoreTLS(),
|
||||||
} as SMTPTransport.Options,
|
} as SMTPTransport.Options,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
case MailOption.Postmark:
|
case MailOption.Postmark:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const storageDriverConfigProvider = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
case StorageOption.S3:
|
case StorageOption.S3:
|
||||||
const s3Config = {
|
{ const s3Config = {
|
||||||
driver,
|
driver,
|
||||||
config: {
|
config: {
|
||||||
region: environmentService.getAwsS3Region(),
|
region: environmentService.getAwsS3Region(),
|
||||||
@ -68,7 +68,7 @@ export const storageDriverConfigProvider = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return s3Config;
|
return s3Config; }
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown storage driver: ${driver}`);
|
throw new Error(`Unknown storage driver: ${driver}`);
|
||||||
|
|||||||
@ -24,7 +24,7 @@ async function bootstrap() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
app.setGlobalPrefix('api');
|
app.setGlobalPrefix('api', { exclude: ['share/:id']});
|
||||||
|
|
||||||
const redisIoAdapter = new WsRedisIoAdapter(app);
|
const redisIoAdapter = new WsRedisIoAdapter(app);
|
||||||
await redisIoAdapter.connectToRedis();
|
await redisIoAdapter.connectToRedis();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "docmost",
|
"name": "docmost",
|
||||||
"homepage": "https://docmost.com",
|
"homepage": "https://docmost.com",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nx run-many -t build",
|
"build": "nx run-many -t build",
|
||||||
|
|||||||
1141
pnpm-lock.yaml
generated
1141
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user