@open-commune/web 0.1.5 → 0.2.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/README.md CHANGED
@@ -36,6 +36,19 @@ Run it with Bun: the database layer uses `bun:sqlite`, so `bun --bun nuxt dev`,
36
36
 
37
37
  Environment is validated at startup by `@open-commune/core`; see `.env.example` in the repository for the full list.
38
38
 
39
+ One variable matters when you deploy a build rather than run it in place. Nitro
40
+ inlines the core into its chunks, but the Drizzle migrations are files: the
41
+ build drops them next to the server output, and the deployment points at them.
42
+
43
+ ```
44
+ COMMUN_MIGRATIONS_DIR=/path/to/.output/drizzle
45
+ ```
46
+
47
+ Leave it unset in development — the layer resolves the folder from the
48
+ installed `@open-commune/core`. Unset in a deployment where `node_modules` is
49
+ gone, migrations are silently skipped and the first query fails on a missing
50
+ table.
51
+
39
52
  ## Licence
40
53
 
41
54
  AGPL-3.0-or-later.
@@ -3,7 +3,7 @@
3
3
  <label v-if="label" :for="name" class="block text-2xs font-light tracking-wider text-gray-500 uppercase">
4
4
  {{ label }}
5
5
  </label>
6
- <!-- Fixed toolbar, same layout as the legacy editor: discoverability for
6
+ <!-- Fixed toolbar: discoverability for
7
7
  authors who never hover a handle or type "/". Gestures stay available. -->
8
8
  <UEditorToolbar
9
9
  v-if="uEditor?.editor"
@@ -149,9 +149,8 @@
149
149
  // contract (shared cache, bounded resolution). Editing surface is
150
150
  // Notion-like: selection bubble, slash commands, per-block drag handle.
151
151
  //
152
- // TRANSITIONAL value contract: accepts the ProseMirror object (target) or
153
- // the legacy stringified JSON, and emits IN THE SAME SHAPE it received —
154
- // the string adapter dies with models/_commun.
152
+ // Value contract: accepts the ProseMirror document as an object or as
153
+ // stringified JSON, and emits IN THE SAME SHAPE it received.
155
154
  import { ref, watch } from 'vue'
156
155
  import { mapEditorItems } from '@nuxt/ui/utils/editor'
157
156
  import { communExtensions, communStarterKit, sanitizeDoc, EMBED_SERVICES } from '~/editor'
@@ -304,7 +303,7 @@ const slashItems = [
304
303
  ],
305
304
  ]
306
305
 
307
- // Fixed toolbar: the legacy composition — block catalog, link, marks, alignment.
306
+ // Fixed toolbar: block catalog, link, marks, alignment.
308
307
  const toolbarItems = [
309
308
  [{ slot: 'blocks' }],
310
309
  [
package/nuxt.config.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { globSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
2
+ import { cp } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { createRequire } from 'node:module';
3
5
  import { fileURLToPath } from 'node:url';
4
6
 
5
7
  // Under Bun's isolated store, the ESM build of @vue/devtools-api only exists
@@ -32,13 +34,21 @@ const devtoolsApiEsm = findDevtoolsApiEsm();
32
34
  // found.
33
35
  const layerDir = dirname(fileURLToPath(import.meta.url));
34
36
 
35
- // Le worker de dev empaquette les sources du core, donc le dossier de
36
- // migrations Drizzle n'y est pas resolvable relativement — on l'epingle ici,
37
- // ou le chemin est connu. (Repris de l'ancien apps/api/nitro.config.ts.)
38
- process.env.COMMUN_MIGRATIONS_DIR ??= resolve(
39
- fileURLToPath(new URL('.', import.meta.url)),
40
- '../../packages/core/drizzle',
41
- );
37
+ // Le worker de dev empaquette les sources du core : le dossier de migrations
38
+ // Drizzle n'y est plus resolvable relativement, on l'epingle ici. Par
39
+ // resolution de module et NON par chemin relatif — « ../../packages/core »
40
+ // n'est vrai que dans le monorepo, jamais chez qui installe la couche.
41
+ function coreDrizzleDir(): string | undefined {
42
+ try {
43
+ const pkg = createRequire(import.meta.url).resolve('@open-commune/core/package.json');
44
+ return join(dirname(pkg), 'drizzle');
45
+ } catch {
46
+ return undefined;
47
+ }
48
+ }
49
+
50
+ const drizzleDir = coreDrizzleDir();
51
+ if (drizzleDir) process.env.COMMUN_MIGRATIONS_DIR ??= drizzleDir;
42
52
 
43
53
  // https://nuxt.com/docs/api/configuration/nuxt-config
44
54
  export default defineNuxtConfig({
@@ -75,6 +85,15 @@ export default defineNuxtConfig({
75
85
  scheduledTasks: {
76
86
  '30 0 * * *': ['deploy'],
77
87
  },
88
+ hooks: {
89
+ // Nitro inline le core dans ses chunks : le dossier de migrations, lui,
90
+ // ne suit pas. On le depose a cote du build pour qu'il voyage avec, et
91
+ // le deploiement pointe COMMUN_MIGRATIONS_DIR dessus (l'image le fait).
92
+ async compiled(nitro) {
93
+ if (!drizzleDir) return;
94
+ await cp(drizzleDir, join(nitro.options.output.dir, 'drizzle'), { recursive: true });
95
+ },
96
+ },
78
97
  },
79
98
  css: [join(layerDir, 'assets/css/main.css')],
80
99
  // Scan nested composables (composables/core/domains/*).
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "@iconify/vue": "^5.0.1",
18
18
  "@nuxt/kit": "^4.5.1",
19
19
  "@nuxt/ui": "^4.3.0",
20
- "@open-commune/core": "^0.1.4",
20
+ "@open-commune/core": "^0.2.0",
21
21
  "@open-commune/editor": "^0.1.4",
22
22
  "@pinia-orm/nuxt": "^2.0.0",
23
23
  "@pinia/nuxt": "^1.0.1",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "description": "The Open Commune application: the admin interface and the server, on a single origin.",
42
42
  "license": "AGPL-3.0-or-later",
43
- "version": "0.1.5",
43
+ "version": "0.2.0",
44
44
  "type": "module",
45
45
  "main": "./nuxt.config.ts",
46
46
  "files": [
@@ -23,11 +23,6 @@
23
23
  />
24
24
  <button-primary type="submit" label="S'identifier" :loading="isLoading" class="w-full" />
25
25
  </form>
26
- <!-- Le lien de réinitialisation était commenté depuis le legacy, qui
27
- n'envoyait AUCUN email : le seul recours était le formulaire de
28
- support. L'événement `passwordResetRequested` étant désormais relayé
29
- vers un fournisseur d'emails, le parcours autonome reprend sa place —
30
- et Fillout redevient ce qu'il doit être, un second recours. -->
31
26
  <div class="flex mt-8 flex-col items-center justify-center">
32
27
  <button-tertiary
33
28
  label="Vous avez oublié votre mot de passe ?"
@@ -1,45 +0,0 @@
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
- });
@@ -1,12 +0,0 @@
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
- });
@@ -1,20 +0,0 @@
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
- });