@jant/core 0.3.26 → 0.3.28
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/client/client.css +1 -0
- package/dist/client/client.js +31561 -0
- package/dist/index.js +15209 -15
- package/package.json +21 -15
- package/src/__tests__/helpers/app.ts +19 -3
- package/src/__tests__/helpers/db.ts +44 -0
- package/src/__tests__/helpers/lingui-core-macro-mock.ts +33 -0
- package/src/app.tsx +112 -173
- package/src/auth.ts +4 -1
- package/src/client.ts +13 -0
- package/src/db/migrations/0007_post_collections_m2m.sql +94 -0
- package/src/db/migrations/0008_add_collection_dividers.sql +8 -0
- package/src/db/migrations/0009_drop_collection_show_divider.sql +2 -0
- package/src/db/migrations/0010_add_performance_indexes.sql +16 -0
- package/src/db/schema.ts +24 -4
- package/src/i18n/locales/en.po +810 -385
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +733 -522
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +733 -522
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +7 -11
- package/src/index.ts +1 -1
- package/src/lib/__tests__/icons.test.ts +178 -0
- package/src/lib/__tests__/resolve-config.test.ts +184 -0
- package/src/lib/__tests__/schemas.test.ts +12 -6
- package/src/lib/__tests__/theme.test.ts +62 -0
- package/src/lib/__tests__/timezones.test.ts +1 -1
- package/src/lib/__tests__/url.test.ts +12 -0
- package/src/lib/__tests__/view.test.ts +1 -5
- package/src/lib/avatar-upload.ts +18 -10
- package/src/lib/collection-form-bridge.ts +52 -0
- package/src/lib/collections-reorder.ts +28 -0
- package/src/lib/compose-bridge.ts +251 -0
- package/src/lib/errors.ts +116 -0
- package/src/lib/excerpt.ts +1 -1
- package/src/lib/favicon.ts +3 -5
- package/src/lib/html.ts +22 -0
- package/src/lib/icon-catalog.ts +181 -0
- package/src/lib/icons.ts +202 -0
- package/src/lib/navigation.ts +18 -33
- package/src/lib/pagination.ts +3 -2
- package/src/lib/post-form-bridge.ts +136 -0
- package/src/lib/render.tsx +11 -4
- package/src/lib/resolve-config.ts +157 -0
- package/src/lib/schemas.ts +76 -12
- package/src/lib/settings-bridge.ts +139 -0
- package/src/lib/storage.ts +37 -16
- package/src/lib/theme.ts +5 -7
- package/src/lib/timeline.ts +4 -8
- package/src/lib/toast.ts +134 -0
- package/src/lib/upload.ts +71 -0
- package/src/lib/url.ts +9 -1
- package/src/lib/version.ts +16 -0
- package/src/lib/view.ts +9 -10
- package/src/middleware/__tests__/auth.test.ts +6 -28
- package/src/middleware/__tests__/onboarding.test.ts +1 -1
- package/src/middleware/auth.ts +6 -12
- package/src/middleware/config.ts +51 -0
- package/src/middleware/error-handler.ts +56 -0
- package/src/middleware/onboarding.ts +1 -1
- package/src/preset.css +6 -0
- package/src/routes/__tests__/compose.test.ts +104 -17
- package/src/routes/api/__tests__/collections.test.ts +93 -2
- package/src/routes/api/__tests__/posts.test.ts +2 -1
- package/src/routes/api/__tests__/settings.test.ts +1 -1
- package/src/routes/api/collections.ts +64 -68
- package/src/routes/api/nav-items.ts +21 -59
- package/src/routes/api/pages.ts +18 -46
- package/src/routes/api/posts.ts +64 -86
- package/src/routes/api/search.ts +6 -4
- package/src/routes/api/settings.ts +8 -24
- package/src/routes/api/upload.ts +55 -53
- package/src/routes/auth/__tests__/setup.test.ts +118 -0
- package/src/routes/auth/reset.tsx +17 -66
- package/src/routes/auth/setup.tsx +67 -11
- package/src/routes/auth/signin.tsx +44 -8
- package/src/routes/compose.tsx +194 -0
- package/src/routes/dash/__tests__/font-theme.test.ts +110 -0
- package/src/routes/dash/__tests__/pages.test.ts +2 -2
- package/src/routes/dash/__tests__/settings-avatar.test.ts +23 -12
- package/src/routes/dash/appearance.tsx +173 -0
- package/src/routes/dash/collections.tsx +80 -14
- package/src/routes/dash/index.tsx +12 -14
- package/src/routes/dash/media.tsx +46 -49
- package/src/routes/dash/pages.tsx +85 -37
- package/src/routes/dash/posts.tsx +60 -23
- package/src/routes/dash/redirects.tsx +43 -33
- package/src/routes/dash/settings.tsx +234 -214
- package/src/routes/feed/__tests__/rss.test.ts +7 -3
- package/src/routes/feed/rss.ts +11 -16
- package/src/routes/feed/sitemap.ts +15 -9
- package/src/routes/pages/__tests__/collections.test.ts +9 -8
- package/src/routes/pages/archive.tsx +2 -2
- package/src/routes/pages/collection.tsx +76 -9
- package/src/routes/pages/collections.tsx +3 -1
- package/src/routes/pages/featured.tsx +2 -2
- package/src/routes/pages/home.tsx +3 -3
- package/src/routes/pages/latest.tsx +2 -2
- package/src/routes/pages/page.tsx +2 -2
- package/src/routes/pages/post.tsx +2 -2
- package/src/routes/pages/search.tsx +2 -2
- package/src/services/__tests__/collection.test.ts +324 -34
- package/src/services/__tests__/media.test.ts +1 -1
- package/src/services/__tests__/page.test.ts +116 -1
- package/src/services/auth.ts +88 -0
- package/src/services/collection.ts +169 -30
- package/src/services/index.ts +8 -3
- package/src/services/media.ts +39 -12
- package/src/services/navigation.ts +17 -5
- package/src/services/page.ts +24 -4
- package/src/services/post.ts +87 -19
- package/src/services/search.ts +0 -1
- package/src/services/settings.ts +21 -13
- package/src/style.css +3 -0
- package/src/styles/components.css +42 -1
- package/src/styles/tokens.css +4 -0
- package/src/styles/ui.css +902 -73
- package/src/types/app-context.ts +25 -0
- package/src/types/bindings.ts +1 -0
- package/src/types/config.ts +60 -23
- package/src/types/entities.ts +12 -2
- package/src/types/lingui-react-macro.d.ts +3 -3
- package/src/types/operations.ts +2 -4
- package/src/types/views.ts +1 -3
- package/src/ui/__tests__/font-themes.test.ts +27 -8
- package/src/ui/color-themes.ts +1 -1
- package/src/ui/components/__tests__/jant-collection-form.test.ts +153 -0
- package/src/ui/components/__tests__/jant-compose-dialog.test.ts +512 -0
- package/src/ui/components/__tests__/jant-compose-editor.test.ts +272 -0
- package/src/ui/components/__tests__/jant-post-form.test.ts +172 -0
- package/src/ui/components/__tests__/jant-settings-avatar.test.ts +235 -0
- package/src/ui/components/__tests__/jant-settings-general.test.ts +319 -0
- package/src/ui/components/collection-types.ts +45 -0
- package/src/ui/components/compose-types.ts +75 -0
- package/src/ui/components/jant-collection-form.ts +512 -0
- package/src/ui/components/jant-compose-dialog.ts +494 -0
- package/src/ui/components/jant-compose-editor.ts +799 -0
- package/src/ui/components/jant-post-form.ts +290 -0
- package/src/ui/components/jant-settings-avatar.ts +231 -0
- package/src/ui/components/jant-settings-general.ts +436 -0
- package/src/ui/components/post-form-template.ts +260 -0
- package/src/ui/components/post-form-types.ts +87 -0
- package/src/ui/components/settings-types.ts +62 -0
- package/src/ui/compose/ComposeDialog.tsx +141 -385
- package/src/ui/compose/ComposePrompt.tsx +3 -3
- package/src/ui/dash/PostList.tsx +55 -61
- package/src/ui/dash/appearance/AdvancedContent.tsx +80 -0
- package/src/ui/dash/appearance/AppearanceNav.tsx +56 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +129 -0
- package/src/ui/dash/appearance/FontThemeContent.tsx +98 -0
- package/src/ui/dash/collections/CollectionForm.tsx +130 -117
- package/src/ui/dash/collections/CollectionsListContent.tsx +102 -41
- package/src/ui/dash/collections/IconPickerGrid.tsx +50 -0
- package/src/ui/dash/collections/ViewCollectionContent.tsx +14 -3
- package/src/ui/dash/index.ts +1 -1
- package/src/ui/dash/posts/PostForm.tsx +248 -0
- package/src/ui/dash/settings/AccountContent.tsx +69 -80
- package/src/ui/dash/settings/GeneralContent.tsx +159 -478
- package/src/ui/dash/settings/SettingsNav.tsx +4 -4
- package/src/ui/font-themes.ts +115 -32
- package/src/ui/layouts/BaseLayout.tsx +49 -19
- package/src/ui/layouts/DashLayout.tsx +14 -9
- package/src/ui/layouts/SiteLayout.tsx +38 -23
- package/src/ui/pages/CollectionPage.tsx +12 -2
- package/src/ui/pages/CollectionsPage.tsx +27 -27
- package/src/ui/pages/HomePage.tsx +15 -6
- package/src/ui/pages/SearchPage.tsx +1 -2
- package/src/ui/shared/CollectionsSidebar.tsx +59 -0
- package/src/ui/shared/Pagination.tsx +2 -2
- package/dist/app.js +0 -265
- package/dist/auth.js +0 -36
- package/dist/client.js +0 -13
- package/dist/db/index.js +0 -10
- package/dist/db/schema.js +0 -224
- package/dist/i18n/Trans.js +0 -24
- package/dist/i18n/context.js +0 -58
- package/dist/i18n/detect.js +0 -26
- package/dist/i18n/i18n.js +0 -49
- package/dist/i18n/index.js +0 -44
- package/dist/i18n/locales/en.js +0 -1
- package/dist/i18n/locales/zh-Hans.js +0 -1
- package/dist/i18n/locales/zh-Hant.js +0 -1
- package/dist/i18n/locales.js +0 -13
- package/dist/i18n/middleware.js +0 -30
- package/dist/lib/avatar-upload.js +0 -134
- package/dist/lib/config.js +0 -143
- package/dist/lib/constants.js +0 -50
- package/dist/lib/excerpt.js +0 -76
- package/dist/lib/favicon.js +0 -102
- package/dist/lib/feed.js +0 -123
- package/dist/lib/image-processor.js +0 -187
- package/dist/lib/image.js +0 -97
- package/dist/lib/index.js +0 -7
- package/dist/lib/markdown.js +0 -83
- package/dist/lib/media-helpers.js +0 -49
- package/dist/lib/media-upload.js +0 -104
- package/dist/lib/nav-reorder.js +0 -27
- package/dist/lib/navigation.js +0 -79
- package/dist/lib/pagination.js +0 -44
- package/dist/lib/render.js +0 -53
- package/dist/lib/schemas.js +0 -174
- package/dist/lib/sqid.js +0 -72
- package/dist/lib/sse.js +0 -218
- package/dist/lib/storage.js +0 -164
- package/dist/lib/theme.js +0 -65
- package/dist/lib/time.js +0 -159
- package/dist/lib/timeline.js +0 -95
- package/dist/lib/timezones.js +0 -388
- package/dist/lib/url.js +0 -89
- package/dist/lib/view.js +0 -217
- package/dist/middleware/auth.js +0 -52
- package/dist/middleware/onboarding.js +0 -41
- package/dist/routes/api/collections.js +0 -124
- package/dist/routes/api/nav-items.js +0 -104
- package/dist/routes/api/pages.js +0 -91
- package/dist/routes/api/posts.js +0 -218
- package/dist/routes/api/search.js +0 -48
- package/dist/routes/api/settings.js +0 -68
- package/dist/routes/api/upload.js +0 -246
- package/dist/routes/auth/reset.js +0 -221
- package/dist/routes/auth/setup.js +0 -194
- package/dist/routes/auth/signin.js +0 -176
- package/dist/routes/compose.js +0 -48
- package/dist/routes/dash/collections.js +0 -115
- package/dist/routes/dash/index.js +0 -118
- package/dist/routes/dash/media.js +0 -106
- package/dist/routes/dash/pages.js +0 -294
- package/dist/routes/dash/posts.js +0 -244
- package/dist/routes/dash/redirects.js +0 -257
- package/dist/routes/dash/settings.js +0 -379
- package/dist/routes/feed/rss.js +0 -62
- package/dist/routes/feed/sitemap.js +0 -49
- package/dist/routes/pages/archive.js +0 -62
- package/dist/routes/pages/collection.js +0 -34
- package/dist/routes/pages/collections.js +0 -28
- package/dist/routes/pages/featured.js +0 -36
- package/dist/routes/pages/home.js +0 -64
- package/dist/routes/pages/latest.js +0 -45
- package/dist/routes/pages/page.js +0 -68
- package/dist/routes/pages/post.js +0 -44
- package/dist/routes/pages/search.js +0 -54
- package/dist/services/collection.js +0 -109
- package/dist/services/index.js +0 -24
- package/dist/services/media.js +0 -117
- package/dist/services/navigation.js +0 -91
- package/dist/services/page.js +0 -84
- package/dist/services/post.js +0 -229
- package/dist/services/redirect.js +0 -48
- package/dist/services/search.js +0 -67
- package/dist/services/settings.js +0 -68
- package/dist/types/bindings.js +0 -3
- package/dist/types/config.js +0 -147
- package/dist/types/constants.js +0 -27
- package/dist/types/entities.js +0 -3
- package/dist/types/lingui-react-macro.d.js +0 -9
- package/dist/types/operations.js +0 -3
- package/dist/types/props.js +0 -3
- package/dist/types/sortablejs.d.js +0 -5
- package/dist/types/views.js +0 -5
- package/dist/types.js +0 -11
- package/dist/ui/color-themes.js +0 -268
- package/dist/ui/compose/ComposeDialog.js +0 -467
- package/dist/ui/compose/ComposePrompt.js +0 -55
- package/dist/ui/dash/ActionButtons.js +0 -46
- package/dist/ui/dash/CrudPageHeader.js +0 -22
- package/dist/ui/dash/DangerZone.js +0 -36
- package/dist/ui/dash/FormatBadge.js +0 -27
- package/dist/ui/dash/ListItemRow.js +0 -21
- package/dist/ui/dash/PageForm.js +0 -195
- package/dist/ui/dash/PostForm.js +0 -395
- package/dist/ui/dash/PostList.js +0 -83
- package/dist/ui/dash/StatusBadge.js +0 -46
- package/dist/ui/dash/collections/CollectionForm.js +0 -152
- package/dist/ui/dash/collections/CollectionsListContent.js +0 -68
- package/dist/ui/dash/collections/ViewCollectionContent.js +0 -96
- package/dist/ui/dash/index.js +0 -10
- package/dist/ui/dash/media/MediaListContent.js +0 -166
- package/dist/ui/dash/media/ViewMediaContent.js +0 -212
- package/dist/ui/dash/pages/LinkFormContent.js +0 -130
- package/dist/ui/dash/pages/UnifiedPagesContent.js +0 -193
- package/dist/ui/dash/settings/AccountContent.js +0 -209
- package/dist/ui/dash/settings/AppearanceContent.js +0 -259
- package/dist/ui/dash/settings/GeneralContent.js +0 -536
- package/dist/ui/dash/settings/SettingsNav.js +0 -41
- package/dist/ui/feed/LinkCard.js +0 -72
- package/dist/ui/feed/NoteCard.js +0 -58
- package/dist/ui/feed/QuoteCard.js +0 -63
- package/dist/ui/feed/ThreadPreview.js +0 -48
- package/dist/ui/feed/TimelineFeed.js +0 -41
- package/dist/ui/feed/TimelineItem.js +0 -27
- package/dist/ui/font-themes.js +0 -36
- package/dist/ui/layouts/BaseLayout.js +0 -153
- package/dist/ui/layouts/DashLayout.js +0 -141
- package/dist/ui/layouts/SiteLayout.js +0 -169
- package/dist/ui/pages/ArchivePage.js +0 -143
- package/dist/ui/pages/CollectionPage.js +0 -70
- package/dist/ui/pages/CollectionsPage.js +0 -76
- package/dist/ui/pages/FeaturedPage.js +0 -24
- package/dist/ui/pages/HomePage.js +0 -24
- package/dist/ui/pages/PostPage.js +0 -55
- package/dist/ui/pages/SearchPage.js +0 -122
- package/dist/ui/pages/SinglePage.js +0 -23
- package/dist/ui/shared/EmptyState.js +0 -27
- package/dist/ui/shared/MediaGallery.js +0 -35
- package/dist/ui/shared/Pagination.js +0 -195
- package/dist/ui/shared/ThreadView.js +0 -108
- package/dist/ui/shared/index.js +0 -5
- package/dist/vendor/datastar.js +0 -1606
- package/src/lib/__tests__/config.test.ts +0 -192
- package/src/lib/config.ts +0 -167
- package/src/routes/compose.ts +0 -63
- package/src/ui/dash/PostForm.tsx +0 -360
- package/src/ui/dash/settings/AppearanceContent.tsx +0 -254
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jant/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "A modern, open-source microblogging platform built on Cloudflare Workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -8,12 +8,7 @@
|
|
|
8
8
|
"types": "./src/index.ts",
|
|
9
9
|
"default": "./dist/index.js"
|
|
10
10
|
},
|
|
11
|
-
"./i18n":
|
|
12
|
-
"types": "./src/i18n/index.ts",
|
|
13
|
-
"default": "./dist/i18n/index.js"
|
|
14
|
-
},
|
|
15
|
-
"./preset.css": "./src/preset.css",
|
|
16
|
-
"./client": "./dist/client.js"
|
|
11
|
+
"./i18n": "./src/i18n/index.ts"
|
|
17
12
|
},
|
|
18
13
|
"files": [
|
|
19
14
|
"dist",
|
|
@@ -25,16 +20,18 @@
|
|
|
25
20
|
"dependencies": {
|
|
26
21
|
"@aws-sdk/client-s3": "^3.987.0",
|
|
27
22
|
"@lingui/core": "^5.9.0",
|
|
23
|
+
"@lingui/react": "^5.9.0",
|
|
28
24
|
"@tailwindcss/typography": "^0.5.19",
|
|
29
25
|
"basecoat-css": "^0.3.10",
|
|
30
26
|
"better-auth": "^1.4.18",
|
|
31
27
|
"drizzle-orm": "^0.45.1",
|
|
32
|
-
"
|
|
28
|
+
"lit": "^3.3.2",
|
|
29
|
+
"lucide-static": "^0.574.0",
|
|
33
30
|
"marked": "^17.0.1",
|
|
31
|
+
"pinyin-pro": "^3.28.0",
|
|
34
32
|
"sortablejs": "^1.15.6",
|
|
35
33
|
"sqids": "^0.3.0",
|
|
36
34
|
"uuidv7": "^1.1.0",
|
|
37
|
-
"vite-ssr-components": "^0.5.2",
|
|
38
35
|
"zod": "^4.3.6"
|
|
39
36
|
},
|
|
40
37
|
"peerDependencies": {
|
|
@@ -42,22 +39,27 @@
|
|
|
42
39
|
"tailwindcss": "^4.0.0"
|
|
43
40
|
},
|
|
44
41
|
"devDependencies": {
|
|
42
|
+
"@cloudflare/vite-plugin": "^1.22.1",
|
|
45
43
|
"@cloudflare/workers-types": "^4.20260131.0",
|
|
46
44
|
"@lingui/cli": "^5.9.0",
|
|
47
45
|
"@lingui/conf": "^5.9.0",
|
|
48
46
|
"@lingui/format-po": "^5.9.0",
|
|
49
47
|
"@lingui/swc-plugin": "^5.10.1",
|
|
50
|
-
"@swc/cli": "^0.6.0",
|
|
51
48
|
"@swc/core": "^1.15.11",
|
|
52
49
|
"@types/better-sqlite3": "^7.6.13",
|
|
53
50
|
"@types/node": "^25.1.0",
|
|
54
51
|
"better-sqlite3": "^12.6.2",
|
|
55
52
|
"drizzle-kit": "^0.31.8",
|
|
56
53
|
"glob": "^13.0.0",
|
|
54
|
+
"happy-dom": "^20.6.3",
|
|
55
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
57
56
|
"tailwindcss": "^4.1.18",
|
|
58
57
|
"tsx": "^4.21.0",
|
|
59
58
|
"typescript": "^5.9.3",
|
|
60
|
-
"
|
|
59
|
+
"unplugin-swc": "^1.5.9",
|
|
60
|
+
"vite": "^7.3.1",
|
|
61
|
+
"vitest": "^4.0.18",
|
|
62
|
+
"wrangler": "^4.61.1"
|
|
61
63
|
},
|
|
62
64
|
"repository": {
|
|
63
65
|
"type": "git",
|
|
@@ -83,14 +85,18 @@
|
|
|
83
85
|
"node": ">=24.0.0"
|
|
84
86
|
},
|
|
85
87
|
"scripts": {
|
|
86
|
-
"build": "pnpm build:lib",
|
|
87
|
-
"build:
|
|
88
|
-
"build:
|
|
89
|
-
"typecheck": "tsc --noEmit",
|
|
88
|
+
"build": "pnpm build:lib && pnpm build:client",
|
|
89
|
+
"build:client": "vite build --config vite.config.client.ts",
|
|
90
|
+
"build:lib": "vite build --config vite.config.worker.ts",
|
|
91
|
+
"typecheck": "tsc -b --noEmit",
|
|
90
92
|
"db:generate": "drizzle-kit generate",
|
|
91
93
|
"i18n:extract": "lingui extract",
|
|
92
94
|
"i18n:compile": "lingui compile --typescript",
|
|
93
95
|
"i18n:build": "pnpm i18n:extract && pnpm i18n:compile",
|
|
96
|
+
"dev": "pnpm db:migrate:local && vite dev",
|
|
97
|
+
"dev:debug": "pnpm db:migrate:local && vite dev --port 19019",
|
|
98
|
+
"db:migrate:local": "yes | wrangler d1 migrations apply DB --local",
|
|
99
|
+
"db:migrate:remote": "wrangler d1 migrations apply DB --remote",
|
|
94
100
|
"test": "vitest run",
|
|
95
101
|
"test:watch": "vitest",
|
|
96
102
|
"test:coverage": "vitest run --coverage"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { Hono } from "hono";
|
|
8
8
|
import type { Bindings } from "../../types.js";
|
|
9
|
-
import type { AppVariables } from "../../app.js";
|
|
9
|
+
import type { AppVariables } from "../../types/app-context.js";
|
|
10
10
|
import { createTestDatabase } from "./db.js";
|
|
11
11
|
import { createPostService } from "../../services/post.js";
|
|
12
12
|
import { createPageService } from "../../services/page.js";
|
|
@@ -16,8 +16,12 @@ import { createMediaService } from "../../services/media.js";
|
|
|
16
16
|
import { createCollectionService } from "../../services/collection.js";
|
|
17
17
|
import { createSearchService } from "../../services/search.js";
|
|
18
18
|
import { createNavItemService } from "../../services/navigation.js";
|
|
19
|
+
import { createAuthService } from "../../services/auth.js";
|
|
19
20
|
import type { Database } from "../../db/index.js";
|
|
20
21
|
import type BetterSqlite3 from "better-sqlite3";
|
|
22
|
+
import { errorHandler } from "../../middleware/error-handler.js";
|
|
23
|
+
import { createI18n } from "../../i18n/i18n.js";
|
|
24
|
+
import { resolveConfig } from "../../lib/resolve-config.js";
|
|
21
25
|
|
|
22
26
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
23
27
|
|
|
@@ -40,19 +44,24 @@ export function createTestApp(options: TestAppOptions = {}) {
|
|
|
40
44
|
// Create a mock D1 for search service
|
|
41
45
|
const mockD1 = createMockD1(sqlite);
|
|
42
46
|
|
|
47
|
+
const settingsService = createSettingsService(db);
|
|
43
48
|
const services = {
|
|
44
49
|
posts: createPostService(db),
|
|
45
50
|
pages: createPageService(db),
|
|
46
|
-
settings:
|
|
51
|
+
settings: settingsService,
|
|
47
52
|
redirects: createRedirectService(db),
|
|
48
53
|
media: createMediaService(db),
|
|
49
54
|
collections: createCollectionService(db),
|
|
50
55
|
search: createSearchService(mockD1),
|
|
51
56
|
navItems: createNavItemService(db),
|
|
57
|
+
auth: createAuthService(db, settingsService),
|
|
52
58
|
};
|
|
53
59
|
|
|
54
60
|
const app = new Hono<Env>();
|
|
55
61
|
|
|
62
|
+
// Global error handler: maps DomainError → HTTP responses
|
|
63
|
+
app.onError(errorHandler);
|
|
64
|
+
|
|
56
65
|
// Inject env bindings and services middleware
|
|
57
66
|
app.use("*", async (c, next) => {
|
|
58
67
|
// Provide mock env bindings so c.env.* works in route handlers
|
|
@@ -61,9 +70,16 @@ export function createTestApp(options: TestAppOptions = {}) {
|
|
|
61
70
|
} as AppVariables["services"] extends never ? never : Bindings;
|
|
62
71
|
|
|
63
72
|
c.set("services", services as AppVariables["services"]);
|
|
64
|
-
|
|
73
|
+
const allSettings = await services.settings.getAll();
|
|
74
|
+
c.set("allSettings", allSettings);
|
|
75
|
+
c.set("appConfig", resolveConfig(c.env, allSettings));
|
|
65
76
|
c.set("storage", null);
|
|
66
77
|
|
|
78
|
+
// i18n (English default for tests)
|
|
79
|
+
const i18n = createI18n("en");
|
|
80
|
+
c.set("lang", "en");
|
|
81
|
+
c.set("i18n", i18n);
|
|
82
|
+
|
|
67
83
|
if (options.authenticated) {
|
|
68
84
|
// Mock auth that always returns a session
|
|
69
85
|
c.set("auth", {
|
|
@@ -89,7 +89,51 @@ export function createTestDatabase(options?: { fts?: boolean }) {
|
|
|
89
89
|
// Apply 0006: rename slug to path on posts
|
|
90
90
|
applyMigration(sqlite, "0006_rename_slug_to_path.sql");
|
|
91
91
|
|
|
92
|
+
// Apply 0007: post_collections M:N junction table
|
|
93
|
+
const m7 = readFileSync(
|
|
94
|
+
resolve(MIGRATIONS_DIR, "0007_post_collections_m2m.sql"),
|
|
95
|
+
"utf-8",
|
|
96
|
+
);
|
|
97
|
+
for (const stmt of m7.split("--> statement-breakpoint")) {
|
|
98
|
+
const trimmed = stmt.trim();
|
|
99
|
+
if (!trimmed) continue;
|
|
100
|
+
// Skip FTS trigger statements if FTS not requested
|
|
101
|
+
const isFts = trimmed.includes("posts_fts");
|
|
102
|
+
if (!options?.fts && isFts) continue;
|
|
103
|
+
try {
|
|
104
|
+
sqlite.exec(trimmed);
|
|
105
|
+
} catch {
|
|
106
|
+
// Ignore DROP TRIGGER failures silently
|
|
107
|
+
if (!trimmed.startsWith("DROP TRIGGER")) {
|
|
108
|
+
throw new Error(`Migration 0007 failed: ${trimmed.slice(0, 100)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Apply 0008: collection_dividers table
|
|
114
|
+
applyMigration(sqlite, "0008_add_collection_dividers.sql");
|
|
115
|
+
|
|
116
|
+
// Apply 0009: drop show_divider column from collections
|
|
117
|
+
applyMigration(sqlite, "0009_drop_collection_show_divider.sql");
|
|
118
|
+
|
|
119
|
+
// Apply 0010: performance indexes
|
|
120
|
+
applyMigration(sqlite, "0010_add_performance_indexes.sql");
|
|
121
|
+
|
|
92
122
|
const db = drizzle(sqlite, { schema });
|
|
93
123
|
|
|
124
|
+
// Polyfill D1 batch() for test compatibility.
|
|
125
|
+
// In production, D1 batch executes statements atomically in a single transaction.
|
|
126
|
+
// In tests, better-sqlite3 is synchronous and single-threaded so sequential
|
|
127
|
+
// execution is effectively atomic.
|
|
128
|
+
Object.defineProperty(db, "batch", {
|
|
129
|
+
value: async (queries: PromiseLike<unknown>[]) => {
|
|
130
|
+
const results = [];
|
|
131
|
+
for (const q of queries) {
|
|
132
|
+
results.push(await q);
|
|
133
|
+
}
|
|
134
|
+
return results;
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
94
138
|
return { db, sqlite };
|
|
95
139
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock for @lingui/core/macro in the Vitest environment.
|
|
3
|
+
*
|
|
4
|
+
* The real module requires the Babel macro ecosystem which is not available
|
|
5
|
+
* under Vitest. This mock replicates the *post-transformation* API surface:
|
|
6
|
+
* `msg()` / `defineMessage()` return a MessageDescriptor whose `id` equals
|
|
7
|
+
* the source `message`. At runtime `i18n._()` falls back to `message` when
|
|
8
|
+
* the ID is not in the catalog, so English source strings propagate through.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { MessageDescriptor } from "@lingui/core";
|
|
12
|
+
|
|
13
|
+
type MacroInput =
|
|
14
|
+
| { id?: string; message: string; comment?: string; context?: string }
|
|
15
|
+
| TemplateStringsArray;
|
|
16
|
+
|
|
17
|
+
function toDescriptor(
|
|
18
|
+
input: MacroInput,
|
|
19
|
+
...args: unknown[]
|
|
20
|
+
): MessageDescriptor {
|
|
21
|
+
if (typeof input === "object" && "message" in input) {
|
|
22
|
+
return { id: input.message, message: input.message } as MessageDescriptor;
|
|
23
|
+
}
|
|
24
|
+
// Template literal form: msg`some text`
|
|
25
|
+
const raw = (input as TemplateStringsArray).reduce(
|
|
26
|
+
(acc, str, i) => acc + str + (args[i] ?? ""),
|
|
27
|
+
"",
|
|
28
|
+
);
|
|
29
|
+
return { id: raw, message: raw } as MessageDescriptor;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const msg = toDescriptor;
|
|
33
|
+
export const defineMessage = toDescriptor;
|
package/src/app.tsx
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { createDatabase } from "./db/index.js";
|
|
7
|
-
import { createServices
|
|
8
|
-
import { createAuth
|
|
7
|
+
import { createServices } from "./services/index.js";
|
|
8
|
+
import { createAuth } from "./auth.js";
|
|
9
9
|
import { i18nMiddleware } from "./i18n/index.js";
|
|
10
|
-
import type { Bindings
|
|
11
|
-
import { SETTINGS_KEYS } from "./lib/constants.js";
|
|
10
|
+
import type { Bindings } from "./types.js";
|
|
12
11
|
|
|
13
12
|
// Routes - Auth
|
|
14
13
|
import { setupRoutes } from "./routes/auth/setup.js";
|
|
@@ -34,6 +33,7 @@ import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
|
|
|
34
33
|
import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
|
|
35
34
|
import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
|
|
36
35
|
import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
|
|
36
|
+
import { appearanceRoutes as dashAppearanceRoutes } from "./routes/dash/appearance.js";
|
|
37
37
|
|
|
38
38
|
// Routes - API
|
|
39
39
|
import { postsApiRoutes } from "./routes/api/posts.js";
|
|
@@ -53,150 +53,148 @@ import { sitemapRoutes } from "./routes/feed/sitemap.js";
|
|
|
53
53
|
// Middleware
|
|
54
54
|
import { requireAuth } from "./middleware/auth.js";
|
|
55
55
|
import { requireOnboarding } from "./middleware/onboarding.js";
|
|
56
|
+
import { errorHandler } from "./middleware/error-handler.js";
|
|
57
|
+
import { withConfig } from "./middleware/config.js";
|
|
56
58
|
|
|
57
|
-
import {
|
|
58
|
-
import { createStorageDriver, type StorageDriver } from "./lib/storage.js";
|
|
59
|
-
import { BUILTIN_FONT_THEMES } from "./ui/font-themes.js";
|
|
60
|
-
import { getMediaUrl, getPublicUrlForProvider } from "./lib/image.js";
|
|
59
|
+
import { createStorageDriver } from "./lib/storage.js";
|
|
61
60
|
import { base64ToUint8Array } from "./lib/favicon.js";
|
|
61
|
+
import { type AppVariables, type App } from "./types/app-context.js";
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
export interface AppVariables {
|
|
65
|
-
services: Services;
|
|
66
|
-
auth: Auth;
|
|
67
|
-
config: JantConfig;
|
|
68
|
-
themeStyle: string;
|
|
69
|
-
customCSS: string;
|
|
70
|
-
isAuthenticated: boolean;
|
|
71
|
-
storage: StorageDriver | null;
|
|
72
|
-
faviconUrl?: string;
|
|
73
|
-
noindex?: boolean;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export type App = Hono<{ Bindings: Bindings; Variables: AppVariables }>;
|
|
63
|
+
export type { AppVariables, App };
|
|
77
64
|
|
|
78
65
|
/**
|
|
79
66
|
* Create a Jant application
|
|
80
67
|
*
|
|
81
|
-
* @param config - Optional configuration
|
|
82
68
|
* @returns Hono app instance
|
|
83
69
|
*
|
|
84
|
-
* Site settings (name, description, language) should be configured via
|
|
85
|
-
* environment variables (SITE_NAME, SITE_DESCRIPTION, SITE_LANGUAGE).
|
|
86
|
-
* They can also be set in the dashboard, which stores them in the database.
|
|
87
|
-
*
|
|
88
70
|
* @example
|
|
89
71
|
* ```typescript
|
|
90
72
|
* import { createApp } from "@jant/core";
|
|
91
73
|
*
|
|
92
|
-
* export default createApp(
|
|
93
|
-
* cssVariables: { "--card-radius": "0" },
|
|
94
|
-
* });
|
|
74
|
+
* export default createApp();
|
|
95
75
|
* ```
|
|
96
76
|
*/
|
|
97
|
-
export function createApp(
|
|
98
|
-
const resolvedConfig: JantConfig = { ...config };
|
|
99
|
-
|
|
77
|
+
export function createApp(): App {
|
|
100
78
|
const app = new Hono<{ Bindings: Bindings; Variables: AppVariables }>();
|
|
101
79
|
|
|
102
|
-
//
|
|
80
|
+
// Global error handler: maps DomainError → HTTP responses
|
|
81
|
+
app.onError(errorHandler);
|
|
82
|
+
|
|
83
|
+
// Lightweight init — no DB queries
|
|
103
84
|
app.use("*", async (c, next) => {
|
|
85
|
+
if (!c.env.AUTH_SECRET) {
|
|
86
|
+
return c.html(
|
|
87
|
+
`<!DOCTYPE html>
|
|
88
|
+
<html lang="en">
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="utf-8">
|
|
91
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
92
|
+
<title>Configuration Error</title>
|
|
93
|
+
<style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;background:#fafafa;color:#111}div{max-width:480px;text-align:center}h1{font-size:1.25rem;font-weight:600}p{color:#666;line-height:1.6}code{background:#eee;padding:2px 6px;border-radius:4px;font-size:.9em}</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<div>
|
|
97
|
+
<h1>AUTH_SECRET is not set</h1>
|
|
98
|
+
<p>Set <code>AUTH_SECRET</code> in <code>.dev.vars</code> or <code>wrangler.toml</code> to start Jant.</p>
|
|
99
|
+
</div>
|
|
100
|
+
</body>
|
|
101
|
+
</html>`,
|
|
102
|
+
500,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
104
106
|
// Use withSession() to enable D1 Read Replication
|
|
105
107
|
const session = c.env.DB.withSession();
|
|
106
108
|
|
|
107
109
|
// Note: Drizzle ORM doesn't officially support D1DatabaseSession yet (issue #2226)
|
|
108
110
|
// but it works at runtime. We use type assertion as a temporary workaround.
|
|
109
111
|
const db = createDatabase(session as unknown as D1Database);
|
|
110
|
-
|
|
111
|
-
c.set("services", services);
|
|
112
|
-
c.set("config", resolvedConfig);
|
|
112
|
+
c.set("services", createServices(db, session as unknown as D1Database));
|
|
113
113
|
c.set("storage", createStorageDriver(c.env));
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (c.env.AUTH_SECRET) {
|
|
123
|
-
const baseURL = c.env.SITE_URL || new URL(c.req.url).origin;
|
|
124
|
-
const auth = createAuth(session as unknown as D1Database, {
|
|
115
|
+
const baseURL = c.env.SITE_URL || new URL(c.req.url).origin;
|
|
116
|
+
const requestUrl = new URL(c.req.url);
|
|
117
|
+
c.set(
|
|
118
|
+
"auth",
|
|
119
|
+
createAuth(session as unknown as D1Database, {
|
|
125
120
|
secret: c.env.AUTH_SECRET,
|
|
126
121
|
baseURL,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
useSecureCookies: requestUrl.protocol === "https:",
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
130
125
|
|
|
131
126
|
await next();
|
|
132
127
|
});
|
|
133
128
|
|
|
134
|
-
//
|
|
135
|
-
app.use("*", requireOnboarding());
|
|
129
|
+
// --- Routes that don't need config/theme ---
|
|
136
130
|
|
|
137
|
-
//
|
|
138
|
-
app.
|
|
139
|
-
const [themeId, fontThemeId, customCSS, noindexValue, avatarKey] =
|
|
140
|
-
await Promise.all([
|
|
141
|
-
c.var.services.settings.get(SETTINGS_KEYS.THEME),
|
|
142
|
-
c.var.services.settings.get("FONT_THEME"),
|
|
143
|
-
c.var.services.settings.get(SETTINGS_KEYS.CUSTOM_CSS),
|
|
144
|
-
c.var.services.settings.get("NOINDEX"),
|
|
145
|
-
c.var.services.settings.get("SITE_AVATAR"),
|
|
146
|
-
]);
|
|
147
|
-
const themes = getAvailableThemes(resolvedConfig);
|
|
148
|
-
const activeTheme = themeId
|
|
149
|
-
? themes.find((t) => t.id === themeId)
|
|
150
|
-
: undefined;
|
|
151
|
-
|
|
152
|
-
// Build font override CSS variables
|
|
153
|
-
const fontTheme = fontThemeId
|
|
154
|
-
? BUILTIN_FONT_THEMES.find((f) => f.id === fontThemeId)
|
|
155
|
-
: undefined;
|
|
156
|
-
const fontOverrides: Record<string, string> = {};
|
|
157
|
-
if (fontTheme) {
|
|
158
|
-
fontOverrides["--font-body"] = fontTheme.fontFamily;
|
|
159
|
-
}
|
|
131
|
+
// Health check
|
|
132
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
160
133
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
c.set("customCSS", customCSS ?? "");
|
|
167
|
-
|
|
168
|
-
// Noindex
|
|
169
|
-
c.set("noindex", noindexValue === "true");
|
|
170
|
-
|
|
171
|
-
// Resolve favicon from avatar storage key
|
|
172
|
-
if (avatarKey) {
|
|
173
|
-
const publicUrl = getPublicUrlForProvider(
|
|
174
|
-
c.env.STORAGE_DRIVER || "r2",
|
|
175
|
-
c.env.R2_PUBLIC_URL,
|
|
176
|
-
c.env.S3_PUBLIC_URL,
|
|
177
|
-
);
|
|
178
|
-
c.set("faviconUrl", getMediaUrl(avatarKey, publicUrl));
|
|
134
|
+
// Media files from storage (path matches storage key: media/YYYY/MM/uuid.ext)
|
|
135
|
+
app.get("/media/*", async (c) => {
|
|
136
|
+
const storage = c.var.storage;
|
|
137
|
+
if (!storage) {
|
|
138
|
+
return c.notFound();
|
|
179
139
|
}
|
|
180
140
|
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
headers: c.req.raw.headers,
|
|
187
|
-
});
|
|
188
|
-
isAuthenticated = !!session;
|
|
189
|
-
} catch {
|
|
190
|
-
// Not authenticated
|
|
191
|
-
}
|
|
141
|
+
// The storage key is the full path without the leading "/"
|
|
142
|
+
const storageKey = c.req.path.slice(1);
|
|
143
|
+
const object = await storage.get(storageKey);
|
|
144
|
+
if (!object) {
|
|
145
|
+
return c.notFound();
|
|
192
146
|
}
|
|
193
|
-
c.set("isAuthenticated", isAuthenticated);
|
|
194
147
|
|
|
195
|
-
|
|
148
|
+
const headers = new Headers();
|
|
149
|
+
headers.set(
|
|
150
|
+
"Content-Type",
|
|
151
|
+
object.contentType || "application/octet-stream",
|
|
152
|
+
);
|
|
153
|
+
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
154
|
+
|
|
155
|
+
return new Response(object.body, { headers });
|
|
196
156
|
});
|
|
197
157
|
|
|
198
|
-
//
|
|
199
|
-
app.
|
|
158
|
+
// better-auth handler
|
|
159
|
+
app.all("/api/auth/*", async (c) => {
|
|
160
|
+
return c.var.auth.handler(c.req.raw);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Favicon routes - serve from DB settings (small files, avoids R2 round-trip)
|
|
164
|
+
app.get("/favicon.ico", async (c) => {
|
|
165
|
+
const data = await c.var.services.settings.get("SITE_FAVICON_ICO");
|
|
166
|
+
if (!data) return c.notFound();
|
|
167
|
+
|
|
168
|
+
return new Response(base64ToUint8Array(data), {
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": "image/x-icon",
|
|
171
|
+
"Cache-Control": "public, max-age=86400",
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
app.get("/apple-touch-icon.png", async (c) => {
|
|
177
|
+
const storage = c.var.storage;
|
|
178
|
+
const storageKey = await c.var.services.settings.get(
|
|
179
|
+
"SITE_FAVICON_APPLE_TOUCH",
|
|
180
|
+
);
|
|
181
|
+
if (!storage || !storageKey) return c.notFound();
|
|
182
|
+
|
|
183
|
+
const object = await storage.get(storageKey);
|
|
184
|
+
if (!object) return c.notFound();
|
|
185
|
+
|
|
186
|
+
return new Response(object.body, {
|
|
187
|
+
headers: {
|
|
188
|
+
"Content-Type": "image/png",
|
|
189
|
+
"Cache-Control": "public, max-age=86400",
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// --- Middleware for all remaining routes ---
|
|
195
|
+
|
|
196
|
+
// Onboarding gate — redirect to /setup if not yet initialized
|
|
197
|
+
app.use("*", requireOnboarding());
|
|
200
198
|
|
|
201
199
|
// Trailing slash redirect (redirect /foo/ to /foo)
|
|
202
200
|
app.use("*", async (c, next) => {
|
|
@@ -224,47 +222,11 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
224
222
|
await next();
|
|
225
223
|
});
|
|
226
224
|
|
|
227
|
-
//
|
|
228
|
-
app.
|
|
229
|
-
|
|
230
|
-
status: "ok",
|
|
231
|
-
auth: c.env.AUTH_SECRET ? "configured" : "missing",
|
|
232
|
-
authSecretLength: c.env.AUTH_SECRET?.length ?? 0,
|
|
233
|
-
}),
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
// Favicon routes - serve from DB settings (small files, avoids R2 round-trip)
|
|
237
|
-
app.get("/favicon.ico", async (c) => {
|
|
238
|
-
const data = await c.var.services.settings.get("SITE_FAVICON_ICO");
|
|
239
|
-
if (!data) return c.notFound();
|
|
240
|
-
|
|
241
|
-
return new Response(base64ToUint8Array(data), {
|
|
242
|
-
headers: {
|
|
243
|
-
"Content-Type": "image/x-icon",
|
|
244
|
-
"Cache-Control": "public, max-age=86400",
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
app.get("/apple-touch-icon.png", async (c) => {
|
|
250
|
-
const data = await c.var.services.settings.get("SITE_FAVICON_APPLE_TOUCH");
|
|
251
|
-
if (!data) return c.notFound();
|
|
252
|
-
|
|
253
|
-
return new Response(base64ToUint8Array(data), {
|
|
254
|
-
headers: {
|
|
255
|
-
"Content-Type": "image/png",
|
|
256
|
-
"Cache-Control": "public, max-age=86400",
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
});
|
|
225
|
+
// Config + i18n — loads settings, resolves config/theme
|
|
226
|
+
app.use("*", withConfig());
|
|
227
|
+
app.use("*", i18nMiddleware());
|
|
260
228
|
|
|
261
|
-
//
|
|
262
|
-
app.all("/api/auth/*", async (c) => {
|
|
263
|
-
if (!c.var.auth) {
|
|
264
|
-
return c.json({ error: "Auth not configured. Set AUTH_SECRET." }, 500);
|
|
265
|
-
}
|
|
266
|
-
return c.var.auth.handler(c.req.raw);
|
|
267
|
-
});
|
|
229
|
+
// --- Routes that need config ---
|
|
268
230
|
|
|
269
231
|
// API Routes
|
|
270
232
|
app.route("/api/posts", postsApiRoutes);
|
|
@@ -285,36 +247,13 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
285
247
|
app.route("/dash/pages", dashPagesRoutes);
|
|
286
248
|
app.route("/dash/media", dashMediaRoutes);
|
|
287
249
|
app.route("/dash/settings", dashSettingsRoutes);
|
|
288
|
-
app.route("/dash/
|
|
250
|
+
app.route("/dash/appearance", dashAppearanceRoutes);
|
|
251
|
+
app.route("/dash/settings/redirects", dashRedirectsRoutes);
|
|
289
252
|
app.route("/dash/collections", dashCollectionsRoutes);
|
|
290
253
|
// API routes
|
|
291
254
|
app.route("/api/upload", uploadApiRoutes);
|
|
292
255
|
app.route("/api/search", searchApiRoutes);
|
|
293
256
|
|
|
294
|
-
// Media files from storage (path matches storage key: media/YYYY/MM/uuid.ext)
|
|
295
|
-
app.get("/media/*", async (c) => {
|
|
296
|
-
const storage = c.var.storage;
|
|
297
|
-
if (!storage) {
|
|
298
|
-
return c.notFound();
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// The storage key is the full path without the leading "/"
|
|
302
|
-
const storageKey = c.req.path.slice(1);
|
|
303
|
-
const object = await storage.get(storageKey);
|
|
304
|
-
if (!object) {
|
|
305
|
-
return c.notFound();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const headers = new Headers();
|
|
309
|
-
headers.set(
|
|
310
|
-
"Content-Type",
|
|
311
|
-
object.contentType || "application/octet-stream",
|
|
312
|
-
);
|
|
313
|
-
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
314
|
-
|
|
315
|
-
return new Response(object.body, { headers });
|
|
316
|
-
});
|
|
317
|
-
|
|
318
257
|
// Compose route (auth enforced in route middleware)
|
|
319
258
|
app.route("/compose", composeRoutes);
|
|
320
259
|
|
package/src/auth.ts
CHANGED
|
@@ -9,7 +9,7 @@ import * as schema from "./db/schema.js";
|
|
|
9
9
|
|
|
10
10
|
export function createAuth(
|
|
11
11
|
d1: D1Database,
|
|
12
|
-
options: { secret: string; baseURL: string },
|
|
12
|
+
options: { secret: string; baseURL: string; useSecureCookies: boolean },
|
|
13
13
|
) {
|
|
14
14
|
const db = drizzle(d1, { schema });
|
|
15
15
|
|
|
@@ -25,6 +25,9 @@ export function createAuth(
|
|
|
25
25
|
}),
|
|
26
26
|
secret: options.secret,
|
|
27
27
|
baseURL: options.baseURL,
|
|
28
|
+
advanced: {
|
|
29
|
+
useSecureCookies: options.useSecureCookies,
|
|
30
|
+
},
|
|
28
31
|
emailAndPassword: {
|
|
29
32
|
enabled: true,
|
|
30
33
|
autoSignIn: true,
|
package/src/client.ts
CHANGED
|
@@ -13,3 +13,16 @@ import "./lib/image-processor.js";
|
|
|
13
13
|
import "./lib/media-upload.js";
|
|
14
14
|
import "./lib/avatar-upload.js";
|
|
15
15
|
import "./lib/nav-reorder.js";
|
|
16
|
+
import "./lib/collections-reorder.js";
|
|
17
|
+
|
|
18
|
+
// Lit Web Components
|
|
19
|
+
import "./ui/components/jant-compose-dialog.js";
|
|
20
|
+
import "./ui/components/jant-compose-editor.js";
|
|
21
|
+
import "./lib/compose-bridge.js";
|
|
22
|
+
import "./ui/components/jant-settings-general.js";
|
|
23
|
+
import "./ui/components/jant-settings-avatar.js";
|
|
24
|
+
import "./lib/settings-bridge.js";
|
|
25
|
+
import "./ui/components/jant-collection-form.js";
|
|
26
|
+
import "./lib/collection-form-bridge.js";
|
|
27
|
+
import "./ui/components/jant-post-form.js";
|
|
28
|
+
import "./lib/post-form-bridge.js";
|