memi-board 0.52.0 → 0.52.2
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 +297 -297
- package/dist/{commentImages-CzXO42gE.js → commentImages-D_JJnuoj.js} +5 -4
- package/dist/commentImages-D_JJnuoj.js.map +1 -0
- package/dist/composables/useMemiBoardAutoTranslate.d.ts +1 -0
- package/dist/composables/useMemiBoardComments.d.ts +35 -2
- package/dist/composables/useMemiBoardWritingAssistant.d.ts +6 -1
- package/dist/{en-CmPLbFcN.js → en-DSCLJyUd.js} +4 -3
- package/dist/en-DSCLJyUd.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +987 -903
- package/dist/index.js.map +1 -1
- package/dist/runtime/components/CommentAi.vue +93 -0
- package/dist/runtime/components/CommentFeed.vue +3 -3
- package/dist/runtime/components/CommentForm.vue +11 -4
- package/dist/runtime/components/CommentItem.vue +20 -8
- package/dist/runtime/components/Editor.vue +0 -2
- package/dist/runtime/components/MiniComments.vue +3 -3
- package/dist/runtime/components/UserCommentList.vue +3 -3
- package/dist/storage.js +1 -1
- package/dist/types.d.ts +10 -1
- package/dist/utils/localizedFields.d.ts +3 -1
- package/docs/firebase-setup.md +85 -85
- package/docs/firestore.rules.example +58 -58
- package/docs/host-integration.md +80 -80
- package/docs/internationalization.md +76 -72
- package/docs/roles-migration.md +18 -18
- package/docs/storage.rules.example +18 -18
- package/package.json +2 -2
- package/dist/commentImages-CzXO42gE.js.map +0 -1
- package/dist/en-CmPLbFcN.js.map +0 -1
package/docs/host-integration.md
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Adding memi-board to another Nuxt site
|
|
2
2
|
|
|
3
|
-
[README](../README.md) · [
|
|
3
|
+
[README](../README.md) · [Internationalization and translation](internationalization.md) · [Firebase setup](firebase-setup.md) · [Role migration](roles-migration.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This is the procedure for adding a board to an existing Nuxt 4 site. The host app handles routes, the Firebase connection, and rule deployment; memi-board provides the board components and composables.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The examples in this guide, including the internationalization and translation options, assume **memi-board 0.47.0 or later**. Required versions: Nuxt 4+, Nuxt UI 4+, Firebase 12+, Vue 3.5+, VueFire 3.2+.
|
|
8
8
|
|
|
9
|
-
## 1.
|
|
9
|
+
## 1. Install the required packages
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Run this from the root of the existing Nuxt project.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
pnpm add memi-board@^0.47.0 @nuxt/ui nuxt-vuefire vuefire firebase
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`dayjs
|
|
17
|
+
`dayjs` is not needed. Add `@nuxtjs/i18n` only if you need site language switching. In production, manage the memi-board version you have verified together with the lockfile.
|
|
18
18
|
|
|
19
|
-
## 2.
|
|
19
|
+
## 2. Choose a language mode first
|
|
20
20
|
|
|
21
|
-
|
|
|
21
|
+
| Service | Recommended setting |
|
|
22
22
|
|---|---|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
23
|
+
| Korean notice/inquiry board | `locale: 'ko', i18n: { enabled: false }` |
|
|
24
|
+
| English-only service | `locale: 'en', i18n: { enabled: false }` |
|
|
25
|
+
| Multilingual UI, posts shown in the language they were written in | `i18n: { enabled: true }, translation: { enabled: false }` |
|
|
26
|
+
| Translate boards and posts into multiple languages | `i18n: { enabled: true }, translation: { enabled: true }` |
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
If omitted, both features are enabled. Content translation requires a separate Firebase AI Logic setup. For what the auto-translate toggle means and when translation actually runs, see [Internationalization and translation](internationalization.md#4-when-translations-run-and-are-saved).
|
|
29
29
|
|
|
30
|
-
## 3. Nuxt
|
|
30
|
+
## 3. Merge into the Nuxt config
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
The following is a starter example that is **Korean-only and does not use AI moderation or translation**. If you already have `modules`, CSS, or Firebase settings, merge rather than registering them twice.
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
// nuxt.config.ts
|
|
@@ -53,7 +53,7 @@ export default defineNuxtConfig({
|
|
|
53
53
|
auth: { providers: ['google'] },
|
|
54
54
|
moderation: { enabled: false },
|
|
55
55
|
seo: {
|
|
56
|
-
siteName: '
|
|
56
|
+
siteName: 'Our Service',
|
|
57
57
|
siteUrl: 'https://example.com',
|
|
58
58
|
basePath: '/board'
|
|
59
59
|
}
|
|
@@ -68,11 +68,11 @@ export default defineNuxtConfig({
|
|
|
68
68
|
})
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
Firebase
|
|
71
|
+
Use the **Firebase web app config of the site you are adding the board to**. Storage bucket names can differ per project, so copy the actual value from the console. Never put a Firebase Admin service account key in the client config.
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Keep SSR for the public list and detail pages. Even with `moderation.enabled: false`, the default local banned-word check remains. This config has no option to turn off the writing AI's proofreading and sentence polishing; using those AI menus requires an AI Logic connection.
|
|
74
74
|
|
|
75
|
-
## 4.
|
|
75
|
+
## 4. Styles and UApp
|
|
76
76
|
|
|
77
77
|
```css
|
|
78
78
|
/* app/assets/css/main.css */
|
|
@@ -81,10 +81,10 @@ Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다.
|
|
|
81
81
|
@source "../../../node_modules/memi-board/dist/runtime/components";
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
`@source
|
|
84
|
+
`@source` is relative to the CSS file above. If your CSS lives elsewhere, adjust the relative path.
|
|
85
85
|
|
|
86
86
|
```vue
|
|
87
|
-
<!-- app/app.vue —
|
|
87
|
+
<!-- app/app.vue — if a UApp already exists, don't wrap it twice -->
|
|
88
88
|
<template>
|
|
89
89
|
<UApp>
|
|
90
90
|
<NuxtPage />
|
|
@@ -92,13 +92,13 @@ Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다.
|
|
|
92
92
|
</template>
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
Apps with layouts keep their existing `<NuxtLayout>` structure. To also pin Nuxt UI's internal strings to Korean, import `ko` from `@nuxt/ui/locale` and set `<UApp :locale="ko">`. The host app also manages `<html lang>`.
|
|
96
96
|
|
|
97
|
-
## 5.
|
|
97
|
+
## 5. Deploy rules and indexes
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
Merge the following files from the installed package into the host files. If the site already has rules, don't overwrite them wholesale.
|
|
100
100
|
|
|
101
|
-
|
|
|
101
|
+
| Package file | Host file |
|
|
102
102
|
|---|---|
|
|
103
103
|
| `node_modules/memi-board/docs/firestore.rules.example` | `firestore.rules` |
|
|
104
104
|
| `node_modules/memi-board/docs/storage.rules.example` | `storage.rules` |
|
|
@@ -108,9 +108,9 @@ Firebase 값은 **붙일 사이트의 Firebase 웹 앱 설정**을 사용한다.
|
|
|
108
108
|
firebase deploy --only firestore:rules,storage,firestore:indexes
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
`firebase.json
|
|
111
|
+
Also set the index file path in `firebase.json`. The bucket the Storage rules are deployed to must match `vuefire.config.storageBucket`. For the initial setup including cross-service IAM, follow [Firebase setup](firebase-setup.md#firestore-read-access-for-storage-rules-required-host-app-setup). Installing from npm or deploying the website does not replace this deployment.
|
|
112
112
|
|
|
113
|
-
## 6.
|
|
113
|
+
## 6. Admin pages and the first board
|
|
114
114
|
|
|
115
115
|
```vue
|
|
116
116
|
<!-- app/pages/board-settings.vue -->
|
|
@@ -126,30 +126,30 @@ firebase deploy --only firestore:rules,storage,firestore:indexes
|
|
|
126
126
|
</template>
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
Add `<MemiBoardSignIn />` to a sign-in page or an existing account screen, then initialize in this order:
|
|
130
130
|
|
|
131
|
-
1. Firebase Authentication
|
|
132
|
-
2.
|
|
133
|
-
3.
|
|
134
|
-
4.
|
|
135
|
-
5. `/board-users
|
|
131
|
+
1. In Firebase Authentication, set up the Google provider and the site's authorized domains.
|
|
132
|
+
2. Sign in with the board's sign-in component to create the `memiBoardUsers/{uid}` document. If the host only has its own sign-in, open a page that also uses the board auth composable.
|
|
133
|
+
3. From the trusted Firebase Console or a server-side Admin SDK, set that document's `role` to `owner`.
|
|
134
|
+
4. After reloading or signing in again, create a board with the ID `free` in `/board-settings`.
|
|
135
|
+
5. In `/board-users`, assign `admin`/`manager`/`staff` roles to the accounts that need them, and assign people in the board settings.
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
Don't assign the first owner or transfer ownership through the client role selection menu. The host app adds route access guards, and actual data access is enforced by the rules. If the host's sign-in provider uses the same Firebase Auth as the board, sign-in state is shared.
|
|
138
138
|
|
|
139
|
-
## 7.
|
|
139
|
+
## 7. List, new post, and detail pages
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
The recommended paths are below. The host app decides the `/board` landing page and menu structure.
|
|
142
142
|
|
|
143
|
-
|
|
|
143
|
+
| File | URL | Value to pass |
|
|
144
144
|
|---|---|---|
|
|
145
|
-
| `app/pages/board/[boardId]/index.vue` | `/board/free` |
|
|
146
|
-
| `app/pages/board/[boardId]/new.vue` | `/board/free/new` |
|
|
147
|
-
| `app/pages/board/[boardId]/[slug]/index.vue` | `/board/free
|
|
148
|
-
| `app/pages/board/[boardId]/[slug]/edit.vue` | `/board/free
|
|
145
|
+
| `app/pages/board/[boardId]/index.vue` | `/board/free` | `boardId` for the list |
|
|
146
|
+
| `app/pages/board/[boardId]/new.vue` | `/board/free/new` | `boardId` for the editor |
|
|
147
|
+
| `app/pages/board/[boardId]/[slug]/index.vue` | `/board/free/post-slug` | Look up the slug and pass it as the detail's `postId` |
|
|
148
|
+
| `app/pages/board/[boardId]/[slug]/edit.vue` | `/board/free/post-slug/edit` | Edit the existing post using the looked-up document ID |
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
Components are auto-imported by the Nuxt module. Import only composables and types from `memi-board/runtime`.
|
|
151
151
|
|
|
152
|
-
###
|
|
152
|
+
### List
|
|
153
153
|
|
|
154
154
|
```vue
|
|
155
155
|
<!-- app/pages/board/[boardId]/index.vue -->
|
|
@@ -178,9 +178,9 @@ await useMemiBoardListSeo({
|
|
|
178
178
|
</template>
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
`canWrite
|
|
181
|
+
In this example, `canWrite` only reflects whether the user is signed in. For staff- or admin-only boards, the host app should also check the board's `writeRole` and assignment scope before showing the button. The rules enforce write permission independently of whether the button is shown.
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
Also create the page that the list's per-board settings link points to.
|
|
184
184
|
|
|
185
185
|
```vue
|
|
186
186
|
<!-- app/pages/board-settings/[boardId].vue -->
|
|
@@ -194,9 +194,9 @@ const boardId = computed(() => String(route.params.boardId || ''))
|
|
|
194
194
|
</template>
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
Use `canConfigureBoard(boardId)` for the manager entry guard and `canManageBoard(boardId)` for content management. The settings component also checks assigned settings permission by default.
|
|
198
198
|
|
|
199
|
-
###
|
|
199
|
+
### New post
|
|
200
200
|
|
|
201
201
|
```vue
|
|
202
202
|
<!-- app/pages/board/[boardId]/new.vue -->
|
|
@@ -217,9 +217,9 @@ const { isSignedIn, rolePending } = useMemiBoardAuth()
|
|
|
217
217
|
</template>
|
|
218
218
|
```
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
For a new post, the `saved` event passes the **slug**; when editing an existing post, the `saved` event passes the **Firestore document ID**. On the edit page, navigate using the slug from the current URL. The editor itself applies the board language.
|
|
221
221
|
|
|
222
|
-
###
|
|
222
|
+
### Detail
|
|
223
223
|
|
|
224
224
|
```vue
|
|
225
225
|
<!-- app/pages/board/[boardId]/[slug]/index.vue -->
|
|
@@ -262,14 +262,14 @@ watch([boardId, slug, seoPost], async ([board, currentSlug, seo], _, onCleanup)
|
|
|
262
262
|
@list="navigateTo(`/board/${boardId}`)"
|
|
263
263
|
@deleted="navigateTo(`/board/${boardId}`)"
|
|
264
264
|
/>
|
|
265
|
-
<p v-else-if="unavailable"
|
|
266
|
-
<p v-else
|
|
265
|
+
<p v-else-if="unavailable">The post could not be found, or you don't have access to it.</p>
|
|
266
|
+
<p v-else>Loading...</p>
|
|
267
267
|
</template>
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
-
`boardId
|
|
270
|
+
`boardId` is the board settings document ID, `postId` is the post's Firestore document ID, and `slug` is the URL string. Don't pass the slug as `postId` to the detail component. In the example above, SEO for public detail pages is filled in during SSR, while client-side reads of drafts and private posts follow sign-in state and the rules.
|
|
271
271
|
|
|
272
|
-
###
|
|
272
|
+
### Edit
|
|
273
273
|
|
|
274
274
|
```vue
|
|
275
275
|
<!-- app/pages/board/[boardId]/[slug]/edit.vue -->
|
|
@@ -294,7 +294,7 @@ watch([boardId, slug, rolePending, isSignedIn], async (_, __, onCleanup) => {
|
|
|
294
294
|
if (active && post && canEdit(post)) postId.value = post.id ?? ''
|
|
295
295
|
}
|
|
296
296
|
catch {
|
|
297
|
-
//
|
|
297
|
+
// Sign-in, permission, deletion, and network errors are handled by the message below.
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
if (active) pending.value = false
|
|
@@ -302,7 +302,7 @@ watch([boardId, slug, rolePending, isSignedIn], async (_, __, onCleanup) => {
|
|
|
302
302
|
</script>
|
|
303
303
|
|
|
304
304
|
<template>
|
|
305
|
-
<p v-if="pending"
|
|
305
|
+
<p v-if="pending">Loading...</p>
|
|
306
306
|
<MemiBoardEditor
|
|
307
307
|
v-else-if="postId"
|
|
308
308
|
:board-id="boardId"
|
|
@@ -310,46 +310,46 @@ watch([boardId, slug, rolePending, isSignedIn], async (_, __, onCleanup) => {
|
|
|
310
310
|
@saved="navigateTo(`/board/${boardId}/${encodeURIComponent(slug)}`)"
|
|
311
311
|
@cancel="navigateTo(`/board/${boardId}/${encodeURIComponent(slug)}`)"
|
|
312
312
|
/>
|
|
313
|
-
<p v-else
|
|
313
|
+
<p v-else>You don't have permission to edit this post, or it could not be loaded.</p>
|
|
314
314
|
</template>
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
-
## 8.
|
|
317
|
+
## 8. Roles and service-specific labels
|
|
318
318
|
|
|
319
319
|
```ts
|
|
320
320
|
memiBoard: {
|
|
321
321
|
roleLabels: {
|
|
322
|
-
user: '
|
|
323
|
-
staff: '
|
|
324
|
-
manager: '
|
|
325
|
-
admin: '
|
|
326
|
-
owner: '
|
|
322
|
+
user: 'Customer',
|
|
323
|
+
staff: 'Store staff',
|
|
324
|
+
manager: 'Store manager',
|
|
325
|
+
admin: 'Operator',
|
|
326
|
+
owner: 'Super admin'
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
```
|
|
330
330
|
|
|
331
|
-
|
|
331
|
+
Only the labels change; permissions do not. A manager manages the settings and staff assignments of the boards listed in `allowedManagerUids`. Linking stores or organizations to boards and syncing assignments is the host app's responsibility. Simply sharing the same Firebase project does not automatically create organization boundaries.
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
Under the default file rules, only a global admin/owner can modify or delete other users' files. Services where assigned staff/managers need to replace other users' attachments or clean up files should add a server API that verifies board assignment. Default Storage file reads are public. For details on roles and migrating existing data, see [Role migration](roles-migration.md).
|
|
334
334
|
|
|
335
|
-
## 9.
|
|
335
|
+
## 9. Separating data from other sites
|
|
336
336
|
|
|
337
|
-
|
|
|
337
|
+
| Setup | Recommended approach |
|
|
338
338
|
|---|---|
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
-
|
|
|
339
|
+
| Independent sites for separate services or clients | Use separate Firebase projects too |
|
|
340
|
+
| Multiple boards within the same service | Same project, separated by `boardId` |
|
|
341
|
+
| Different collection names within one project | Change the 6 names such as `postsCollection` together with the rules, Storage paths, and indexes |
|
|
342
342
|
|
|
343
|
-
|
|
343
|
+
Collection name options and `boardId` do not, by themselves, guarantee tenant isolation for security. The default admin/owner manages the entire collection. If you need independent administrator boundaries, separate the projects or design the host rules separately.
|
|
344
344
|
|
|
345
|
-
## 10.
|
|
345
|
+
## 10. Pre-deployment checklist
|
|
346
346
|
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
350
|
-
-
|
|
351
|
-
-
|
|
352
|
-
-
|
|
353
|
-
-
|
|
347
|
+
- After the first owner signs in, verify creating the `free` board and writing posts and comments with a regular account.
|
|
348
|
+
- On opening a post detail page, verify the slug → document ID lookup and that the original content is shown.
|
|
349
|
+
- Verify the language/translation UI and whether AI requests are made, according to the chosen internationalization mode.
|
|
350
|
+
- Verify that a manager can configure only their assigned boards and not other boards.
|
|
351
|
+
- Verify that the rules are deployed to the Storage bucket the app uses, and test image and attachment uploads.
|
|
352
|
+
- Verify on the deployed URL that the public list and detail HTML contain OG meta tags.
|
|
353
|
+
- Check the results of the web app deployment and the Firebase rules/index deployment separately.
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
For troubleshooting steps by error, see [README Troubleshooting](../README.md#troubleshooting).
|