@commonpub/layer 0.29.0 → 0.30.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/components/ContentCard.vue +13 -3
- package/components/CpubMarkdown.vue +46 -0
- package/components/NotificationItem.vue +45 -14
- package/components/contest/ContestEntries.vue +6 -3
- package/components/contest/ContestHero.vue +23 -2
- package/components/contest/ContestPrizes.vue +2 -2
- package/components/contest/ContestRules.vue +9 -9
- package/composables/useFeatures.ts +8 -0
- package/nuxt.config.ts +1 -0
- package/package.json +8 -8
- package/pages/contests/[slug]/edit.vue +80 -15
- package/pages/contests/[slug]/index.vue +2 -1
- package/pages/contests/[slug]/results.vue +20 -5
- package/pages/contests/create.vue +24 -13
- package/pages/events/[slug]/index.vue +1 -1
- package/pages/notifications.vue +9 -0
- package/server/api/admin/api-keys/[id]/usage.get.ts +1 -1
- package/server/api/admin/api-keys/[id].delete.ts +1 -1
- package/server/api/admin/api-keys/index.get.ts +1 -1
- package/server/api/admin/api-keys/index.post.ts +1 -1
- package/server/api/admin/audit.get.ts +1 -1
- package/server/api/admin/categories/[id].delete.ts +1 -1
- package/server/api/admin/categories/[id].patch.ts +1 -1
- package/server/api/admin/categories/index.get.ts +1 -1
- package/server/api/admin/categories/index.post.ts +1 -1
- package/server/api/admin/content/[id].delete.ts +1 -1
- package/server/api/admin/content/[id].patch.ts +1 -1
- package/server/api/admin/content/bulk-editorial.post.ts +1 -1
- package/server/api/admin/features/index.get.ts +1 -1
- package/server/api/admin/features/index.put.ts +1 -1
- package/server/api/admin/federation/activity.get.ts +1 -1
- package/server/api/admin/federation/clients.get.ts +1 -1
- package/server/api/admin/federation/clients.post.ts +1 -1
- package/server/api/admin/federation/hub-mirrors/[id]/backfill.post.ts +1 -1
- package/server/api/admin/federation/hub-mirrors/index.get.ts +1 -1
- package/server/api/admin/federation/hub-mirrors/index.post.ts +1 -1
- package/server/api/admin/federation/mirrors/[id]/backfill.post.ts +1 -1
- package/server/api/admin/federation/mirrors/[id].delete.ts +1 -1
- package/server/api/admin/federation/mirrors/[id].get.ts +1 -1
- package/server/api/admin/federation/mirrors/[id].put.ts +1 -1
- package/server/api/admin/federation/mirrors/index.get.ts +1 -1
- package/server/api/admin/federation/mirrors/index.post.ts +1 -1
- package/server/api/admin/federation/pending.get.ts +1 -1
- package/server/api/admin/federation/refederate.post.ts +1 -1
- package/server/api/admin/federation/repair-types.post.ts +1 -1
- package/server/api/admin/federation/retry.post.ts +1 -1
- package/server/api/admin/federation/stats.get.ts +1 -1
- package/server/api/admin/federation/trusted-instances.delete.ts +1 -1
- package/server/api/admin/federation/trusted-instances.get.ts +1 -1
- package/server/api/admin/federation/trusted-instances.post.ts +1 -1
- package/server/api/admin/homepage/sections.get.ts +1 -1
- package/server/api/admin/homepage/sections.put.ts +1 -1
- package/server/api/admin/layouts/[id]/publish.post.ts +1 -1
- package/server/api/admin/layouts/[id]/versions/[versionId]/revert.post.ts +1 -1
- package/server/api/admin/layouts/[id]/versions/index.get.ts +1 -1
- package/server/api/admin/layouts/[id].delete.ts +1 -1
- package/server/api/admin/layouts/[id].get.ts +1 -1
- package/server/api/admin/layouts/[id].put.ts +1 -1
- package/server/api/admin/layouts/index.get.ts +1 -1
- package/server/api/admin/layouts/index.post.ts +1 -1
- package/server/api/admin/layouts/migrate-homepage.post.ts +1 -1
- package/server/api/admin/layouts/seed-homepage.post.ts +1 -1
- package/server/api/admin/navigation/items.get.ts +1 -1
- package/server/api/admin/navigation/items.put.ts +1 -1
- package/server/api/admin/reports/[id]/resolve.post.ts +1 -1
- package/server/api/admin/reports.get.ts +1 -1
- package/server/api/admin/search/reindex.post.ts +1 -1
- package/server/api/admin/settings.get.ts +1 -1
- package/server/api/admin/settings.put.ts +1 -1
- package/server/api/admin/stats.get.ts +1 -1
- package/server/api/admin/storage/backfill-cdn-urls.post.ts +1 -1
- package/server/api/admin/themes/[id].delete.ts +1 -1
- package/server/api/admin/themes/[id].get.ts +1 -1
- package/server/api/admin/themes/[id].put.ts +1 -1
- package/server/api/admin/themes/discover.get.ts +1 -1
- package/server/api/admin/themes/index.get.ts +1 -1
- package/server/api/admin/themes/index.post.ts +1 -1
- package/server/api/admin/users/[id]/role.put.ts +1 -1
- package/server/api/admin/users/[id]/status.put.ts +1 -1
- package/server/api/admin/users/[id].delete.ts +1 -1
- package/server/api/admin/users.get.ts +1 -1
- package/server/api/contests/[slug]/entries.get.ts +3 -1
- package/server/api/contests/[slug]/index.delete.ts +4 -1
- package/server/api/contests/[slug]/judges/[userId].delete.ts +1 -1
- package/server/api/contests/[slug]/judges/index.post.ts +1 -1
- package/server/api/contests/[slug]/stakeholders/[userId].delete.ts +1 -1
- package/server/api/contests/[slug]/stakeholders/index.get.ts +1 -1
- package/server/api/contests/[slug]/stakeholders/index.post.ts +1 -1
- package/server/api/docs/migrate-content.post.ts +1 -1
- package/server/api/events/[slug].delete.ts +1 -1
- package/server/api/events/[slug].put.ts +1 -1
- package/server/api/layouts/by-route.get.ts +1 -1
- package/server/api/products/[id].delete.ts +1 -1
- package/server/api/videos/categories/[id].delete.ts +1 -1
- package/server/api/videos/categories/[id].put.ts +1 -1
- package/server/api/videos/categories.post.ts +1 -1
- package/server/middleware/auth.ts +22 -0
- package/server/utils/auth.ts +12 -5
- package/server/utils/permissions.ts +97 -0
- package/server/utils/requirePermission.ts +102 -0
|
@@ -8,6 +8,7 @@ const { extract: extractError } = useApiError();
|
|
|
8
8
|
const saving = ref(false);
|
|
9
9
|
|
|
10
10
|
const title = ref('');
|
|
11
|
+
const subheading = ref('');
|
|
11
12
|
const description = ref('');
|
|
12
13
|
const rules = ref('');
|
|
13
14
|
const bannerUrl = ref('');
|
|
@@ -90,6 +91,7 @@ async function handleCreate(): Promise<void> {
|
|
|
90
91
|
method: 'POST',
|
|
91
92
|
body: {
|
|
92
93
|
title: title.value,
|
|
94
|
+
subheading: subheading.value || undefined,
|
|
93
95
|
description: description.value || undefined,
|
|
94
96
|
rules: rules.value || undefined,
|
|
95
97
|
bannerUrl: bannerUrl.value || undefined,
|
|
@@ -103,13 +105,13 @@ async function handleCreate(): Promise<void> {
|
|
|
103
105
|
eligibleContentTypes: eligibleContentTypes.value.length ? eligibleContentTypes.value : undefined,
|
|
104
106
|
maxEntriesPerUser: maxEntriesPerUser.value && maxEntriesPerUser.value > 0 ? maxEntriesPerUser.value : undefined,
|
|
105
107
|
prizes: prizes.value
|
|
106
|
-
.filter(p => p.title.trim())
|
|
108
|
+
.filter(p => p.title.trim() || p.description.trim() || p.category.trim() || (typeof p.place === 'number' && p.place > 0))
|
|
107
109
|
.map(p => ({
|
|
108
110
|
place: typeof p.place === 'number' && Number.isFinite(p.place) && p.place > 0 ? p.place : undefined,
|
|
109
111
|
category: p.category.trim() || undefined,
|
|
110
|
-
title: p.title,
|
|
111
|
-
description: p.description || undefined,
|
|
112
|
-
value: p.value || undefined,
|
|
112
|
+
title: p.title.trim() || undefined,
|
|
113
|
+
description: p.description.trim() || undefined,
|
|
114
|
+
value: p.value.trim() || undefined,
|
|
113
115
|
})),
|
|
114
116
|
judgingCriteria: criteria.value
|
|
115
117
|
.filter(c => c.label.trim())
|
|
@@ -129,10 +131,13 @@ async function handleCreate(): Promise<void> {
|
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
function prizeLabel(prize: Prize
|
|
134
|
+
function prizeLabel(prize: Prize): string {
|
|
133
135
|
if (prize.category.trim()) return prize.category;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
if (prize.place && prize.place > 0) {
|
|
137
|
+
const labels = ['1st', '2nd', '3rd', '4th', '5th', '6th'];
|
|
138
|
+
return `${labels[prize.place - 1] || `${prize.place}th`} Place`;
|
|
139
|
+
}
|
|
140
|
+
return 'Prize';
|
|
136
141
|
}
|
|
137
142
|
</script>
|
|
138
143
|
|
|
@@ -149,17 +154,23 @@ function prizeLabel(prize: Prize, idx: number): string {
|
|
|
149
154
|
<label for="contest-title" class="cpub-form-label">Title</label>
|
|
150
155
|
<input id="contest-title" v-model="title" type="text" class="cpub-form-input" required placeholder="Maker Challenge 2026" />
|
|
151
156
|
</div>
|
|
157
|
+
<div class="cpub-form-field">
|
|
158
|
+
<label for="contest-subheading" class="cpub-form-label">Subheading</label>
|
|
159
|
+
<input id="contest-subheading" v-model="subheading" type="text" maxlength="300" class="cpub-form-input" placeholder="One-line tagline shown in the contest header" />
|
|
160
|
+
<p class="cpub-form-hint">Short plain-text tagline shown under the title in the hero. The Description below is the full body.</p>
|
|
161
|
+
</div>
|
|
152
162
|
<div class="cpub-form-field">
|
|
153
163
|
<label for="contest-desc" class="cpub-form-label">Description</label>
|
|
154
|
-
<textarea id="contest-desc" v-model="description" class="cpub-form-textarea" rows="
|
|
164
|
+
<textarea id="contest-desc" v-model="description" class="cpub-form-textarea" rows="4" placeholder="Describe your contest. Supports Markdown — # headings, - lists, **bold**, [links](url)…" />
|
|
165
|
+
<p class="cpub-form-hint">Supports Markdown (headings, lists, bold, links) and inline HTML. Shown formatted on the contest page.</p>
|
|
155
166
|
</div>
|
|
156
167
|
<div class="cpub-form-field">
|
|
157
168
|
<label for="contest-rules" class="cpub-form-label">Rules</label>
|
|
158
|
-
<textarea id="contest-rules" v-model="rules" class="cpub-form-textarea" rows="
|
|
169
|
+
<textarea id="contest-rules" v-model="rules" class="cpub-form-textarea" rows="6" placeholder="Contest rules and requirements. Supports Markdown — one rule per line, or full Markdown." />
|
|
170
|
+
<p class="cpub-form-hint">Supports Markdown. Plain one-rule-per-line text is rendered as a numbered list.</p>
|
|
159
171
|
</div>
|
|
160
172
|
<div class="cpub-form-field">
|
|
161
|
-
<
|
|
162
|
-
<input id="contest-banner" v-model="bannerUrl" type="url" class="cpub-form-input" placeholder="https://..." />
|
|
173
|
+
<ImageUpload v-model="bannerUrl" purpose="banner" label="Banner Image" hint="Wide image shown across the top of the contest page (~4:1)." />
|
|
163
174
|
</div>
|
|
164
175
|
</section>
|
|
165
176
|
|
|
@@ -274,11 +285,11 @@ function prizeLabel(prize: Prize, idx: number): string {
|
|
|
274
285
|
</button>
|
|
275
286
|
</div>
|
|
276
287
|
|
|
277
|
-
<p class="cpub-form-hint">Use <strong>place</strong> for ranked prizes (1st/2nd/3rd)
|
|
288
|
+
<p class="cpub-form-hint">Every field is optional. Use <strong>place</strong> for ranked prizes (1st/2nd/3rd), a <strong>category</strong> for themed awards (e.g. "Best in Show"), or just a <strong>description</strong>. Cash value is optional.</p>
|
|
278
289
|
<div v-for="(prize, idx) in prizes" :key="idx" class="cpub-prize-card">
|
|
279
290
|
<div class="cpub-prize-header">
|
|
280
291
|
<span class="cpub-prize-place">
|
|
281
|
-
<i class="fa-solid fa-trophy"></i> {{ prizeLabel(prize
|
|
292
|
+
<i class="fa-solid fa-trophy"></i> {{ prizeLabel(prize) }}
|
|
282
293
|
</span>
|
|
283
294
|
<button v-if="prizes.length > 1" type="button" class="cpub-delete-btn" aria-label="Remove prize" @click="removePrize(idx)">
|
|
284
295
|
<i class="fa-solid fa-xmark"></i>
|
package/pages/notifications.vue
CHANGED
|
@@ -27,6 +27,14 @@ async function deleteNotification(id: string): Promise<void> {
|
|
|
27
27
|
await $fetch(`/api/notifications/${id}`, { method: 'DELETE' });
|
|
28
28
|
refresh();
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
// Fired when a row is clicked/navigated — mark it read so the unread highlight
|
|
32
|
+
// + nav badge clear. Fire-and-forget: navigation proceeds regardless.
|
|
33
|
+
function onNotificationRead(id: string): void {
|
|
34
|
+
$fetch('/api/notifications/read', { method: 'POST', body: { notificationId: id } })
|
|
35
|
+
.then(() => refresh())
|
|
36
|
+
.catch(() => {});
|
|
37
|
+
}
|
|
30
38
|
</script>
|
|
31
39
|
|
|
32
40
|
<template>
|
|
@@ -59,6 +67,7 @@ async function deleteNotification(id: string): Promise<void> {
|
|
|
59
67
|
v-for="n in filteredNotifications"
|
|
60
68
|
:key="n.id"
|
|
61
69
|
:notification="n"
|
|
70
|
+
@read="onNotificationRead"
|
|
62
71
|
/>
|
|
63
72
|
<div v-if="!filteredNotifications.length" class="cpub-empty-state">
|
|
64
73
|
<div class="cpub-empty-state-icon"><i class="fa-solid fa-bell-slash"></i></div>
|
|
@@ -6,7 +6,7 @@ const querySchema = z.object({
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
|
-
|
|
9
|
+
requirePermission(event, 'apikeys.manage');
|
|
10
10
|
const id = getRouterParam(event, 'id');
|
|
11
11
|
if (!id) throw createError({ statusCode: 400, statusMessage: 'Missing id' });
|
|
12
12
|
const parsed = querySchema.safeParse(getQuery(event));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { revokeApiKey, createAuditEntry } from '@commonpub/server';
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
|
-
const user =
|
|
4
|
+
const user = requirePermission(event, 'apikeys.manage');
|
|
5
5
|
const id = getRouterParam(event, 'id');
|
|
6
6
|
if (!id) throw createError({ statusCode: 400, statusMessage: 'Missing id' });
|
|
7
7
|
const db = useDB();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { listApiKeys } from '@commonpub/server';
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
|
-
|
|
4
|
+
requirePermission(event, 'apikeys.manage');
|
|
5
5
|
const query = getQuery(event);
|
|
6
6
|
const includeRevoked = query.includeRevoked === 'true' || query.includeRevoked === '1';
|
|
7
7
|
const db = useDB();
|
|
@@ -14,7 +14,7 @@ import { createApiKeySchema } from '@commonpub/schema';
|
|
|
14
14
|
* land in the metadata column.
|
|
15
15
|
*/
|
|
16
16
|
export default defineEventHandler(async (event) => {
|
|
17
|
-
const user =
|
|
17
|
+
const user = requirePermission(event, 'apikeys.manage');
|
|
18
18
|
const body = await readBody(event);
|
|
19
19
|
const parsed = createApiKeySchema.safeParse(body);
|
|
20
20
|
if (!parsed.success) {
|
|
@@ -9,7 +9,7 @@ const auditQuerySchema = z.object({
|
|
|
9
9
|
|
|
10
10
|
export default defineEventHandler(async (event): Promise<PaginatedResponse<AuditLogItem>> => {
|
|
11
11
|
requireFeature('admin');
|
|
12
|
-
|
|
12
|
+
requirePermission(event, 'audit.read');
|
|
13
13
|
const db = useDB();
|
|
14
14
|
const filters = parseQueryParams(event, auditQuerySchema);
|
|
15
15
|
|
|
@@ -5,7 +5,7 @@ import { deleteContentCategory } from '@commonpub/server';
|
|
|
5
5
|
* Delete a content category (admin only). System categories cannot be deleted.
|
|
6
6
|
*/
|
|
7
7
|
export default defineEventHandler(async (event) => {
|
|
8
|
-
|
|
8
|
+
requirePermission(event, 'categories.manage');
|
|
9
9
|
const db = useDB();
|
|
10
10
|
const { id } = parseParams(event, { id: 'uuid' });
|
|
11
11
|
|
|
@@ -6,7 +6,7 @@ import { updateContentCategorySchema } from '@commonpub/schema';
|
|
|
6
6
|
* Update a content category (admin only).
|
|
7
7
|
*/
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
|
-
|
|
9
|
+
requirePermission(event, 'categories.manage');
|
|
10
10
|
const db = useDB();
|
|
11
11
|
const { id } = parseParams(event, { id: 'uuid' });
|
|
12
12
|
const body = await parseBody(event, updateContentCategorySchema);
|
|
@@ -5,7 +5,7 @@ import { listContentCategories } from '@commonpub/server';
|
|
|
5
5
|
* List all content categories (admin).
|
|
6
6
|
*/
|
|
7
7
|
export default defineEventHandler(async (event) => {
|
|
8
|
-
|
|
8
|
+
requirePermission(event, 'categories.manage');
|
|
9
9
|
const db = useDB();
|
|
10
10
|
return listContentCategories(db);
|
|
11
11
|
});
|
|
@@ -6,7 +6,7 @@ import { createContentCategorySchema } from '@commonpub/schema';
|
|
|
6
6
|
* Create a new content category (admin only).
|
|
7
7
|
*/
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
|
-
|
|
9
|
+
requirePermission(event, 'categories.manage');
|
|
10
10
|
const db = useDB();
|
|
11
11
|
const body = await parseBody(event, createContentCategorySchema);
|
|
12
12
|
return createContentCategory(db, body);
|
|
@@ -2,7 +2,7 @@ import { removeContent, removeFederatedContent } from '@commonpub/server';
|
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event): Promise<void> => {
|
|
4
4
|
requireFeature('admin');
|
|
5
|
-
const admin =
|
|
5
|
+
const admin = requirePermission(event, 'content.moderate');
|
|
6
6
|
const db = useDB();
|
|
7
7
|
const { id } = parseParams(event, { id: 'uuid' });
|
|
8
8
|
|
|
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
* Update admin-managed content fields (featured, editorial, category).
|
|
8
8
|
*/
|
|
9
9
|
export default defineEventHandler(async (event) => {
|
|
10
|
-
|
|
10
|
+
requirePermission(event, 'content.editorial');
|
|
11
11
|
|
|
12
12
|
const { id: contentId } = parseParams(event, { id: 'uuid' });
|
|
13
13
|
const body = await parseBody(event, z.object({
|
|
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
* Bulk update editorial status on multiple content items (admin only).
|
|
8
8
|
*/
|
|
9
9
|
export default defineEventHandler(async (event) => {
|
|
10
|
-
|
|
10
|
+
requirePermission(event, 'content.editorial');
|
|
11
11
|
|
|
12
12
|
const body = await parseBody(event, z.object({
|
|
13
13
|
ids: z.array(z.string().uuid()).min(1).max(100),
|
|
@@ -6,7 +6,7 @@ import type { FeatureFlags } from '@commonpub/config';
|
|
|
6
6
|
* Returns current feature flags with metadata about defaults vs overrides.
|
|
7
7
|
*/
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
|
-
|
|
9
|
+
requirePermission(event, 'settings.manage');
|
|
10
10
|
|
|
11
11
|
const db = useDB();
|
|
12
12
|
const config = useConfig();
|
|
@@ -15,7 +15,7 @@ const updateFeaturesSchema = z.object({
|
|
|
15
15
|
* To remove an override, omit the key from overrides.
|
|
16
16
|
*/
|
|
17
17
|
export default defineEventHandler(async (event) => {
|
|
18
|
-
const user =
|
|
18
|
+
const user = requirePermission(event, 'settings.manage');
|
|
19
19
|
|
|
20
20
|
const body = await parseBody(event, updateFeaturesSchema);
|
|
21
21
|
const db = useDB();
|
|
@@ -5,7 +5,7 @@ const VALID_STATUSES = ['pending', 'delivered', 'failed', 'processed'] as const;
|
|
|
5
5
|
|
|
6
6
|
export default defineEventHandler(async (event) => {
|
|
7
7
|
requireFeature('admin');
|
|
8
|
-
|
|
8
|
+
requirePermission(event, 'federation.manage');
|
|
9
9
|
const db = useDB();
|
|
10
10
|
|
|
11
11
|
const query = getQuery(event);
|
|
@@ -2,7 +2,7 @@ import { listOAuthClients } from '@commonpub/server';
|
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
requireFeature('federation');
|
|
5
|
-
|
|
5
|
+
requirePermission(event, 'federation.manage');
|
|
6
6
|
const db = useDB();
|
|
7
7
|
|
|
8
8
|
return listOAuthClients(db);
|
|
@@ -8,7 +8,7 @@ const registerSchema = z.object({
|
|
|
8
8
|
|
|
9
9
|
export default defineEventHandler(async (event) => {
|
|
10
10
|
requireFeature('federation');
|
|
11
|
-
|
|
11
|
+
requirePermission(event, 'federation.manage');
|
|
12
12
|
const db = useDB();
|
|
13
13
|
const { instanceDomain, redirectUris } = await parseBody(event, registerSchema);
|
|
14
14
|
|
|
@@ -3,7 +3,7 @@ import { backfillHubFromOutbox, fetchRemoteHubFollowers, repairFederatedHubPostA
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
requireFeature('federation');
|
|
5
5
|
requireFeature('federateHubs');
|
|
6
|
-
|
|
6
|
+
requirePermission(event, 'federation.manage');
|
|
7
7
|
|
|
8
8
|
const db = useDB();
|
|
9
9
|
const config = useConfig();
|
|
@@ -3,7 +3,7 @@ import { listFederatedHubs } from '@commonpub/server';
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
requireFeature('federation');
|
|
5
5
|
requireFeature('federateHubs');
|
|
6
|
-
|
|
6
|
+
requirePermission(event, 'federation.manage');
|
|
7
7
|
|
|
8
8
|
const db = useDB();
|
|
9
9
|
return listFederatedHubs(db);
|
|
@@ -8,7 +8,7 @@ const bodySchema = z.object({
|
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
9
|
requireFeature('federation');
|
|
10
10
|
requireFeature('federateHubs');
|
|
11
|
-
|
|
11
|
+
requirePermission(event, 'federation.manage');
|
|
12
12
|
|
|
13
13
|
const db = useDB();
|
|
14
14
|
const config = useConfig();
|
|
@@ -11,7 +11,7 @@ function extractDomain(url: string): string {
|
|
|
11
11
|
* Admin only.
|
|
12
12
|
*/
|
|
13
13
|
export default defineEventHandler(async (event) => {
|
|
14
|
-
|
|
14
|
+
requirePermission(event, 'federation.manage');
|
|
15
15
|
|
|
16
16
|
const config = useConfig();
|
|
17
17
|
if (!config.features.federation) {
|
|
@@ -2,7 +2,7 @@ import { cancelMirror } from '@commonpub/server';
|
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
requireFeature('federation');
|
|
5
|
-
|
|
5
|
+
requirePermission(event, 'federation.manage');
|
|
6
6
|
const db = useDB();
|
|
7
7
|
const { id } = parseParams(event, { id: 'uuid' });
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import { getMirror } from '@commonpub/server';
|
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
requireFeature('federation');
|
|
5
|
-
|
|
5
|
+
requirePermission(event, 'federation.manage');
|
|
6
6
|
const db = useDB();
|
|
7
7
|
const { id } = parseParams(event, { id: 'uuid' });
|
|
8
8
|
|
|
@@ -7,7 +7,7 @@ const updateMirrorSchema = z.object({
|
|
|
7
7
|
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
9
|
requireFeature('federation');
|
|
10
|
-
|
|
10
|
+
requirePermission(event, 'federation.manage');
|
|
11
11
|
const db = useDB();
|
|
12
12
|
const { id } = parseParams(event, { id: 'uuid' });
|
|
13
13
|
const { action } = await parseBody(event, updateMirrorSchema);
|
|
@@ -11,7 +11,7 @@ const createMirrorSchema = z.object({
|
|
|
11
11
|
|
|
12
12
|
export default defineEventHandler(async (event) => {
|
|
13
13
|
requireFeature('federation');
|
|
14
|
-
|
|
14
|
+
requirePermission(event, 'federation.manage');
|
|
15
15
|
const db = useDB();
|
|
16
16
|
const input = await parseBody(event, createMirrorSchema);
|
|
17
17
|
|
|
@@ -18,7 +18,7 @@ export default defineEventHandler(async (event) => {
|
|
|
18
18
|
if (cliSecret && cliSecret === runtimeConfig.authSecret) {
|
|
19
19
|
// Authorized via shared secret
|
|
20
20
|
} else {
|
|
21
|
-
|
|
21
|
+
requirePermission(event, 'federation.manage');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const config = useConfig();
|
|
@@ -7,7 +7,7 @@ import { repairFederatedContentTypes } from '@commonpub/server';
|
|
|
7
7
|
*/
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
9
|
requireFeature('admin');
|
|
10
|
-
|
|
10
|
+
requirePermission(event, 'federation.manage');
|
|
11
11
|
const db = useDB();
|
|
12
12
|
|
|
13
13
|
return repairFederatedContentTypes(db);
|
|
@@ -12,7 +12,7 @@ const retrySchema = z.object({
|
|
|
12
12
|
* Optionally filter by activity ID.
|
|
13
13
|
*/
|
|
14
14
|
export default defineEventHandler(async (event) => {
|
|
15
|
-
|
|
15
|
+
requirePermission(event, 'federation.manage');
|
|
16
16
|
|
|
17
17
|
const config = useConfig();
|
|
18
18
|
if (!config.features.federation) {
|
|
@@ -3,7 +3,7 @@ import { activities, followRelationships } from '@commonpub/schema';
|
|
|
3
3
|
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
requireFeature('admin');
|
|
6
|
-
|
|
6
|
+
requirePermission(event, 'federation.manage');
|
|
7
7
|
const db = useDB();
|
|
8
8
|
|
|
9
9
|
const [inbound, outbound, pending, failed, followers, following] = await Promise.all([
|
|
@@ -7,7 +7,7 @@ const removeSchema = z.object({
|
|
|
7
7
|
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
9
|
requireFeature('admin');
|
|
10
|
-
|
|
10
|
+
requirePermission(event, 'federation.manage');
|
|
11
11
|
const db = useDB();
|
|
12
12
|
const { domain } = await parseBody(event, removeSchema);
|
|
13
13
|
|
|
@@ -2,7 +2,7 @@ import { getStoredTrustedInstances } from '@commonpub/server';
|
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
requireFeature('admin');
|
|
5
|
-
|
|
5
|
+
requirePermission(event, 'federation.manage');
|
|
6
6
|
const db = useDB();
|
|
7
7
|
const config = useConfig();
|
|
8
8
|
|
|
@@ -7,7 +7,7 @@ const addSchema = z.object({
|
|
|
7
7
|
|
|
8
8
|
export default defineEventHandler(async (event) => {
|
|
9
9
|
requireFeature('admin');
|
|
10
|
-
|
|
10
|
+
requirePermission(event, 'federation.manage');
|
|
11
11
|
const db = useDB();
|
|
12
12
|
const { domain } = await parseBody(event, addSchema);
|
|
13
13
|
|
|
@@ -5,7 +5,7 @@ import { getHomepageSections } from '@commonpub/server';
|
|
|
5
5
|
* Returns homepage sections for admin editing.
|
|
6
6
|
*/
|
|
7
7
|
export default defineEventHandler(async (event) => {
|
|
8
|
-
|
|
8
|
+
requirePermission(event, 'layout.manage');
|
|
9
9
|
const db = useDB();
|
|
10
10
|
return getHomepageSections(db);
|
|
11
11
|
});
|
|
@@ -34,7 +34,7 @@ const updateSectionsSchema = z.object({
|
|
|
34
34
|
* Save homepage section configuration.
|
|
35
35
|
*/
|
|
36
36
|
export default defineEventHandler(async (event) => {
|
|
37
|
-
const user =
|
|
37
|
+
const user = requirePermission(event, 'layout.manage');
|
|
38
38
|
const db = useDB();
|
|
39
39
|
const body = await parseBody(event, updateSectionsSchema);
|
|
40
40
|
|
|
@@ -14,7 +14,7 @@ import { invalidateLayoutsByRouteCache } from '../../../../utils/layoutCache';
|
|
|
14
14
|
export default defineEventHandler(async (event) => {
|
|
15
15
|
requireFeature('admin');
|
|
16
16
|
requireFeature('layoutEngine');
|
|
17
|
-
const admin =
|
|
17
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
18
18
|
const db = useDB();
|
|
19
19
|
|
|
20
20
|
const id = getRouterParam(event, 'id');
|
|
@@ -15,7 +15,7 @@ import { invalidateLayoutsByRouteCache } from '../../../../../../utils/layoutCac
|
|
|
15
15
|
export default defineEventHandler(async (event) => {
|
|
16
16
|
requireFeature('admin');
|
|
17
17
|
requireFeature('layoutEngine');
|
|
18
|
-
const admin =
|
|
18
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
19
19
|
const db = useDB();
|
|
20
20
|
|
|
21
21
|
const id = getRouterParam(event, 'id');
|
|
@@ -12,7 +12,7 @@ import { getLayoutById, listLayoutVersions } from '@commonpub/server';
|
|
|
12
12
|
export default defineEventHandler(async (event) => {
|
|
13
13
|
requireFeature('admin');
|
|
14
14
|
requireFeature('layoutEngine');
|
|
15
|
-
|
|
15
|
+
requirePermission(event, 'layout.manage');
|
|
16
16
|
const db = useDB();
|
|
17
17
|
|
|
18
18
|
const id = getRouterParam(event, 'id');
|
|
@@ -15,7 +15,7 @@ import { invalidateLayoutsByRouteCache } from '../../../utils/layoutCache';
|
|
|
15
15
|
export default defineEventHandler(async (event): Promise<{ ok: true; id: string }> => {
|
|
16
16
|
requireFeature('admin');
|
|
17
17
|
requireFeature('layoutEngine');
|
|
18
|
-
const admin =
|
|
18
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
19
19
|
const db = useDB();
|
|
20
20
|
|
|
21
21
|
const id = getRouterParam(event, 'id');
|
|
@@ -11,7 +11,7 @@ import { getLayoutById } from '@commonpub/server';
|
|
|
11
11
|
export default defineEventHandler(async (event) => {
|
|
12
12
|
requireFeature('admin');
|
|
13
13
|
requireFeature('layoutEngine');
|
|
14
|
-
|
|
14
|
+
requirePermission(event, 'layout.manage');
|
|
15
15
|
|
|
16
16
|
const id = getRouterParam(event, 'id');
|
|
17
17
|
if (!id) {
|
|
@@ -27,7 +27,7 @@ import { validateSectionConfigs } from '../../../utils/validateSectionConfigs';
|
|
|
27
27
|
export default defineEventHandler(async (event) => {
|
|
28
28
|
requireFeature('admin');
|
|
29
29
|
requireFeature('layoutEngine');
|
|
30
|
-
const admin =
|
|
30
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
31
31
|
const db = useDB();
|
|
32
32
|
|
|
33
33
|
const id = getRouterParam(event, 'id');
|
|
@@ -12,7 +12,7 @@ import { listLayouts } from '@commonpub/server';
|
|
|
12
12
|
export default defineEventHandler(async (event) => {
|
|
13
13
|
requireFeature('admin');
|
|
14
14
|
requireFeature('layoutEngine');
|
|
15
|
-
|
|
15
|
+
requirePermission(event, 'layout.manage');
|
|
16
16
|
|
|
17
17
|
const db = useDB();
|
|
18
18
|
const query = getQuery(event) as { scope?: string };
|
|
@@ -19,7 +19,7 @@ import { validateSectionConfigs } from '../../../utils/validateSectionConfigs';
|
|
|
19
19
|
export default defineEventHandler(async (event) => {
|
|
20
20
|
requireFeature('admin');
|
|
21
21
|
requireFeature('layoutEngine');
|
|
22
|
-
const admin =
|
|
22
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
23
23
|
const db = useDB();
|
|
24
24
|
|
|
25
25
|
const body = await parseBody(event, layoutCreateSchema);
|
|
@@ -38,7 +38,7 @@ const bodySchema = z.object({
|
|
|
38
38
|
export default defineEventHandler(async (event) => {
|
|
39
39
|
requireFeature('admin');
|
|
40
40
|
requireFeature('layoutEngine');
|
|
41
|
-
const admin =
|
|
41
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
42
42
|
|
|
43
43
|
const body = await readBody(event).catch(() => ({}));
|
|
44
44
|
const { force } = bodySchema.parse(body ?? {});
|
|
@@ -24,7 +24,7 @@ import { invalidateLayoutsByRouteCache } from '../../../utils/layoutCache';
|
|
|
24
24
|
export default defineEventHandler(async (event) => {
|
|
25
25
|
requireFeature('admin');
|
|
26
26
|
requireFeature('layoutEngine');
|
|
27
|
-
const admin =
|
|
27
|
+
const admin = requirePermission(event, 'layout.manage');
|
|
28
28
|
const db = useDB();
|
|
29
29
|
|
|
30
30
|
const result = await seedHomepageLayout(db, { adminId: admin.id });
|
|
@@ -5,7 +5,7 @@ import { getNavItems } from '@commonpub/server';
|
|
|
5
5
|
* Returns navigation items for admin editing.
|
|
6
6
|
*/
|
|
7
7
|
export default defineEventHandler(async (event) => {
|
|
8
|
-
|
|
8
|
+
requirePermission(event, 'navigation.manage');
|
|
9
9
|
const db = useDB();
|
|
10
10
|
return getNavItems(db);
|
|
11
11
|
});
|
|
@@ -26,7 +26,7 @@ const updateNavSchema = z.object({
|
|
|
26
26
|
* Save navigation item configuration.
|
|
27
27
|
*/
|
|
28
28
|
export default defineEventHandler(async (event) => {
|
|
29
|
-
const user =
|
|
29
|
+
const user = requirePermission(event, 'navigation.manage');
|
|
30
30
|
const db = useDB();
|
|
31
31
|
const body = await parseBody(event, updateNavSchema);
|
|
32
32
|
|