@commonpub/layer 0.7.0 → 0.7.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/package.json +7 -7
- package/pages/federated-hubs/[id]/index.vue +12 -5
- package/server/api/docs/[siteSlug]/pages/[pageId].get.ts +2 -10
- package/server/api/docs/[siteSlug]/pages/index.get.ts +2 -9
- package/server/api/files/upload-from-url.post.ts +1 -2
- package/server/plugins/notification-email.ts +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/layer",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"files": [
|
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
"zod": "^4.3.6",
|
|
53
53
|
"@commonpub/auth": "0.5.0",
|
|
54
54
|
"@commonpub/config": "0.8.0",
|
|
55
|
-
"@commonpub/docs": "0.6.0",
|
|
56
|
-
"@commonpub/explainer": "0.7.1",
|
|
57
55
|
"@commonpub/editor": "0.5.0",
|
|
58
56
|
"@commonpub/learning": "0.5.0",
|
|
59
|
-
"@commonpub/
|
|
60
|
-
"@commonpub/schema": "0.9.
|
|
61
|
-
"@commonpub/
|
|
62
|
-
"@commonpub/
|
|
57
|
+
"@commonpub/docs": "0.6.1",
|
|
58
|
+
"@commonpub/schema": "0.9.1",
|
|
59
|
+
"@commonpub/ui": "0.8.4",
|
|
60
|
+
"@commonpub/explainer": "0.7.2",
|
|
61
|
+
"@commonpub/server": "2.27.1",
|
|
62
|
+
"@commonpub/protocol": "0.9.7"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@testing-library/jest-dom": "^6.9.1",
|
|
@@ -205,13 +205,20 @@ async function handleDiscPost(): Promise<void> {
|
|
|
205
205
|
// --- Instance mirror status ---
|
|
206
206
|
const mirrorStatus = computed(() => hub.value?.followStatus ?? 'pending');
|
|
207
207
|
|
|
208
|
-
// --- Per-user join state ---
|
|
209
|
-
const
|
|
210
|
-
() => `/api/federation/hub-follow-status?federatedHubId=${id}`,
|
|
211
|
-
{ default: () => ({ joined: false, status: null }) },
|
|
212
|
-
);
|
|
208
|
+
// --- Per-user join state (only fetch when authenticated) ---
|
|
209
|
+
const userFollowState = ref<{ joined: boolean; status: string | null }>({ joined: false, status: null });
|
|
213
210
|
const userJoined = computed(() => userFollowState.value?.joined ?? false);
|
|
214
211
|
|
|
212
|
+
async function refreshFollowState(): Promise<void> {
|
|
213
|
+
if (!isAuthenticated.value) return;
|
|
214
|
+
try {
|
|
215
|
+
userFollowState.value = await $fetch<{ joined: boolean; status: string | null }>(
|
|
216
|
+
`/api/federation/hub-follow-status?federatedHubId=${id}`,
|
|
217
|
+
);
|
|
218
|
+
} catch { /* best-effort */ }
|
|
219
|
+
}
|
|
220
|
+
onMounted(() => { refreshFollowState(); });
|
|
221
|
+
|
|
215
222
|
const remoteFollowRef = ref<{ show: () => void } | null>(null);
|
|
216
223
|
const hubFollowing = ref(false);
|
|
217
224
|
const hubFollowStatus = computed(() => hub.value?.followStatus ?? '');
|
|
@@ -19,16 +19,8 @@ export default defineEventHandler(async (event) => {
|
|
|
19
19
|
const page = pages.find((p) => p.slug === pageSlug);
|
|
20
20
|
if (!page) throw createError({ statusCode: 404, statusMessage: 'Page not found' });
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
let content: string | unknown[] = page.content ?? '';
|
|
25
|
-
if (typeof content === 'string' && content.trimStart().startsWith('[')) {
|
|
26
|
-
try {
|
|
27
|
-
content = JSON.parse(content);
|
|
28
|
-
} catch {
|
|
29
|
-
// Not valid JSON — keep as markdown string
|
|
30
|
-
}
|
|
31
|
-
}
|
|
22
|
+
// Content is JSONB — arrays come back parsed, legacy strings stay as strings
|
|
23
|
+
const content = page.content ?? '';
|
|
32
24
|
|
|
33
25
|
if (Array.isArray(content)) {
|
|
34
26
|
// New BlockTuple format — extract text for TOC generation
|
|
@@ -32,16 +32,9 @@ export default defineEventHandler(async (event) => {
|
|
|
32
32
|
|
|
33
33
|
const pages = await listDocsPages(db, version.id);
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// Content is JSONB — arrays come back parsed, legacy strings stay as strings
|
|
36
36
|
return pages.map((page) => {
|
|
37
|
-
|
|
38
|
-
if (typeof content === 'string' && content.startsWith('[')) {
|
|
39
|
-
try {
|
|
40
|
-
content = JSON.parse(content);
|
|
41
|
-
} catch {
|
|
42
|
-
// Not valid JSON — keep as markdown string
|
|
43
|
-
}
|
|
44
|
-
}
|
|
37
|
+
const content = page.content ?? '';
|
|
45
38
|
return { ...page, content };
|
|
46
39
|
});
|
|
47
40
|
});
|
|
@@ -68,8 +68,7 @@ export default defineEventHandler(async (event) => {
|
|
|
68
68
|
const ext = contentType.split('/')[1] || 'jpg';
|
|
69
69
|
const key = generateStorageKey(purpose, ext);
|
|
70
70
|
|
|
71
|
-
await
|
|
72
|
-
const resultUrl = (storage as any).getPublicUrl(key);
|
|
71
|
+
const resultUrl = await storage.upload(key, buffer, contentType);
|
|
73
72
|
|
|
74
73
|
return { url: resultUrl };
|
|
75
74
|
});
|
|
@@ -76,6 +76,9 @@ export default defineNitroPlugin((nitro) => {
|
|
|
76
76
|
}
|
|
77
77
|
}, 5_000);
|
|
78
78
|
|
|
79
|
+
// Track the last date digests were sent to prevent duplicates on server restart during 8am hour
|
|
80
|
+
let lastDigestDate = '';
|
|
81
|
+
|
|
79
82
|
async function runDigest(siteUrl: string, siteName: string): Promise<void> {
|
|
80
83
|
try {
|
|
81
84
|
const db = useDB();
|
|
@@ -88,6 +91,11 @@ export default defineNitroPlugin((nitro) => {
|
|
|
88
91
|
|
|
89
92
|
if (!isDigestHour) return;
|
|
90
93
|
|
|
94
|
+
// Prevent duplicate sends if server restarts during digest hour
|
|
95
|
+
const todayKey = now.toISOString().slice(0, 10);
|
|
96
|
+
if (lastDigestDate === todayKey) return;
|
|
97
|
+
lastDigestDate = todayKey;
|
|
98
|
+
|
|
91
99
|
// Find users with digest preferences
|
|
92
100
|
const digestUsers = await db
|
|
93
101
|
.select({
|