@underpostnet/underpost 2.97.0 → 2.97.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/README.md +2 -2
- package/baremetal/commission-workflows.json +33 -3
- package/bin/deploy.js +1 -1
- package/cli.md +7 -2
- package/conf.js +3 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/packer/scripts/fuse-tar-root +3 -3
- package/scripts/disk-clean.sh +23 -23
- package/scripts/gpu-diag.sh +2 -2
- package/scripts/ip-info.sh +11 -11
- package/scripts/maas-upload-boot-resource.sh +1 -1
- package/scripts/nvim.sh +1 -1
- package/scripts/packer-setup.sh +13 -13
- package/scripts/rocky-setup.sh +2 -2
- package/scripts/rpmfusion-ffmpeg-setup.sh +4 -4
- package/scripts/ssl.sh +7 -7
- package/src/api/core/core.service.js +0 -5
- package/src/api/default/default.service.js +7 -5
- package/src/api/document/document.model.js +30 -1
- package/src/api/document/document.router.js +6 -0
- package/src/api/document/document.service.js +423 -51
- package/src/api/file/file.model.js +112 -4
- package/src/api/file/file.ref.json +42 -0
- package/src/api/file/file.service.js +380 -32
- package/src/api/user/user.model.js +38 -1
- package/src/api/user/user.router.js +96 -63
- package/src/api/user/user.service.js +81 -48
- package/src/cli/baremetal.js +689 -329
- package/src/cli/cluster.js +50 -52
- package/src/cli/db.js +424 -166
- package/src/cli/deploy.js +1 -1
- package/src/cli/index.js +12 -1
- package/src/cli/lxd.js +3 -3
- package/src/cli/repository.js +1 -1
- package/src/cli/run.js +2 -1
- package/src/cli/ssh.js +10 -10
- package/src/client/components/core/Account.js +327 -36
- package/src/client/components/core/AgGrid.js +3 -0
- package/src/client/components/core/Auth.js +9 -3
- package/src/client/components/core/Chat.js +2 -2
- package/src/client/components/core/Content.js +159 -78
- package/src/client/components/core/Css.js +16 -2
- package/src/client/components/core/CssCore.js +16 -12
- package/src/client/components/core/FileExplorer.js +115 -8
- package/src/client/components/core/Input.js +204 -11
- package/src/client/components/core/LogIn.js +42 -20
- package/src/client/components/core/Modal.js +257 -177
- package/src/client/components/core/Panel.js +324 -27
- package/src/client/components/core/PanelForm.js +280 -73
- package/src/client/components/core/PublicProfile.js +888 -0
- package/src/client/components/core/Router.js +117 -15
- package/src/client/components/core/SearchBox.js +1117 -0
- package/src/client/components/core/SignUp.js +26 -7
- package/src/client/components/core/SocketIo.js +6 -3
- package/src/client/components/core/Translate.js +98 -0
- package/src/client/components/core/Validator.js +15 -0
- package/src/client/components/core/windowGetDimensions.js +6 -6
- package/src/client/components/default/MenuDefault.js +59 -12
- package/src/client/components/default/RoutesDefault.js +1 -0
- package/src/client/services/core/core.service.js +163 -1
- package/src/client/services/default/default.management.js +451 -64
- package/src/client/services/default/default.service.js +13 -6
- package/src/client/services/document/document.service.js +23 -0
- package/src/client/services/file/file.service.js +43 -16
- package/src/client/services/user/user.service.js +13 -9
- package/src/db/DataBaseProvider.js +1 -1
- package/src/db/mongo/MongooseDB.js +1 -1
- package/src/index.js +1 -1
- package/src/mailer/MailerProvider.js +4 -4
- package/src/runtime/express/Express.js +2 -1
- package/src/runtime/lampp/Lampp.js +2 -2
- package/src/server/auth.js +3 -6
- package/src/server/data-query.js +449 -0
- package/src/server/dns.js +4 -4
- package/src/server/object-layer.js +0 -3
- package/src/ws/IoInterface.js +2 -2
|
@@ -6,16 +6,79 @@ import { NotificationManager } from './NotificationManager.js';
|
|
|
6
6
|
import { DocumentService } from '../../services/document/document.service.js';
|
|
7
7
|
import { FileService } from '../../services/file/file.service.js';
|
|
8
8
|
import { getSrcFromFileData } from './Input.js';
|
|
9
|
-
import { imageShimmer, renderCssAttr } from './Css.js';
|
|
9
|
+
import { imageShimmer, renderCssAttr, darkTheme, ThemeEvents, subThemeManager, lightenHex, darkenHex } from './Css.js';
|
|
10
10
|
import { Translate } from './Translate.js';
|
|
11
11
|
import { Modal } from './Modal.js';
|
|
12
12
|
import { closeModalRouteChangeEvents, listenQueryPathInstance, setQueryPath, getQueryParams } from './Router.js';
|
|
13
13
|
import { Scroll } from './Scroll.js';
|
|
14
14
|
import { LoadingAnimation } from './LoadingAnimation.js';
|
|
15
15
|
import { loggerFactory } from './Logger.js';
|
|
16
|
+
import { getApiBaseUrl } from '../../services/core/core.service.js';
|
|
16
17
|
|
|
17
18
|
const logger = loggerFactory(import.meta, { trace: true });
|
|
18
19
|
|
|
20
|
+
function sanitizeFilename(title, options = {}) {
|
|
21
|
+
const { replacement = '-', maxLength = 255, preserveExtension = true } = options;
|
|
22
|
+
|
|
23
|
+
if (typeof title !== 'string' || title.trim() === '') {
|
|
24
|
+
return 'untitled';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 1) Extract extension (optional)
|
|
28
|
+
let name = title;
|
|
29
|
+
let ext = '';
|
|
30
|
+
if (preserveExtension) {
|
|
31
|
+
const match = title.match(/(\.[^.\s]{1,10})$/u);
|
|
32
|
+
if (match) {
|
|
33
|
+
ext = match[1];
|
|
34
|
+
name = title.slice(0, -ext.length);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2) Normalize Unicode and remove diacritics
|
|
39
|
+
name = name.normalize('NFKD').replace(/[\u0300-\u036f]/g, '');
|
|
40
|
+
|
|
41
|
+
// 3) Remove control characters and null bytes
|
|
42
|
+
name = name.replace(/[\x00-\x1f\x7f]/g, '');
|
|
43
|
+
|
|
44
|
+
// 4) Remove forbidden filename characters (Windows / POSIX)
|
|
45
|
+
name = name.replace(/[<>:"/\\|?*\u0000]/g, '');
|
|
46
|
+
|
|
47
|
+
// 5) Collapse whitespace and replace with separator
|
|
48
|
+
name = name.replace(/\s+/g, replacement);
|
|
49
|
+
|
|
50
|
+
// 6) Collapse multiple separators
|
|
51
|
+
const escaped = replacement.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
52
|
+
name = name.replace(new RegExp(`${escaped}{2,}`, 'g'), replacement);
|
|
53
|
+
|
|
54
|
+
// 7) Trim dots and separators from edges
|
|
55
|
+
name = name.replace(new RegExp(`^[\\.${escaped}]+|[\\.${escaped}]+$`, 'g'), '');
|
|
56
|
+
|
|
57
|
+
// 8) Protect against Windows reserved names
|
|
58
|
+
if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(name)) {
|
|
59
|
+
name = '_' + name;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 9) Enforce max length
|
|
63
|
+
const maxNameLength = Math.max(1, maxLength - ext.length);
|
|
64
|
+
if (name.length > maxNameLength) {
|
|
65
|
+
name = name.slice(0, maxNameLength);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 10) Fallback
|
|
69
|
+
if (!name) name = 'untitled';
|
|
70
|
+
|
|
71
|
+
return name + ext;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const userInfoFactory = (userDoc) => ({
|
|
75
|
+
username: userDoc.userId.username,
|
|
76
|
+
email: userDoc.userId.email,
|
|
77
|
+
_id: userDoc.userId._id,
|
|
78
|
+
profileImageId: userDoc.userId.profileImageId,
|
|
79
|
+
briefDescription: userDoc.userId.briefDescription,
|
|
80
|
+
});
|
|
81
|
+
|
|
19
82
|
const PanelForm = {
|
|
20
83
|
Data: {},
|
|
21
84
|
instance: async function (
|
|
@@ -30,11 +93,14 @@ const PanelForm = {
|
|
|
30
93
|
share: {
|
|
31
94
|
copyLink: false,
|
|
32
95
|
},
|
|
96
|
+
showCreatorProfile: false,
|
|
33
97
|
},
|
|
34
98
|
) {
|
|
35
99
|
const { idPanel, defaultUrlImage, Elements } = options;
|
|
36
100
|
|
|
37
|
-
|
|
101
|
+
// Authenticated users don't need 'public' tag - they see all their own posts
|
|
102
|
+
// Only include 'public' for unauthenticated users (handled by backend)
|
|
103
|
+
let prefixTags = [idPanel];
|
|
38
104
|
this.Data[idPanel] = {
|
|
39
105
|
originData: [],
|
|
40
106
|
data: [],
|
|
@@ -112,6 +178,7 @@ const PanelForm = {
|
|
|
112
178
|
route: options.route,
|
|
113
179
|
formContainerClass: 'session-in-log-in',
|
|
114
180
|
share: options.share,
|
|
181
|
+
showCreatorProfile: options.showCreatorProfile,
|
|
115
182
|
onClick: async function ({ payload }) {
|
|
116
183
|
if (options.route) {
|
|
117
184
|
setQueryPath({ path: options.route, queryPath: payload._id });
|
|
@@ -125,29 +192,40 @@ const PanelForm = {
|
|
|
125
192
|
render: imageShimmer(),
|
|
126
193
|
});
|
|
127
194
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
195
|
+
|
|
196
|
+
// Get the filesData for this item
|
|
197
|
+
const filesDataItem = PanelForm.Data[idPanel].filesData.find((f) => f._id === options.data._id);
|
|
198
|
+
|
|
199
|
+
// Priority 1: Check if there's an actual file (not markdown content)
|
|
200
|
+
// fileId array defaults to [null] for batch upload logic
|
|
201
|
+
const fileBlob = filesDataItem?.fileId?.fileBlob;
|
|
202
|
+
if (fileBlob) {
|
|
203
|
+
return await options.fileRender({
|
|
204
|
+
file: fileBlob,
|
|
205
|
+
style: {
|
|
206
|
+
overflow: 'auto',
|
|
207
|
+
width: '100%',
|
|
208
|
+
height: 'auto',
|
|
209
|
+
},
|
|
143
210
|
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Priority 2: If no actual file, show default image
|
|
214
|
+
// (Don't show markdown content in file area - mdFileId stays in content area)
|
|
215
|
+
return await options.htmlRender({
|
|
216
|
+
render: html`
|
|
217
|
+
<img
|
|
218
|
+
class="abs center"
|
|
219
|
+
style="${renderCssAttr({
|
|
220
|
+
style: {
|
|
221
|
+
width: '100px',
|
|
222
|
+
height: '100px',
|
|
223
|
+
opacity: 0.2,
|
|
224
|
+
},
|
|
225
|
+
})}"
|
|
226
|
+
src="${defaultUrlImage}"
|
|
227
|
+
/>
|
|
228
|
+
`,
|
|
151
229
|
});
|
|
152
230
|
},
|
|
153
231
|
on: {
|
|
@@ -322,11 +400,37 @@ const PanelForm = {
|
|
|
322
400
|
return { data: [], status: 'error', message: 'Must provide either content or attach a file' };
|
|
323
401
|
}
|
|
324
402
|
|
|
403
|
+
// Sanitize title for filename - normalize UTF-8 string
|
|
404
|
+
// In browser, strings are already UTF-16, just ensure valid characters
|
|
405
|
+
const sanitizedTitle = sanitizeFilename(data.title);
|
|
406
|
+
|
|
325
407
|
let mdFileId;
|
|
326
|
-
const mdFileName = `${getCapVariableName(
|
|
408
|
+
const mdFileName = `${getCapVariableName(sanitizedTitle)}.md`;
|
|
327
409
|
const location = `${prefixTags.join('/')}`;
|
|
328
|
-
|
|
329
|
-
|
|
410
|
+
|
|
411
|
+
// Only create markdown file if there's actual content
|
|
412
|
+
let md = null;
|
|
413
|
+
let mdBlob = null;
|
|
414
|
+
let mdPlain = null;
|
|
415
|
+
|
|
416
|
+
if (hasMdContent) {
|
|
417
|
+
// Markdown content is already UTF-16 in browser, use as-is
|
|
418
|
+
const blob = new Blob([data.mdFileId], { type: 'text/markdown' });
|
|
419
|
+
md = new File([blob], mdFileName, { type: 'text/markdown' });
|
|
420
|
+
|
|
421
|
+
mdBlob = {
|
|
422
|
+
data: {
|
|
423
|
+
data: await getDataFromInputFile(md),
|
|
424
|
+
},
|
|
425
|
+
mimetype: md.type,
|
|
426
|
+
name: md.name,
|
|
427
|
+
};
|
|
428
|
+
mdPlain = await getRawContentFile(getBlobFromUint8ArrayFile(mdBlob.data.data, mdBlob.mimetype));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Parse and normalize tags
|
|
432
|
+
// Note: 'public' tag is automatically extracted by the backend and converted to isPublic field
|
|
433
|
+
// It will be filtered from the tags array to keep visibility control separate from content tags
|
|
330
434
|
const tags = data.tags
|
|
331
435
|
? uniqueArray(
|
|
332
436
|
data.tags
|
|
@@ -348,18 +452,25 @@ const PanelForm = {
|
|
|
348
452
|
}
|
|
349
453
|
}
|
|
350
454
|
|
|
351
|
-
const mdBlob = {
|
|
352
|
-
data: {
|
|
353
|
-
data: await getDataFromInputFile(md),
|
|
354
|
-
},
|
|
355
|
-
mimetype: md.type,
|
|
356
|
-
name: md.name,
|
|
357
|
-
};
|
|
358
|
-
const mdPlain = await getRawContentFile(getBlobFromUint8ArrayFile(mdBlob.data.data, mdBlob.mimetype));
|
|
359
455
|
const baseNewDoc = newInstance(data);
|
|
360
456
|
baseNewDoc.tags = tags.filter((t) => !prefixTags.includes(t));
|
|
361
|
-
baseNewDoc.mdFileId = marked.parse(data.mdFileId);
|
|
362
|
-
baseNewDoc.userId = Elements.Data.user
|
|
457
|
+
baseNewDoc.mdFileId = hasMdContent ? marked.parse(data.mdFileId) : null;
|
|
458
|
+
baseNewDoc.userId = Elements.Data.user?.main?.model?.user?._id;
|
|
459
|
+
|
|
460
|
+
// Ensure profileImageId is properly formatted as object with _id property
|
|
461
|
+
const profileImageIdValue = Elements.Data.user?.main?.model?.user?.profileImageId;
|
|
462
|
+
const formattedProfileImageId = profileImageIdValue
|
|
463
|
+
? typeof profileImageIdValue === 'string'
|
|
464
|
+
? { _id: profileImageIdValue }
|
|
465
|
+
: profileImageIdValue
|
|
466
|
+
: null;
|
|
467
|
+
|
|
468
|
+
baseNewDoc.userInfo = {
|
|
469
|
+
username: Elements.Data.user?.main?.model?.user?.username,
|
|
470
|
+
email: Elements.Data.user?.main?.model?.user?.email,
|
|
471
|
+
_id: Elements.Data.user?.main?.model?.user?._id,
|
|
472
|
+
profileImageId: formattedProfileImageId,
|
|
473
|
+
};
|
|
363
474
|
baseNewDoc.tools = true;
|
|
364
475
|
|
|
365
476
|
const documents = [];
|
|
@@ -371,32 +482,49 @@ const PanelForm = {
|
|
|
371
482
|
|
|
372
483
|
for (const file of inputFiles) {
|
|
373
484
|
indexFormDoc++;
|
|
374
|
-
let fileId;
|
|
485
|
+
let fileId = undefined; // Reset for each iteration - only set if user uploaded a file
|
|
375
486
|
|
|
376
487
|
await (async () => {
|
|
377
488
|
const body = new FormData();
|
|
378
|
-
|
|
489
|
+
// Only append md file if it was created (has content)
|
|
490
|
+
if (md) body.append('md', md);
|
|
379
491
|
if (file) body.append('file', file);
|
|
380
|
-
const { status, data } = await FileService.post({ body });
|
|
492
|
+
const { status, data: uploadedFiles } = await FileService.post({ body });
|
|
381
493
|
// await timer(3000);
|
|
382
494
|
NotificationManager.Push({
|
|
383
495
|
html: Translate.Render(`${status}-upload-file`),
|
|
384
496
|
status,
|
|
385
497
|
});
|
|
386
|
-
if (status === 'success') {
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
// -
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
498
|
+
if (status === 'success' && uploadedFiles && Array.isArray(uploadedFiles)) {
|
|
499
|
+
// CRITICAL DIFFERENTIATION:
|
|
500
|
+
// - mdFileId: markdown file GENERATED FROM rich text editor content
|
|
501
|
+
// - fileId: file UPLOADED BY USER (could be .md, .pdf, image, etc.)
|
|
502
|
+
//
|
|
503
|
+
// Both can be markdown files, but we must distinguish:
|
|
504
|
+
// Rich text editor content → mdFileId
|
|
505
|
+
// User-uploaded file → fileId
|
|
506
|
+
|
|
507
|
+
for (const uploadedFile of uploadedFiles) {
|
|
508
|
+
if (hasMdContent && uploadedFile.name === mdFileName) {
|
|
509
|
+
// This is the markdown file created FROM rich text editor
|
|
393
510
|
mdFileId = uploadedFile._id;
|
|
394
|
-
|
|
511
|
+
logger.info(`Assigned rich text markdown to mdFileId: ${mdFileName}`);
|
|
512
|
+
} else if (!hasMdContent || uploadedFile.name !== mdFileName) {
|
|
513
|
+
// This is a file uploaded by user (even if it's an .md file)
|
|
395
514
|
fileId = uploadedFile._id;
|
|
515
|
+
logger.info(`Assigned user-uploaded file to fileId: ${uploadedFile.name}`);
|
|
396
516
|
}
|
|
397
517
|
}
|
|
518
|
+
|
|
519
|
+
// Validation: mdFileId should exist only if rich text content was provided
|
|
520
|
+
if (hasMdContent && !mdFileId) {
|
|
521
|
+
logger.error(
|
|
522
|
+
`ERROR: No markdown content file found. Expected: ${mdFileName}, Got: ${uploadedFiles.map((f) => f.name).join(', ')}`,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
398
525
|
}
|
|
399
526
|
})();
|
|
527
|
+
// Backend will automatically extract 'public' from tags and set isPublic field
|
|
400
528
|
const body = {
|
|
401
529
|
location,
|
|
402
530
|
tags,
|
|
@@ -420,6 +548,15 @@ const PanelForm = {
|
|
|
420
548
|
_id: documentData._id,
|
|
421
549
|
id: documentData._id,
|
|
422
550
|
createdAt: documentData.createdAt,
|
|
551
|
+
// Use server response data - backend has already processed tags and isPublic
|
|
552
|
+
isPublic: documentData.isPublic || false,
|
|
553
|
+
tags: (documentData.tags || []).filter((t) => !prefixTags.includes(t)),
|
|
554
|
+
// Ensure userInfo is present for profile header rendering
|
|
555
|
+
userInfo:
|
|
556
|
+
baseNewDoc.userInfo ||
|
|
557
|
+
(documentData.userId && typeof documentData.userId === 'object'
|
|
558
|
+
? userInfoFactory(documentData)
|
|
559
|
+
: null),
|
|
423
560
|
};
|
|
424
561
|
|
|
425
562
|
if (documentStatus === 'error') status = 'error';
|
|
@@ -428,7 +565,7 @@ const PanelForm = {
|
|
|
428
565
|
const filesData = {
|
|
429
566
|
id: documentData._id,
|
|
430
567
|
_id: documentData._id,
|
|
431
|
-
mdFileId: { mdBlob, mdPlain },
|
|
568
|
+
mdFileId: mdBlob && mdPlain ? { mdBlob, mdPlain } : null,
|
|
432
569
|
fileId: {
|
|
433
570
|
fileBlob: file
|
|
434
571
|
? {
|
|
@@ -526,29 +663,50 @@ const PanelForm = {
|
|
|
526
663
|
let mdFileId, fileId;
|
|
527
664
|
let mdBlob, fileBlob;
|
|
528
665
|
let mdPlain, filePlain;
|
|
666
|
+
let parsedMarkdown = '';
|
|
529
667
|
|
|
530
668
|
try {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
669
|
+
// Fetch markdown content if mdFileId exists
|
|
670
|
+
if (documentObject.mdFileId) {
|
|
671
|
+
const mdFileIdValue = documentObject.mdFileId._id || documentObject.mdFileId;
|
|
672
|
+
try {
|
|
673
|
+
// Get markdown content from blob endpoint using FileService
|
|
674
|
+
const { data: blobArray, status } = await FileService.get({ id: `blob/${mdFileIdValue}` });
|
|
675
|
+
if (status === 'success' && blobArray && blobArray[0]) {
|
|
676
|
+
mdPlain = await blobArray[0].text();
|
|
677
|
+
// Parse markdown with proper error handling
|
|
678
|
+
try {
|
|
679
|
+
parsedMarkdown = mdPlain ? marked.parse(mdPlain) : '';
|
|
680
|
+
} catch (parseError) {
|
|
681
|
+
logger.error('Error parsing markdown for document:', documentObject._id, parseError);
|
|
682
|
+
parsedMarkdown = `<p><strong>Error rendering markdown:</strong> ${parseError.message}</p>`;
|
|
683
|
+
}
|
|
684
|
+
} else {
|
|
685
|
+
logger.warn('Failed to fetch markdown blob content');
|
|
686
|
+
parsedMarkdown = '';
|
|
687
|
+
}
|
|
688
|
+
} catch (fetchError) {
|
|
689
|
+
logger.error('Error fetching markdown content:', mdFileIdValue, fetchError);
|
|
690
|
+
parsedMarkdown = '';
|
|
691
|
+
}
|
|
540
692
|
}
|
|
693
|
+
|
|
694
|
+
// Handle optional fileId
|
|
541
695
|
if (documentObject.fileId) {
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
696
|
+
const fileIdValue = documentObject.fileId._id || documentObject.fileId;
|
|
697
|
+
try {
|
|
698
|
+
// Get file metadata for display
|
|
699
|
+
const { data: fileArray } = await FileService.get({ id: fileIdValue });
|
|
700
|
+
if (fileArray && fileArray[0]) {
|
|
701
|
+
fileBlob = fileArray[0];
|
|
702
|
+
fileId = getSrcFromFileData(fileArray[0]);
|
|
703
|
+
}
|
|
704
|
+
} catch (fetchError) {
|
|
705
|
+
logger.error('Error fetching file metadata:', fileIdValue, fetchError);
|
|
706
|
+
}
|
|
550
707
|
}
|
|
551
708
|
|
|
709
|
+
// Store file metadata and references
|
|
552
710
|
panelData.filesData.push({
|
|
553
711
|
id: documentObject._id,
|
|
554
712
|
_id: documentObject._id,
|
|
@@ -556,22 +714,67 @@ const PanelForm = {
|
|
|
556
714
|
fileId: { fileBlob, filePlain },
|
|
557
715
|
});
|
|
558
716
|
|
|
717
|
+
// Add to data array for display - use pre-parsed markdown
|
|
559
718
|
panelData.data.push({
|
|
560
719
|
id: documentObject._id,
|
|
561
720
|
title: documentObject.title,
|
|
562
721
|
createdAt: documentObject.createdAt,
|
|
722
|
+
// Backend filters 'public' tag automatically - it's converted to isPublic field
|
|
563
723
|
tags: documentObject.tags.filter((t) => !prefixTags.includes(t)),
|
|
564
|
-
mdFileId:
|
|
565
|
-
userId:
|
|
724
|
+
mdFileId: parsedMarkdown,
|
|
725
|
+
userId:
|
|
726
|
+
documentObject.userId && typeof documentObject.userId === 'object'
|
|
727
|
+
? documentObject.userId._id
|
|
728
|
+
: documentObject.userId,
|
|
729
|
+
userInfo:
|
|
730
|
+
documentObject.userId && typeof documentObject.userId === 'object'
|
|
731
|
+
? userInfoFactory(documentObject)
|
|
732
|
+
: null,
|
|
566
733
|
fileId,
|
|
567
|
-
tools:
|
|
734
|
+
tools:
|
|
735
|
+
documentObject.userId &&
|
|
736
|
+
typeof documentObject.userId === 'object' &&
|
|
737
|
+
Elements.Data.user?.main?.model?.user?._id &&
|
|
738
|
+
documentObject.userId._id === Elements.Data.user.main.model.user._id,
|
|
568
739
|
_id: documentObject._id,
|
|
569
740
|
totalCopyShareLinkCount: documentObject.totalCopyShareLinkCount || 0,
|
|
741
|
+
isPublic: documentObject.isPublic || false,
|
|
570
742
|
});
|
|
571
743
|
} catch (fileError) {
|
|
572
|
-
logger.error('Error
|
|
744
|
+
logger.error('Error processing files for document:', documentObject._id, fileError);
|
|
573
745
|
// Still add the document to originData even if file fetching fails
|
|
574
|
-
//
|
|
746
|
+
// Add minimal data without file references
|
|
747
|
+
panelData.filesData.push({
|
|
748
|
+
id: documentObject._id,
|
|
749
|
+
_id: documentObject._id,
|
|
750
|
+
mdFileId: { mdBlob: null, mdPlain: '' },
|
|
751
|
+
fileId: { fileBlob: null, filePlain: undefined },
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
panelData.data.push({
|
|
755
|
+
id: documentObject._id,
|
|
756
|
+
title: documentObject.title,
|
|
757
|
+
createdAt: documentObject.createdAt,
|
|
758
|
+
tags: documentObject.tags.filter((t) => !prefixTags.includes(t)),
|
|
759
|
+
mdFileId: '',
|
|
760
|
+
userId:
|
|
761
|
+
documentObject.userId && typeof documentObject.userId === 'object'
|
|
762
|
+
? documentObject.userId._id
|
|
763
|
+
: documentObject.userId,
|
|
764
|
+
userInfo:
|
|
765
|
+
documentObject.userId && typeof documentObject.userId === 'object'
|
|
766
|
+
? userInfoFactory(documentObject)
|
|
767
|
+
: null,
|
|
768
|
+
fileId: null,
|
|
769
|
+
tools:
|
|
770
|
+
documentObject.userId &&
|
|
771
|
+
typeof documentObject.userId === 'object' &&
|
|
772
|
+
Elements.Data.user?.main?.model?.user?._id &&
|
|
773
|
+
documentObject.userId._id === Elements.Data.user.main.model.user._id,
|
|
774
|
+
_id: documentObject._id,
|
|
775
|
+
totalCopyShareLinkCount: documentObject.totalCopyShareLinkCount || 0,
|
|
776
|
+
isPublic: documentObject.isPublic || false,
|
|
777
|
+
});
|
|
575
778
|
}
|
|
576
779
|
}
|
|
577
780
|
|
|
@@ -672,7 +875,9 @@ const PanelForm = {
|
|
|
672
875
|
cid,
|
|
673
876
|
forceUpdate,
|
|
674
877
|
},
|
|
675
|
-
|
|
878
|
+
Elements.Data.user?.main?.model?.user
|
|
879
|
+
? JSON.stringify(Elements.Data.user.main.model.user, null, 4)
|
|
880
|
+
: 'No user data',
|
|
676
881
|
);
|
|
677
882
|
|
|
678
883
|
// Normalize empty values for comparison (undefined, null, '' should all be treated as empty)
|
|
@@ -681,7 +886,9 @@ const PanelForm = {
|
|
|
681
886
|
|
|
682
887
|
if (loadingGetData || (normalizedLastCid === normalizedCid && !forceUpdate)) return;
|
|
683
888
|
loadingGetData = true;
|
|
684
|
-
lastUserId =
|
|
889
|
+
lastUserId = Elements.Data.user?.main?.model?.user?._id
|
|
890
|
+
? newInstance(Elements.Data.user.main.model.user._id)
|
|
891
|
+
: null;
|
|
685
892
|
lastCid = cid;
|
|
686
893
|
|
|
687
894
|
logger.warn('Init render panel data');
|