@commonpub/layer 0.6.0 → 0.6.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -50,15 +50,15 @@
50
50
  "vue": "^3.4.0",
51
51
  "vue-router": "^4.3.0",
52
52
  "zod": "^4.3.6",
53
- "@commonpub/auth": "0.5.0",
54
- "@commonpub/config": "0.8.0",
55
53
  "@commonpub/docs": "0.6.0",
56
54
  "@commonpub/editor": "0.5.0",
57
55
  "@commonpub/learning": "0.5.0",
58
- "@commonpub/explainer": "0.7.1",
56
+ "@commonpub/auth": "0.5.0",
59
57
  "@commonpub/schema": "0.8.18",
60
- "@commonpub/server": "2.26.0",
61
58
  "@commonpub/protocol": "0.9.7",
59
+ "@commonpub/config": "0.8.0",
60
+ "@commonpub/server": "2.26.0",
61
+ "@commonpub/explainer": "0.7.1",
62
62
  "@commonpub/ui": "0.8.4"
63
63
  },
64
64
  "devDependencies": {
@@ -0,0 +1,56 @@
1
+ import { contentItems, users } from '@commonpub/schema';
2
+ import { eq, and, isNull } from 'drizzle-orm';
3
+
4
+ /**
5
+ * Server middleware: redirect old content URLs /{type}/{slug} → /u/{author}/{type}/{slug}.
6
+ *
7
+ * Runs before page rendering so the client receives a real HTTP 301,
8
+ * not a client-side navigation after a 200.
9
+ *
10
+ * Only matches known content types to avoid intercepting /hubs, /learn, /docs, etc.
11
+ */
12
+ const CONTENT_TYPES = new Set(['project', 'article', 'blog', 'explainer']);
13
+
14
+ export default defineEventHandler(async (event) => {
15
+ const path = getRequestURL(event).pathname;
16
+
17
+ // Match /{type}/{slug} or /{type}/{slug}/edit — only for known content types
18
+ const match = path.match(/^\/([a-z]+)\/([a-z0-9][a-z0-9-]*)(\/(edit))?$/);
19
+ if (!match) return;
20
+
21
+ const type = match[1]!;
22
+ const slug = match[2]!;
23
+ const isEdit = !!match[4];
24
+
25
+ if (!CONTENT_TYPES.has(type)) return;
26
+
27
+ // Skip AP requests — the old /content/[slug].ts AP route handles those
28
+ const accept = getRequestHeader(event, 'accept') ?? '';
29
+ if (accept.includes('application/activity+json') || accept.includes('application/ld+json')) return;
30
+
31
+ // /{type}/new/edit → /u/{username}/{type}/new/edit (requires auth context)
32
+ if (slug === 'new' && isEdit) {
33
+ // Can't redirect without knowing the user — let the page handler do it client-side
34
+ return;
35
+ }
36
+
37
+ // Look up the content author for existing content
38
+ const db = useDB();
39
+ const [row] = await db
40
+ .select({ username: users.username, type: contentItems.type, slug: contentItems.slug })
41
+ .from(contentItems)
42
+ .innerJoin(users, eq(contentItems.authorId, users.id))
43
+ .where(and(eq(contentItems.slug, slug), isNull(contentItems.deletedAt)))
44
+ .limit(1);
45
+
46
+ if (!row) return; // Content not found — let the page handler show 404
47
+
48
+ const newPath = isEdit
49
+ ? `/u/${row.username}/${row.type}/${row.slug}/edit`
50
+ : `/u/${row.username}/${row.type}/${row.slug}`;
51
+
52
+ // Preserve query string
53
+ const query = getRequestURL(event).search;
54
+
55
+ return sendRedirect(event, `${newPath}${query}`, 301);
56
+ });