@jant/core 0.3.7 → 0.3.9
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/dist/app.js +11 -4
- package/dist/client.js +1 -0
- package/dist/db/schema.js +15 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/lib/image.js +39 -15
- package/dist/lib/media-helpers.js +49 -0
- package/dist/lib/nav-reorder.js +27 -0
- package/dist/lib/navigation.js +35 -0
- package/dist/lib/storage.js +164 -0
- package/dist/lib/theme-components.js +49 -0
- package/dist/routes/api/posts.js +12 -7
- package/dist/routes/api/timeline.js +116 -0
- package/dist/routes/api/upload.js +35 -24
- package/dist/routes/dash/media.js +24 -14
- package/dist/routes/dash/navigation.js +274 -0
- package/dist/routes/dash/posts.js +4 -1
- package/dist/routes/feed/rss.js +3 -2
- package/dist/routes/pages/archive.js +14 -27
- package/dist/routes/pages/collection.js +10 -19
- package/dist/routes/pages/home.js +84 -126
- package/dist/routes/pages/page.js +19 -38
- package/dist/routes/pages/post.js +47 -56
- package/dist/routes/pages/search.js +13 -26
- package/dist/services/index.js +3 -1
- package/dist/services/media.js +8 -6
- package/dist/services/navigation.js +115 -0
- package/dist/services/post.js +26 -1
- package/dist/theme/components/PostForm.js +4 -3
- package/dist/theme/components/PostList.js +5 -0
- package/dist/theme/components/index.js +2 -0
- package/dist/theme/components/timeline/ArticleCard.js +50 -0
- package/dist/theme/components/timeline/ImageCard.js +86 -0
- package/dist/theme/components/timeline/LinkCard.js +62 -0
- package/dist/theme/components/timeline/NoteCard.js +37 -0
- package/dist/theme/components/timeline/QuoteCard.js +51 -0
- package/dist/theme/components/timeline/ThreadPreview.js +52 -0
- package/dist/theme/components/timeline/TimelineFeed.js +43 -0
- package/dist/theme/components/timeline/TimelineItem.js +25 -0
- package/dist/theme/components/timeline/index.js +8 -0
- package/dist/theme/layouts/DashLayout.js +8 -0
- package/dist/theme/layouts/SiteLayout.js +160 -0
- package/dist/theme/layouts/index.js +1 -0
- package/dist/types/sortablejs.d.js +5 -0
- package/dist/types.js +32 -0
- package/package.json +4 -2
- package/src/__tests__/helpers/app.ts +1 -0
- package/src/__tests__/helpers/db.ts +20 -0
- package/src/app.tsx +12 -7
- package/src/client.ts +1 -0
- package/src/db/migrations/0003_add_navigation_links.sql +8 -0
- package/src/db/migrations/0004_add_storage_provider.sql +3 -0
- package/src/db/migrations/meta/0003_snapshot.json +821 -0
- package/src/db/migrations/meta/_journal.json +21 -0
- package/src/db/schema.ts +15 -1
- package/src/i18n/locales/en.po +148 -80
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +150 -103
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +150 -103
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +5 -0
- package/src/lib/__tests__/image.test.ts +96 -0
- package/src/lib/__tests__/storage.test.ts +162 -0
- package/src/lib/__tests__/theme-components.test.ts +107 -0
- package/src/lib/image.ts +46 -16
- package/src/lib/media-helpers.ts +65 -0
- package/src/lib/nav-reorder.ts +26 -0
- package/src/lib/navigation.ts +46 -0
- package/src/lib/storage.ts +236 -0
- package/src/lib/theme-components.ts +76 -0
- package/src/routes/api/__tests__/posts.test.ts +8 -8
- package/src/routes/api/__tests__/timeline.test.ts +242 -0
- package/src/routes/api/posts.ts +20 -6
- package/src/routes/api/timeline.tsx +152 -0
- package/src/routes/api/upload.ts +52 -25
- package/src/routes/dash/media.tsx +40 -8
- package/src/routes/dash/navigation.tsx +306 -0
- package/src/routes/dash/posts.tsx +5 -0
- package/src/routes/feed/rss.ts +3 -2
- package/src/routes/pages/archive.tsx +15 -23
- package/src/routes/pages/collection.tsx +8 -15
- package/src/routes/pages/home.tsx +118 -122
- package/src/routes/pages/page.tsx +17 -30
- package/src/routes/pages/post.tsx +63 -60
- package/src/routes/pages/search.tsx +18 -22
- package/src/services/__tests__/media.test.ts +73 -28
- package/src/services/__tests__/navigation.test.ts +213 -0
- package/src/services/__tests__/post-timeline.test.ts +220 -0
- package/src/services/index.ts +7 -0
- package/src/services/media.ts +12 -8
- package/src/services/navigation.ts +165 -0
- package/src/services/post.ts +48 -1
- package/src/styles/components.css +59 -0
- package/src/theme/components/PostForm.tsx +13 -2
- package/src/theme/components/PostList.tsx +7 -0
- package/src/theme/components/index.ts +12 -0
- package/src/theme/components/timeline/ArticleCard.tsx +57 -0
- package/src/theme/components/timeline/ImageCard.tsx +80 -0
- package/src/theme/components/timeline/LinkCard.tsx +66 -0
- package/src/theme/components/timeline/NoteCard.tsx +41 -0
- package/src/theme/components/timeline/QuoteCard.tsx +55 -0
- package/src/theme/components/timeline/ThreadPreview.tsx +49 -0
- package/src/theme/components/timeline/TimelineFeed.tsx +52 -0
- package/src/theme/components/timeline/TimelineItem.tsx +39 -0
- package/src/theme/components/timeline/index.ts +8 -0
- package/src/theme/layouts/DashLayout.tsx +10 -0
- package/src/theme/layouts/SiteLayout.tsx +184 -0
- package/src/theme/layouts/index.ts +1 -0
- package/src/types/sortablejs.d.ts +23 -0
- package/src/types.ts +102 -1
- package/dist/app.d.ts +0 -38
- package/dist/app.d.ts.map +0 -1
- package/dist/auth.d.ts +0 -25
- package/dist/auth.d.ts.map +0 -1
- package/dist/db/index.d.ts +0 -10
- package/dist/db/index.d.ts.map +0 -1
- package/dist/db/schema.d.ts +0 -1543
- package/dist/db/schema.d.ts.map +0 -1
- package/dist/i18n/Trans.d.ts +0 -25
- package/dist/i18n/Trans.d.ts.map +0 -1
- package/dist/i18n/context.d.ts +0 -69
- package/dist/i18n/context.d.ts.map +0 -1
- package/dist/i18n/detect.d.ts +0 -20
- package/dist/i18n/detect.d.ts.map +0 -1
- package/dist/i18n/i18n.d.ts +0 -32
- package/dist/i18n/i18n.d.ts.map +0 -1
- package/dist/i18n/index.d.ts +0 -41
- package/dist/i18n/index.d.ts.map +0 -1
- package/dist/i18n/locales/en.d.ts +0 -3
- package/dist/i18n/locales/en.d.ts.map +0 -1
- package/dist/i18n/locales/zh-Hans.d.ts +0 -3
- package/dist/i18n/locales/zh-Hans.d.ts.map +0 -1
- package/dist/i18n/locales/zh-Hant.d.ts +0 -3
- package/dist/i18n/locales/zh-Hant.d.ts.map +0 -1
- package/dist/i18n/locales.d.ts +0 -11
- package/dist/i18n/locales.d.ts.map +0 -1
- package/dist/i18n/middleware.d.ts +0 -21
- package/dist/i18n/middleware.d.ts.map +0 -1
- package/dist/index.d.ts +0 -16
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/config.d.ts +0 -83
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/constants.d.ts +0 -37
- package/dist/lib/constants.d.ts.map +0 -1
- package/dist/lib/image.d.ts +0 -73
- package/dist/lib/image.d.ts.map +0 -1
- package/dist/lib/index.d.ts +0 -9
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/markdown.d.ts +0 -60
- package/dist/lib/markdown.d.ts.map +0 -1
- package/dist/lib/schemas.d.ts +0 -130
- package/dist/lib/schemas.d.ts.map +0 -1
- package/dist/lib/sqid.d.ts +0 -60
- package/dist/lib/sqid.d.ts.map +0 -1
- package/dist/lib/sse.d.ts +0 -192
- package/dist/lib/sse.d.ts.map +0 -1
- package/dist/lib/theme.d.ts +0 -44
- package/dist/lib/theme.d.ts.map +0 -1
- package/dist/lib/time.d.ts +0 -90
- package/dist/lib/time.d.ts.map +0 -1
- package/dist/lib/url.d.ts +0 -82
- package/dist/lib/url.d.ts.map +0 -1
- package/dist/middleware/auth.d.ts +0 -24
- package/dist/middleware/auth.d.ts.map +0 -1
- package/dist/middleware/onboarding.d.ts +0 -26
- package/dist/middleware/onboarding.d.ts.map +0 -1
- package/dist/routes/api/posts.d.ts +0 -13
- package/dist/routes/api/posts.d.ts.map +0 -1
- package/dist/routes/api/search.d.ts +0 -13
- package/dist/routes/api/search.d.ts.map +0 -1
- package/dist/routes/api/upload.d.ts +0 -16
- package/dist/routes/api/upload.d.ts.map +0 -1
- package/dist/routes/dash/collections.d.ts +0 -13
- package/dist/routes/dash/collections.d.ts.map +0 -1
- package/dist/routes/dash/index.d.ts +0 -15
- package/dist/routes/dash/index.d.ts.map +0 -1
- package/dist/routes/dash/media.d.ts +0 -16
- package/dist/routes/dash/media.d.ts.map +0 -1
- package/dist/routes/dash/pages.d.ts +0 -15
- package/dist/routes/dash/pages.d.ts.map +0 -1
- package/dist/routes/dash/posts.d.ts +0 -13
- package/dist/routes/dash/posts.d.ts.map +0 -1
- package/dist/routes/dash/redirects.d.ts +0 -13
- package/dist/routes/dash/redirects.d.ts.map +0 -1
- package/dist/routes/dash/settings.d.ts +0 -15
- package/dist/routes/dash/settings.d.ts.map +0 -1
- package/dist/routes/feed/rss.d.ts +0 -13
- package/dist/routes/feed/rss.d.ts.map +0 -1
- package/dist/routes/feed/sitemap.d.ts +0 -13
- package/dist/routes/feed/sitemap.d.ts.map +0 -1
- package/dist/routes/pages/archive.d.ts +0 -15
- package/dist/routes/pages/archive.d.ts.map +0 -1
- package/dist/routes/pages/collection.d.ts +0 -13
- package/dist/routes/pages/collection.d.ts.map +0 -1
- package/dist/routes/pages/home.d.ts +0 -13
- package/dist/routes/pages/home.d.ts.map +0 -1
- package/dist/routes/pages/page.d.ts +0 -15
- package/dist/routes/pages/page.d.ts.map +0 -1
- package/dist/routes/pages/post.d.ts +0 -13
- package/dist/routes/pages/post.d.ts.map +0 -1
- package/dist/routes/pages/search.d.ts +0 -13
- package/dist/routes/pages/search.d.ts.map +0 -1
- package/dist/services/collection.d.ts +0 -32
- package/dist/services/collection.d.ts.map +0 -1
- package/dist/services/index.d.ts +0 -28
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/media.d.ts +0 -34
- package/dist/services/media.d.ts.map +0 -1
- package/dist/services/post.d.ts +0 -31
- package/dist/services/post.d.ts.map +0 -1
- package/dist/services/redirect.d.ts +0 -15
- package/dist/services/redirect.d.ts.map +0 -1
- package/dist/services/search.d.ts +0 -26
- package/dist/services/search.d.ts.map +0 -1
- package/dist/services/settings.d.ts +0 -18
- package/dist/services/settings.d.ts.map +0 -1
- package/dist/theme/color-themes.d.ts +0 -30
- package/dist/theme/color-themes.d.ts.map +0 -1
- package/dist/theme/components/ActionButtons.d.ts +0 -43
- package/dist/theme/components/ActionButtons.d.ts.map +0 -1
- package/dist/theme/components/CrudPageHeader.d.ts +0 -23
- package/dist/theme/components/CrudPageHeader.d.ts.map +0 -1
- package/dist/theme/components/DangerZone.d.ts +0 -36
- package/dist/theme/components/DangerZone.d.ts.map +0 -1
- package/dist/theme/components/EmptyState.d.ts +0 -27
- package/dist/theme/components/EmptyState.d.ts.map +0 -1
- package/dist/theme/components/ListItemRow.d.ts +0 -15
- package/dist/theme/components/ListItemRow.d.ts.map +0 -1
- package/dist/theme/components/MediaGallery.d.ts +0 -13
- package/dist/theme/components/MediaGallery.d.ts.map +0 -1
- package/dist/theme/components/PageForm.d.ts +0 -14
- package/dist/theme/components/PageForm.d.ts.map +0 -1
- package/dist/theme/components/Pagination.d.ts +0 -46
- package/dist/theme/components/Pagination.d.ts.map +0 -1
- package/dist/theme/components/PostForm.d.ts +0 -16
- package/dist/theme/components/PostForm.d.ts.map +0 -1
- package/dist/theme/components/PostList.d.ts +0 -10
- package/dist/theme/components/PostList.d.ts.map +0 -1
- package/dist/theme/components/ThreadView.d.ts +0 -15
- package/dist/theme/components/ThreadView.d.ts.map +0 -1
- package/dist/theme/components/TypeBadge.d.ts +0 -12
- package/dist/theme/components/TypeBadge.d.ts.map +0 -1
- package/dist/theme/components/VisibilityBadge.d.ts +0 -12
- package/dist/theme/components/VisibilityBadge.d.ts.map +0 -1
- package/dist/theme/components/index.d.ts +0 -14
- package/dist/theme/components/index.d.ts.map +0 -1
- package/dist/theme/index.d.ts +0 -21
- package/dist/theme/index.d.ts.map +0 -1
- package/dist/theme/layouts/BaseLayout.d.ts +0 -23
- package/dist/theme/layouts/BaseLayout.d.ts.map +0 -1
- package/dist/theme/layouts/DashLayout.d.ts +0 -17
- package/dist/theme/layouts/DashLayout.d.ts.map +0 -1
- package/dist/theme/layouts/index.d.ts +0 -3
- package/dist/theme/layouts/index.d.ts.map +0 -1
- package/dist/types.d.ts +0 -237
- package/dist/types.d.ts.map +0 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site Layout
|
|
3
|
+
*
|
|
4
|
+
* Two-column layout for public pages with sidebar navigation.
|
|
5
|
+
* On mobile, uses a slide-out drawer menu.
|
|
6
|
+
*/ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
7
|
+
/**
|
|
8
|
+
* Determine if a navigation link is active based on the current path.
|
|
9
|
+
*
|
|
10
|
+
* @param linkUrl - The link's URL
|
|
11
|
+
* @param currentPath - The current page path
|
|
12
|
+
* @returns Whether the link should be shown as active
|
|
13
|
+
*/ function isLinkActive(linkUrl, currentPath) {
|
|
14
|
+
// External links are never active
|
|
15
|
+
if (linkUrl.startsWith("http://") || linkUrl.startsWith("https://")) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
// Exact match for home
|
|
19
|
+
if (linkUrl === "/") {
|
|
20
|
+
return currentPath === "/";
|
|
21
|
+
}
|
|
22
|
+
// Prefix match for other internal links
|
|
23
|
+
return currentPath === linkUrl || currentPath.startsWith(linkUrl + "/");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a URL is external
|
|
27
|
+
*/ function isExternalUrl(url) {
|
|
28
|
+
return url.startsWith("http://") || url.startsWith("https://");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Render navigation links with dot indicator for active state.
|
|
32
|
+
*/ function NavLinks({ navigationLinks, currentPath }) {
|
|
33
|
+
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
34
|
+
children: navigationLinks.map((link)=>{
|
|
35
|
+
const active = isLinkActive(link.url, currentPath);
|
|
36
|
+
const external = isExternalUrl(link.url);
|
|
37
|
+
return /*#__PURE__*/ _jsxs("a", {
|
|
38
|
+
href: link.url,
|
|
39
|
+
class: `text-sm flex items-center gap-2 py-0.5 ${active ? "text-primary font-medium" : "text-muted-foreground hover:text-foreground"}`,
|
|
40
|
+
...external ? {
|
|
41
|
+
target: "_blank",
|
|
42
|
+
rel: "noopener noreferrer"
|
|
43
|
+
} : {},
|
|
44
|
+
children: [
|
|
45
|
+
/*#__PURE__*/ _jsx("span", {
|
|
46
|
+
class: `size-1.5 rounded-full shrink-0 ${active ? "bg-primary" : "bg-transparent"}`
|
|
47
|
+
}),
|
|
48
|
+
link.label,
|
|
49
|
+
external && /*#__PURE__*/ _jsx("span", {
|
|
50
|
+
class: "ml-1 text-xs opacity-50",
|
|
51
|
+
children: "↗"
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
}, link.id);
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export const SiteLayout = ({ siteName, navigationLinks, currentPath, children })=>{
|
|
59
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
60
|
+
class: "container py-8 md:flex md:gap-12",
|
|
61
|
+
"data-signals": JSON.stringify({
|
|
62
|
+
_drawerOpen: false
|
|
63
|
+
}),
|
|
64
|
+
children: [
|
|
65
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
66
|
+
class: "flex items-center justify-between mb-6 md:hidden",
|
|
67
|
+
children: [
|
|
68
|
+
/*#__PURE__*/ _jsx("a", {
|
|
69
|
+
href: "/",
|
|
70
|
+
class: "text-xl font-semibold",
|
|
71
|
+
children: siteName
|
|
72
|
+
}),
|
|
73
|
+
/*#__PURE__*/ _jsx("button", {
|
|
74
|
+
"data-on:click": "$_drawerOpen = true",
|
|
75
|
+
class: "p-2 -mr-2 text-muted-foreground hover:text-foreground",
|
|
76
|
+
"aria-label": "Open menu",
|
|
77
|
+
children: /*#__PURE__*/ _jsx("svg", {
|
|
78
|
+
class: "size-5",
|
|
79
|
+
fill: "none",
|
|
80
|
+
viewBox: "0 0 24 24",
|
|
81
|
+
"stroke-width": "1.5",
|
|
82
|
+
stroke: "currentColor",
|
|
83
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
84
|
+
"stroke-linecap": "round",
|
|
85
|
+
"stroke-linejoin": "round",
|
|
86
|
+
d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
}),
|
|
92
|
+
/*#__PURE__*/ _jsx("div", {
|
|
93
|
+
class: "fixed inset-0 bg-black/50 z-40 opacity-0 pointer-events-none transition-opacity duration-300 ease-in-out md:hidden",
|
|
94
|
+
"data-class": "{'opacity-100 pointer-events-auto': $_drawerOpen, 'opacity-0 pointer-events-none': !$_drawerOpen}",
|
|
95
|
+
"data-on:click": "$_drawerOpen = false"
|
|
96
|
+
}),
|
|
97
|
+
/*#__PURE__*/ _jsxs("aside", {
|
|
98
|
+
class: "fixed inset-y-0 left-0 w-64 bg-background z-50 p-6 overflow-y-auto shadow-lg -translate-x-full transition-transform duration-300 ease-in-out md:hidden",
|
|
99
|
+
"data-class": "{'translate-x-0': $_drawerOpen, '-translate-x-full': !$_drawerOpen}",
|
|
100
|
+
children: [
|
|
101
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
102
|
+
class: "flex items-center justify-between mb-8",
|
|
103
|
+
children: [
|
|
104
|
+
/*#__PURE__*/ _jsx("a", {
|
|
105
|
+
href: "/",
|
|
106
|
+
class: "text-xl font-semibold",
|
|
107
|
+
children: siteName
|
|
108
|
+
}),
|
|
109
|
+
/*#__PURE__*/ _jsx("button", {
|
|
110
|
+
"data-on:click": "$_drawerOpen = false",
|
|
111
|
+
class: "p-2 -mr-2 text-muted-foreground hover:text-foreground",
|
|
112
|
+
"aria-label": "Close menu",
|
|
113
|
+
children: /*#__PURE__*/ _jsx("svg", {
|
|
114
|
+
class: "size-5",
|
|
115
|
+
fill: "none",
|
|
116
|
+
viewBox: "0 0 24 24",
|
|
117
|
+
"stroke-width": "1.5",
|
|
118
|
+
stroke: "currentColor",
|
|
119
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
120
|
+
"stroke-linecap": "round",
|
|
121
|
+
"stroke-linejoin": "round",
|
|
122
|
+
d: "M6 18L18 6M6 6l12 12"
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
}),
|
|
128
|
+
/*#__PURE__*/ _jsx("nav", {
|
|
129
|
+
class: "flex flex-col gap-0.5",
|
|
130
|
+
children: /*#__PURE__*/ _jsx(NavLinks, {
|
|
131
|
+
navigationLinks: navigationLinks,
|
|
132
|
+
currentPath: currentPath
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
]
|
|
136
|
+
}),
|
|
137
|
+
/*#__PURE__*/ _jsxs("aside", {
|
|
138
|
+
class: "hidden md:block md:w-48 md:shrink-0 md:sticky md:top-8 md:self-start",
|
|
139
|
+
children: [
|
|
140
|
+
/*#__PURE__*/ _jsx("a", {
|
|
141
|
+
href: "/",
|
|
142
|
+
class: "text-xl font-semibold block mb-20",
|
|
143
|
+
children: siteName
|
|
144
|
+
}),
|
|
145
|
+
/*#__PURE__*/ _jsx("nav", {
|
|
146
|
+
class: "flex flex-col gap-0.5",
|
|
147
|
+
children: /*#__PURE__*/ _jsx(NavLinks, {
|
|
148
|
+
navigationLinks: navigationLinks,
|
|
149
|
+
currentPath: currentPath
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
}),
|
|
154
|
+
/*#__PURE__*/ _jsx("main", {
|
|
155
|
+
class: "flex-1 min-w-0",
|
|
156
|
+
children: children
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
};
|
package/dist/types.js
CHANGED
|
@@ -38,6 +38,10 @@ export const MAX_MEDIA_ATTACHMENTS = 20;
|
|
|
38
38
|
],
|
|
39
39
|
page: null
|
|
40
40
|
};
|
|
41
|
+
export const STORAGE_DRIVERS = [
|
|
42
|
+
"r2",
|
|
43
|
+
"s3"
|
|
44
|
+
];
|
|
41
45
|
export const VISIBILITY_LEVELS = [
|
|
42
46
|
"featured",
|
|
43
47
|
"quiet",
|
|
@@ -94,5 +98,33 @@ export const VISIBILITY_LEVELS = [
|
|
|
94
98
|
DEMO_PASSWORD: {
|
|
95
99
|
defaultValue: "",
|
|
96
100
|
envOnly: true
|
|
101
|
+
},
|
|
102
|
+
STORAGE_DRIVER: {
|
|
103
|
+
defaultValue: "r2",
|
|
104
|
+
envOnly: true
|
|
105
|
+
},
|
|
106
|
+
S3_ENDPOINT: {
|
|
107
|
+
defaultValue: "",
|
|
108
|
+
envOnly: true
|
|
109
|
+
},
|
|
110
|
+
S3_BUCKET: {
|
|
111
|
+
defaultValue: "",
|
|
112
|
+
envOnly: true
|
|
113
|
+
},
|
|
114
|
+
S3_ACCESS_KEY_ID: {
|
|
115
|
+
defaultValue: "",
|
|
116
|
+
envOnly: true
|
|
117
|
+
},
|
|
118
|
+
S3_SECRET_ACCESS_KEY: {
|
|
119
|
+
defaultValue: "",
|
|
120
|
+
envOnly: true
|
|
121
|
+
},
|
|
122
|
+
S3_REGION: {
|
|
123
|
+
defaultValue: "auto",
|
|
124
|
+
envOnly: true
|
|
125
|
+
},
|
|
126
|
+
S3_PUBLIC_URL: {
|
|
127
|
+
defaultValue: "",
|
|
128
|
+
envOnly: true
|
|
97
129
|
}
|
|
98
130
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jant/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "A modern, open-source microblogging platform built on Cloudflare Workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,12 +31,14 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@aws-sdk/client-s3": "^3.987.0",
|
|
34
35
|
"@lingui/core": "^5.9.0",
|
|
35
36
|
"basecoat-css": "^0.3.10",
|
|
36
37
|
"better-auth": "^1.4.18",
|
|
37
38
|
"drizzle-orm": "^0.45.1",
|
|
38
39
|
"hono": "^4.11.7",
|
|
39
40
|
"marked": "^17.0.1",
|
|
41
|
+
"sortablejs": "^1.15.6",
|
|
40
42
|
"sqids": "^0.3.0",
|
|
41
43
|
"uuidv7": "^1.1.0",
|
|
42
44
|
"vite-ssr-components": "^0.5.2",
|
|
@@ -90,7 +92,7 @@
|
|
|
90
92
|
"build": "pnpm build:lib",
|
|
91
93
|
"build:lib": "swc src -d dist --strip-leading-paths --ignore '**/__tests__/**' && pnpm build:types",
|
|
92
94
|
"build:types": "tsc -p tsconfig.build.json",
|
|
93
|
-
"typecheck": "tsc --noEmit
|
|
95
|
+
"typecheck": "tsc --noEmit",
|
|
94
96
|
"db:generate": "drizzle-kit generate",
|
|
95
97
|
"i18n:extract": "lingui extract",
|
|
96
98
|
"i18n:compile": "lingui compile --typescript",
|
|
@@ -89,6 +89,26 @@ export function createTestDatabase(options?: { fts?: boolean }) {
|
|
|
89
89
|
if (trimmed) sqlite.exec(trimmed);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// Apply navigation links migration
|
|
93
|
+
const migration3 = readFileSync(
|
|
94
|
+
resolve(MIGRATIONS_DIR, "0003_add_navigation_links.sql"),
|
|
95
|
+
"utf-8",
|
|
96
|
+
);
|
|
97
|
+
for (const sql of migration3.split("--> statement-breakpoint")) {
|
|
98
|
+
const trimmed = sql.trim();
|
|
99
|
+
if (trimmed) sqlite.exec(trimmed);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Apply storage provider migration
|
|
103
|
+
const migration4 = readFileSync(
|
|
104
|
+
resolve(MIGRATIONS_DIR, "0004_add_storage_provider.sql"),
|
|
105
|
+
"utf-8",
|
|
106
|
+
);
|
|
107
|
+
for (const sql of migration4.split("--> statement-breakpoint")) {
|
|
108
|
+
const trimmed = sql.trim();
|
|
109
|
+
if (trimmed) sqlite.exec(trimmed);
|
|
110
|
+
}
|
|
111
|
+
|
|
92
112
|
const db = drizzle(sqlite, { schema });
|
|
93
113
|
|
|
94
114
|
return { db, sqlite };
|
package/src/app.tsx
CHANGED
|
@@ -29,11 +29,13 @@ import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
|
|
|
29
29
|
import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
|
|
30
30
|
import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
|
|
31
31
|
import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
|
|
32
|
+
import { navigationRoutes as dashNavigationRoutes } from "./routes/dash/navigation.js";
|
|
32
33
|
|
|
33
34
|
// Routes - API
|
|
34
35
|
import { postsApiRoutes } from "./routes/api/posts.js";
|
|
35
36
|
import { uploadApiRoutes } from "./routes/api/upload.js";
|
|
36
37
|
import { searchApiRoutes } from "./routes/api/search.js";
|
|
38
|
+
import { timelineApiRoutes } from "./routes/api/timeline.js";
|
|
37
39
|
|
|
38
40
|
// Routes - Feed
|
|
39
41
|
import { rssRoutes } from "./routes/feed/rss.js";
|
|
@@ -47,6 +49,7 @@ import { requireOnboarding } from "./middleware/onboarding.js";
|
|
|
47
49
|
import { BaseLayout } from "./theme/layouts/index.js";
|
|
48
50
|
import { dsRedirect, dsToast } from "./lib/sse.js";
|
|
49
51
|
import { getAvailableThemes, buildThemeStyle } from "./lib/theme.js";
|
|
52
|
+
import { createStorageDriver, type StorageDriver } from "./lib/storage.js";
|
|
50
53
|
|
|
51
54
|
// Extend Hono's context variables
|
|
52
55
|
export interface AppVariables {
|
|
@@ -54,6 +57,7 @@ export interface AppVariables {
|
|
|
54
57
|
auth: Auth;
|
|
55
58
|
config: JantConfig;
|
|
56
59
|
themeStyle: string;
|
|
60
|
+
storage: StorageDriver | null;
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
export type App = Hono<{ Bindings: Bindings; Variables: AppVariables }>;
|
|
@@ -93,6 +97,7 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
93
97
|
const services = createServices(db, session as unknown as D1Database);
|
|
94
98
|
c.set("services", services);
|
|
95
99
|
c.set("config", config);
|
|
100
|
+
c.set("storage", createStorageDriver(c.env));
|
|
96
101
|
|
|
97
102
|
if (c.env.AUTH_SECRET) {
|
|
98
103
|
const auth = createAuth(session as unknown as D1Database, {
|
|
@@ -168,6 +173,7 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
168
173
|
|
|
169
174
|
// API Routes
|
|
170
175
|
app.route("/api/posts", postsApiRoutes);
|
|
176
|
+
app.route("/api/timeline", timelineApiRoutes);
|
|
171
177
|
|
|
172
178
|
// Setup page component
|
|
173
179
|
const SetupContent: FC = () => {
|
|
@@ -660,13 +666,15 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
660
666
|
app.route("/dash/settings", dashSettingsRoutes);
|
|
661
667
|
app.route("/dash/redirects", dashRedirectsRoutes);
|
|
662
668
|
app.route("/dash/collections", dashCollectionsRoutes);
|
|
669
|
+
app.route("/dash/navigation", dashNavigationRoutes);
|
|
663
670
|
// API routes
|
|
664
671
|
app.route("/api/upload", uploadApiRoutes);
|
|
665
672
|
app.route("/api/search", searchApiRoutes);
|
|
666
673
|
|
|
667
|
-
// Media files from
|
|
674
|
+
// Media files from storage (UUIDv7-based URLs with extension)
|
|
668
675
|
app.get("/media/:idWithExt", async (c) => {
|
|
669
|
-
|
|
676
|
+
const storage = c.var.storage;
|
|
677
|
+
if (!storage) {
|
|
670
678
|
return c.notFound();
|
|
671
679
|
}
|
|
672
680
|
|
|
@@ -679,16 +687,13 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
679
687
|
return c.notFound();
|
|
680
688
|
}
|
|
681
689
|
|
|
682
|
-
const object = await
|
|
690
|
+
const object = await storage.get(media.storageKey);
|
|
683
691
|
if (!object) {
|
|
684
692
|
return c.notFound();
|
|
685
693
|
}
|
|
686
694
|
|
|
687
695
|
const headers = new Headers();
|
|
688
|
-
headers.set(
|
|
689
|
-
"Content-Type",
|
|
690
|
-
object.httpMetadata?.contentType || media.mimeType,
|
|
691
|
-
);
|
|
696
|
+
headers.set("Content-Type", object.contentType || media.mimeType);
|
|
692
697
|
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
693
698
|
|
|
694
699
|
return new Response(object.body, { headers });
|
package/src/client.ts
CHANGED