mirror of
https://github.com/docmost/docmost.git
synced 2026-05-09 07:43:06 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b99e3d67f5 | |||
| 184811911f | |||
| 980521f957 | |||
| fe44dc92a9 | |||
| fad410ef23 | |||
| 15b8908b1a | |||
| 8e15b22d8c | |||
| ec83fc82d5 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.80.0",
|
"version": "0.80.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
"emoji-mart": "^5.6.0",
|
"emoji-mart": "^5.6.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"highlightjs-sap-abap": "^0.3.0",
|
"highlightjs-sap-abap": "^0.3.0",
|
||||||
"i18next": "^25.10.1",
|
"i18next": "25.10.1",
|
||||||
"i18next-http-backend": "^3.0.2",
|
"i18next-http-backend": "3.0.6",
|
||||||
"jotai": "^2.18.1",
|
"jotai": "^2.18.1",
|
||||||
"jotai-optics": "^0.4.0",
|
"jotai-optics": "^0.4.0",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
"mantine-form-zod-resolver": "^1.3.0",
|
"mantine-form-zod-resolver": "^1.3.0",
|
||||||
"mermaid": "^11.13.0",
|
"mermaid": "^11.13.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"posthog-js": "1.370.0",
|
"posthog-js": "1.372.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-arborist": "3.4.0",
|
"react-arborist": "3.4.0",
|
||||||
"react-clear-modal": "^2.0.18",
|
"react-clear-modal": "^2.0.18",
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
"react-drawio": "^1.0.7",
|
"react-drawio": "^1.0.7",
|
||||||
"react-error-boundary": "^6.1.1",
|
"react-error-boundary": "^6.1.1",
|
||||||
"react-helmet-async": "^3.0.0",
|
"react-helmet-async": "^3.0.0",
|
||||||
"react-i18next": "^16.5.8",
|
"react-i18next": "16.5.8",
|
||||||
"react-router-dom": "^7.13.1",
|
"react-router-dom": "^7.13.1",
|
||||||
"semver": "^7.7.4",
|
"semver": "^7.7.4",
|
||||||
"socket.io-client": "^4.8.3",
|
"socket.io-client": "^4.8.3",
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
"eslint-plugin-react-refresh": "^0.5.2",
|
||||||
"globals": "^15.13.0",
|
"globals": "^15.13.0",
|
||||||
"optics-ts": "^2.4.1",
|
"optics-ts": "^2.4.1",
|
||||||
"postcss": "^8.5.8",
|
"postcss": "^8.5.12",
|
||||||
"postcss-preset-mantine": "^1.18.0",
|
"postcss-preset-mantine": "^1.18.0",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.80.0",
|
"version": "0.80.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -33,9 +33,9 @@
|
|||||||
"@ai-sdk/google": "^3.0.52",
|
"@ai-sdk/google": "^3.0.52",
|
||||||
"@ai-sdk/openai": "^3.0.47",
|
"@ai-sdk/openai": "^3.0.47",
|
||||||
"@ai-sdk/openai-compatible": "^2.0.37",
|
"@ai-sdk/openai-compatible": "^2.0.37",
|
||||||
"@aws-sdk/client-s3": "3.1014.0",
|
"@aws-sdk/client-s3": "3.1041.0",
|
||||||
"@aws-sdk/lib-storage": "3.1014.0",
|
"@aws-sdk/lib-storage": "3.1041.0",
|
||||||
"@aws-sdk/s3-request-presigner": "3.1014.0",
|
"@aws-sdk/s3-request-presigner": "3.1041.0",
|
||||||
"@clickhouse/client": "^1.18.2",
|
"@clickhouse/client": "^1.18.2",
|
||||||
"@fastify/cookie": "^11.0.2",
|
"@fastify/cookie": "^11.0.2",
|
||||||
"@fastify/multipart": "^10.0.0",
|
"@fastify/multipart": "^10.0.0",
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"sanitize-filename-ts": "1.0.2",
|
"sanitize-filename": "1.6.3",
|
||||||
"socket.io": "^4.8.3",
|
"socket.io": "^4.8.3",
|
||||||
"stripe": "^17.7.0",
|
"stripe": "^17.7.0",
|
||||||
"tlds": "^1.261.0",
|
"tlds": "^1.261.0",
|
||||||
@@ -166,6 +166,9 @@
|
|||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
},
|
},
|
||||||
|
"transformIgnorePatterns": [
|
||||||
|
"/node_modules/(?!(\\.pnpm/)?(nanoid|uuid|image-dimensions|marked)(@|/))"
|
||||||
|
],
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
"**/*.(t|j)s"
|
"**/*.(t|j)s"
|
||||||
],
|
],
|
||||||
|
|||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
import { sanitize } from 'sanitize-filename-ts';
|
import sanitize = require('sanitize-filename');
|
||||||
import { FastifyRequest } from 'fastify';
|
import { FastifyRequest } from 'fastify';
|
||||||
import { Readable, Transform } from 'stream';
|
import { Readable, Transform } from 'stream';
|
||||||
|
|
||||||
@@ -72,11 +72,33 @@ export function extractDateFromUuid7(uuid7: string) {
|
|||||||
return new Date(timestamp);
|
return new Date(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeFileName(fileName: string): string {
|
export type SanitizeFileNameOptions = {
|
||||||
const sanitizedFilename = sanitize(fileName)
|
/** Keep spaces and `#` instead of replacing them with `_`. Useful for
|
||||||
.replace(/ /g, '_')
|
* download filenames where readability matters. Defaults to false. */
|
||||||
.replace(/#/g, '_');
|
preserveSpaces?: boolean;
|
||||||
return sanitizedFilename.slice(0, 255);
|
};
|
||||||
|
|
||||||
|
export function sanitizeFileName(
|
||||||
|
fileName: string,
|
||||||
|
options: SanitizeFileNameOptions = {},
|
||||||
|
): string {
|
||||||
|
// Decode percent-encoded sequences so that bypasses like "..%2F" reach
|
||||||
|
// sanitize() as literal "../" and get stripped. sanitize-filename only
|
||||||
|
// strips literal characters and won't catch encoded path separators
|
||||||
|
// on its own.
|
||||||
|
const decoded = fileName.replace(/%[0-9a-fA-F]{2}/g, (m) => {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(m);
|
||||||
|
} catch {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sanitized = sanitize(decoded);
|
||||||
|
if (options.preserveSpaces) {
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
return sanitized.replace(/ /g, '_').replace(/#/g, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeAccent(str: string): string {
|
export function removeAccent(str: string): string {
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ import { EnvironmentService } from '../../integrations/environment/environment.s
|
|||||||
import { TokenService } from '../auth/services/token.service';
|
import { TokenService } from '../auth/services/token.service';
|
||||||
import { JwtAttachmentPayload, JwtType } from '../auth/dto/jwt-payload';
|
import { JwtAttachmentPayload, JwtType } from '../auth/dto/jwt-payload';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { sanitize } from 'sanitize-filename-ts';
|
|
||||||
import { AttachmentInfoDto, RemoveIconDto } from './dto/attachment.dto';
|
import { AttachmentInfoDto, RemoveIconDto } from './dto/attachment.dto';
|
||||||
import { PageAccessService } from '../page/page-access/page-access.service';
|
import { PageAccessService } from '../page/page-access/page-access.service';
|
||||||
import { AuditEvent, AuditResource } from '../../common/events/audit-events';
|
import { AuditEvent, AuditResource } from '../../common/events/audit-events';
|
||||||
@@ -357,13 +356,19 @@ export class AttachmentController {
|
|||||||
throw new BadRequestException('Invalid image attachment type');
|
throw new BadRequestException('Invalid image attachment type');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fileName || sanitize(fileName) !== fileName) {
|
if (!fileName) {
|
||||||
throw new BadRequestException('Invalid file name');
|
throw new BadRequestException('Invalid file name');
|
||||||
}
|
}
|
||||||
|
|
||||||
const filenameWithoutExt = path.basename(fileName, path.extname(fileName));
|
const ext = path.extname(fileName);
|
||||||
if (!isValidUUID(filenameWithoutExt)) {
|
const filenameWithoutExt = path.basename(fileName, ext);
|
||||||
throw new BadRequestException('Invalid file id');
|
|
||||||
|
if (
|
||||||
|
!ext ||
|
||||||
|
!isValidUUID(filenameWithoutExt) ||
|
||||||
|
`${filenameWithoutExt}${ext}` !== fileName
|
||||||
|
) {
|
||||||
|
throw new BadRequestException('Invalid file name');
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = `${getAttachmentFolderPath(attachmentType, workspace.id)}/${fileName}`;
|
const filePath = `${getAttachmentFolderPath(attachmentType, workspace.id)}/${fileName}`;
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: e703b8bf47...4101fc427b
@@ -112,7 +112,10 @@ export class EnvironmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAwsS3ForcePathStyle(): boolean {
|
getAwsS3ForcePathStyle(): boolean {
|
||||||
return this.configService.get<boolean>('AWS_S3_FORCE_PATH_STYLE');
|
const forcePathStyle = this.configService
|
||||||
|
.get<string>('AWS_S3_FORCE_PATH_STYLE', 'false')
|
||||||
|
.toLowerCase();
|
||||||
|
return forcePathStyle === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
getAwsS3Url(): string {
|
getAwsS3Url(): string {
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ import {
|
|||||||
SpaceCaslSubject,
|
SpaceCaslSubject,
|
||||||
} from '../../core/casl/interfaces/space-ability.type';
|
} from '../../core/casl/interfaces/space-ability.type';
|
||||||
import { FastifyReply } from 'fastify';
|
import { FastifyReply } from 'fastify';
|
||||||
import { sanitize } from 'sanitize-filename-ts';
|
|
||||||
import { getExportExtension } from './utils';
|
import { getExportExtension } from './utils';
|
||||||
import { getMimeType, getPageTitle } from '../../common/helpers';
|
import {
|
||||||
|
getMimeType,
|
||||||
|
getPageTitle,
|
||||||
|
sanitizeFileName,
|
||||||
|
} from '../../common/helpers';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { AuditEvent, AuditResource } from '../../common/events/audit-events';
|
import { AuditEvent, AuditResource } from '../../common/events/audit-events';
|
||||||
import {
|
import {
|
||||||
@@ -85,7 +88,9 @@ export class ExportController {
|
|||||||
|
|
||||||
if (result.type === 'file') {
|
if (result.type === 'file') {
|
||||||
const ext = getExportExtension(dto.format);
|
const ext = getExportExtension(dto.format);
|
||||||
const fileName = sanitize(page.title || 'untitled') + ext;
|
const fileName =
|
||||||
|
sanitizeFileName(page.title || 'untitled', { preserveSpaces: true }) +
|
||||||
|
ext;
|
||||||
const contentType = getMimeType(path.extname(fileName));
|
const contentType = getMimeType(path.extname(fileName));
|
||||||
|
|
||||||
res.headers({
|
res.headers({
|
||||||
@@ -96,7 +101,9 @@ export class ExportController {
|
|||||||
|
|
||||||
res.send(result.content);
|
res.send(result.content);
|
||||||
} else {
|
} else {
|
||||||
const fileName = sanitize(page.title || 'untitled') + '.zip';
|
const fileName =
|
||||||
|
sanitizeFileName(page.title || 'untitled', { preserveSpaces: true }) +
|
||||||
|
'.zip';
|
||||||
|
|
||||||
res.headers({
|
res.headers({
|
||||||
'Content-Type': 'application/zip',
|
'Content-Type': 'application/zip',
|
||||||
@@ -144,7 +151,9 @@ export class ExportController {
|
|||||||
'Content-Type': 'application/zip',
|
'Content-Type': 'application/zip',
|
||||||
'Content-Disposition':
|
'Content-Disposition':
|
||||||
'attachment; filename="' +
|
'attachment; filename="' +
|
||||||
encodeURIComponent(sanitize(exportFile.fileName)) +
|
encodeURIComponent(
|
||||||
|
sanitizeFileName(exportFile.fileName, { preserveSpaces: true }),
|
||||||
|
) +
|
||||||
'"',
|
'"',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
|
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
|
||||||
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||||
import { MultipartFile } from '@fastify/multipart';
|
import { MultipartFile } from '@fastify/multipart';
|
||||||
import { sanitize } from 'sanitize-filename-ts';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {
|
import {
|
||||||
htmlToJson,
|
htmlToJson,
|
||||||
@@ -53,8 +52,8 @@ export class ImportService {
|
|||||||
const file = await filePromise;
|
const file = await filePromise;
|
||||||
const fileBuffer = await file.toBuffer();
|
const fileBuffer = await file.toBuffer();
|
||||||
const fileExtension = path.extname(file.filename).toLowerCase();
|
const fileExtension = path.extname(file.filename).toLowerCase();
|
||||||
const fileName = sanitize(
|
const fileName = sanitizeFileName(
|
||||||
path.basename(file.filename, fileExtension).slice(0, 255),
|
path.basename(file.filename, fileExtension),
|
||||||
);
|
);
|
||||||
const fileContent = fileBuffer.toString();
|
const fileContent = fileBuffer.toString();
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "docmost",
|
"name": "docmost",
|
||||||
"homepage": "https://docmost.com",
|
"homepage": "https://docmost.com",
|
||||||
"version": "0.80.0",
|
"version": "0.80.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nx run-many -t build",
|
"build": "nx run-many -t build",
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"rfc6902": "5.2.0",
|
"rfc6902": "5.2.0",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^14.0.0",
|
||||||
"y-indexeddb": "^9.0.12",
|
"y-indexeddb": "^9.0.12",
|
||||||
"y-prosemirror": "1.3.7",
|
"y-prosemirror": "1.3.7",
|
||||||
"yjs": "^13.6.30"
|
"yjs": "^13.6.30"
|
||||||
@@ -128,11 +128,12 @@
|
|||||||
"yaml@>=2.0.0 <2.8.3": "2.8.3",
|
"yaml@>=2.0.0 <2.8.3": "2.8.3",
|
||||||
"path-to-regexp@^8": "8.4.0",
|
"path-to-regexp@^8": "8.4.0",
|
||||||
"brace-expansion@^5": "5.0.5",
|
"brace-expansion@^5": "5.0.5",
|
||||||
"@xmldom/xmldom": "0.8.12",
|
"@xmldom/xmldom": "0.8.13",
|
||||||
"handlebars": "4.7.9",
|
"handlebars": "4.7.9",
|
||||||
"axios": "1.15.0",
|
"axios": "1.15.0",
|
||||||
"langsmith": "0.5.19",
|
"langsmith": "0.5.19",
|
||||||
"follow-redirects": "1.16.0"
|
"follow-redirects": "1.16.0",
|
||||||
|
"protobufjs": "7.5.5"
|
||||||
},
|
},
|
||||||
"neverBuiltDependencies": []
|
"neverBuiltDependencies": []
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+661
-656
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user