memi-board 0.44.1 → 0.45.1
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 +64 -3
- package/dist/ar-BCQdRw_S.js +622 -0
- package/dist/ar-BCQdRw_S.js.map +1 -0
- package/dist/composables/fetchPublicSeo.d.ts +4 -1
- package/dist/composables/useMemiBoardAutoTranslate.d.ts +33 -0
- package/dist/composables/useMemiBoardComments.d.ts +2 -3
- package/dist/composables/useMemiBoardModeration.d.ts +5 -1
- package/dist/composables/useMemiBoardReports.d.ts +6 -1
- package/dist/composables/useMemiBoardSections.d.ts +2 -1
- package/dist/composables/useMemiBoardUsers.d.ts +4 -0
- package/dist/composables/useMemiBoardWritingAssistant.d.ts +9 -1
- package/dist/config.d.ts +5 -0
- package/dist/de-ddWQNaqs.js +622 -0
- package/dist/de-ddWQNaqs.js.map +1 -0
- package/dist/en-DxkqyZq2.js +649 -0
- package/dist/en-DxkqyZq2.js.map +1 -0
- package/dist/es-7JHjGVlM.js +622 -0
- package/dist/es-7JHjGVlM.js.map +1 -0
- package/dist/fr-C3NCMNoy.js +622 -0
- package/dist/fr-C3NCMNoy.js.map +1 -0
- package/dist/id-C8hCAMPJ.js +622 -0
- package/dist/id-C8hCAMPJ.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +1370 -1630
- package/dist/index.js.map +1 -1
- package/dist/ja-oTWri4hY.js +622 -0
- package/dist/ja-oTWri4hY.js.map +1 -0
- package/dist/module.mjs +55 -31
- package/dist/module.mjs.map +1 -1
- package/dist/pt-B9EH4scA.js +622 -0
- package/dist/pt-B9EH4scA.js.map +1 -0
- package/dist/runtime/components/Attachments.vue +4 -2
- package/dist/runtime/components/AuthorMenu.vue +14 -12
- package/dist/runtime/components/AvatarCropModal.vue +8 -5
- package/dist/runtime/components/CategorySettings.vue +68 -44
- package/dist/runtime/components/CommentFeed.vue +9 -7
- package/dist/runtime/components/CommentForm.vue +12 -10
- package/dist/runtime/components/CommentItem.vue +21 -26
- package/dist/runtime/components/CommentList.vue +7 -4
- package/dist/runtime/components/CommentThread.vue +8 -6
- package/dist/runtime/components/Detail.vue +20 -15
- package/dist/runtime/components/Editor.vue +142 -135
- package/dist/runtime/components/Footer.vue +14 -13
- package/dist/runtime/components/List.vue +31 -29
- package/dist/runtime/components/ListDefault.vue +4 -3
- package/dist/runtime/components/ListDense.vue +6 -5
- package/dist/runtime/components/ListImage.vue +4 -2
- package/dist/runtime/components/ListVideo.vue +3 -2
- package/dist/runtime/components/ListViewSwitch.vue +10 -6
- package/dist/runtime/components/MiniCard.vue +3 -2
- package/dist/runtime/components/MiniComments.vue +5 -4
- package/dist/runtime/components/MiniList.vue +3 -2
- package/dist/runtime/components/MiniPicker.vue +10 -9
- package/dist/runtime/components/MiniPost.vue +3 -2
- package/dist/runtime/components/MiniPosts.vue +5 -4
- package/dist/runtime/components/Profile.vue +12 -7
- package/dist/runtime/components/ProfileEditModal.vue +14 -13
- package/dist/runtime/components/ReportButton.vue +16 -10
- package/dist/runtime/components/ReportList.vue +11 -10
- package/dist/runtime/components/Section.vue +3 -2
- package/dist/runtime/components/Settings.vue +84 -62
- package/dist/runtime/components/ShareButton.vue +3 -1
- package/dist/runtime/components/SignIn.vue +10 -8
- package/dist/runtime/components/SwipeHint.vue +5 -3
- package/dist/runtime/components/UserCommentList.vue +8 -6
- package/dist/runtime/components/UserPostList.vue +6 -4
- package/dist/runtime/components/Users.vue +16 -10
- package/dist/runtime/composables/useMemiBoardSeo.ts +16 -10
- package/dist/slugify-BuOMFLxN.js +943 -0
- package/dist/slugify-BuOMFLxN.js.map +1 -0
- package/dist/storage.js +88 -74
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/utils/boardSeo.d.ts +5 -5
- package/dist/utils/compressImage.d.ts +2 -1
- package/dist/utils/deletePostCascade.d.ts +0 -2
- package/dist/utils/findReadablePost.d.ts +4 -0
- package/dist/utils/formatDate.d.ts +6 -4
- package/dist/utils/moderation-prompt.d.ts +18 -1
- package/dist/utils/moderation-strike.d.ts +2 -1
- package/dist/utils/saveBoardVisibility.d.ts +3 -0
- package/dist/utils/section.d.ts +15 -2
- package/dist/utils/subscribeCommentRange.d.ts +9 -0
- package/dist/zh-D5rDYV6E.js +622 -0
- package/dist/zh-D5rDYV6E.js.map +1 -0
- package/docs/firebase-setup.md +20 -1
- package/docs/firestore.rules.example +36 -7
- package/docs/storage.rules.example +25 -14
- package/package.json +2 -3
- package/dist/slugify-Cm_pi2al.js +0 -124
- package/dist/slugify-Cm_pi2al.js.map +0 -1
package/README.md
CHANGED
|
@@ -146,6 +146,7 @@ export default defineNuxtConfig({
|
|
|
146
146
|
| `seo.siteUrl` | `''` | 절대 URL origin. 비우면 `runtimeConfig.public.siteUrl` → 요청 origin 순 |
|
|
147
147
|
| `seo.defaultOgImage` | `''` | 대표 이미지 없을 때 (상대경로 또는 `https://…`) |
|
|
148
148
|
| `seo.basePath` | `'/board'` | canonical: `{base}`, `{base}/{category}`, `{base}/{category}/{slug}` |
|
|
149
|
+
| `locale` | `'ko'` | 기본 화면 언어. 호스트에 `@nuxtjs/i18n`이 있으면 그 언어가 우선 (아래 다국어 절) |
|
|
149
150
|
|
|
150
151
|
#### routeRules 원칙
|
|
151
152
|
|
|
@@ -252,7 +253,57 @@ watch([categoryId, id], async ([category, slug]) => {
|
|
|
252
253
|
@source "../../../node_modules/memi-board/dist/runtime/components";
|
|
253
254
|
```
|
|
254
255
|
|
|
255
|
-
### 5.
|
|
256
|
+
### 5. 다국어 (@nuxtjs/i18n, 권장)
|
|
257
|
+
|
|
258
|
+
게시판 UI는 10개 언어를 지원한다: `ko` `en` `ja` `de` `fr` `es` `pt` `zh` `ar` `id` (`MEMI_BOARD_LOCALES`).
|
|
259
|
+
|
|
260
|
+
- **언어 결정 순서**: 보드 설정의 "게시판 언어" → 호스트 `@nuxtjs/i18n`의 현재 locale → `memiBoard.locale` → `'ko'`.
|
|
261
|
+
- **번역 범위**: ko·en은 모든 문구, 나머지 8개 언어는 짧은 화면 문구만 번역돼 있다. 긴 안내문(삭제 확인 등)은 영어로 폴백된다.
|
|
262
|
+
- **로딩**: 언어별 번역 파일은 별도 청크로 그 언어를 쓸 때만 받는다(언어당 gzip 약 7KB). ko는 기본 번들에 들어 있다.
|
|
263
|
+
- **날짜**: `Intl`로 그 언어 형식에 맞춰 표시한다. dayjs는 더 이상 쓰지 않는다.
|
|
264
|
+
- **AI**: 검열·글쓰기 도우미 프롬프트도 보드 언어를 따른다(ko는 한국어 프롬프트, 그 외는 영어 프롬프트 + 해당 언어로 응답).
|
|
265
|
+
- **아랍어**: 게시판 목록·상세·글쓰기 루트에 `dir="rtl"`이 붙는다.
|
|
266
|
+
|
|
267
|
+
호스트에 `@nuxtjs/i18n`을 설치하면 사이트 언어 전환에 게시판이 자동으로 따라간다. locale `code`는 위 코드와 같게 둔다(`en-US`처럼 써도 앞 두 글자로 맞춘다).
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
pnpm add @nuxtjs/i18n
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
// nuxt.config.ts — '@nuxtjs/i18n'은 'memi-board'보다 앞에 둔다
|
|
275
|
+
modules: ['@nuxt/ui', '@nuxtjs/i18n', 'nuxt-vuefire', 'memi-board'],
|
|
276
|
+
i18n: {
|
|
277
|
+
locales: [
|
|
278
|
+
{ code: 'ko', language: 'ko-KR', name: '한국어', file: 'ko.json' },
|
|
279
|
+
{ code: 'en', language: 'en-US', name: 'English', file: 'en.json' },
|
|
280
|
+
// ja, de, fr, es, pt, zh, ar(dir: 'rtl'), id …
|
|
281
|
+
],
|
|
282
|
+
defaultLocale: 'ko',
|
|
283
|
+
strategy: 'no_prefix',
|
|
284
|
+
langDir: 'locales/',
|
|
285
|
+
},
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Nuxt UI 내부 문구(닫기·검색 등)와 `<html lang dir>`도 맞추려면 `app.vue`에서:
|
|
289
|
+
|
|
290
|
+
```vue
|
|
291
|
+
<script setup lang="ts">
|
|
292
|
+
import { ar, de, en, es, fr, id, ja, ko, pt_br, zh_cn } from '@nuxt/ui/locale'
|
|
293
|
+
const { locale } = useI18n()
|
|
294
|
+
const uiLocales = { ko, en, ja, de, fr, es, pt: pt_br, zh: zh_cn, ar, id }
|
|
295
|
+
const uiLocale = computed(() => uiLocales[locale.value as keyof typeof uiLocales] ?? ko)
|
|
296
|
+
useHead({ htmlAttrs: { lang: () => uiLocale.value.code, dir: () => uiLocale.value.dir } })
|
|
297
|
+
</script>
|
|
298
|
+
|
|
299
|
+
<template>
|
|
300
|
+
<UApp :locale="uiLocale">…</UApp>
|
|
301
|
+
</template>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
`@nuxtjs/i18n` 없이 한 언어로만 쓰려면 `memiBoard: { locale: 'en' }`만 지정한다. 호스트 화면에서 같은 번역을 쓰려면 `useMemiBoardI18n()`의 `t`, `formatRelativeDate` 등을 쓴다.
|
|
305
|
+
|
|
306
|
+
### 6. 읽은 글 표시 (선택)
|
|
256
307
|
|
|
257
308
|
목록·미니 컴포넌트의 글 링크는 `.memi-board-post-link` 클래스가 붙어 있고, 제목 텍스트는 `text-inherit`로 링크 색을 따른다. 패키지는 색을 강제하지 않으므로, 방문한 글을 흐리게 보이고 싶으면 호스트 CSS에 `:visited` 스타일만 추가하면 된다.
|
|
258
309
|
|
|
@@ -271,7 +322,7 @@ a.memi-board-post-link:visited {
|
|
|
271
322
|
|
|
272
323
|
브라우저 네이티브 `:visited` 라서 서버·DB 저장이 필요 없다. 단, SPA 클라이언트 라우팅(`NuxtLink`)으로만 이동한 경우 브라우저에 "실제 방문"으로 기록되지 않을 수 있어 완전히 신뢰할 수는 없다 — 정확한 읽음 추적이 필요하면 별도로 방문 글 ID를 저장하는 방식을 구현해야 한다.
|
|
273
324
|
|
|
274
|
-
###
|
|
325
|
+
### 7. Security Rules + 인덱스 배포
|
|
275
326
|
|
|
276
327
|
1. [docs/firestore.rules.example](docs/firestore.rules.example) → 호스트 `firestore.rules`에 병합
|
|
277
328
|
(`memiBoardPosts`/`memiBoardComments`/`memiBoardLikes`/`memiBoardSettings`/`memiBoardUsers` — 전부 최상위 flat 컬렉션)
|
|
@@ -283,6 +334,7 @@ a.memi-board-post-link:visited {
|
|
|
283
334
|
```bash
|
|
284
335
|
firebase deploy --only firestore:rules,storage,firestore:indexes
|
|
285
336
|
```
|
|
337
|
+
6. Storage의 Firestore 조회 권한 설정: 배포 시 교차 서비스 IAM 역할 추가 안내를 승인하고, 호스트 Firebase 프로젝트의 Storage 서비스 계정에 `roles/firebaserules.firestoreServiceAgent`가 있는지 확인한다. [부모/호스트 설정 절차](docs/firebase-setup.md#storage-규칙의-firestore-조회-권한-부모호스트-프로젝트-필수-설정) 참고. CI 배포 성공만으로 이 권한 설정을 보장하지 않는다.
|
|
286
338
|
|
|
287
339
|
**Rules를 파일에만 넣고 배포 안 하면** 글쓰기에서 `permission-denied` — 가장 흔한 설치 실수.
|
|
288
340
|
**Storage 규칙을 앱이 쓰는 버킷이 아닌 곳에 배포하면** 본문 이미지에서 `storage/unauthorized`.
|
|
@@ -293,7 +345,7 @@ a.memi-board-post-link:visited {
|
|
|
293
345
|
|
|
294
346
|
> ⚠️ npm 패키지 tarball에도 `docs/` 가 포함된다(0.4.7+). 구버전이면 GitHub의 `docs/`를 보면 된다.
|
|
295
347
|
|
|
296
|
-
###
|
|
348
|
+
### 8. 최초 관리자와 카테고리 만들기
|
|
297
349
|
|
|
298
350
|
Rules는 일반 사용자가 스스로 관리자가 되는 것을 막는다. 따라서 최초 한 번은 다음 순서가 필요하다.
|
|
299
351
|
|
|
@@ -538,6 +590,7 @@ export default defineNuxtPlugin(() => {
|
|
|
538
590
|
- [ ] CSS `@source` …/memi-board/dist/runtime/components
|
|
539
591
|
- [ ] (선택) `.memi-board-post-link:visited` 읽은 글 표시 CSS
|
|
540
592
|
- [ ] Rules 예시 병합 + **deploy**
|
|
593
|
+
- [ ] Storage 서비스 계정의 Firestore 조회 IAM 역할 확인 ([호스트 설정](docs/firebase-setup.md#storage-규칙의-firestore-조회-권한-부모호스트-프로젝트-필수-설정))
|
|
541
594
|
- [ ] 카테고리·listed 쿼리 사용 시 indexes deploy
|
|
542
595
|
- [ ] 페이지·미들웨어 직접 작성 (또는 playground 복사)
|
|
543
596
|
- [ ] 최초 로그인 후 Firebase Console에서 `memiBoardUsers/{uid}.role = 'admin'`
|
|
@@ -570,6 +623,12 @@ App Check 토큰, `appId` 일치, 모델명(3.x), AI Logic 활성화, 네트워
|
|
|
570
623
|
3. 호스트 `storage.rules`에 `docs/storage.rules.example`의 `memiBoardPosts/{postId}/images` 규칙이 있는지 확인. 쓰기는 `image/*` 이어야 한다(붙여넣기 PNG).
|
|
571
624
|
4. `firebase deploy --only storage` 후 다시 올려 본다.
|
|
572
625
|
|
|
626
|
+
### ZIP 등 첨부파일 `storage/unauthorized`
|
|
627
|
+
|
|
628
|
+
`memiBoardPosts/{postId}/attachments/...`에서 실패하면 로그인 상태, 앱이 쓰는 버킷에 배포된 첨부 규칙, **파일당 20MiB 미만** 조건을 먼저 확인한다. 예시 규칙은 ZIP의 MIME 형식이나 `.exe.zip` 이름을 차단하지 않는다.
|
|
629
|
+
|
|
630
|
+
조건을 충족해도 실패하면 **호스트 Firebase 프로젝트의 Storage 서비스 계정에 Firestore 조회 역할이 있는지** 확인한다. 첨부 규칙은 `postExists`·`isPostOwner`·`isBoardStaff`에서 Firestore를 조회한다. 조회 권한이 없으면 새 글의 문서 존재 여부 확인부터 실패할 수 있다. [부모/호스트 설정 절차](docs/firebase-setup.md#storage-규칙의-firestore-조회-권한-부모호스트-프로젝트-필수-설정)에 따라 `roles/firebaserules.firestoreServiceAgent`를 설정한다.
|
|
631
|
+
|
|
573
632
|
### 글 삭제 `storage/unauthorized`
|
|
574
633
|
|
|
575
634
|
작성자 본인 글 삭제가 `User does not have permission to access 'memiBoardPosts/{postId}'` 로 실패하면, 파일 삭제가 아니라 **폴더 목록(`listAll`)** 이 막힌 것이다.
|
|
@@ -586,6 +645,8 @@ match /memiBoardPosts/{postId}/{allPaths=**} {
|
|
|
586
645
|
|
|
587
646
|
`write` 는 이 블록에 넣지 않는다(용량·`image/*` 제한은 `images`/`attachments` match 가 담당). `firebase deploy --only storage` 후 다시 삭제한다. 0.38.1+ 패키지는 `images/`·`attachments/`만 목록으로 비운다.
|
|
588
647
|
|
|
648
|
+
목록 조회는 성공하지만 **개별 파일 삭제**가 거부되면, 위 [첨부파일 오류](#zip-등-첨부파일-storageunauthorized)와 같은 Firestore 조회 IAM 역할도 확인한다. 삭제 규칙 역시 글 소유권·사용자 역할을 Firestore에서 읽는다.
|
|
649
|
+
|
|
589
650
|
### monorepo `link:` / `file:` 로 개발할 때
|
|
590
651
|
|
|
591
652
|
링크하기 전에 패키지 의존성과 `dist`를 준비한다.
|