@open-commune/web 0.1.3
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 +41 -0
- package/app.config.ts +10 -0
- package/app.vue +13 -0
- package/assets/css/main.css +13 -0
- package/components/data/display/row/data-avatar.vue +23 -0
- package/components/data/display/row/data-badge.vue +34 -0
- package/components/data/display/row/data-color.vue +17 -0
- package/components/data/display/row/data-display-badge-publish-status.vue +29 -0
- package/components/data/display/row/data-label.vue +37 -0
- package/components/data/display/row/data-last-update.vue +45 -0
- package/components/data/display/row/data-spacer.vue +4 -0
- package/components/data/display/row/data-summary.vue +44 -0
- package/components/data/display/row/data-text.vue +14 -0
- package/components/data/display/row/data-toggle.vue +16 -0
- package/components/data/display/row.vue +111 -0
- package/components/data/lists/data-list.vue +85 -0
- package/components/data/lists/data-lists-table.vue +155 -0
- package/components/elements/avatars/avatar-circular.vue +68 -0
- package/components/elements/avatars/avatar-with-text.vue +47 -0
- package/components/elements/badges/badge.vue +85 -0
- package/components/elements/buttons/primary.vue +32 -0
- package/components/elements/buttons/secondary.vue +35 -0
- package/components/elements/buttons/tertiary.vue +25 -0
- package/components/elements/dropdowns/dots.vue +75 -0
- package/components/elements/media/image.vue +82 -0
- package/components/elements/notifications/default.vue +90 -0
- package/components/forms/handlers/handler-collections.vue +189 -0
- package/components/forms/handlers/handler-publication-status.vue +193 -0
- package/components/forms/handlers/handler-records.vue +138 -0
- package/components/forms/handlers/handler-roles.vue +176 -0
- package/components/forms/handlers/handler-schedules.vue +218 -0
- package/components/forms/handlers/handler-steps.vue +147 -0
- package/components/forms/handlers/handler-tokens.vue +79 -0
- package/components/forms/inputs/input-color-picker.vue +159 -0
- package/components/forms/inputs/input-editor.vue +363 -0
- package/components/forms/inputs/input-files-media.vue +150 -0
- package/components/forms/inputs/input-files.vue +98 -0
- package/components/forms/inputs/input-json.vue +65 -0
- package/components/forms/inputs/input-location.vue +179 -0
- package/components/forms/inputs/input-text-area.vue +65 -0
- package/components/forms/inputs/input-text.vue +122 -0
- package/components/forms/inputs/input-toggle.vue +125 -0
- package/components/forms/inputs/media-section/media-editor.vue +95 -0
- package/components/forms/layouts/form.vue +203 -0
- package/components/forms/layouts/layout.vue +47 -0
- package/components/forms/layouts/modal.vue +96 -0
- package/components/forms/layouts/navigation.vue +48 -0
- package/components/forms/layouts/section.vue +118 -0
- package/components/forms/selects/select-collection-enum.vue +195 -0
- package/components/forms/selects/select-date.vue +245 -0
- package/components/forms/selects/select-enum.vue +186 -0
- package/components/forms/selects/select-record.vue +274 -0
- package/components/layout/headings/headings-collection.vue +29 -0
- package/components/layout/headings/headings-record.vue +33 -0
- package/components/layout/navigations/primary/dropdown-publish.vue +147 -0
- package/components/layout/navigations/primary.vue +132 -0
- package/components/layout/notifications/notifications-list.vue +19 -0
- package/composables/core/domains/use-definitions.ts +57 -0
- package/composables/core/domains/use-entries.ts +62 -0
- package/composables/core/domains/use-media.ts +82 -0
- package/composables/core/use-core.ts +12 -0
- package/composables/use-ability.js +38 -0
- package/composables/use-models.js +27 -0
- package/composables/use-session.ts +80 -0
- package/composables/use-timer.js +62 -0
- package/composables/use-trpc.js +17 -0
- package/editor/index.ts +44 -0
- package/editor/views/CalloutView.vue +22 -0
- package/editor/views/DetailsContentView.vue +11 -0
- package/editor/views/DetailsSummaryView.vue +11 -0
- package/editor/views/DetailsView.vue +72 -0
- package/editor/views/EmbedView.vue +53 -0
- package/editor/views/FileView.vue +79 -0
- package/editor/views/ImageView.vue +56 -0
- package/editor/views/UploadView.vue +71 -0
- package/layouts/default.vue +21 -0
- package/layouts/entrance.vue +10 -0
- package/layouts/record.vue +21 -0
- package/middleware/00.auth.global.js +16 -0
- package/middleware/01.instance.global.js +21 -0
- package/middleware/03.verify-permissions.global.js +11 -0
- package/middleware/04.prefetch-page.global.js +18 -0
- package/middleware/05.headings-page.global.js +25 -0
- package/models/_commun.js +243 -0
- package/models/_factory.js +34 -0
- package/models/collection.js +87 -0
- package/models/entrance.js +42 -0
- package/models/media.js +82 -0
- package/models/organization.js +92 -0
- package/models/record.js +165 -0
- package/models/role.js +66 -0
- package/models/token.js +56 -0
- package/models/user.js +97 -0
- package/nuxt.config.ts +151 -0
- package/package.json +71 -0
- package/pages/[collection]/[record]/index.vue +88 -0
- package/pages/[collection]/index.vue +55 -0
- package/pages/_lab/editor.vue +149 -0
- package/pages/auth/callback.vue +31 -0
- package/pages/index/index.vue +85 -0
- package/pages/index/password/define/[token].vue +79 -0
- package/pages/index/password/recover/index.vue +72 -0
- package/pages/index/welcome/[token].vue +90 -0
- package/pages/index.vue +44 -0
- package/pages/management/index.vue +15 -0
- package/pages/management/members.vue +176 -0
- package/pages/management/organizations.vue +192 -0
- package/pages/management/roles.vue +183 -0
- package/pages/overview.vue +15 -0
- package/pages/settings.vue +197 -0
- package/plugins/01.session.client.ts +5 -0
- package/plugins/02.vue-query.ts +25 -0
- package/public/favicon.ico +0 -0
- package/public/logo-black.svg +1 -0
- package/public/logo-white.svg +1 -0
- package/server/api/trpc/[...trpc].ts +28 -0
- package/server/api/v1/content/deployment.get.ts +45 -0
- package/server/api/v1/content/records.get.ts +12 -0
- package/server/middleware/0.security.ts +19 -0
- package/server/middleware/1.cors.ts +31 -0
- package/server/middleware/2.session.ts +20 -0
- package/server/middleware/3.api-token.ts +20 -0
- package/server/plugins/core.ts +8 -0
- package/server/routes/_tasks/[name].post.ts +20 -0
- package/server/routes/health.get.ts +6 -0
- package/server/tasks/deploy.ts +26 -0
- package/server/types.d.ts +8 -0
- package/server/utils/core.ts +18 -0
- package/store/layout/headings.js +21 -0
- package/store/layout/modal.js +80 -0
- package/store/layout/notifications.js +52 -0
- package/store/layout/workspace.js +80 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="w-full">
|
|
3
|
+
<!-- Rôles figés par Commun (admin / rédacteur) : consultation seule. -->
|
|
4
|
+
<p class="mb-6 rounded-md bg-amber-50 border border-amber-200 px-4 py-3 text-sm text-amber-800">
|
|
5
|
+
Les rôles sont désormais prédéfinis (administrateur, rédacteur) et ne sont plus personnalisables.
|
|
6
|
+
</p>
|
|
7
|
+
<default-table
|
|
8
|
+
v-if="!$route.params.event"
|
|
9
|
+
:headers="TABLE_HEADERS"
|
|
10
|
+
:filters="filters"
|
|
11
|
+
:items="records"
|
|
12
|
+
:actions="abilities"
|
|
13
|
+
:pagination-size="limit"
|
|
14
|
+
display-search-bar
|
|
15
|
+
paginated-content
|
|
16
|
+
@fetch-more="_fetchMore"
|
|
17
|
+
@update="_openModalForm('update', $event)"
|
|
18
|
+
@remove="_handle('remove', $event)"
|
|
19
|
+
/>
|
|
20
|
+
</section>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup>
|
|
24
|
+
import buttonSecondary from '~/components/elements/buttons/secondary'
|
|
25
|
+
import defaultTable from '~/components/data/lists/data-lists-table'
|
|
26
|
+
import { useModalStore } from '~/store/layout/modal'
|
|
27
|
+
|
|
28
|
+
definePageMeta({
|
|
29
|
+
permissions: ['manage:all'],
|
|
30
|
+
preFetch: ['Role'],
|
|
31
|
+
headings: {
|
|
32
|
+
title: 'Membres',
|
|
33
|
+
description: 'Retrouvez & managez ici toutes les membres de la plateforme.'
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const { Role } = useModels()
|
|
38
|
+
const $route = useRoute()
|
|
39
|
+
const $modalStore = useModalStore()
|
|
40
|
+
|
|
41
|
+
// Prepare constants
|
|
42
|
+
const TABLE_HEADERS = [
|
|
43
|
+
{
|
|
44
|
+
title: 'Nom',
|
|
45
|
+
property: 'name',
|
|
46
|
+
component: 'data-text'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: '',
|
|
50
|
+
component: 'actions'
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
// Prepare reactive data
|
|
55
|
+
const limit = ref(25)
|
|
56
|
+
|
|
57
|
+
// Prepare computed
|
|
58
|
+
const records = computed(() => Role.repo.all())
|
|
59
|
+
const filters = computed(() => [])
|
|
60
|
+
// Rôles figés : aucune action d'édition/suppression proposée.
|
|
61
|
+
const abilities = computed(() => [])
|
|
62
|
+
|
|
63
|
+
// Prepare methods
|
|
64
|
+
const _fetchMore = async (skip = 0) => {
|
|
65
|
+
await Role.list(undefined, undefined, { skip, limit: limit.value })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Prepare methods
|
|
69
|
+
const _handle = async (action, data = {}) => {
|
|
70
|
+
const { _id } = data
|
|
71
|
+
switch (action) {
|
|
72
|
+
case 'add':
|
|
73
|
+
await Role.create(data)
|
|
74
|
+
break
|
|
75
|
+
case 'update':
|
|
76
|
+
await Role.update(_id, data)
|
|
77
|
+
break
|
|
78
|
+
case 'remove':
|
|
79
|
+
await Role.remove(_id)
|
|
80
|
+
break
|
|
81
|
+
default:
|
|
82
|
+
throw new Error('E_UNHANDLED')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const _openModalForm = (action, record) => {
|
|
87
|
+
$modalStore.open({
|
|
88
|
+
onSubmit: async (data) => _handle(action, data),
|
|
89
|
+
details: {
|
|
90
|
+
title: `${action === 'add' ? 'Ajouter' : 'Modifier'} un rôle`,
|
|
91
|
+
subTitle: 'Reinseignez les informations de ce rôle.',
|
|
92
|
+
acceptButton: `${action === 'add' ? 'Ajouter' : 'Modifier'}`,
|
|
93
|
+
closeButton: 'Annuler'
|
|
94
|
+
},
|
|
95
|
+
attributes: [
|
|
96
|
+
{
|
|
97
|
+
property: 'name',
|
|
98
|
+
componentOptions: {
|
|
99
|
+
label: 'Nom du rôle',
|
|
100
|
+
name: 'name'
|
|
101
|
+
},
|
|
102
|
+
component: 'input-text'
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
property: 'description',
|
|
106
|
+
componentOptions: {
|
|
107
|
+
label: 'Description rôle',
|
|
108
|
+
name: 'description'
|
|
109
|
+
},
|
|
110
|
+
component: 'input-text'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
property: 'permissions',
|
|
114
|
+
componentOptions: {
|
|
115
|
+
label: 'Permissions du rôle',
|
|
116
|
+
name: 'permissions',
|
|
117
|
+
items: [
|
|
118
|
+
'manage:all',
|
|
119
|
+
'read:deployments',
|
|
120
|
+
'create:deployments',
|
|
121
|
+
'admin:organizations',
|
|
122
|
+
'create:organizations',
|
|
123
|
+
'read:organizations',
|
|
124
|
+
'update:organizations',
|
|
125
|
+
'create:collections',
|
|
126
|
+
'read:collections',
|
|
127
|
+
'update:collections',
|
|
128
|
+
'delete:collections',
|
|
129
|
+
'publish:records',
|
|
130
|
+
'create:records',
|
|
131
|
+
'read:records',
|
|
132
|
+
'update:records',
|
|
133
|
+
'delete:records',
|
|
134
|
+
'create:media',
|
|
135
|
+
'read:media',
|
|
136
|
+
'update:media',
|
|
137
|
+
'delete:media',
|
|
138
|
+
'create:users',
|
|
139
|
+
'read:users',
|
|
140
|
+
'update:users',
|
|
141
|
+
'delete:users',
|
|
142
|
+
'create:roles',
|
|
143
|
+
'read:roles',
|
|
144
|
+
'update:roles',
|
|
145
|
+
'delete:roles',
|
|
146
|
+
'create:tokens',
|
|
147
|
+
'read:tokens',
|
|
148
|
+
'update:tokens',
|
|
149
|
+
'delete:tokens'
|
|
150
|
+
],
|
|
151
|
+
enableMultiple: true,
|
|
152
|
+
object: false,
|
|
153
|
+
placeHolder: 'Ajouter une ou plusieurs permissions',
|
|
154
|
+
},
|
|
155
|
+
component: 'select-enum'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
property: 'onlyOwn',
|
|
159
|
+
componentOptions: {
|
|
160
|
+
label: 'Restriction créateur',
|
|
161
|
+
description: 'Restreindre le champs d\'action d\'un utilisateur à ses propres ressources uniquement.',
|
|
162
|
+
name: 'onlyOwn',
|
|
163
|
+
property: 'onlyOwn',
|
|
164
|
+
readOnly: true
|
|
165
|
+
},
|
|
166
|
+
component: 'input-toggle'
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
property: 'mfa',
|
|
170
|
+
componentOptions: {
|
|
171
|
+
label: 'Authentification multi-facteur',
|
|
172
|
+
description: 'Vérifier l\'authentification d\'un utilisateur via un facteur supplémentaire (authenticator, sms, ...).',
|
|
173
|
+
name: 'mfa',
|
|
174
|
+
property: 'mfa',
|
|
175
|
+
readOnly: true
|
|
176
|
+
},
|
|
177
|
+
component: 'input-toggle'
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
record
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
</script>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<forms-layout
|
|
3
|
+
v-bind="{ record, ...ORGANIZATION_FORM }"
|
|
4
|
+
class="mt-4"
|
|
5
|
+
/>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup>
|
|
9
|
+
import { useWorkspaceStore } from '~/store/layout/workspace'
|
|
10
|
+
import formsLayout from '~/components/forms/layouts/layout'
|
|
11
|
+
|
|
12
|
+
definePageMeta({
|
|
13
|
+
permissions: ['manage:all'],
|
|
14
|
+
preFetch: ['Organization'],
|
|
15
|
+
headings: {
|
|
16
|
+
title: 'Paramètres',
|
|
17
|
+
description: 'Retrouvez ici tous les paramètres de l\'organisation.'
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const $workspaceStore = useWorkspaceStore()
|
|
22
|
+
const ORGANIZATION_FORM = ref({
|
|
23
|
+
layout: [
|
|
24
|
+
{
|
|
25
|
+
icon: '',
|
|
26
|
+
sections: [
|
|
27
|
+
{
|
|
28
|
+
componentOptions: {
|
|
29
|
+
title: 'Logo de l\'organization',
|
|
30
|
+
description: 'Cliquez sur votre logo pour ajouter une image depuis vos fichiers.',
|
|
31
|
+
hint: 'Le logo est optionnel mais fortement recommandé.',
|
|
32
|
+
attributes: [
|
|
33
|
+
{
|
|
34
|
+
property: 'logo',
|
|
35
|
+
componentOptions: {
|
|
36
|
+
name: 'cover',
|
|
37
|
+
limit: 1,
|
|
38
|
+
label: 'Logo de l\'organization'
|
|
39
|
+
},
|
|
40
|
+
component: 'input-files-media'
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
component: 'form-section-default'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
componentOptions: {
|
|
48
|
+
title: 'Nom de l\'organization',
|
|
49
|
+
description: 'Ces informations seront visibles publiquement, soyez donc attentif avec ce que vous partagerez.',
|
|
50
|
+
hint: 'Merci de ne pas dépasser 128 charactères.',
|
|
51
|
+
attributes: [
|
|
52
|
+
{
|
|
53
|
+
property: 'name',
|
|
54
|
+
componentOptions: {
|
|
55
|
+
previewSlug: false,
|
|
56
|
+
editableSlug: false,
|
|
57
|
+
trim: true,
|
|
58
|
+
placeHolder: 'Nom de l\'organisation',
|
|
59
|
+
required: true
|
|
60
|
+
},
|
|
61
|
+
component: 'input-text'
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
component: 'form-section-default'
|
|
66
|
+
}
|
|
67
|
+
// {
|
|
68
|
+
// componentOptions: {
|
|
69
|
+
// title: 'Localisation de l\'organization',
|
|
70
|
+
// description: 'Ces informations seront visibles publiquement, soyez donc attentif avec ce que vous partagerez.',
|
|
71
|
+
// attributes: [
|
|
72
|
+
// {
|
|
73
|
+
// property: 'location',
|
|
74
|
+
// componentOptions: {
|
|
75
|
+
// placeHolder: 'Localisation de l\'organisation',
|
|
76
|
+
// name: 'organization'
|
|
77
|
+
// },
|
|
78
|
+
// component: 'input-location'
|
|
79
|
+
// }
|
|
80
|
+
// ]
|
|
81
|
+
// },
|
|
82
|
+
// component: 'form-section-default'
|
|
83
|
+
// }
|
|
84
|
+
],
|
|
85
|
+
title: 'General'
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
icon: '',
|
|
89
|
+
sections: [
|
|
90
|
+
{
|
|
91
|
+
componentOptions: {
|
|
92
|
+
title: 'Collections',
|
|
93
|
+
description: 'Ces collections permettent de définir les différents jeux de données de l\'organisation.',
|
|
94
|
+
hint: '🚦 Soyez vigilant lorsque vous modifiez ce champs.',
|
|
95
|
+
attributes: [
|
|
96
|
+
{
|
|
97
|
+
property: 'collections',
|
|
98
|
+
componentOptions: {},
|
|
99
|
+
component: 'handler-collections'
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
noAction: true
|
|
103
|
+
},
|
|
104
|
+
component: 'form-section-default'
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
title: 'Collections'
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
icon: '',
|
|
111
|
+
sections: [
|
|
112
|
+
{
|
|
113
|
+
componentOptions: {
|
|
114
|
+
title: 'Les thématiques',
|
|
115
|
+
description: 'Vous pouvez modifier ici les thématiques de cette organisation.',
|
|
116
|
+
attributes: [
|
|
117
|
+
{
|
|
118
|
+
componentOptions: {
|
|
119
|
+
collection: 'labels',
|
|
120
|
+
alwaysPublish: true
|
|
121
|
+
},
|
|
122
|
+
component: 'handler-records'
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
noAction: true
|
|
126
|
+
},
|
|
127
|
+
component: 'form-section-default'
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
title: 'Thématiques'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
icon: '',
|
|
134
|
+
sections: [
|
|
135
|
+
{
|
|
136
|
+
componentOptions: {
|
|
137
|
+
title: 'Deployment settings',
|
|
138
|
+
description: 'Renseignez la définition du déploiement.',
|
|
139
|
+
attributes: [
|
|
140
|
+
{
|
|
141
|
+
property: 'deployment',
|
|
142
|
+
componentOptions: {
|
|
143
|
+
label: 'Paramètres de déploiement',
|
|
144
|
+
name: 'deployment'
|
|
145
|
+
},
|
|
146
|
+
component: 'input-json'
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
component: 'form-section-default'
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
componentOptions: {
|
|
154
|
+
title: 'Tokens de déploiement',
|
|
155
|
+
description: 'Ces tokens permettent d\'autres applications à accéder à un contenu restreint de cette organisation.Soyez attentif!',
|
|
156
|
+
attributes: [
|
|
157
|
+
{
|
|
158
|
+
componentOptions: {},
|
|
159
|
+
component: 'handler-tokens'
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
noAction: true
|
|
163
|
+
},
|
|
164
|
+
component: 'form-section-default'
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
title: 'Déploiement'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
icon: '',
|
|
171
|
+
sections: [
|
|
172
|
+
{
|
|
173
|
+
componentOptions: {
|
|
174
|
+
title: 'Configuration de l\'injecteur',
|
|
175
|
+
description: 'Définissez les différentes pipelines de données ici',
|
|
176
|
+
attributes: [
|
|
177
|
+
{
|
|
178
|
+
property: 'injector',
|
|
179
|
+
componentOptions: {
|
|
180
|
+
label: 'Définition de l\'injecteur',
|
|
181
|
+
name: 'injector'
|
|
182
|
+
},
|
|
183
|
+
component: 'input-json'
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
component: 'form-section-default'
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
title: 'Synchronisation des données'
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// Prepare computed
|
|
196
|
+
const record = computed(() => $workspaceStore.workspace)
|
|
197
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Query cache — server state lives here, never in a store. Pages talk to the
|
|
5
|
+
* domain composables only.
|
|
6
|
+
*
|
|
7
|
+
* Key convention (prefix invalidation):
|
|
8
|
+
* ['definitions'] · ['entries', collectionId] · ['entries', 'byId', id]
|
|
9
|
+
* A write mutates then invalidates its domain prefix; every view showing it
|
|
10
|
+
* refreshes without a reload.
|
|
11
|
+
*/
|
|
12
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
13
|
+
const queryClient = new QueryClient({
|
|
14
|
+
defaultOptions: {
|
|
15
|
+
queries: {
|
|
16
|
+
// Freshness window, not eviction: data stays cached (gcTime) and is
|
|
17
|
+
// revalidated on next use once older than this.
|
|
18
|
+
staleTime: 30_000,
|
|
19
|
+
retry: 1,
|
|
20
|
+
refetchOnWindowFocus: false,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
nuxtApp.vueApp.use(VueQueryPlugin, { queryClient })
|
|
25
|
+
})
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436.25 186.76"><g id="Calque_2" data-name="Calque 2"><g id="Calque_1-2" data-name="Calque 1"><path d="M0,78.58H16.89q14.6,0,19.63,1.26a21.85,21.85,0,0,1,11.9,7.38q4.59,5.55,4.58,14t-4.49,13.93a22,22,0,0,1-12.39,7.42Q30.33,124,14.54,124H8.47v39.49H0Zm8.47,8.24v28.85l14.43.17q8.64,0,12.67-1.57a13.08,13.08,0,0,0,6.36-5.15,14.56,14.56,0,0,0,0-15.77,12.72,12.72,0,0,0-6.1-5q-3.78-1.49-12.36-1.49Z"/><path d="M98.22,97.93a31.17,31.17,0,0,1,24,10.47,33.74,33.74,0,0,1-.51,45.65q-9.16,9.81-23.53,9.81t-23.55-9.81a33.69,33.69,0,0,1-.49-45.59A31.09,31.09,0,0,1,98.22,97.93Zm0,7.9A23.16,23.16,0,0,0,81,113.27a24.87,24.87,0,0,0-7.21,18A25.83,25.83,0,0,0,77.07,144a23.16,23.16,0,0,0,8.9,9.11,25.43,25.43,0,0,0,24.59,0A23,23,0,0,0,119.4,144a26,26,0,0,0,3.26-12.73,24.87,24.87,0,0,0-7.21-18A23.16,23.16,0,0,0,98.22,105.83Z"/><path d="M144.7,99.53h8.13v29.3q0,10.77,1.14,14.77a16.44,16.44,0,0,0,6.58,9.13,20.13,20.13,0,0,0,11.68,3.35,19.61,19.61,0,0,0,11.45-3.26,16.68,16.68,0,0,0,6.47-8.59q1.24-3.6,1.25-15.4V99.53h8.25v30.85q0,13-3.07,19.55a23.2,23.2,0,0,1-9.12,10.24q-6.08,3.69-15.23,3.69t-15.34-3.69a22.81,22.81,0,0,1-9.16-10.36q-3-6.66-3-20.06Z"/><path d="M216.25,76.75h8.12v87.06h-8.12Z"/><path d="M258.52,101.1a35.52,35.52,0,0,1,14.39-2.94,31,31,0,0,1,22.9,9.61,31.8,31.8,0,0,1,9.5,23.18,32.34,32.34,0,0,1-9.45,23.47,30.6,30.6,0,0,1-22.72,9.67A31.09,31.09,0,0,1,259.35,161a33.81,33.81,0,0,1-11.39-9.3v35h-8V127.29a43.43,43.43,0,0,1,6.18-16.89A36.22,36.22,0,0,1,258.52,101.1Zm-3.92,12.11a25,25,0,0,0-7.22,18.26,25.93,25.93,0,0,0,3.21,12.85,23.37,23.37,0,0,0,9.16,9.1,25.31,25.31,0,0,0,12.59,3.35,24.06,24.06,0,0,0,12.28-3.35,24.57,24.57,0,0,0,9.16-9.45,25.71,25.71,0,0,0,3.4-12.73,25.39,25.39,0,0,0-3.38-12.71,24.63,24.63,0,0,0-9.07-9.3,24.24,24.24,0,0,0-12.5-3.35A23.75,23.75,0,0,0,254.6,113.21Z"/><path d="M401.3,4.32c-37.74-13.13-70.44,5.59-79.08,30.43-3.34,9.26-.69,14.91,2.11,18,5.32,5.88,14.82,6.74,19.87,5.48a14.89,14.89,0,0,0,4-1.73,9.79,9.79,0,0,0,1.15,3.84,7.13,7.13,0,0,0,6.43,3.57c3.73,0,7.56-2,11.54-6a9.2,9.2,0,0,0,.84,2.11,7.81,7.81,0,0,0,7.09,3.84c8.75,0,15.57-10.58,22.16-20.81.94-1.46,1.86-2.89,2.76-4.25,1.77,3.74,3.68,8.52,3.91,11.84a7,7,0,0,1-1.76,5.43,4.81,4.81,0,0,1-2.85,1.63c-2,0-2-.54-2-.94a2,2,0,0,1,.45-.6,3.29,3.29,0,0,0-4.12-5.12,7.7,7.7,0,0,0-2.89,5.6c0,3.52,2.24,7.63,8.57,7.63a11.09,11.09,0,0,0,7.65-3.71,13.62,13.62,0,0,0,3.51-10.36c-.51-7.65-6.92-18.92-7.19-19.4a3.28,3.28,0,0,0-5.5-.31c-2,2.74-4.07,5.92-6.05,9-5.11,7.94-11.48,17.81-16.64,17.81-1.07,0-1.27-.36-1.4-.57-.84-1.46-.43-5.44,1.51-8.83,1.56-2.72,3.09-5.54,4.23-7.68.23-.42.46-.87.69-1.3.55-1,.93-1.77,1.1-2.11a3.94,3.94,0,0,0,.3-.88,3.21,3.21,0,0,0-5.64-2.84,27,27,0,0,0-1.37,2.37c-.18.35-.39.75-.62,1.17-7,13-13.73,20.67-18.27,20.67-.57,0-.65-.14-.74-.29-.83-1.44-.29-5.72,1.65-9.09,3.06-5.35,6.43-11.2,6.43-11.2a4.59,4.59,0,0,0,.24-.59,3.27,3.27,0,0,0-5.9-2.76c-.94,1.56-1.82,3-2.66,4.47-4.56,7.71-7.57,12.8-12.24,14-3.41.85-10.13.09-13.39-3.51-2.33-2.58-2.6-6.41-.79-11.42C336,15.06,365.23-1.27,399.14,10.53c19.65,6.83,35.93,24.11,29,43.92-7.89,22.69-36.71,30.73-61.51,30.73h-1.24a45.27,45.27,0,0,1-8-.79l-.47-.1a11.66,11.66,0,0,0-7.84,1.07L330.2,95.14c5.05-9.58,9-19.17,7.18-23.2A3.36,3.36,0,0,0,333,70.18a3.2,3.2,0,0,0-1.69,4.25c.51,2.92-5.89,16.33-12.19,27a3.28,3.28,0,0,0,2.83,5,3.35,3.35,0,0,0,1.51-.36l28.64-14.86a5.09,5.09,0,0,1,3.42-.47l.63.13a52.64,52.64,0,0,0,9.2.91c27.36.29,59.86-8.78,69-35.14C440.68,38.53,431.33,14.77,401.3,4.32Z"/></g></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436.25 186.76"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="Calque_2" data-name="Calque 2"><g id="Calque_1-2" data-name="Calque 1"><path class="cls-1" d="M0,78.58H16.89q14.6,0,19.63,1.26a21.85,21.85,0,0,1,11.9,7.38q4.59,5.55,4.58,14t-4.49,13.93a22,22,0,0,1-12.39,7.42Q30.33,124,14.54,124H8.47v39.49H0Zm8.47,8.24v28.85l14.43.17q8.64,0,12.67-1.57a13.08,13.08,0,0,0,6.36-5.15,14.56,14.56,0,0,0,0-15.77,12.72,12.72,0,0,0-6.1-5q-3.78-1.49-12.36-1.49Z"/><path class="cls-1" d="M98.22,97.93a31.17,31.17,0,0,1,24,10.47,33.74,33.74,0,0,1-.51,45.65q-9.16,9.81-23.53,9.81t-23.55-9.81a33.69,33.69,0,0,1-.49-45.59A31.09,31.09,0,0,1,98.22,97.93Zm0,7.9A23.16,23.16,0,0,0,81,113.27a24.87,24.87,0,0,0-7.21,18A25.83,25.83,0,0,0,77.07,144a23.16,23.16,0,0,0,8.9,9.11,25.43,25.43,0,0,0,24.59,0A23,23,0,0,0,119.4,144a26,26,0,0,0,3.26-12.73,24.87,24.87,0,0,0-7.21-18A23.16,23.16,0,0,0,98.22,105.83Z"/><path class="cls-1" d="M144.7,99.53h8.13v29.3q0,10.77,1.14,14.77a16.44,16.44,0,0,0,6.58,9.13,20.13,20.13,0,0,0,11.68,3.35,19.61,19.61,0,0,0,11.45-3.26,16.68,16.68,0,0,0,6.47-8.59q1.24-3.6,1.25-15.4V99.53h8.25v30.85q0,13-3.07,19.55a23.2,23.2,0,0,1-9.12,10.24q-6.08,3.69-15.23,3.69t-15.34-3.69a22.81,22.81,0,0,1-9.16-10.36q-3-6.66-3-20.06Z"/><path class="cls-1" d="M216.25,76.75h8.12v87.06h-8.12Z"/><path class="cls-1" d="M258.52,101.1a35.52,35.52,0,0,1,14.39-2.94,31,31,0,0,1,22.9,9.61,31.8,31.8,0,0,1,9.5,23.18,32.34,32.34,0,0,1-9.45,23.47,30.6,30.6,0,0,1-22.72,9.67A31.09,31.09,0,0,1,259.35,161a33.81,33.81,0,0,1-11.39-9.3v35h-8V127.29a43.43,43.43,0,0,1,6.18-16.89A36.22,36.22,0,0,1,258.52,101.1Zm-3.92,12.11a25,25,0,0,0-7.22,18.26,25.93,25.93,0,0,0,3.21,12.85,23.37,23.37,0,0,0,9.16,9.1,25.31,25.31,0,0,0,12.59,3.35,24.06,24.06,0,0,0,12.28-3.35,24.57,24.57,0,0,0,9.16-9.45,25.71,25.71,0,0,0,3.4-12.73,25.39,25.39,0,0,0-3.38-12.71,24.63,24.63,0,0,0-9.07-9.3,24.24,24.24,0,0,0-12.5-3.35A23.75,23.75,0,0,0,254.6,113.21Z"/><path class="cls-1" d="M401.3,4.32c-37.74-13.13-70.44,5.59-79.08,30.43-3.34,9.26-.69,14.91,2.11,18,5.32,5.88,14.82,6.74,19.87,5.48a14.89,14.89,0,0,0,4-1.73,9.79,9.79,0,0,0,1.15,3.84,7.13,7.13,0,0,0,6.43,3.57c3.73,0,7.56-2,11.54-6a9.2,9.2,0,0,0,.84,2.11,7.81,7.81,0,0,0,7.09,3.84c8.75,0,15.57-10.58,22.16-20.81.94-1.46,1.86-2.89,2.76-4.25,1.77,3.74,3.68,8.52,3.91,11.84a7,7,0,0,1-1.76,5.43,4.81,4.81,0,0,1-2.85,1.63c-2,0-2-.54-2-.94a2,2,0,0,1,.45-.6,3.29,3.29,0,0,0-4.12-5.12,7.7,7.7,0,0,0-2.89,5.6c0,3.52,2.24,7.63,8.57,7.63a11.09,11.09,0,0,0,7.65-3.71,13.62,13.62,0,0,0,3.51-10.36c-.51-7.65-6.92-18.92-7.19-19.4a3.28,3.28,0,0,0-5.5-.31c-2,2.74-4.07,5.92-6.05,9-5.11,7.94-11.48,17.81-16.64,17.81-1.07,0-1.27-.36-1.4-.57-.84-1.46-.43-5.44,1.51-8.83,1.56-2.72,3.09-5.54,4.23-7.68.23-.42.46-.87.69-1.3.55-1,.93-1.77,1.1-2.11a3.94,3.94,0,0,0,.3-.88,3.21,3.21,0,0,0-5.64-2.84,27,27,0,0,0-1.37,2.37c-.18.35-.39.75-.62,1.17-7,13-13.73,20.67-18.27,20.67-.57,0-.65-.14-.74-.29-.83-1.44-.29-5.72,1.65-9.09,3.06-5.35,6.43-11.2,6.43-11.2a4.59,4.59,0,0,0,.24-.59,3.27,3.27,0,0,0-5.9-2.76c-.94,1.56-1.82,3-2.66,4.47-4.56,7.71-7.57,12.8-12.24,14-3.41.85-10.13.09-13.39-3.51-2.33-2.58-2.6-6.41-.79-11.42C336,15.06,365.23-1.27,399.14,10.53c19.65,6.83,35.93,24.11,29,43.92-7.89,22.69-36.71,30.73-61.51,30.73h-1.24a45.27,45.27,0,0,1-8-.79l-.47-.1a11.66,11.66,0,0,0-7.84,1.07L330.2,95.14c5.05-9.58,9-19.17,7.18-23.2A3.36,3.36,0,0,0,333,70.18a3.2,3.2,0,0,0-1.69,4.25c.51,2.92-5.89,16.33-12.19,27a3.28,3.28,0,0,0,2.83,5,3.35,3.35,0,0,0,1.51-.36l28.64-14.86a5.09,5.09,0,0,1,3.42-.47l.63.13a52.64,52.64,0,0,0,9.2.91c27.36.29,59.86-8.78,69-35.14C440.68,38.53,431.33,14.77,401.3,4.32Z"/></g></g></svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineEventHandler, toWebRequest } from 'h3';
|
|
2
|
+
import { appRouter } from '@open-commune/core/trpc';
|
|
3
|
+
import type { AuthSession } from '@open-commune/core';
|
|
4
|
+
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
|
|
5
|
+
import { useCore } from '../../utils/core.ts';
|
|
6
|
+
|
|
7
|
+
// Mounts the core tRPC router under /api/trpc/*. The context is assembled from
|
|
8
|
+
// what the plugin and the session middleware already attached to the event.
|
|
9
|
+
export default defineEventHandler(async (event) => {
|
|
10
|
+
return fetchRequestHandler({
|
|
11
|
+
endpoint: '/api/trpc',
|
|
12
|
+
req: toWebRequest(event),
|
|
13
|
+
router: appRouter,
|
|
14
|
+
createContext: () => ({
|
|
15
|
+
services: useCore().services,
|
|
16
|
+
session: (event.context.session as AuthSession | null | undefined) ?? null,
|
|
17
|
+
requestMeta: {
|
|
18
|
+
ua: event.node.req.headers['user-agent'] ?? null,
|
|
19
|
+
ip:
|
|
20
|
+
(event.node.req.headers['x-forwarded-for'] as string | undefined)
|
|
21
|
+
?.split(',')[0]
|
|
22
|
+
?.trim() ??
|
|
23
|
+
event.node.req.socket.remoteAddress ??
|
|
24
|
+
null,
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createError, defineEventHandler } from 'h3';
|
|
2
|
+
import { useCore } from '../../../utils/core.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Legacy-compatible plane: the payload current site builds consume —
|
|
6
|
+
* `_theme` (visual identity), `_pages` (page definitions) and `slugs`
|
|
7
|
+
* (static paths plus every published `/collection/slug`).
|
|
8
|
+
*/
|
|
9
|
+
export default defineEventHandler(async () => {
|
|
10
|
+
const { organization, collections, media } = useCore().services;
|
|
11
|
+
|
|
12
|
+
const settings = await organization.get();
|
|
13
|
+
if (!settings)
|
|
14
|
+
throw createError({ statusCode: 404, statusMessage: 'organization not initialized' });
|
|
15
|
+
|
|
16
|
+
const deployment = (settings.deployment ?? {}) as {
|
|
17
|
+
theme?: unknown;
|
|
18
|
+
definition?: Array<{ path?: string }>;
|
|
19
|
+
sort?: unknown;
|
|
20
|
+
};
|
|
21
|
+
// The theme lives under deployment.theme; older records may still carry it
|
|
22
|
+
// under settings.theme, hence the fallback below.
|
|
23
|
+
const theme =
|
|
24
|
+
deployment.theme ?? (settings.settings as { theme?: unknown } | null)?.theme ?? undefined;
|
|
25
|
+
const pageSlugs = (deployment.definition ?? [])
|
|
26
|
+
.map((page) => page.path ?? '')
|
|
27
|
+
.filter((path) => path && !path.includes(':'));
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
name: 'success',
|
|
31
|
+
description: 'Action succeed.',
|
|
32
|
+
data: {
|
|
33
|
+
// The organisation's imported identifier, when it has one.
|
|
34
|
+
_id: (settings.legacyExtra as { legacyId?: string } | null)?.legacyId ?? settings.id,
|
|
35
|
+
name: settings.name,
|
|
36
|
+
...(deployment.sort !== undefined ? { sort: deployment.sort } : {}),
|
|
37
|
+
// Iso legacy `_parseRecursively` : les chaînes `_media:<id>` de _theme/_pages
|
|
38
|
+
// sont remplacées par le média signé. Clé OMISE quand le thème est absent
|
|
39
|
+
// (an organisation may have no deployment.theme at all).
|
|
40
|
+
...(theme !== undefined ? { _theme: await media.resolveMediaPlaceholders(theme) } : {}),
|
|
41
|
+
_pages: await media.resolveMediaPlaceholders(deployment.definition ?? []),
|
|
42
|
+
slugs: [...pageSlugs, ...(await collections.publishedSlugs())],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineEventHandler } from 'h3';
|
|
2
|
+
import { useCore } from '../../../utils/core.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Legacy-compatible plane: the current site builds call this exact path.
|
|
6
|
+
* Flat map of published entries keyed by id, media resolved, wrapped in the
|
|
7
|
+
* `{ name, description, data }` envelope those clients expect.
|
|
8
|
+
*/
|
|
9
|
+
export default defineEventHandler(async () => {
|
|
10
|
+
const records = await useCore().services.collections.legacyRecordsPayload();
|
|
11
|
+
return { name: 'success', description: 'Action succeed.', data: { records } };
|
|
12
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { defineEventHandler, getRequestHeader, setResponseHeader } from 'h3';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* First middleware in the chain: correlation id and baseline security headers.
|
|
6
|
+
* Rate limiting belongs to the reverse proxy, not here.
|
|
7
|
+
*/
|
|
8
|
+
export default defineEventHandler((event) => {
|
|
9
|
+
const requestId = getRequestHeader(event, 'x-request-id') ?? randomUUID();
|
|
10
|
+
setResponseHeader(event, 'x-request-id', requestId);
|
|
11
|
+
|
|
12
|
+
setResponseHeader(event, 'x-content-type-options', 'nosniff');
|
|
13
|
+
setResponseHeader(event, 'x-frame-options', 'DENY');
|
|
14
|
+
setResponseHeader(event, 'referrer-policy', 'no-referrer');
|
|
15
|
+
// HSTS only behind TLS — never over plain http in dev.
|
|
16
|
+
if (getRequestHeader(event, 'x-forwarded-proto') === 'https') {
|
|
17
|
+
setResponseHeader(event, 'strict-transport-security', 'max-age=31536000; includeSubDomains');
|
|
18
|
+
}
|
|
19
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineEventHandler, handleCors } from 'h3';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Any origin is allowed: auth is a Bearer header, never a cookie, so a browser
|
|
5
|
+
* never attaches credentials on its own.
|
|
6
|
+
*
|
|
7
|
+
* The origin is REFLECTED rather than wildcarded — browsers reject
|
|
8
|
+
* `Access-Control-Allow-Origin: *` on credentialed requests, and nuxt-auth
|
|
9
|
+
* sends `credentials: 'include'`.
|
|
10
|
+
*/
|
|
11
|
+
export default defineEventHandler((event) => {
|
|
12
|
+
// h3 v1: returns true when it has answered the preflight itself.
|
|
13
|
+
if (
|
|
14
|
+
handleCors(event, {
|
|
15
|
+
origin: () => true,
|
|
16
|
+
credentials: true,
|
|
17
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
|
18
|
+
allowHeaders: [
|
|
19
|
+
'Content-Type',
|
|
20
|
+
'Authorization',
|
|
21
|
+
'x-trpc-source',
|
|
22
|
+
'x-trpc-batch-mode',
|
|
23
|
+
'Accept',
|
|
24
|
+
'trpc-accept',
|
|
25
|
+
],
|
|
26
|
+
maxAge: '600',
|
|
27
|
+
})
|
|
28
|
+
) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineEventHandler, getRequestHeader } from 'h3';
|
|
2
|
+
import { useCore } from '../utils/core.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Session resolution. Middlewares are numbered because order matters.
|
|
6
|
+
*
|
|
7
|
+
* Auth travels as a Bearer token, never a cookie, and is verified against the
|
|
8
|
+
* database on every request. Handlers read `event.context.session`.
|
|
9
|
+
*/
|
|
10
|
+
function readSessionToken(authorization: string): string | null {
|
|
11
|
+
if (!authorization.startsWith('Bearer ')) return null;
|
|
12
|
+
const token = authorization.slice('Bearer '.length);
|
|
13
|
+
// API tokens (commun_ prefix) belong to the content plane, not to sessions.
|
|
14
|
+
return token.startsWith('commun_') ? null : token;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default defineEventHandler(async (event) => {
|
|
18
|
+
const token = readSessionToken(getRequestHeader(event, 'authorization') ?? '');
|
|
19
|
+
event.context.session = token ? await useCore().services.users.verifySession(token) : null;
|
|
20
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getRequestHeader, getRequestURL } from 'h3';
|
|
2
|
+
import { useCore } from '../utils/core.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* API-token guard of the public content plane; runs after `2.session`.
|
|
6
|
+
*
|
|
7
|
+
* Accepts both `Authorization: Bearer <token>` and the bare
|
|
8
|
+
* `Authorization: <token>` form that existing site builds send.
|
|
9
|
+
*/
|
|
10
|
+
const GUARDED_PATHS = new Set(['/api/v1/content/records', '/api/v1/content/deployment']);
|
|
11
|
+
|
|
12
|
+
export default defineEventHandler(async (event) => {
|
|
13
|
+
if (!GUARDED_PATHS.has(getRequestURL(event).pathname)) return;
|
|
14
|
+
|
|
15
|
+
const auth = getRequestHeader(event, 'authorization') ?? '';
|
|
16
|
+
const token = auth.startsWith('Bearer ') ? auth.slice('Bearer '.length) : auth;
|
|
17
|
+
if (!token || !(await useCore().services.users.verifyApiToken(token))) {
|
|
18
|
+
throw createError({ statusCode: 401, statusMessage: 'missing or invalid API token' });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineNitroPlugin } from 'nitropack/runtime';
|
|
2
|
+
import { useCore } from '../utils/core.ts';
|
|
3
|
+
|
|
4
|
+
/** Instantiate the Core at server START, so migrations and config validation
|
|
5
|
+
* run at boot rather than on the first request. */
|
|
6
|
+
export default defineNitroPlugin(() => {
|
|
7
|
+
useCore();
|
|
8
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getRouterParam } from 'h3';
|
|
2
|
+
import { runTask } from 'nitropack/runtime';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Runs a Nitro task over HTTP, for the E2E suite only: Nitro's own task
|
|
6
|
+
* endpoint exists in the dev preset, and the suite boots the production bundle.
|
|
7
|
+
*
|
|
8
|
+
* DEAD unless COMMUN_TASKS_HTTP=1, which only the E2E harness sets — never the
|
|
9
|
+
* Docker image, the CI smoke test, or a deployed instance.
|
|
10
|
+
*/
|
|
11
|
+
export default defineEventHandler(async (event) => {
|
|
12
|
+
if (process.env.COMMUN_TASKS_HTTP !== '1') {
|
|
13
|
+
throw createError({ statusCode: 404 });
|
|
14
|
+
}
|
|
15
|
+
const name = getRouterParam(event, 'name');
|
|
16
|
+
if (!name) {
|
|
17
|
+
throw createError({ statusCode: 400, statusMessage: 'missing task name' });
|
|
18
|
+
}
|
|
19
|
+
return runTask(name);
|
|
20
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { defineEventHandler } from 'h3';
|
|
2
|
+
import { useCore } from '../utils/core.ts';
|
|
3
|
+
|
|
4
|
+
// Returns the HealthStatus DTO from @open-commune/core untouched (review: no
|
|
5
|
+
// object rebuilding in the transport layer).
|
|
6
|
+
export default defineEventHandler(() => useCore().services.health.check());
|