memi-board 0.31.0 → 0.32.0
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 -1
- package/dist/index.js +286 -269
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/Editor.vue +203 -13
- package/dist/runtime/components/Settings.vue +20 -3
- package/dist/utils/deletePostCascade.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -394,8 +394,10 @@ function qe(e) {
|
|
|
394
394
|
//#region src/utils/storagePath.ts
|
|
395
395
|
function Je(e) {
|
|
396
396
|
try {
|
|
397
|
-
let t = e.
|
|
398
|
-
|
|
397
|
+
let t = new URL(e), n = t.hostname.toLowerCase();
|
|
398
|
+
if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
|
|
399
|
+
let r = t.pathname.match(/\/o\/([^?]+)/);
|
|
400
|
+
return r?.[1] ? decodeURIComponent(r[1]) : null;
|
|
399
401
|
} catch {
|
|
400
402
|
return null;
|
|
401
403
|
}
|
|
@@ -404,46 +406,86 @@ function Ye(e) {
|
|
|
404
406
|
return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
|
|
405
407
|
}
|
|
406
408
|
//#endregion
|
|
409
|
+
//#region src/utils/deletePostCascade.ts
|
|
410
|
+
async function Xe(e) {
|
|
411
|
+
let t = await _e(e);
|
|
412
|
+
await Promise.all([...t.items.map((e) => he(e)), ...t.prefixes.map((e) => Xe(e))]);
|
|
413
|
+
}
|
|
414
|
+
async function Ze(e, t) {
|
|
415
|
+
let n = [...new Set([...t].filter(Boolean))];
|
|
416
|
+
await Promise.all(n.map((t) => he(ve(e, t)).catch(() => {})));
|
|
417
|
+
}
|
|
418
|
+
async function Qe(t, n, r, a) {
|
|
419
|
+
let s = i(t, r, a), c = E(t, r, a), [l, u] = await Promise.all([R(s), R(c)]);
|
|
420
|
+
if (!l.exists()) return;
|
|
421
|
+
let d = l.data(), f = u.exists() ? String(u.data().content ?? "") : "", p = await z(W(o(t, r), Y("postId", "==", a)));
|
|
422
|
+
for (let e = 0; e < p.docs.length; e += 450) {
|
|
423
|
+
let n = X(t);
|
|
424
|
+
p.docs.slice(e, e + 450).forEach((e) => n.delete(e.ref)), await n.commit();
|
|
425
|
+
}
|
|
426
|
+
let m = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set();
|
|
427
|
+
for (let e of d.attachments ?? []) {
|
|
428
|
+
if (!e?.path) continue;
|
|
429
|
+
m.add(e.path);
|
|
430
|
+
let t = Ye(e.path);
|
|
431
|
+
t && t !== a && h.add(t);
|
|
432
|
+
}
|
|
433
|
+
for (let e of qe(f)) {
|
|
434
|
+
let t = Je(e);
|
|
435
|
+
if (!t) continue;
|
|
436
|
+
m.add(t);
|
|
437
|
+
let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
438
|
+
n !== t && n.includes("/images/thumbnails/") && m.add(n);
|
|
439
|
+
let r = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
440
|
+
r && !t.includes("/thumbnails/") && m.add(`${r[1]}thumbnails/${r[2]}.jpg`);
|
|
441
|
+
let i = Ye(t);
|
|
442
|
+
i && i !== a && h.add(i);
|
|
443
|
+
}
|
|
444
|
+
await P(c), await Xe(ve(n, e(r, a)));
|
|
445
|
+
for (let t of h) await Xe(ve(n, e(r, t)));
|
|
446
|
+
await Ze(n, m), await P(s);
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
407
449
|
//#region src/utils/postBody.ts
|
|
408
|
-
function
|
|
450
|
+
function $e(e) {
|
|
409
451
|
return String(e || "").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<img\b[^>]*>/gi, " ").replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, " ").replace(/<video\b[\s\S]*?<\/video>/gi, " ").replace(/<audio\b[\s\S]*?<\/audio>/gi, " ").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/ /g, " ").replace(/&[a-z]+;/gi, " ").replace(/&#\d+;/g, " ").replace(/[\u200B-\u200D\uFEFF\u00AD]/g, "").replace(/\s+/g, " ").trim();
|
|
410
452
|
}
|
|
411
|
-
function
|
|
412
|
-
let n =
|
|
453
|
+
function et(e, t = 40) {
|
|
454
|
+
let n = $e(e);
|
|
413
455
|
if (!n) return "";
|
|
414
456
|
if (n.length <= t) return n;
|
|
415
457
|
let r = n.slice(0, t), i = r.lastIndexOf(" ");
|
|
416
458
|
return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
|
|
417
459
|
}
|
|
418
|
-
function
|
|
419
|
-
return
|
|
460
|
+
function tt(e) {
|
|
461
|
+
return $e(e).length > 0;
|
|
420
462
|
}
|
|
421
|
-
function
|
|
463
|
+
function nt(e, t) {
|
|
422
464
|
return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
|
|
423
465
|
}
|
|
424
|
-
function
|
|
425
|
-
return !
|
|
466
|
+
function rt(e) {
|
|
467
|
+
return !tt(e);
|
|
426
468
|
}
|
|
427
469
|
//#endregion
|
|
428
470
|
//#region src/utils/postPreview.ts
|
|
429
|
-
function
|
|
471
|
+
function it(e) {
|
|
430
472
|
return e.replace(/&/g, "&").replace(/"/g, "\"").replace(/'/g, "'");
|
|
431
473
|
}
|
|
432
|
-
function
|
|
474
|
+
function at(e) {
|
|
433
475
|
if (e) try {
|
|
434
|
-
let t = new URL(
|
|
476
|
+
let t = new URL(it(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
|
|
435
477
|
if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
|
|
436
478
|
if (n.endsWith("youtube.com") || n === "youtube-nocookie.com") return t.pathname.startsWith("/embed/") || t.pathname.startsWith("/shorts/") ? t.pathname.split("/")[2] : t.searchParams.get("v") || void 0;
|
|
437
479
|
} catch {
|
|
438
480
|
return;
|
|
439
481
|
}
|
|
440
482
|
}
|
|
441
|
-
function
|
|
442
|
-
let t =
|
|
483
|
+
function ot(e) {
|
|
484
|
+
let t = at(e.videoUrl);
|
|
443
485
|
return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
|
|
444
486
|
}
|
|
445
|
-
function
|
|
446
|
-
let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ?
|
|
487
|
+
function st(e, t = []) {
|
|
488
|
+
let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ? it(r[1]) : a, s = o ? at(o) : void 0, c = n ? it(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
|
|
447
489
|
return {
|
|
448
490
|
summary: e.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|blockquote)>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/[^\S\n]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{2,}/g, "\n").trim().slice(0, 160),
|
|
449
491
|
...c ? { previewImage: c } : {},
|
|
@@ -452,7 +494,7 @@ function it(e, t = []) {
|
|
|
452
494
|
}
|
|
453
495
|
//#endregion
|
|
454
496
|
//#region src/composables/useMemiBoardSettings.ts
|
|
455
|
-
var
|
|
497
|
+
var ct = [
|
|
456
498
|
{
|
|
457
499
|
id: "free",
|
|
458
500
|
label: "자유",
|
|
@@ -480,45 +522,45 @@ var at = [
|
|
|
480
522
|
visibility: "public",
|
|
481
523
|
order: 2
|
|
482
524
|
}
|
|
483
|
-
],
|
|
484
|
-
function
|
|
525
|
+
], lt = ct;
|
|
526
|
+
function ut(e) {
|
|
485
527
|
return e === "hidden" ? "hidden" : "public";
|
|
486
528
|
}
|
|
487
|
-
function
|
|
529
|
+
function dt(e, t, n) {
|
|
488
530
|
return {
|
|
489
531
|
id: e,
|
|
490
532
|
label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
|
|
491
533
|
order: typeof t.order == "number" ? t.order : n,
|
|
492
534
|
description: (typeof t.description == "string" ? t.description : "") || "",
|
|
493
|
-
visibility:
|
|
535
|
+
visibility: ut(t.visibility),
|
|
494
536
|
listView: t.listView ?? "default",
|
|
495
537
|
writeRole: t.writeRole ?? "user",
|
|
496
538
|
commentWriteRole: t.commentWriteRole ?? "user",
|
|
497
539
|
allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : []
|
|
498
540
|
};
|
|
499
541
|
}
|
|
500
|
-
function
|
|
501
|
-
let e = () => m(), r = Z(), { isSignedIn:
|
|
502
|
-
function
|
|
503
|
-
if (e) return
|
|
542
|
+
function ft() {
|
|
543
|
+
let e = () => m(), r = Z(), i = oe(), { isSignedIn: a } = Ke(), o = O(() => W(n(r, e()), U("order", "asc"))), { data: s, pending: c } = re(o, { ssrKey: d(e(), "boards") }), l = O(() => s.value.map((e, t) => dt(e.id, e, t))), u = l, f = O(() => l.value.filter((e) => e.visibility !== "hidden")), p = f;
|
|
544
|
+
function h(e) {
|
|
545
|
+
if (e) return l.value.find((t) => t.id === e)?.label ?? e;
|
|
504
546
|
}
|
|
505
|
-
let
|
|
506
|
-
function
|
|
507
|
-
if (e) return
|
|
547
|
+
let g = h;
|
|
548
|
+
function _(e) {
|
|
549
|
+
if (e) return l.value.find((t) => t.id === e)?.description?.trim() || void 0;
|
|
508
550
|
}
|
|
509
|
-
let
|
|
510
|
-
function
|
|
511
|
-
return e ?
|
|
551
|
+
let y = _;
|
|
552
|
+
function b(e) {
|
|
553
|
+
return e ? l.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
|
|
512
554
|
}
|
|
513
|
-
let
|
|
514
|
-
function
|
|
515
|
-
return
|
|
555
|
+
let x = b;
|
|
556
|
+
function S(e) {
|
|
557
|
+
return b(e) === "hidden";
|
|
516
558
|
}
|
|
517
|
-
let
|
|
518
|
-
function
|
|
519
|
-
if (e) return
|
|
559
|
+
let C = S;
|
|
560
|
+
function w(e) {
|
|
561
|
+
if (e) return l.value.find((t) => t.id === e);
|
|
520
562
|
}
|
|
521
|
-
async function
|
|
563
|
+
async function E(t, n) {
|
|
522
564
|
let i;
|
|
523
565
|
for (;;) {
|
|
524
566
|
let a = await z(W(T(r, e()), Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
|
|
@@ -528,11 +570,11 @@ function lt() {
|
|
|
528
570
|
if (await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
|
|
529
571
|
}
|
|
530
572
|
}
|
|
531
|
-
function
|
|
573
|
+
function D(e, t) {
|
|
532
574
|
return {
|
|
533
575
|
label: e.label.trim(),
|
|
534
576
|
description: (e.description ?? "").trim(),
|
|
535
|
-
visibility:
|
|
577
|
+
visibility: ut(e.visibility),
|
|
536
578
|
listView: e.listView ?? "default",
|
|
537
579
|
writeRole: e.writeRole ?? "user",
|
|
538
580
|
commentWriteRole: e.commentWriteRole ?? "user",
|
|
@@ -541,124 +583,120 @@ function lt() {
|
|
|
541
583
|
updatedAt: G()
|
|
542
584
|
};
|
|
543
585
|
}
|
|
544
|
-
async function
|
|
545
|
-
if (!
|
|
586
|
+
async function k(n, i = n.order ?? 0) {
|
|
587
|
+
if (!a.value) throw Error("로그인이 필요합니다.");
|
|
546
588
|
let o = n.id.trim(), s = n.label.trim();
|
|
547
589
|
if (!o || !s) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
548
|
-
let
|
|
590
|
+
let c = ut(n.visibility), u = l.value.find((e) => e.id === o)?.visibility ?? "public", d = D({
|
|
549
591
|
...n,
|
|
550
592
|
label: s,
|
|
551
|
-
visibility:
|
|
552
|
-
},
|
|
553
|
-
await K(t(r, e(), o), d, { merge: !0 }), (u !==
|
|
554
|
-
}
|
|
555
|
-
let
|
|
556
|
-
async function
|
|
557
|
-
if (!
|
|
558
|
-
let
|
|
559
|
-
n.forEach((n,
|
|
593
|
+
visibility: c
|
|
594
|
+
}, i);
|
|
595
|
+
await K(t(r, e(), o), d, { merge: !0 }), (u !== c || c === "public") && await E(o, c !== "hidden");
|
|
596
|
+
}
|
|
597
|
+
let A = k;
|
|
598
|
+
async function j(n) {
|
|
599
|
+
if (!a.value) throw Error("로그인이 필요합니다.");
|
|
600
|
+
let i = new Map(l.value.map((e) => [e.id, e.visibility ?? "public"])), o = X(r), s = [];
|
|
601
|
+
n.forEach((n, a) => {
|
|
560
602
|
let c = n.id.trim(), l = n.label.trim();
|
|
561
603
|
if (!c || !l) throw Error("보드 ID와 이름을 입력해 주세요.");
|
|
562
|
-
let u =
|
|
604
|
+
let u = ut(n.visibility), d = D({
|
|
563
605
|
...n,
|
|
564
606
|
label: l,
|
|
565
607
|
visibility: u
|
|
566
|
-
},
|
|
567
|
-
o.set(t(r, e(), c), d, { merge: !0 }), (
|
|
608
|
+
}, a);
|
|
609
|
+
o.set(t(r, e(), c), d, { merge: !0 }), (i.get(c) ?? "public") !== u && s.push({
|
|
568
610
|
id: c,
|
|
569
611
|
listed: u !== "hidden"
|
|
570
612
|
});
|
|
571
613
|
}), await o.commit();
|
|
572
|
-
for (let e of s) await
|
|
614
|
+
for (let e of s) await E(e.id, e.listed);
|
|
573
615
|
}
|
|
574
|
-
let
|
|
575
|
-
async function
|
|
576
|
-
if (!
|
|
616
|
+
let M = j;
|
|
617
|
+
async function N(n) {
|
|
618
|
+
if (!a.value) throw Error("로그인이 필요합니다.");
|
|
619
|
+
let o = n.trim();
|
|
620
|
+
if (!o) throw Error("삭제할 게시판 ID가 필요합니다.");
|
|
621
|
+
for (;;) {
|
|
622
|
+
let t = await z(W(T(r, e()), Y("boardId", "==", o), V(25)));
|
|
623
|
+
if (t.empty) break;
|
|
624
|
+
for (let n of t.docs) await Qe(r, ge(i), e(), n.id);
|
|
625
|
+
}
|
|
577
626
|
await P(t(r, e(), n));
|
|
578
627
|
}
|
|
579
|
-
let
|
|
580
|
-
async function
|
|
628
|
+
let F = N;
|
|
629
|
+
async function I() {
|
|
581
630
|
return Promise.resolve();
|
|
582
631
|
}
|
|
583
|
-
async function
|
|
632
|
+
async function L(e) {
|
|
584
633
|
let t = e.trim();
|
|
585
634
|
if (!t) throw Error("보드 이름을 입력해 주세요.");
|
|
586
|
-
if (!
|
|
587
|
-
let n =
|
|
635
|
+
if (!a.value) throw Error("로그인이 필요합니다.");
|
|
636
|
+
let n = l.value.find((e) => e.label === t);
|
|
588
637
|
if (n) return n.id;
|
|
589
|
-
let r = v(t) || `board-${Date.now()}`,
|
|
590
|
-
for (; s.has(
|
|
591
|
-
return await
|
|
592
|
-
id:
|
|
638
|
+
let r = v(t) || `board-${Date.now()}`, i = r, o = 2, s = new Set(l.value.map((e) => e.id));
|
|
639
|
+
for (; s.has(i);) i = `${r}-${o++}`;
|
|
640
|
+
return await k({
|
|
641
|
+
id: i,
|
|
593
642
|
label: t,
|
|
594
643
|
visibility: "public",
|
|
595
644
|
listView: "default",
|
|
596
645
|
writeRole: "user",
|
|
597
646
|
commentWriteRole: "user",
|
|
598
|
-
order:
|
|
599
|
-
}),
|
|
647
|
+
order: l.value.length
|
|
648
|
+
}), i;
|
|
600
649
|
}
|
|
601
650
|
return {
|
|
602
|
-
boards:
|
|
603
|
-
publicBoards:
|
|
604
|
-
boardLabel:
|
|
605
|
-
boardDescription:
|
|
606
|
-
boardVisibility:
|
|
607
|
-
isBoardHidden:
|
|
608
|
-
getBoard:
|
|
609
|
-
categories:
|
|
610
|
-
publicCategories:
|
|
611
|
-
categoryLabel:
|
|
612
|
-
categoryDescription:
|
|
613
|
-
categoryVisibility:
|
|
614
|
-
isCategoryHidden:
|
|
615
|
-
settingsPending:
|
|
616
|
-
ensureSettings:
|
|
617
|
-
addBoard:
|
|
618
|
-
addCategory:
|
|
619
|
-
saveBoard:
|
|
620
|
-
saveCategory:
|
|
621
|
-
saveBoards:
|
|
622
|
-
saveCategories:
|
|
623
|
-
deleteBoard:
|
|
624
|
-
deleteCategory:
|
|
625
|
-
DEFAULT_BOARDS:
|
|
626
|
-
DEFAULT_CATEGORIES:
|
|
651
|
+
boards: l,
|
|
652
|
+
publicBoards: f,
|
|
653
|
+
boardLabel: h,
|
|
654
|
+
boardDescription: _,
|
|
655
|
+
boardVisibility: b,
|
|
656
|
+
isBoardHidden: S,
|
|
657
|
+
getBoard: w,
|
|
658
|
+
categories: u,
|
|
659
|
+
publicCategories: p,
|
|
660
|
+
categoryLabel: g,
|
|
661
|
+
categoryDescription: y,
|
|
662
|
+
categoryVisibility: x,
|
|
663
|
+
isCategoryHidden: C,
|
|
664
|
+
settingsPending: c,
|
|
665
|
+
ensureSettings: I,
|
|
666
|
+
addBoard: L,
|
|
667
|
+
addCategory: L,
|
|
668
|
+
saveBoard: k,
|
|
669
|
+
saveCategory: A,
|
|
670
|
+
saveBoards: j,
|
|
671
|
+
saveCategories: M,
|
|
672
|
+
deleteBoard: N,
|
|
673
|
+
deleteCategory: F,
|
|
674
|
+
DEFAULT_BOARDS: ct,
|
|
675
|
+
DEFAULT_CATEGORIES: lt
|
|
627
676
|
};
|
|
628
677
|
}
|
|
629
678
|
//#endregion
|
|
630
679
|
//#region src/composables/useMemiBoardPosts.ts
|
|
631
|
-
|
|
632
|
-
let t = await _e(e).catch(() => ({
|
|
633
|
-
items: [],
|
|
634
|
-
prefixes: []
|
|
635
|
-
}));
|
|
636
|
-
await Promise.all([...t.items.map((e) => he(e)), ...t.prefixes.map((e) => ut(e))]);
|
|
637
|
-
}
|
|
638
|
-
async function dt(e, t) {
|
|
639
|
-
let n = [...new Set([...t].filter(Boolean))];
|
|
640
|
-
await Promise.all(n.map((t) => he(ve(e, t)).catch(() => {})));
|
|
641
|
-
}
|
|
642
|
-
function ft(e) {
|
|
680
|
+
function pt(e) {
|
|
643
681
|
if (!e || typeof e != "object") return !1;
|
|
644
682
|
let t = "code" in e ? String(e.code) : "";
|
|
645
683
|
return t === "permission-denied" || t === "firestore/permission-denied";
|
|
646
684
|
}
|
|
647
|
-
function
|
|
685
|
+
function mt(e) {
|
|
648
686
|
let t = M(e)?.trim();
|
|
649
687
|
if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
|
|
650
688
|
return t;
|
|
651
689
|
}
|
|
652
|
-
function
|
|
653
|
-
let
|
|
654
|
-
function
|
|
655
|
-
return !
|
|
690
|
+
function ht(e) {
|
|
691
|
+
let t = Z(), n = oe(), { isBoardHidden: r, getBoard: a } = ft(), o = () => m(), s = () => mt(e), c = () => T(t, o()), l = (e) => i(t, o(), e), u = (e) => E(t, o(), e);
|
|
692
|
+
function d() {
|
|
693
|
+
return !r(s());
|
|
656
694
|
}
|
|
657
|
-
function
|
|
658
|
-
return s(
|
|
695
|
+
function f() {
|
|
696
|
+
return a(s())?.listView === "image";
|
|
659
697
|
}
|
|
660
|
-
function
|
|
661
|
-
let t =
|
|
698
|
+
function p(e) {
|
|
699
|
+
let t = f(), n = tt(e.content), r = nt(e.content, e.attachments);
|
|
662
700
|
if (t) {
|
|
663
701
|
if (!r) throw Error("사진을 올려 주세요.");
|
|
664
702
|
return;
|
|
@@ -666,13 +704,13 @@ function mt(t) {
|
|
|
666
704
|
if (!n && !r) throw Error("본문에 글자를 입력하거나 이미지를 첨부해 주세요.");
|
|
667
705
|
if (!e.title?.trim()) throw Error("제목을 입력해 주세요.");
|
|
668
706
|
}
|
|
669
|
-
function
|
|
670
|
-
return I(
|
|
707
|
+
function h() {
|
|
708
|
+
return I(c()).id;
|
|
671
709
|
}
|
|
672
|
-
async function
|
|
673
|
-
let t = e.pageSize ?? 20, n = [Y("boardId", "==",
|
|
710
|
+
async function g(e = {}) {
|
|
711
|
+
let t = e.pageSize ?? 20, n = [Y("boardId", "==", s()), Y("isPublished", "==", !0)];
|
|
674
712
|
e.publicOnly !== !1 && n.push(Y("listed", "==", !0)), n.push(U("createdAt", "desc")), e.cursor && n.push(q(e.cursor)), n.push(V(t + 1));
|
|
675
|
-
let r = await z(W(
|
|
713
|
+
let r = await z(W(c(), ...n)), i = r.docs.slice(0, t);
|
|
676
714
|
return {
|
|
677
715
|
posts: i.map((e) => ({
|
|
678
716
|
id: e.id,
|
|
@@ -682,49 +720,49 @@ function mt(t) {
|
|
|
682
720
|
hasMore: r.docs.length > t
|
|
683
721
|
};
|
|
684
722
|
}
|
|
685
|
-
async function
|
|
686
|
-
let [t, n] = await Promise.all([R(
|
|
723
|
+
async function _(e) {
|
|
724
|
+
let [t, n] = await Promise.all([R(l(e)), R(u(e))]);
|
|
687
725
|
return t.exists() ? {
|
|
688
726
|
id: t.id,
|
|
689
727
|
...t.data(),
|
|
690
728
|
content: n.exists() ? n.data().content ?? "" : ""
|
|
691
729
|
} : null;
|
|
692
730
|
}
|
|
693
|
-
async function
|
|
731
|
+
async function y(e) {
|
|
694
732
|
try {
|
|
695
|
-
let t = (await z(W(
|
|
733
|
+
let t = (await z(W(c(), Y("boardId", "==", s()), Y("slug", "==", e.trim()), V(1)))).docs[0];
|
|
696
734
|
if (!t) return { status: "not-found" };
|
|
697
735
|
try {
|
|
698
|
-
let e = await
|
|
736
|
+
let e = await _(t.id);
|
|
699
737
|
return e ? {
|
|
700
738
|
status: "ok",
|
|
701
739
|
post: e
|
|
702
740
|
} : { status: "not-found" };
|
|
703
741
|
} catch (e) {
|
|
704
|
-
return
|
|
742
|
+
return pt(e) ? { status: "permission-denied" } : {
|
|
705
743
|
status: "error",
|
|
706
744
|
message: e instanceof Error ? e.message : String(e)
|
|
707
745
|
};
|
|
708
746
|
}
|
|
709
747
|
} catch (e) {
|
|
710
|
-
return
|
|
748
|
+
return pt(e) ? { status: "permission-denied" } : {
|
|
711
749
|
status: "error",
|
|
712
750
|
message: e instanceof Error ? e.message : String(e)
|
|
713
751
|
};
|
|
714
752
|
}
|
|
715
753
|
}
|
|
716
|
-
async function
|
|
754
|
+
async function b(e) {
|
|
717
755
|
if (!e.createdAt) return {
|
|
718
756
|
previous: null,
|
|
719
757
|
next: null
|
|
720
758
|
};
|
|
721
759
|
try {
|
|
722
760
|
let t = [
|
|
723
|
-
Y("boardId", "==",
|
|
761
|
+
Y("boardId", "==", s()),
|
|
724
762
|
Y("isPublished", "==", !0),
|
|
725
763
|
...e.listed === !1 ? [] : [Y("listed", "==", !0)],
|
|
726
764
|
U("createdAt", "desc")
|
|
727
|
-
], [n, r] = await Promise.all([z(W(
|
|
765
|
+
], [n, r] = await Promise.all([z(W(c(), ...t, q(e.createdAt), V(1))), z(W(c(), ...t, ee(e.createdAt), te(1)))]), i = (e) => {
|
|
728
766
|
let t = e.docs[0];
|
|
729
767
|
return t ? {
|
|
730
768
|
id: t.id,
|
|
@@ -742,20 +780,20 @@ function mt(t) {
|
|
|
742
780
|
};
|
|
743
781
|
}
|
|
744
782
|
}
|
|
745
|
-
async function
|
|
746
|
-
|
|
747
|
-
let t = e.postId ||
|
|
748
|
-
if (n) r = t, i =
|
|
783
|
+
async function x(e) {
|
|
784
|
+
p(e);
|
|
785
|
+
let t = e.postId || h(), n = f(), r, i;
|
|
786
|
+
if (n) r = t, i = et(e.content);
|
|
749
787
|
else {
|
|
750
788
|
i = e.title.trim();
|
|
751
789
|
let t = v(i) || "post";
|
|
752
790
|
r = t;
|
|
753
791
|
let n = 1;
|
|
754
|
-
for (; !(await z(W(
|
|
792
|
+
for (; !(await z(W(c(), Y("boardId", "==", s()), Y("slug", "==", r), V(1)))).empty;) n++, r = `${t}-${n}`;
|
|
755
793
|
}
|
|
756
|
-
let a =
|
|
757
|
-
return await K(
|
|
758
|
-
boardId:
|
|
794
|
+
let a = st(e.content, e.attachments), o = d();
|
|
795
|
+
return await K(l(t), {
|
|
796
|
+
boardId: s(),
|
|
759
797
|
slug: r,
|
|
760
798
|
title: i,
|
|
761
799
|
...a,
|
|
@@ -773,12 +811,12 @@ function mt(t) {
|
|
|
773
811
|
moderationModel: e.moderationModel ?? null,
|
|
774
812
|
createdAt: G(),
|
|
775
813
|
updatedAt: G()
|
|
776
|
-
}), await K(
|
|
814
|
+
}), await K(u(t), { content: e.content }), r;
|
|
777
815
|
}
|
|
778
|
-
async function
|
|
779
|
-
|
|
780
|
-
let n =
|
|
781
|
-
await Promise.all([J(
|
|
816
|
+
async function S(e, t) {
|
|
817
|
+
p(t);
|
|
818
|
+
let n = f() ? et(t.content) : t.title.trim(), r = st(t.content, t.attachments), i = d();
|
|
819
|
+
await Promise.all([J(l(e), {
|
|
782
820
|
title: n,
|
|
783
821
|
...r,
|
|
784
822
|
previewImage: r.previewImage ? r.previewImage : F(),
|
|
@@ -787,59 +825,33 @@ function mt(t) {
|
|
|
787
825
|
listed: i,
|
|
788
826
|
attachments: t.attachments ?? [],
|
|
789
827
|
updatedAt: G()
|
|
790
|
-
}), K(
|
|
828
|
+
}), K(u(e), { content: t.content }, { merge: !0 })]);
|
|
791
829
|
}
|
|
792
|
-
async function
|
|
793
|
-
await J(
|
|
830
|
+
async function C(e) {
|
|
831
|
+
await J(l(e), {
|
|
794
832
|
isPublished: !0,
|
|
795
833
|
publishedAt: G(),
|
|
796
834
|
updatedAt: G()
|
|
797
835
|
});
|
|
798
836
|
}
|
|
799
|
-
async function
|
|
800
|
-
|
|
801
|
-
if (!i.exists()) return;
|
|
802
|
-
let o = i.data(), s = a.exists() ? String(a.data().content ?? "") : "", l = await z(W(p(), Y("postId", "==", t)));
|
|
803
|
-
for (let e = 0; e < l.docs.length; e += 450) {
|
|
804
|
-
let t = X(n);
|
|
805
|
-
l.docs.slice(e, e + 450).forEach((e) => t.delete(e.ref)), await t.commit();
|
|
806
|
-
}
|
|
807
|
-
let u = ge(r), m = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set();
|
|
808
|
-
for (let e of o.attachments ?? []) if (e?.path) {
|
|
809
|
-
m.add(e.path);
|
|
810
|
-
let n = Ye(e.path);
|
|
811
|
-
n && n !== t && h.add(n);
|
|
812
|
-
}
|
|
813
|
-
for (let e of qe(s)) {
|
|
814
|
-
let n = Je(e);
|
|
815
|
-
if (!n) continue;
|
|
816
|
-
m.add(n);
|
|
817
|
-
let r = n.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
|
|
818
|
-
r !== n && r.includes("/images/thumbnails/") && m.add(r);
|
|
819
|
-
let i = n.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
|
|
820
|
-
i && !n.includes("/thumbnails/") && m.add(`${i[1]}thumbnails/${i[2]}.jpg`);
|
|
821
|
-
let a = Ye(n);
|
|
822
|
-
a && a !== t && h.add(a);
|
|
823
|
-
}
|
|
824
|
-
await P(f(t)), await ut(ve(u, e(c(), t)));
|
|
825
|
-
for (let t of h) await ut(ve(u, e(c(), t)));
|
|
826
|
-
await dt(u, m), await P(d(t));
|
|
837
|
+
async function w(e) {
|
|
838
|
+
await Qe(t, ge(n), o(), e);
|
|
827
839
|
}
|
|
828
840
|
return {
|
|
829
|
-
createPostId:
|
|
830
|
-
getPosts:
|
|
831
|
-
getPost:
|
|
832
|
-
getPostBySlug:
|
|
833
|
-
getAdjacentPosts:
|
|
834
|
-
createPost:
|
|
835
|
-
updatePost:
|
|
836
|
-
publishPost:
|
|
837
|
-
deletePost:
|
|
841
|
+
createPostId: h,
|
|
842
|
+
getPosts: g,
|
|
843
|
+
getPost: _,
|
|
844
|
+
getPostBySlug: y,
|
|
845
|
+
getAdjacentPosts: b,
|
|
846
|
+
createPost: x,
|
|
847
|
+
updatePost: S,
|
|
848
|
+
publishPost: C,
|
|
849
|
+
deletePost: w
|
|
838
850
|
};
|
|
839
851
|
}
|
|
840
|
-
var
|
|
841
|
-
function
|
|
842
|
-
let n = Z(), { isAdmin: r, isStaff: i, user: a } = Ke(), o = () => m(), s = t.pageSize ??
|
|
852
|
+
var gt = 10;
|
|
853
|
+
function _t(e, t = {}) {
|
|
854
|
+
let n = Z(), { isAdmin: r, isStaff: i, user: a } = Ke(), o = () => m(), s = t.pageSize ?? gt, c = O(() => a.value?.uid ?? ""), l = O(() => mt(e)), u = () => T(n, o()), d = O(() => t.publicOnly === void 0 ? !(r.value || i.value) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), f = j([]), p = j([]), h = j([]), g = j(!0), _ = j(!0), v = j(!1), y = j(""), b = null, x = null, S, C;
|
|
843
855
|
function w(e) {
|
|
844
856
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
845
857
|
}
|
|
@@ -922,8 +934,8 @@ function gt(e, t = {}) {
|
|
|
922
934
|
loadMore: P
|
|
923
935
|
};
|
|
924
936
|
}
|
|
925
|
-
function
|
|
926
|
-
let n = Z(), r = () => m(), a = O(() =>
|
|
937
|
+
function vt(e, t) {
|
|
938
|
+
let n = Z(), r = () => m(), a = O(() => mt(e)), o = O(() => typeof t == "string" ? t : t.value), s = O(() => i(n, r(), o.value)), c = O(() => E(n, r(), o.value)), { data: l, pending: u } = ae(s), { data: d, pending: f } = ae(c);
|
|
927
939
|
return {
|
|
928
940
|
post: O(() => !l.value || l.value.boardId !== a.value ? null : {
|
|
929
941
|
...l.value,
|
|
@@ -935,9 +947,9 @@ function _t(e, t) {
|
|
|
935
947
|
}
|
|
936
948
|
//#endregion
|
|
937
949
|
//#region src/composables/useMemiBoardUserPosts.ts
|
|
938
|
-
var
|
|
939
|
-
function
|
|
940
|
-
let n = Z(), r = () => m(), i = () => T(n, r()), a = t.pageSize ??
|
|
950
|
+
var yt = 10;
|
|
951
|
+
function bt(e, t = {}) {
|
|
952
|
+
let n = Z(), r = () => m(), i = () => T(n, r()), a = t.pageSize ?? yt, o = O(() => M(e)?.trim() || ""), s = j([]), c = j([]), l = j(!0), u = j(!0), d = j(!1), f = j(""), p = null, h = null, g;
|
|
941
953
|
function _(e) {
|
|
942
954
|
return {
|
|
943
955
|
id: e.id,
|
|
@@ -1003,9 +1015,9 @@ function yt(e, t = {}) {
|
|
|
1003
1015
|
}
|
|
1004
1016
|
//#endregion
|
|
1005
1017
|
//#region src/composables/useMemiBoardUserComments.ts
|
|
1006
|
-
var
|
|
1007
|
-
function
|
|
1008
|
-
let n = Z(), r = () => m(), i = () => o(n, r()), a = t.pageSize ??
|
|
1018
|
+
var xt = 10;
|
|
1019
|
+
function St(e, t = {}) {
|
|
1020
|
+
let n = Z(), r = () => m(), i = () => o(n, r()), a = t.pageSize ?? xt, s = O(() => M(e)?.trim() || ""), c = j([]), l = j([]), u = j(!0), d = j(!0), f = j(!1), p = j(""), h = null, g = null, _;
|
|
1009
1021
|
function v(e) {
|
|
1010
1022
|
return {
|
|
1011
1023
|
id: e.id,
|
|
@@ -1067,9 +1079,9 @@ function xt(e, t = {}) {
|
|
|
1067
1079
|
}
|
|
1068
1080
|
//#endregion
|
|
1069
1081
|
//#region src/composables/useMemiBoardAuthorMemo.ts
|
|
1070
|
-
var
|
|
1082
|
+
var Ct = 100, wt = /* @__PURE__ */ new Map();
|
|
1071
1083
|
function Q(e) {
|
|
1072
|
-
let t =
|
|
1084
|
+
let t = wt.get(e);
|
|
1073
1085
|
return t || (t = A({
|
|
1074
1086
|
text: "",
|
|
1075
1087
|
sentiment: null,
|
|
@@ -1077,9 +1089,9 @@ function Q(e) {
|
|
|
1077
1089
|
pending: !1,
|
|
1078
1090
|
saving: !1,
|
|
1079
1091
|
error: ""
|
|
1080
|
-
}),
|
|
1092
|
+
}), wt.set(e, t)), t;
|
|
1081
1093
|
}
|
|
1082
|
-
function
|
|
1094
|
+
function Tt(e) {
|
|
1083
1095
|
let t = Z(), n = () => m(), { user: r } = Ke(), i = O(() => M(e)), a = O(() => `${r.value?.uid ?? ""}:${i.value}`), o = O(() => {
|
|
1084
1096
|
let e = r.value?.uid, a = i.value;
|
|
1085
1097
|
return !e || !a ? null : u(t, n(), e, a);
|
|
@@ -1149,14 +1161,14 @@ function wt(e) {
|
|
|
1149
1161
|
}
|
|
1150
1162
|
//#endregion
|
|
1151
1163
|
//#region src/composables/useMemiBoardComments.ts
|
|
1152
|
-
var
|
|
1153
|
-
function
|
|
1164
|
+
var Et = 10, Dt = 5, Ot = 1e3;
|
|
1165
|
+
function kt(e) {
|
|
1154
1166
|
let t = e.trim();
|
|
1155
1167
|
if (!t) throw Error("댓글 내용을 입력해 주세요.");
|
|
1156
|
-
if (t.length > 1e3) throw Error(`댓글은 ${
|
|
1168
|
+
if (t.length > 1e3) throw Error(`댓글은 ${Ot.toLocaleString()}자까지 작성할 수 있습니다.`);
|
|
1157
1169
|
return t;
|
|
1158
1170
|
}
|
|
1159
|
-
function
|
|
1171
|
+
function At(e, t, n = {}) {
|
|
1160
1172
|
let r = Z(), a = () => m(), s = () => o(r, a()), c = O(() => M(e)), l = O(() => M(t)), u = j([]), d = j([]), p = j(n.subscribe !== !1), h = j([]), g = j(!1), _ = j(n.subscribe !== !1), v = j(n.subscribe !== !1), y = null, b, x;
|
|
1161
1173
|
function S(e) {
|
|
1162
1174
|
return e.createdAt?.toMillis?.() ?? 0;
|
|
@@ -1211,11 +1223,11 @@ function kt(e, t, n = {}) {
|
|
|
1211
1223
|
U(L(), "asc"),
|
|
1212
1224
|
...y ? [q(y)] : [],
|
|
1213
1225
|
V(11)
|
|
1214
|
-
], t = await z(W(s(), ...e)), n = t.docs.slice(0,
|
|
1226
|
+
], t = await z(W(s(), ...e)), n = t.docs.slice(0, Et), r = n.map((e) => ({
|
|
1215
1227
|
id: e.id,
|
|
1216
1228
|
...e.data()
|
|
1217
1229
|
})), i = new Set(h.value.map((e) => e.id));
|
|
1218
|
-
h.value.push(...r.filter((e) => !i.has(e.id))), y = n.at(-1) ?? y, t.size <=
|
|
1230
|
+
h.value.push(...r.filter((e) => !i.has(e.id))), y = n.at(-1) ?? y, t.size <= Et && (_.value = !1, b = setTimeout(() => {
|
|
1219
1231
|
b = void 0, w();
|
|
1220
1232
|
}, 1e3));
|
|
1221
1233
|
} finally {
|
|
@@ -1227,7 +1239,7 @@ function kt(e, t, n = {}) {
|
|
|
1227
1239
|
clearTimeout(b), x?.();
|
|
1228
1240
|
});
|
|
1229
1241
|
async function E(e) {
|
|
1230
|
-
let t =
|
|
1242
|
+
let t = kt(e.body), n = X(r), o = I(s());
|
|
1231
1243
|
n.set(o, {
|
|
1232
1244
|
postId: l.value,
|
|
1233
1245
|
boardId: c.value,
|
|
@@ -1248,7 +1260,7 @@ function kt(e, t, n = {}) {
|
|
|
1248
1260
|
}), n.update(i(r, a(), l.value), { commentCount: B(1) }), await n.commit();
|
|
1249
1261
|
}
|
|
1250
1262
|
async function D(e) {
|
|
1251
|
-
let t =
|
|
1263
|
+
let t = kt(e.body);
|
|
1252
1264
|
if (!e.parent.id) throw Error("답글 대상 댓글을 찾을 수 없습니다.");
|
|
1253
1265
|
let n = X(r), o = I(s()), u = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
|
|
1254
1266
|
if (!u) throw Error("댓글 스레드를 찾을 수 없습니다.");
|
|
@@ -1279,7 +1291,7 @@ function kt(e, t, n = {}) {
|
|
|
1279
1291
|
t.delete(f(r, a(), e.id)), e.parentId && e.rootId && t.update(f(r, a(), e.rootId), { replyCount: B(-1) }), t.update(i(r, a(), l.value), { commentCount: B(-1) }), await t.commit(), h.value = h.value.filter((t) => t.id !== e.id), d.value = d.value.filter((t) => t.id !== e.id), u.value = u.value.filter((t) => t.id !== e.id);
|
|
1280
1292
|
}
|
|
1281
1293
|
async function P(e, t) {
|
|
1282
|
-
let n =
|
|
1294
|
+
let n = kt(t);
|
|
1283
1295
|
await J(f(r, a(), e), {
|
|
1284
1296
|
body: n,
|
|
1285
1297
|
updatedAt: G()
|
|
@@ -1305,7 +1317,7 @@ function kt(e, t, n = {}) {
|
|
|
1305
1317
|
deleteComment: A
|
|
1306
1318
|
};
|
|
1307
1319
|
}
|
|
1308
|
-
function
|
|
1320
|
+
function jt(e, t, n) {
|
|
1309
1321
|
let r = Z(), i = () => m(), a = () => o(r, i()), s = j([]), c = j(!1), l = j(!1), u = j(!0), d = null, f;
|
|
1310
1322
|
function p(e) {
|
|
1311
1323
|
return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
|
|
@@ -1333,11 +1345,11 @@ function At(e, t, n) {
|
|
|
1333
1345
|
U(L(), "asc"),
|
|
1334
1346
|
...d ? [q(d)] : [],
|
|
1335
1347
|
V(6)
|
|
1336
|
-
], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0,
|
|
1348
|
+
], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Dt), o = i.map((e) => ({
|
|
1337
1349
|
id: e.id,
|
|
1338
1350
|
...e.data()
|
|
1339
1351
|
})).filter((e) => !r.has(e.id));
|
|
1340
|
-
s.value = p([...s.value, ...o]), d = i.at(-1) ?? d, l.value = !0, t.size <=
|
|
1352
|
+
s.value = p([...s.value, ...o]), d = i.at(-1) ?? d, l.value = !0, t.size <= Dt && (u.value = !1, h());
|
|
1341
1353
|
} finally {
|
|
1342
1354
|
c.value = !1;
|
|
1343
1355
|
}
|
|
@@ -1360,7 +1372,7 @@ function At(e, t, n) {
|
|
|
1360
1372
|
}
|
|
1361
1373
|
//#endregion
|
|
1362
1374
|
//#region src/composables/useMemiBoardLikes.ts
|
|
1363
|
-
function
|
|
1375
|
+
function Mt(e, t) {
|
|
1364
1376
|
let n = () => m(), r = Z(), a = ie(), o = j(!1), s = j(!1), c;
|
|
1365
1377
|
function l(e) {
|
|
1366
1378
|
return w(r, n(), t, e);
|
|
@@ -1401,7 +1413,7 @@ function jt(e, t) {
|
|
|
1401
1413
|
}
|
|
1402
1414
|
//#endregion
|
|
1403
1415
|
//#region src/composables/useMemiBoardViews.ts
|
|
1404
|
-
function
|
|
1416
|
+
function Nt() {
|
|
1405
1417
|
let e = () => m(), t = Z();
|
|
1406
1418
|
function n(t, n) {
|
|
1407
1419
|
let { postsCollection: r } = e();
|
|
@@ -1441,7 +1453,7 @@ function Mt() {
|
|
|
1441
1453
|
}
|
|
1442
1454
|
//#endregion
|
|
1443
1455
|
//#region src/utils/moderation-prompt.ts
|
|
1444
|
-
var
|
|
1456
|
+
var Pt = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\n아래 텍스트에 욕설·비속어·성희롱·혐오·위협·스팸·도배성 광고가 있으면 반드시 거부(flagged=true)합니다.\n\nflagged=true 예:\n- 욕설/비속어: 씨발, 시발, 병신, 지랄, 좆, 개새끼, 존나, fuck, shit, bitch 등\n- 성적 모욕, 혐오 발언, 협박\n- 게시판 주제와 무관한 도배성 광고/스팸\n\nflagged=false:\n- 게시판 주제에 맞는 정상적인 글/댓글 (강한 의견이나 정당한 비판은 허용)\n\nJSON만 출력:\n{\"flagged\":boolean,\"category\":\"none\"|\"abuse\"|\"spam\"|\"adult\"|\"violence\"|\"other\",\"reason\":string}\n- flagged=true면 category를 고르고, reason은 한국어로 **무엇이 문제인지** 구체적으로 1문장\n (예: \"욕설(비속어)이 포함되어 게시할 수 없습니다.\", \"광고·스팸성 문구가 있어 게시할 수 없습니다.\")\n 욕설 단어 전체를 그대로 길게 인용하지 말고, 유형(욕설/혐오/스팸 등)을 분명히 밝힌다.\n- flagged=false면 category는 \"none\", reason은 \"\"", Ft = {
|
|
1445
1457
|
abuse: "욕설·비속어·모욕",
|
|
1446
1458
|
spam: "스팸·광고",
|
|
1447
1459
|
adult: "선정적·성인 내용",
|
|
@@ -1449,17 +1461,17 @@ var Nt = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
|
|
|
1449
1461
|
other: "부적절한 내용",
|
|
1450
1462
|
none: ""
|
|
1451
1463
|
};
|
|
1452
|
-
function
|
|
1464
|
+
function It(e) {
|
|
1453
1465
|
if (e.via === "local" && e.localHit) {
|
|
1454
1466
|
let t = e.localHit.trim();
|
|
1455
1467
|
return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
|
|
1456
1468
|
}
|
|
1457
1469
|
let t = (e.reason || "").trim();
|
|
1458
1470
|
if (t) return t;
|
|
1459
|
-
let n = e.category && e.category !== "none" ?
|
|
1471
|
+
let n = e.category && e.category !== "none" ? Ft[e.category] || e.category : "";
|
|
1460
1472
|
return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
|
|
1461
1473
|
}
|
|
1462
|
-
var
|
|
1474
|
+
var Lt = [
|
|
1463
1475
|
"씨발",
|
|
1464
1476
|
"시발",
|
|
1465
1477
|
"시이발",
|
|
@@ -1485,10 +1497,10 @@ var It = [
|
|
|
1485
1497
|
"bitch",
|
|
1486
1498
|
"asshole"
|
|
1487
1499
|
];
|
|
1488
|
-
function
|
|
1500
|
+
function Rt(e) {
|
|
1489
1501
|
return new RegExp(e.join("|"), "i");
|
|
1490
1502
|
}
|
|
1491
|
-
var
|
|
1503
|
+
var zt = /* @__PURE__ */ new Set([
|
|
1492
1504
|
"none",
|
|
1493
1505
|
"abuse",
|
|
1494
1506
|
"spam",
|
|
@@ -1496,7 +1508,7 @@ var Rt = /* @__PURE__ */ new Set([
|
|
|
1496
1508
|
"violence",
|
|
1497
1509
|
"other"
|
|
1498
1510
|
]);
|
|
1499
|
-
function
|
|
1511
|
+
function Bt(e) {
|
|
1500
1512
|
let t = e.trim();
|
|
1501
1513
|
if (!t) return null;
|
|
1502
1514
|
let n;
|
|
@@ -1514,7 +1526,7 @@ function zt(e) {
|
|
|
1514
1526
|
if (typeof n != "object" || !n) return null;
|
|
1515
1527
|
let r = n;
|
|
1516
1528
|
if (typeof r.flagged != "boolean") return null;
|
|
1517
|
-
let i = typeof r.category == "string" &&
|
|
1529
|
+
let i = typeof r.category == "string" && zt.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
|
|
1518
1530
|
return {
|
|
1519
1531
|
flagged: r.flagged,
|
|
1520
1532
|
category: i,
|
|
@@ -1523,20 +1535,20 @@ function zt(e) {
|
|
|
1523
1535
|
}
|
|
1524
1536
|
//#endregion
|
|
1525
1537
|
//#region src/composables/useMemiBoardModeration.ts
|
|
1526
|
-
var
|
|
1538
|
+
var Vt = {
|
|
1527
1539
|
flagged: !1,
|
|
1528
1540
|
category: "none",
|
|
1529
1541
|
reason: "",
|
|
1530
1542
|
via: "empty"
|
|
1531
1543
|
};
|
|
1532
|
-
function
|
|
1533
|
-
let e = s().moderation ?? {}, t = oe(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = Ke(), a =
|
|
1544
|
+
function Ht() {
|
|
1545
|
+
let e = s().moderation ?? {}, t = oe(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = Ke(), a = Rt([...Lt, ...e.localBlocklist ?? []]);
|
|
1534
1546
|
function o(e) {
|
|
1535
1547
|
let t = e.match(a);
|
|
1536
1548
|
return t ? {
|
|
1537
1549
|
flagged: !0,
|
|
1538
1550
|
category: "abuse",
|
|
1539
|
-
reason:
|
|
1551
|
+
reason: It({
|
|
1540
1552
|
via: "local",
|
|
1541
1553
|
localHit: t[0] || "",
|
|
1542
1554
|
category: "abuse"
|
|
@@ -1551,7 +1563,7 @@ function Vt() {
|
|
|
1551
1563
|
});
|
|
1552
1564
|
return Se(i, {
|
|
1553
1565
|
model: n,
|
|
1554
|
-
systemInstruction:
|
|
1566
|
+
systemInstruction: Pt,
|
|
1555
1567
|
generationConfig: {
|
|
1556
1568
|
temperature: 0,
|
|
1557
1569
|
maxOutputTokens: 256,
|
|
@@ -1580,7 +1592,7 @@ function Vt() {
|
|
|
1580
1592
|
};
|
|
1581
1593
|
}
|
|
1582
1594
|
function u(e, t = "") {
|
|
1583
|
-
let n =
|
|
1595
|
+
let n = It({
|
|
1584
1596
|
reason: e.reason,
|
|
1585
1597
|
category: e.category,
|
|
1586
1598
|
via: e.via
|
|
@@ -1608,22 +1620,22 @@ function Vt() {
|
|
|
1608
1620
|
};
|
|
1609
1621
|
let i = t.trim();
|
|
1610
1622
|
if (!i) return {
|
|
1611
|
-
...
|
|
1623
|
+
...Vt,
|
|
1612
1624
|
via: "empty"
|
|
1613
1625
|
};
|
|
1614
1626
|
let a = o(i);
|
|
1615
1627
|
if (a) return d(a);
|
|
1616
1628
|
if (e.enabled === !1) return {
|
|
1617
|
-
...
|
|
1629
|
+
...Vt,
|
|
1618
1630
|
via: "disabled"
|
|
1619
1631
|
};
|
|
1620
1632
|
try {
|
|
1621
|
-
let e =
|
|
1633
|
+
let e = Bt((await c().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
|
|
1622
1634
|
if (!e) return l();
|
|
1623
1635
|
let t = {
|
|
1624
1636
|
flagged: e.flagged,
|
|
1625
1637
|
category: e.category,
|
|
1626
|
-
reason:
|
|
1638
|
+
reason: It({
|
|
1627
1639
|
reason: e.reason,
|
|
1628
1640
|
category: e.category,
|
|
1629
1641
|
via: "ai"
|
|
@@ -1643,14 +1655,14 @@ function Vt() {
|
|
|
1643
1655
|
via: "restricted"
|
|
1644
1656
|
};
|
|
1645
1657
|
if (e.enabled === !1 || !e.moderateImages) return {
|
|
1646
|
-
...
|
|
1658
|
+
...Vt,
|
|
1647
1659
|
via: "disabled"
|
|
1648
1660
|
};
|
|
1649
1661
|
try {
|
|
1650
1662
|
let e = await new Promise((e, n) => {
|
|
1651
1663
|
let r = new FileReader();
|
|
1652
1664
|
r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
|
|
1653
|
-
}), n =
|
|
1665
|
+
}), n = Bt((await c().generateContent([{ inlineData: {
|
|
1654
1666
|
mimeType: t.type || "image/jpeg",
|
|
1655
1667
|
data: e
|
|
1656
1668
|
} }, "이 이미지를 심사해 주세요."])).response.text());
|
|
@@ -1658,7 +1670,7 @@ function Vt() {
|
|
|
1658
1670
|
let r = {
|
|
1659
1671
|
flagged: n.flagged,
|
|
1660
1672
|
category: n.category,
|
|
1661
|
-
reason:
|
|
1673
|
+
reason: It({
|
|
1662
1674
|
reason: n.reason,
|
|
1663
1675
|
category: n.category,
|
|
1664
1676
|
via: "ai"
|
|
@@ -1677,7 +1689,7 @@ function Vt() {
|
|
|
1677
1689
|
}
|
|
1678
1690
|
//#endregion
|
|
1679
1691
|
//#region src/composables/useMemiBoardUsers.ts
|
|
1680
|
-
var
|
|
1692
|
+
var Ut = [
|
|
1681
1693
|
{
|
|
1682
1694
|
value: "admin",
|
|
1683
1695
|
label: "관리자",
|
|
@@ -1694,10 +1706,10 @@ var Ht = [
|
|
|
1694
1706
|
description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
|
|
1695
1707
|
}
|
|
1696
1708
|
];
|
|
1697
|
-
function
|
|
1709
|
+
function Wt(e = {}) {
|
|
1698
1710
|
let t = () => m(), n = Z(), r = O(() => M(e.enabled) ?? !0 ? p(n, t()) : null), i = re(r, { ssrKey: d(t(), "users-management") });
|
|
1699
1711
|
async function a(e, r) {
|
|
1700
|
-
if (!
|
|
1712
|
+
if (!Ut.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
|
|
1701
1713
|
await J(b(n, t(), e), {
|
|
1702
1714
|
role: r,
|
|
1703
1715
|
updatedAt: G()
|
|
@@ -1711,7 +1723,7 @@ function Ut(e = {}) {
|
|
|
1711
1723
|
}
|
|
1712
1724
|
//#endregion
|
|
1713
1725
|
//#region src/composables/useMemiBoardUserProfile.ts
|
|
1714
|
-
function
|
|
1726
|
+
function Gt(e) {
|
|
1715
1727
|
let t = Z(), n = () => m(), r = O(() => M(e)), i = O(() => {
|
|
1716
1728
|
let e = r.value;
|
|
1717
1729
|
return e ? b(t, n(), e) : null;
|
|
@@ -1723,7 +1735,12 @@ function Wt(e) {
|
|
|
1723
1735
|
}
|
|
1724
1736
|
//#endregion
|
|
1725
1737
|
//#region src/data/versionHistory.ts
|
|
1726
|
-
var
|
|
1738
|
+
var Kt = [
|
|
1739
|
+
{
|
|
1740
|
+
version: "0.32.0",
|
|
1741
|
+
date: "2026-08-14",
|
|
1742
|
+
highlights: ["글쓰기에서 사진 파일을 올리거나 외부 이미지 주소를 넣을 수 있어요", "게시판을 지우면 그 안의 글·댓글·이미지도 함께 삭제돼요"]
|
|
1743
|
+
},
|
|
1727
1744
|
{
|
|
1728
1745
|
version: "0.31.0",
|
|
1729
1746
|
date: "2026-08-14",
|
|
@@ -1928,7 +1945,7 @@ var Gt = [
|
|
|
1928
1945
|
highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
|
|
1929
1946
|
note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
|
|
1930
1947
|
}
|
|
1931
|
-
],
|
|
1948
|
+
], qt = /* @__PURE__ */ r(((e, t) => {
|
|
1932
1949
|
(function(n, r) {
|
|
1933
1950
|
typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs = r();
|
|
1934
1951
|
})(e, (function() {
|
|
@@ -2222,7 +2239,7 @@ var Gt = [
|
|
|
2222
2239
|
return w(1e3 * e);
|
|
2223
2240
|
}, w.en = b[y], w.Ls = b, w.p = {}, w;
|
|
2224
2241
|
}));
|
|
2225
|
-
})),
|
|
2242
|
+
})), Jt = /* @__PURE__ */ r(((e, t) => {
|
|
2226
2243
|
(function(n, r) {
|
|
2227
2244
|
typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_plugin_relativeTime = r();
|
|
2228
2245
|
})(e, (function() {
|
|
@@ -2326,9 +2343,9 @@ var Gt = [
|
|
|
2326
2343
|
};
|
|
2327
2344
|
};
|
|
2328
2345
|
}));
|
|
2329
|
-
})),
|
|
2346
|
+
})), Yt = /* @__PURE__ */ r(((e, t) => {
|
|
2330
2347
|
(function(n, r) {
|
|
2331
|
-
typeof e == "object" && t !== void 0 ? t.exports = r(
|
|
2348
|
+
typeof e == "object" && t !== void 0 ? t.exports = r(qt()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
|
|
2332
2349
|
})(e, (function(e) {
|
|
2333
2350
|
function t(e) {
|
|
2334
2351
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
@@ -2376,24 +2393,24 @@ var Gt = [
|
|
|
2376
2393
|
};
|
|
2377
2394
|
return n.default.locale(r, null, !0), r;
|
|
2378
2395
|
}));
|
|
2379
|
-
})),
|
|
2380
|
-
|
|
2381
|
-
function
|
|
2396
|
+
})), Xt = /* @__PURE__ */ h(qt(), 1), Zt = /* @__PURE__ */ h(Jt(), 1);
|
|
2397
|
+
Yt(), Xt.default.extend(Zt.default), Xt.default.locale("ko");
|
|
2398
|
+
function Qt(e) {
|
|
2382
2399
|
return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
|
|
2383
2400
|
}
|
|
2384
|
-
function
|
|
2385
|
-
return e?.toDate ? (0,
|
|
2401
|
+
function $t(e, t = Date.now()) {
|
|
2402
|
+
return e?.toDate ? (0, Xt.default)(e.toDate()).from((0, Xt.default)(t)) : "방금 전";
|
|
2386
2403
|
}
|
|
2387
|
-
function
|
|
2388
|
-
return e?.toDate ? (0,
|
|
2404
|
+
function en(e) {
|
|
2405
|
+
return e?.toDate ? (0, Xt.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
|
|
2389
2406
|
}
|
|
2390
|
-
function
|
|
2391
|
-
let n =
|
|
2392
|
-
return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${
|
|
2407
|
+
function tn(e, t) {
|
|
2408
|
+
let n = en(e), r = e?.toMillis?.(), i = t?.toMillis?.();
|
|
2409
|
+
return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${en(t)}`];
|
|
2393
2410
|
}
|
|
2394
2411
|
//#endregion
|
|
2395
2412
|
//#region src/composables/useMemiBoardWritingAssistant.ts
|
|
2396
|
-
var
|
|
2413
|
+
var nn = 10, rn = 864e5, an = {
|
|
2397
2414
|
proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
|
|
2398
2415
|
polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
|
|
2399
2416
|
continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
|
|
@@ -2402,13 +2419,13 @@ var tn = 10, nn = 864e5, rn = {
|
|
|
2402
2419
|
"translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
|
|
2403
2420
|
title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
|
|
2404
2421
|
};
|
|
2405
|
-
function
|
|
2422
|
+
function on(e) {
|
|
2406
2423
|
return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
2407
2424
|
}
|
|
2408
|
-
function
|
|
2425
|
+
function sn(e) {
|
|
2409
2426
|
return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
|
|
2410
2427
|
}
|
|
2411
|
-
function
|
|
2428
|
+
function cn() {
|
|
2412
2429
|
let e = oe(), t = Z(), n = ie(), r = s();
|
|
2413
2430
|
async function i() {
|
|
2414
2431
|
let e = n.value?.uid;
|
|
@@ -2416,9 +2433,9 @@ function sn() {
|
|
|
2416
2433
|
await ne(t, async (n) => {
|
|
2417
2434
|
let r = b(t, m(), e), i = await n.get(r);
|
|
2418
2435
|
if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
|
|
2419
|
-
let a = i.data(), o =
|
|
2436
|
+
let a = i.data(), o = sn(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < rn;
|
|
2420
2437
|
if (l && s >= 10) {
|
|
2421
|
-
let e = o +
|
|
2438
|
+
let e = o + rn, t = Math.max(1, Math.ceil((e - c) / 36e5));
|
|
2422
2439
|
throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
|
|
2423
2440
|
}
|
|
2424
2441
|
n.update(r, {
|
|
@@ -2447,10 +2464,10 @@ function sn() {
|
|
|
2447
2464
|
}), s = t.content.trim();
|
|
2448
2465
|
if (!s) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
|
|
2449
2466
|
let c = [
|
|
2450
|
-
`작업: ${
|
|
2467
|
+
`작업: ${an[t.action]}`,
|
|
2451
2468
|
t.title ? `현재 제목: ${t.title}` : "",
|
|
2452
2469
|
`본문:\n${s.slice(0, 12e3)}`
|
|
2453
|
-
].filter(Boolean).join("\n\n"), l =
|
|
2470
|
+
].filter(Boolean).join("\n\n"), l = on((await o.generateContent(c)).response.text());
|
|
2454
2471
|
if (!l) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
|
|
2455
2472
|
return l;
|
|
2456
2473
|
}
|
|
@@ -2461,11 +2478,11 @@ function sn() {
|
|
|
2461
2478
|
function $(e) {
|
|
2462
2479
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2463
2480
|
}
|
|
2464
|
-
function
|
|
2481
|
+
function ln(e) {
|
|
2465
2482
|
let t = e;
|
|
2466
2483
|
return t = t.replace(/`([^`]+)`/g, "<code>$1</code>"), t = t.replace(/!\[([^\]]*)\]\((https?:\/\/[^)\s]+)\)/g, "<img src=\"$2\" alt=\"$1\" class=\"max-w-full h-auto rounded-lg my-3\" loading=\"lazy\" />"), t = t.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary underline\">$1</a>"), t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"), t = t.replace(/\*([^*]+)\*/g, "<em>$1</em>"), t = t.replace(/~~([^~]+)~~/g, "<del>$1</del>"), t;
|
|
2467
2484
|
}
|
|
2468
|
-
function
|
|
2485
|
+
function un(e) {
|
|
2469
2486
|
if (!e?.trim()) return "";
|
|
2470
2487
|
let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
|
|
2471
2488
|
function c() {
|
|
@@ -2474,7 +2491,7 @@ function ln(e) {
|
|
|
2474
2491
|
return;
|
|
2475
2492
|
}
|
|
2476
2493
|
let e = o;
|
|
2477
|
-
n.push(`<${e}>${s.map((e) => `<li>${
|
|
2494
|
+
n.push(`<${e}>${s.map((e) => `<li>${ln(e)}</li>`).join("")}</${e}>`), o = null, s = [];
|
|
2478
2495
|
}
|
|
2479
2496
|
for (; r < t.length;) {
|
|
2480
2497
|
let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
|
|
@@ -2494,14 +2511,14 @@ function ln(e) {
|
|
|
2494
2511
|
if (u) {
|
|
2495
2512
|
c();
|
|
2496
2513
|
let e = u[1].length;
|
|
2497
|
-
n.push(`<h${e} class="font-semibold mt-4 mb-2">${
|
|
2514
|
+
n.push(`<h${e} class="font-semibold mt-4 mb-2">${ln($(u[2]))}</h${e}>`), r++;
|
|
2498
2515
|
continue;
|
|
2499
2516
|
}
|
|
2500
2517
|
if (e.startsWith("> ")) {
|
|
2501
2518
|
c();
|
|
2502
2519
|
let e = [];
|
|
2503
2520
|
for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push($((t[r] ?? "").slice(2))), r++;
|
|
2504
|
-
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${
|
|
2521
|
+
n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${ln(e.join("<br />"))}</blockquote>`);
|
|
2505
2522
|
continue;
|
|
2506
2523
|
}
|
|
2507
2524
|
let d = e.match(/^[-*+]\s+(.+)$/);
|
|
@@ -2525,11 +2542,11 @@ function ln(e) {
|
|
|
2525
2542
|
if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
|
|
2526
2543
|
p.push($(e)), r++;
|
|
2527
2544
|
}
|
|
2528
|
-
n.push(`<p class="my-2 leading-relaxed">${
|
|
2545
|
+
n.push(`<p class="my-2 leading-relaxed">${ln(p.join("<br />"))}</p>`);
|
|
2529
2546
|
}
|
|
2530
2547
|
return c(), i && a.length && n.push(`<pre class="overflow-x-auto rounded-lg bg-muted p-3 text-sm"><code>${a.join("\n")}</code></pre>`), n.join("\n");
|
|
2531
2548
|
}
|
|
2532
2549
|
//#endregion
|
|
2533
|
-
export {
|
|
2550
|
+
export { Ct as AUTHOR_MEMO_MAX_LENGTH, Ut as BOARD_USER_ROLES, Ot as COMMENT_BODY_MAX_LENGTH, Re as DEFAULT_BLOCK_BAN_DECAY_MS, Le as DEFAULT_BLOCK_BAN_THRESHOLD, ct as DEFAULT_BOARDS, lt as DEFAULT_CATEGORIES, y as DEFAULT_COMMENTS_COLLECTION, S as DEFAULT_LIKES_COLLECTION, l as DEFAULT_POSTS_COLLECTION, g as DEFAULT_SETTINGS_COLLECTION, C as DEFAULT_USERS_COLLECTION, nn as WRITING_ASSISTANT_DAILY_LIMIT, Ce as asHttpUrl, ke as boardListOgDescription, Oe as boardListOgTitle, De as boardPostOgDescription, Ee as boardPostOgTitle, d as boardSsrKey, b as boardUserDoc, p as boardUsersCol, st as buildPostPreview, f as commentDoc, o as commentsCol, x as configureMemiBoard, ze as effectiveModerationBlockCount, qe as extractEditorImageUrls, Fe as fetchPublicListForSeo, Pe as fetchPublicPostForSeo, Qt as formatDate, en as formatFullDate, $t as formatRelativeDate, He as formatRestrictedUntilLabel, tn as formatTimestampDetails, nt as hasBodyImage, tt as hasBodyText, rt as isContentEmpty, pt as isFirestorePermissionDenied, Ve as isModerationWriteRestricted, w as likeDoc, D as likeDocId, c as likesCol, Be as moderationWriteRestrictedUntilMs, Ae as normalizeBasePath, $e as plainTextFromHtml, E as postBodyDoc, i as postDoc, Ye as postNamespaceFromStoragePath, e as postStorageFolder, T as postsCol, un as renderMarkdownToHtml, a as resolveBoardPathConfig, je as resolvePublicSeoDb, n as settingsCol, t as settingsDoc, v as slugify, Je as storagePathFromDownloadUrl, et as titleFromBody, Te as toAbsoluteUrl, we as toBoardOgImageUrl, m as useBoardPathConfig, Ke as useMemiBoardAuth, Tt as useMemiBoardAuthorMemo, At as useMemiBoardComments, s as useMemiBoardConfig, Mt as useMemiBoardLikes, Ht as useMemiBoardModeration, vt as useMemiBoardPost, _t as useMemiBoardPostList, ht as useMemiBoardPosts, jt as useMemiBoardReplies, ft as useMemiBoardSettings, St as useMemiBoardUserComments, bt as useMemiBoardUserPosts, Gt as useMemiBoardUserProfile, Wt as useMemiBoardUsers, Nt as useMemiBoardViews, cn as useMemiBoardWritingAssistant, Kt as versionHistory, ot as videoListCoverUrl, at as youtubeId };
|
|
2534
2551
|
|
|
2535
2552
|
//# sourceMappingURL=index.js.map
|