5htp-core 0.4.9-2 → 0.4.9-5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -1
- package/src/client/assets/css/components/lists.less +3 -3
- package/src/client/components/Form.ts +1 -1
- package/src/client/components/Select/ChoiceElement.tsx +20 -27
- package/src/client/components/Select/index.tsx +24 -42
- package/src/client/components/inputv3/Rte/Editor.tsx +16 -8
- package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +6 -1
- package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +23 -23
- package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +26 -24
- package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +10 -38
- package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +349 -356
- package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +80 -84
- package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +308 -347
- package/src/client/components/inputv3/Rte/style.less +0 -2
- package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +1 -1
- package/src/client/components/inputv3/base.less +0 -6
- package/src/client/components/inputv3/file/index.tsx +50 -48
- package/src/client/components/inputv3/index.tsx +1 -1
- package/src/client/services/router/request/api.ts +29 -13
- package/src/common/router/request/api.ts +6 -4
- package/src/server/services/disks/driver.ts +2 -0
- package/src/server/services/disks/drivers/s3/index.ts +30 -1
- package/src/server/utils/rte.ts +217 -59
- package/src/server/utils/slug.ts +67 -0
- package/src/server/services/schema/rte.ts +0 -110
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import escapeStringRegexp from 'escape-regexp';
|
|
7
|
+
import slugify from 'slugify';
|
|
8
|
+
import { removeStopwords, eng } from 'stopword';
|
|
9
|
+
|
|
10
|
+
// Core
|
|
11
|
+
import type SQL from "@server/services/database";
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- TYPES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- SERVICE
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
export class Slug {
|
|
21
|
+
|
|
22
|
+
public async generate( label: string );
|
|
23
|
+
public async generate( label: string, SQL: SQL, table: string, column: string );
|
|
24
|
+
public async generate( label: string, SQL?: SQL, table?: string, column?: string ) {
|
|
25
|
+
|
|
26
|
+
// Generate slug
|
|
27
|
+
let slug = slugify(label, {
|
|
28
|
+
replacement: '-', // replace spaces with replacement character, defaults to `-`
|
|
29
|
+
remove: undefined, // remove characters that match regex, defaults to `undefined`
|
|
30
|
+
lower: true, // convert to lower case, defaults to `false`
|
|
31
|
+
strict: false, // strip special characters except replacement, defaults to `false`
|
|
32
|
+
locale: 'vi', // language code of the locale to use
|
|
33
|
+
trim: true // trim leading and trailing replacement chars, defaults to `true`
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
slug = removeStopwords( slug.split('-'), eng).join('-');
|
|
37
|
+
|
|
38
|
+
// Check if already existing
|
|
39
|
+
if (SQL !== undefined) {
|
|
40
|
+
|
|
41
|
+
const escapedSlug = escapeStringRegexp(slug);
|
|
42
|
+
|
|
43
|
+
const duplicates = await SQL.selectVal<number>(`
|
|
44
|
+
SELECT
|
|
45
|
+
IF( ${column} LIKE ${SQL.esc(slug)},
|
|
46
|
+
1,
|
|
47
|
+
CAST(SUBSTRING_INDEX(slug, '-', -1) AS UNSIGNED)
|
|
48
|
+
) AS duplicates
|
|
49
|
+
FROM ${table}
|
|
50
|
+
WHERE
|
|
51
|
+
${column} LIKE ${SQL.esc(slug)}
|
|
52
|
+
OR
|
|
53
|
+
${column} REGEXP '^${escapedSlug}-[0-9]+$'
|
|
54
|
+
ORDER BY duplicates DESC
|
|
55
|
+
LIMIT 1
|
|
56
|
+
`);
|
|
57
|
+
|
|
58
|
+
if (duplicates && duplicates > 0)
|
|
59
|
+
slug += `-${duplicates + 1}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return slug;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default new Slug;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Node
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import md5 from 'md5';
|
|
8
|
-
import { fromBuffer } from 'file-type';
|
|
9
|
-
|
|
10
|
-
// Npm
|
|
11
|
-
import type { SerializedEditorState, SerializedLexicalNode } from 'lexical';
|
|
12
|
-
|
|
13
|
-
// Core
|
|
14
|
-
import type Driver from '@server/services/disks/driver';
|
|
15
|
-
|
|
16
|
-
type SerializedLexicalNodeWithSrc = SerializedLexicalNode & { src: string };
|
|
17
|
-
|
|
18
|
-
/*----------------------------------
|
|
19
|
-
- METHODS
|
|
20
|
-
----------------------------------*/
|
|
21
|
-
const uploadAttachments = async (
|
|
22
|
-
value: SerializedEditorState,
|
|
23
|
-
disk: Driver,
|
|
24
|
-
destination: string,
|
|
25
|
-
oldState?: SerializedEditorState
|
|
26
|
-
) => {
|
|
27
|
-
|
|
28
|
-
const usedFilesUrl: string[] = [];
|
|
29
|
-
|
|
30
|
-
// Deep check for images / files
|
|
31
|
-
const findFiles = async (
|
|
32
|
-
node: SerializedLexicalNode,
|
|
33
|
-
callback: (node: SerializedLexicalNodeWithSrc) => Promise<void>
|
|
34
|
-
) => {
|
|
35
|
-
|
|
36
|
-
// Attachment
|
|
37
|
-
if (node.type === 'image' || node.type === 'file') {
|
|
38
|
-
if (typeof node.src === 'string') {
|
|
39
|
-
await callback(node as SerializedLexicalNodeWithSrc);
|
|
40
|
-
}
|
|
41
|
-
// Recursion
|
|
42
|
-
} else if (node.children) {
|
|
43
|
-
for (const child of node.children) {
|
|
44
|
-
await findFiles(child, callback);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const uploadFile = async (node: SerializedLexicalNodeWithSrc) => {
|
|
50
|
-
|
|
51
|
-
// Already uploaded
|
|
52
|
-
if (!node.src.startsWith('data:')) {
|
|
53
|
-
usedFilesUrl.push(node.src);
|
|
54
|
-
return node.src;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Transform into buffer
|
|
58
|
-
node.src = node.src.replace(/^data:\w+\/\w+;base64,/, '');
|
|
59
|
-
const fileData = Buffer.from(node.src, 'base64');
|
|
60
|
-
|
|
61
|
-
// Parse file type from buffer
|
|
62
|
-
const fileType = await fromBuffer(fileData);
|
|
63
|
-
if (!fileType)
|
|
64
|
-
throw new Error('Failed to detect file type');
|
|
65
|
-
|
|
66
|
-
// Upload file to disk
|
|
67
|
-
const fileName = md5(node.src) + '.' + fileType.ext;
|
|
68
|
-
const filePath = path.join(destination, fileName);
|
|
69
|
-
const upoadedFile = await disk.outputFile('data', filePath, fileData, {
|
|
70
|
-
encoding: 'binary'
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Replace node.src with url
|
|
74
|
-
node.src = upoadedFile.path;
|
|
75
|
-
usedFilesUrl.push(node.src);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const deleteUnusedFile = async (node: SerializedLexicalNodeWithSrc) => {
|
|
79
|
-
|
|
80
|
-
// Should be a URL
|
|
81
|
-
if (node.src.startsWith('data:') || !node.src.startsWith('http'))
|
|
82
|
-
return;
|
|
83
|
-
|
|
84
|
-
// This file is used
|
|
85
|
-
if (usedFilesUrl.includes(node.src))
|
|
86
|
-
return;
|
|
87
|
-
|
|
88
|
-
// Extract file name
|
|
89
|
-
const fileName = path.basename(node.src);
|
|
90
|
-
const filePath = path.join(destination, fileName);
|
|
91
|
-
|
|
92
|
-
// Delete file from disk
|
|
93
|
-
await disk.delete('data', filePath);
|
|
94
|
-
console.log('Deleted file:', filePath);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Find files in the editor state
|
|
98
|
-
for (const child of value.root.children) {
|
|
99
|
-
await findFiles(child, uploadFile);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Old state given: remove unused attachments
|
|
103
|
-
if (oldState !== undefined) {
|
|
104
|
-
for (const child of oldState.root.children) {
|
|
105
|
-
await findFiles(child, deleteUnusedFile);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export default { uploadAttachments }
|