@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.
|
|
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/
|
|
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
|
+
});
|