@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.
Files changed (314) hide show
  1. package/dist/client/client.css +1 -0
  2. package/dist/client/client.js +31561 -0
  3. package/dist/index.js +15209 -15
  4. package/package.json +21 -15
  5. package/src/__tests__/helpers/app.ts +19 -3
  6. package/src/__tests__/helpers/db.ts +44 -0
  7. package/src/__tests__/helpers/lingui-core-macro-mock.ts +33 -0
  8. package/src/app.tsx +112 -173
  9. package/src/auth.ts +4 -1
  10. package/src/client.ts +13 -0
  11. package/src/db/migrations/0007_post_collections_m2m.sql +94 -0
  12. package/src/db/migrations/0008_add_collection_dividers.sql +8 -0
  13. package/src/db/migrations/0009_drop_collection_show_divider.sql +2 -0
  14. package/src/db/migrations/0010_add_performance_indexes.sql +16 -0
  15. package/src/db/schema.ts +24 -4
  16. package/src/i18n/locales/en.po +810 -385
  17. package/src/i18n/locales/en.ts +1 -1
  18. package/src/i18n/locales/zh-Hans.po +733 -522
  19. package/src/i18n/locales/zh-Hans.ts +1 -1
  20. package/src/i18n/locales/zh-Hant.po +733 -522
  21. package/src/i18n/locales/zh-Hant.ts +1 -1
  22. package/src/i18n/middleware.ts +7 -11
  23. package/src/index.ts +1 -1
  24. package/src/lib/__tests__/icons.test.ts +178 -0
  25. package/src/lib/__tests__/resolve-config.test.ts +184 -0
  26. package/src/lib/__tests__/schemas.test.ts +12 -6
  27. package/src/lib/__tests__/theme.test.ts +62 -0
  28. package/src/lib/__tests__/timezones.test.ts +1 -1
  29. package/src/lib/__tests__/url.test.ts +12 -0
  30. package/src/lib/__tests__/view.test.ts +1 -5
  31. package/src/lib/avatar-upload.ts +18 -10
  32. package/src/lib/collection-form-bridge.ts +52 -0
  33. package/src/lib/collections-reorder.ts +28 -0
  34. package/src/lib/compose-bridge.ts +251 -0
  35. package/src/lib/errors.ts +116 -0
  36. package/src/lib/excerpt.ts +1 -1
  37. package/src/lib/favicon.ts +3 -5
  38. package/src/lib/html.ts +22 -0
  39. package/src/lib/icon-catalog.ts +181 -0
  40. package/src/lib/icons.ts +202 -0
  41. package/src/lib/navigation.ts +18 -33
  42. package/src/lib/pagination.ts +3 -2
  43. package/src/lib/post-form-bridge.ts +136 -0
  44. package/src/lib/render.tsx +11 -4
  45. package/src/lib/resolve-config.ts +157 -0
  46. package/src/lib/schemas.ts +76 -12
  47. package/src/lib/settings-bridge.ts +139 -0
  48. package/src/lib/storage.ts +37 -16
  49. package/src/lib/theme.ts +5 -7
  50. package/src/lib/timeline.ts +4 -8
  51. package/src/lib/toast.ts +134 -0
  52. package/src/lib/upload.ts +71 -0
  53. package/src/lib/url.ts +9 -1
  54. package/src/lib/version.ts +16 -0
  55. package/src/lib/view.ts +9 -10
  56. package/src/middleware/__tests__/auth.test.ts +6 -28
  57. package/src/middleware/__tests__/onboarding.test.ts +1 -1
  58. package/src/middleware/auth.ts +6 -12
  59. package/src/middleware/config.ts +51 -0
  60. package/src/middleware/error-handler.ts +56 -0
  61. package/src/middleware/onboarding.ts +1 -1
  62. package/src/preset.css +6 -0
  63. package/src/routes/__tests__/compose.test.ts +104 -17
  64. package/src/routes/api/__tests__/collections.test.ts +93 -2
  65. package/src/routes/api/__tests__/posts.test.ts +2 -1
  66. package/src/routes/api/__tests__/settings.test.ts +1 -1
  67. package/src/routes/api/collections.ts +64 -68
  68. package/src/routes/api/nav-items.ts +21 -59
  69. package/src/routes/api/pages.ts +18 -46
  70. package/src/routes/api/posts.ts +64 -86
  71. package/src/routes/api/search.ts +6 -4
  72. package/src/routes/api/settings.ts +8 -24
  73. package/src/routes/api/upload.ts +55 -53
  74. package/src/routes/auth/__tests__/setup.test.ts +118 -0
  75. package/src/routes/auth/reset.tsx +17 -66
  76. package/src/routes/auth/setup.tsx +67 -11
  77. package/src/routes/auth/signin.tsx +44 -8
  78. package/src/routes/compose.tsx +194 -0
  79. package/src/routes/dash/__tests__/font-theme.test.ts +110 -0
  80. package/src/routes/dash/__tests__/pages.test.ts +2 -2
  81. package/src/routes/dash/__tests__/settings-avatar.test.ts +23 -12
  82. package/src/routes/dash/appearance.tsx +173 -0
  83. package/src/routes/dash/collections.tsx +80 -14
  84. package/src/routes/dash/index.tsx +12 -14
  85. package/src/routes/dash/media.tsx +46 -49
  86. package/src/routes/dash/pages.tsx +85 -37
  87. package/src/routes/dash/posts.tsx +60 -23
  88. package/src/routes/dash/redirects.tsx +43 -33
  89. package/src/routes/dash/settings.tsx +234 -214
  90. package/src/routes/feed/__tests__/rss.test.ts +7 -3
  91. package/src/routes/feed/rss.ts +11 -16
  92. package/src/routes/feed/sitemap.ts +15 -9
  93. package/src/routes/pages/__tests__/collections.test.ts +9 -8
  94. package/src/routes/pages/archive.tsx +2 -2
  95. package/src/routes/pages/collection.tsx +76 -9
  96. package/src/routes/pages/collections.tsx +3 -1
  97. package/src/routes/pages/featured.tsx +2 -2
  98. package/src/routes/pages/home.tsx +3 -3
  99. package/src/routes/pages/latest.tsx +2 -2
  100. package/src/routes/pages/page.tsx +2 -2
  101. package/src/routes/pages/post.tsx +2 -2
  102. package/src/routes/pages/search.tsx +2 -2
  103. package/src/services/__tests__/collection.test.ts +324 -34
  104. package/src/services/__tests__/media.test.ts +1 -1
  105. package/src/services/__tests__/page.test.ts +116 -1
  106. package/src/services/auth.ts +88 -0
  107. package/src/services/collection.ts +169 -30
  108. package/src/services/index.ts +8 -3
  109. package/src/services/media.ts +39 -12
  110. package/src/services/navigation.ts +17 -5
  111. package/src/services/page.ts +24 -4
  112. package/src/services/post.ts +87 -19
  113. package/src/services/search.ts +0 -1
  114. package/src/services/settings.ts +21 -13
  115. package/src/style.css +3 -0
  116. package/src/styles/components.css +42 -1
  117. package/src/styles/tokens.css +4 -0
  118. package/src/styles/ui.css +902 -73
  119. package/src/types/app-context.ts +25 -0
  120. package/src/types/bindings.ts +1 -0
  121. package/src/types/config.ts +60 -23
  122. package/src/types/entities.ts +12 -2
  123. package/src/types/lingui-react-macro.d.ts +3 -3
  124. package/src/types/operations.ts +2 -4
  125. package/src/types/views.ts +1 -3
  126. package/src/ui/__tests__/font-themes.test.ts +27 -8
  127. package/src/ui/color-themes.ts +1 -1
  128. package/src/ui/components/__tests__/jant-collection-form.test.ts +153 -0
  129. package/src/ui/components/__tests__/jant-compose-dialog.test.ts +512 -0
  130. package/src/ui/components/__tests__/jant-compose-editor.test.ts +272 -0
  131. package/src/ui/components/__tests__/jant-post-form.test.ts +172 -0
  132. package/src/ui/components/__tests__/jant-settings-avatar.test.ts +235 -0
  133. package/src/ui/components/__tests__/jant-settings-general.test.ts +319 -0
  134. package/src/ui/components/collection-types.ts +45 -0
  135. package/src/ui/components/compose-types.ts +75 -0
  136. package/src/ui/components/jant-collection-form.ts +512 -0
  137. package/src/ui/components/jant-compose-dialog.ts +494 -0
  138. package/src/ui/components/jant-compose-editor.ts +799 -0
  139. package/src/ui/components/jant-post-form.ts +290 -0
  140. package/src/ui/components/jant-settings-avatar.ts +231 -0
  141. package/src/ui/components/jant-settings-general.ts +436 -0
  142. package/src/ui/components/post-form-template.ts +260 -0
  143. package/src/ui/components/post-form-types.ts +87 -0
  144. package/src/ui/components/settings-types.ts +62 -0
  145. package/src/ui/compose/ComposeDialog.tsx +141 -385
  146. package/src/ui/compose/ComposePrompt.tsx +3 -3
  147. package/src/ui/dash/PostList.tsx +55 -61
  148. package/src/ui/dash/appearance/AdvancedContent.tsx +80 -0
  149. package/src/ui/dash/appearance/AppearanceNav.tsx +56 -0
  150. package/src/ui/dash/appearance/ColorThemeContent.tsx +129 -0
  151. package/src/ui/dash/appearance/FontThemeContent.tsx +98 -0
  152. package/src/ui/dash/collections/CollectionForm.tsx +130 -117
  153. package/src/ui/dash/collections/CollectionsListContent.tsx +102 -41
  154. package/src/ui/dash/collections/IconPickerGrid.tsx +50 -0
  155. package/src/ui/dash/collections/ViewCollectionContent.tsx +14 -3
  156. package/src/ui/dash/index.ts +1 -1
  157. package/src/ui/dash/posts/PostForm.tsx +248 -0
  158. package/src/ui/dash/settings/AccountContent.tsx +69 -80
  159. package/src/ui/dash/settings/GeneralContent.tsx +159 -478
  160. package/src/ui/dash/settings/SettingsNav.tsx +4 -4
  161. package/src/ui/font-themes.ts +115 -32
  162. package/src/ui/layouts/BaseLayout.tsx +49 -19
  163. package/src/ui/layouts/DashLayout.tsx +14 -9
  164. package/src/ui/layouts/SiteLayout.tsx +38 -23
  165. package/src/ui/pages/CollectionPage.tsx +12 -2
  166. package/src/ui/pages/CollectionsPage.tsx +27 -27
  167. package/src/ui/pages/HomePage.tsx +15 -6
  168. package/src/ui/pages/SearchPage.tsx +1 -2
  169. package/src/ui/shared/CollectionsSidebar.tsx +59 -0
  170. package/src/ui/shared/Pagination.tsx +2 -2
  171. package/dist/app.js +0 -265
  172. package/dist/auth.js +0 -36
  173. package/dist/client.js +0 -13
  174. package/dist/db/index.js +0 -10
  175. package/dist/db/schema.js +0 -224
  176. package/dist/i18n/Trans.js +0 -24
  177. package/dist/i18n/context.js +0 -58
  178. package/dist/i18n/detect.js +0 -26
  179. package/dist/i18n/i18n.js +0 -49
  180. package/dist/i18n/index.js +0 -44
  181. package/dist/i18n/locales/en.js +0 -1
  182. package/dist/i18n/locales/zh-Hans.js +0 -1
  183. package/dist/i18n/locales/zh-Hant.js +0 -1
  184. package/dist/i18n/locales.js +0 -13
  185. package/dist/i18n/middleware.js +0 -30
  186. package/dist/lib/avatar-upload.js +0 -134
  187. package/dist/lib/config.js +0 -143
  188. package/dist/lib/constants.js +0 -50
  189. package/dist/lib/excerpt.js +0 -76
  190. package/dist/lib/favicon.js +0 -102
  191. package/dist/lib/feed.js +0 -123
  192. package/dist/lib/image-processor.js +0 -187
  193. package/dist/lib/image.js +0 -97
  194. package/dist/lib/index.js +0 -7
  195. package/dist/lib/markdown.js +0 -83
  196. package/dist/lib/media-helpers.js +0 -49
  197. package/dist/lib/media-upload.js +0 -104
  198. package/dist/lib/nav-reorder.js +0 -27
  199. package/dist/lib/navigation.js +0 -79
  200. package/dist/lib/pagination.js +0 -44
  201. package/dist/lib/render.js +0 -53
  202. package/dist/lib/schemas.js +0 -174
  203. package/dist/lib/sqid.js +0 -72
  204. package/dist/lib/sse.js +0 -218
  205. package/dist/lib/storage.js +0 -164
  206. package/dist/lib/theme.js +0 -65
  207. package/dist/lib/time.js +0 -159
  208. package/dist/lib/timeline.js +0 -95
  209. package/dist/lib/timezones.js +0 -388
  210. package/dist/lib/url.js +0 -89
  211. package/dist/lib/view.js +0 -217
  212. package/dist/middleware/auth.js +0 -52
  213. package/dist/middleware/onboarding.js +0 -41
  214. package/dist/routes/api/collections.js +0 -124
  215. package/dist/routes/api/nav-items.js +0 -104
  216. package/dist/routes/api/pages.js +0 -91
  217. package/dist/routes/api/posts.js +0 -218
  218. package/dist/routes/api/search.js +0 -48
  219. package/dist/routes/api/settings.js +0 -68
  220. package/dist/routes/api/upload.js +0 -246
  221. package/dist/routes/auth/reset.js +0 -221
  222. package/dist/routes/auth/setup.js +0 -194
  223. package/dist/routes/auth/signin.js +0 -176
  224. package/dist/routes/compose.js +0 -48
  225. package/dist/routes/dash/collections.js +0 -115
  226. package/dist/routes/dash/index.js +0 -118
  227. package/dist/routes/dash/media.js +0 -106
  228. package/dist/routes/dash/pages.js +0 -294
  229. package/dist/routes/dash/posts.js +0 -244
  230. package/dist/routes/dash/redirects.js +0 -257
  231. package/dist/routes/dash/settings.js +0 -379
  232. package/dist/routes/feed/rss.js +0 -62
  233. package/dist/routes/feed/sitemap.js +0 -49
  234. package/dist/routes/pages/archive.js +0 -62
  235. package/dist/routes/pages/collection.js +0 -34
  236. package/dist/routes/pages/collections.js +0 -28
  237. package/dist/routes/pages/featured.js +0 -36
  238. package/dist/routes/pages/home.js +0 -64
  239. package/dist/routes/pages/latest.js +0 -45
  240. package/dist/routes/pages/page.js +0 -68
  241. package/dist/routes/pages/post.js +0 -44
  242. package/dist/routes/pages/search.js +0 -54
  243. package/dist/services/collection.js +0 -109
  244. package/dist/services/index.js +0 -24
  245. package/dist/services/media.js +0 -117
  246. package/dist/services/navigation.js +0 -91
  247. package/dist/services/page.js +0 -84
  248. package/dist/services/post.js +0 -229
  249. package/dist/services/redirect.js +0 -48
  250. package/dist/services/search.js +0 -67
  251. package/dist/services/settings.js +0 -68
  252. package/dist/types/bindings.js +0 -3
  253. package/dist/types/config.js +0 -147
  254. package/dist/types/constants.js +0 -27
  255. package/dist/types/entities.js +0 -3
  256. package/dist/types/lingui-react-macro.d.js +0 -9
  257. package/dist/types/operations.js +0 -3
  258. package/dist/types/props.js +0 -3
  259. package/dist/types/sortablejs.d.js +0 -5
  260. package/dist/types/views.js +0 -5
  261. package/dist/types.js +0 -11
  262. package/dist/ui/color-themes.js +0 -268
  263. package/dist/ui/compose/ComposeDialog.js +0 -467
  264. package/dist/ui/compose/ComposePrompt.js +0 -55
  265. package/dist/ui/dash/ActionButtons.js +0 -46
  266. package/dist/ui/dash/CrudPageHeader.js +0 -22
  267. package/dist/ui/dash/DangerZone.js +0 -36
  268. package/dist/ui/dash/FormatBadge.js +0 -27
  269. package/dist/ui/dash/ListItemRow.js +0 -21
  270. package/dist/ui/dash/PageForm.js +0 -195
  271. package/dist/ui/dash/PostForm.js +0 -395
  272. package/dist/ui/dash/PostList.js +0 -83
  273. package/dist/ui/dash/StatusBadge.js +0 -46
  274. package/dist/ui/dash/collections/CollectionForm.js +0 -152
  275. package/dist/ui/dash/collections/CollectionsListContent.js +0 -68
  276. package/dist/ui/dash/collections/ViewCollectionContent.js +0 -96
  277. package/dist/ui/dash/index.js +0 -10
  278. package/dist/ui/dash/media/MediaListContent.js +0 -166
  279. package/dist/ui/dash/media/ViewMediaContent.js +0 -212
  280. package/dist/ui/dash/pages/LinkFormContent.js +0 -130
  281. package/dist/ui/dash/pages/UnifiedPagesContent.js +0 -193
  282. package/dist/ui/dash/settings/AccountContent.js +0 -209
  283. package/dist/ui/dash/settings/AppearanceContent.js +0 -259
  284. package/dist/ui/dash/settings/GeneralContent.js +0 -536
  285. package/dist/ui/dash/settings/SettingsNav.js +0 -41
  286. package/dist/ui/feed/LinkCard.js +0 -72
  287. package/dist/ui/feed/NoteCard.js +0 -58
  288. package/dist/ui/feed/QuoteCard.js +0 -63
  289. package/dist/ui/feed/ThreadPreview.js +0 -48
  290. package/dist/ui/feed/TimelineFeed.js +0 -41
  291. package/dist/ui/feed/TimelineItem.js +0 -27
  292. package/dist/ui/font-themes.js +0 -36
  293. package/dist/ui/layouts/BaseLayout.js +0 -153
  294. package/dist/ui/layouts/DashLayout.js +0 -141
  295. package/dist/ui/layouts/SiteLayout.js +0 -169
  296. package/dist/ui/pages/ArchivePage.js +0 -143
  297. package/dist/ui/pages/CollectionPage.js +0 -70
  298. package/dist/ui/pages/CollectionsPage.js +0 -76
  299. package/dist/ui/pages/FeaturedPage.js +0 -24
  300. package/dist/ui/pages/HomePage.js +0 -24
  301. package/dist/ui/pages/PostPage.js +0 -55
  302. package/dist/ui/pages/SearchPage.js +0 -122
  303. package/dist/ui/pages/SinglePage.js +0 -23
  304. package/dist/ui/shared/EmptyState.js +0 -27
  305. package/dist/ui/shared/MediaGallery.js +0 -35
  306. package/dist/ui/shared/Pagination.js +0 -195
  307. package/dist/ui/shared/ThreadView.js +0 -108
  308. package/dist/ui/shared/index.js +0 -5
  309. package/dist/vendor/datastar.js +0 -1606
  310. package/src/lib/__tests__/config.test.ts +0 -192
  311. package/src/lib/config.ts +0 -167
  312. package/src/routes/compose.ts +0 -63
  313. package/src/ui/dash/PostForm.tsx +0 -360
  314. package/src/ui/dash/settings/AppearanceContent.tsx +0 -254
@@ -1,221 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
- /**
3
- * Password Reset Routes
4
- *
5
- * One-time token-based password reset flow.
6
- */ import { Hono } from "hono";
7
- import { useLingui as $_useLingui } from "@jant/core/i18n";
8
- import { hashPassword } from "better-auth/crypto";
9
- import { BaseLayout } from "../../ui/layouts/BaseLayout.js";
10
- import { dsRedirect, dsToast } from "../../lib/sse.js";
11
- import { SETTINGS_KEYS } from "../../lib/constants.js";
12
- import { ResetPasswordSchema } from "../../lib/schemas.js";
13
- const ResetContent = ({ token })=>{
14
- const { i18n: $__i18n, _: $__ } = $_useLingui();
15
- const signals = JSON.stringify({
16
- password: "",
17
- confirmPassword: "",
18
- token
19
- }).replace(/</g, "\\u003c");
20
- return /*#__PURE__*/ _jsx("div", {
21
- class: "min-h-screen flex items-center justify-center",
22
- children: /*#__PURE__*/ _jsxs("div", {
23
- class: "card max-w-md w-full",
24
- children: [
25
- /*#__PURE__*/ _jsxs("header", {
26
- children: [
27
- /*#__PURE__*/ _jsx("h2", {
28
- children: $__i18n._({
29
- id: "KbS2K9",
30
- message: "Reset Password"
31
- })
32
- }),
33
- /*#__PURE__*/ _jsx("p", {
34
- children: $__i18n._({
35
- id: "hWOZIv",
36
- message: "Enter your new password."
37
- })
38
- })
39
- ]
40
- }),
41
- /*#__PURE__*/ _jsx("section", {
42
- children: /*#__PURE__*/ _jsxs("form", {
43
- "data-signals": signals,
44
- "data-on:submit__prevent": "@post('/reset')",
45
- "data-indicator": "_loading",
46
- class: "flex flex-col gap-4",
47
- children: [
48
- /*#__PURE__*/ _jsxs("div", {
49
- class: "field",
50
- children: [
51
- /*#__PURE__*/ _jsx("label", {
52
- class: "label",
53
- children: $__i18n._({
54
- id: "7vhWI8",
55
- message: "New Password"
56
- })
57
- }),
58
- /*#__PURE__*/ _jsx("input", {
59
- type: "password",
60
- "data-bind": "password",
61
- class: "input",
62
- required: true,
63
- minLength: 8,
64
- autocomplete: "new-password"
65
- })
66
- ]
67
- }),
68
- /*#__PURE__*/ _jsxs("div", {
69
- class: "field",
70
- children: [
71
- /*#__PURE__*/ _jsx("label", {
72
- class: "label",
73
- children: $__i18n._({
74
- id: "p2/GCq",
75
- message: "Confirm Password"
76
- })
77
- }),
78
- /*#__PURE__*/ _jsx("input", {
79
- type: "password",
80
- "data-bind": "confirmPassword",
81
- class: "input",
82
- required: true,
83
- minLength: 8,
84
- autocomplete: "new-password"
85
- })
86
- ]
87
- }),
88
- /*#__PURE__*/ _jsxs("button", {
89
- type: "submit",
90
- class: "btn",
91
- "data-attr:disabled": "$_loading",
92
- children: [
93
- /*#__PURE__*/ _jsx("svg", {
94
- "data-show": "$_loading",
95
- style: "display:none",
96
- class: "animate-spin size-4",
97
- xmlns: "http://www.w3.org/2000/svg",
98
- viewBox: "0 0 24 24",
99
- fill: "none",
100
- stroke: "currentColor",
101
- "stroke-width": "2",
102
- "stroke-linecap": "round",
103
- "stroke-linejoin": "round",
104
- role: "status",
105
- children: /*#__PURE__*/ _jsx("path", {
106
- d: "M21 12a9 9 0 1 1-6.219-8.56"
107
- })
108
- }),
109
- $__i18n._({
110
- id: "KbS2K9",
111
- message: "Reset Password"
112
- })
113
- ]
114
- })
115
- ]
116
- })
117
- })
118
- ]
119
- })
120
- });
121
- };
122
- const ResetErrorContent = ()=>{
123
- const { i18n: $__i18n, _: $__ } = $_useLingui();
124
- return /*#__PURE__*/ _jsx("div", {
125
- class: "min-h-screen flex items-center justify-center",
126
- children: /*#__PURE__*/ _jsxs("div", {
127
- class: "card max-w-md w-full",
128
- children: [
129
- /*#__PURE__*/ _jsx("header", {
130
- children: /*#__PURE__*/ _jsx("h2", {
131
- children: $__i18n._({
132
- id: "7aECQB",
133
- message: "Invalid or Expired Link"
134
- })
135
- })
136
- }),
137
- /*#__PURE__*/ _jsx("section", {
138
- children: /*#__PURE__*/ _jsx("p", {
139
- class: "text-muted-foreground",
140
- children: $__i18n._({
141
- id: "GbVAnd",
142
- message: "This password reset link is invalid or has expired. Please generate a new one."
143
- })
144
- })
145
- })
146
- ]
147
- })
148
- });
149
- };
150
- /**
151
- * Validate a password reset token against the stored value.
152
- * Returns true if the token is valid and not expired.
153
- */ async function validateResetToken(settings, token) {
154
- const stored = await settings.get(SETTINGS_KEYS.PASSWORD_RESET_TOKEN);
155
- if (!stored) return false;
156
- const separatorIndex = stored.lastIndexOf(":");
157
- const storedToken = stored.substring(0, separatorIndex);
158
- const expiry = parseInt(stored.substring(separatorIndex + 1), 10);
159
- const now = Math.floor(Date.now() / 1000);
160
- return token === storedToken && now <= expiry;
161
- }
162
- export const resetRoutes = new Hono();
163
- resetRoutes.get("/reset", async (c)=>{
164
- const token = c.req.query("token");
165
- if (!token) {
166
- return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
167
- title: "Reset Password - Jant",
168
- c: c,
169
- children: /*#__PURE__*/ _jsx(ResetErrorContent, {})
170
- }));
171
- }
172
- const isValid = await validateResetToken(c.var.services.settings, token);
173
- if (!isValid) {
174
- return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
175
- title: "Reset Password - Jant",
176
- c: c,
177
- children: /*#__PURE__*/ _jsx(ResetErrorContent, {})
178
- }));
179
- }
180
- return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
181
- title: "Reset Password - Jant",
182
- c: c,
183
- children: /*#__PURE__*/ _jsx(ResetContent, {
184
- token: token
185
- })
186
- }));
187
- });
188
- resetRoutes.post("/reset", async (c)=>{
189
- const body = await c.req.json();
190
- const parsed = ResetPasswordSchema.safeParse(body);
191
- if (!parsed.success) {
192
- const msg = parsed.error.errors[0]?.message ?? "Invalid input";
193
- return dsToast(msg, "error");
194
- }
195
- const { password, token } = parsed.data;
196
- // Validate token
197
- const isValid = await validateResetToken(c.var.services.settings, token);
198
- if (!isValid) {
199
- return dsToast("Invalid or expired reset link.", "error");
200
- }
201
- try {
202
- const hashedPassword = await hashPassword(password);
203
- const db = c.env.DB.withSession();
204
- // Get admin user
205
- const userResult = await db.prepare("SELECT id FROM user LIMIT 1").first();
206
- if (!userResult) {
207
- return dsToast("No user account found.", "error");
208
- }
209
- // Update password
210
- await db.prepare("UPDATE account SET password = ? WHERE user_id = ? AND provider_id = 'credential'").bind(hashedPassword, userResult.id).run();
211
- // Delete all sessions
212
- await db.prepare("DELETE FROM session WHERE user_id = ?").bind(userResult.id).run();
213
- // Delete the reset token
214
- await c.var.services.settings.remove(SETTINGS_KEYS.PASSWORD_RESET_TOKEN);
215
- return dsRedirect("/signin?reset");
216
- } catch (err) {
217
- // eslint-disable-next-line no-console -- Error logging is intentional
218
- console.error("Password reset error:", err);
219
- return dsToast("Failed to reset password.", "error");
220
- }
221
- });
@@ -1,194 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
- /**
3
- * Setup Routes
4
- *
5
- * Initial admin account creation during first-time setup.
6
- */ import { Hono } from "hono";
7
- import { useLingui as $_useLingui } from "@jant/core/i18n";
8
- import { BaseLayout } from "../../ui/layouts/BaseLayout.js";
9
- import { dsRedirect, dsToast } from "../../lib/sse.js";
10
- import { SetupSchema } from "../../lib/schemas.js";
11
- import { mapIanaToTimezone } from "../../lib/timezones.js";
12
- const SetupContent = ()=>{
13
- const { i18n: $__i18n, _: $__ } = $_useLingui();
14
- return /*#__PURE__*/ _jsx("div", {
15
- class: "min-h-screen flex items-center justify-center",
16
- children: /*#__PURE__*/ _jsxs("div", {
17
- class: "card max-w-md w-full",
18
- children: [
19
- /*#__PURE__*/ _jsxs("header", {
20
- children: [
21
- /*#__PURE__*/ _jsx("h2", {
22
- children: $__i18n._({
23
- id: "GorKul",
24
- message: "Welcome to Jant"
25
- })
26
- }),
27
- /*#__PURE__*/ _jsx("p", {
28
- children: $__i18n._({
29
- id: "GX2VMa",
30
- message: "Create your admin account."
31
- })
32
- })
33
- ]
34
- }),
35
- /*#__PURE__*/ _jsx("section", {
36
- children: /*#__PURE__*/ _jsxs("form", {
37
- "data-signals": "{name: '', email: '', password: '', _timezone: ''}",
38
- "data-init": "$_timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || ''",
39
- "data-on:submit__prevent": "@post('/setup')",
40
- "data-indicator": "_loading",
41
- class: "flex flex-col gap-4",
42
- children: [
43
- /*#__PURE__*/ _jsxs("div", {
44
- class: "field",
45
- children: [
46
- /*#__PURE__*/ _jsx("label", {
47
- class: "label",
48
- children: $__i18n._({
49
- id: "/Rj5P4",
50
- message: "Your Name"
51
- })
52
- }),
53
- /*#__PURE__*/ _jsx("input", {
54
- type: "text",
55
- "data-bind": "name",
56
- class: "input",
57
- required: true,
58
- placeholder: "John Doe"
59
- })
60
- ]
61
- }),
62
- /*#__PURE__*/ _jsxs("div", {
63
- class: "field",
64
- children: [
65
- /*#__PURE__*/ _jsx("label", {
66
- class: "label",
67
- children: $__i18n._({
68
- id: "O3oNi5",
69
- message: "Email"
70
- })
71
- }),
72
- /*#__PURE__*/ _jsx("input", {
73
- type: "email",
74
- "data-bind": "email",
75
- class: "input",
76
- required: true,
77
- placeholder: "you@example.com"
78
- })
79
- ]
80
- }),
81
- /*#__PURE__*/ _jsxs("div", {
82
- class: "field",
83
- children: [
84
- /*#__PURE__*/ _jsx("label", {
85
- class: "label",
86
- children: $__i18n._({
87
- id: "8ZsakT",
88
- message: "Password"
89
- })
90
- }),
91
- /*#__PURE__*/ _jsx("input", {
92
- type: "password",
93
- "data-bind": "password",
94
- class: "input",
95
- required: true,
96
- minLength: 8
97
- })
98
- ]
99
- }),
100
- /*#__PURE__*/ _jsxs("button", {
101
- type: "submit",
102
- class: "btn",
103
- "data-attr:disabled": "$_loading",
104
- children: [
105
- /*#__PURE__*/ _jsx("svg", {
106
- "data-show": "$_loading",
107
- style: "display:none",
108
- class: "animate-spin size-4",
109
- xmlns: "http://www.w3.org/2000/svg",
110
- viewBox: "0 0 24 24",
111
- fill: "none",
112
- stroke: "currentColor",
113
- "stroke-width": "2",
114
- "stroke-linecap": "round",
115
- "stroke-linejoin": "round",
116
- role: "status",
117
- children: /*#__PURE__*/ _jsx("path", {
118
- d: "M21 12a9 9 0 1 1-6.219-8.56"
119
- })
120
- }),
121
- $__i18n._({
122
- id: "EGwzOK",
123
- message: "Complete Setup"
124
- })
125
- ]
126
- })
127
- ]
128
- })
129
- })
130
- ]
131
- })
132
- });
133
- };
134
- export const setupRoutes = new Hono();
135
- setupRoutes.get("/setup", async (c)=>{
136
- const isComplete = await c.var.services.settings.isOnboardingComplete();
137
- if (isComplete) return c.redirect("/");
138
- return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
139
- title: "Setup - Jant",
140
- c: c,
141
- children: /*#__PURE__*/ _jsx(SetupContent, {})
142
- }));
143
- });
144
- setupRoutes.post("/setup", async (c)=>{
145
- const isComplete = await c.var.services.settings.isOnboardingComplete();
146
- if (isComplete) return c.redirect("/");
147
- const body = await c.req.json();
148
- const parsed = SetupSchema.safeParse(body);
149
- const browserTimezone = body._timezone;
150
- if (!parsed.success) {
151
- const msg = parsed.error.errors[0]?.message ?? "Invalid input";
152
- return dsToast(msg, "error");
153
- }
154
- const { name, email, password } = parsed.data;
155
- if (!c.var.auth) {
156
- return dsToast("AUTH_SECRET not configured", "error");
157
- }
158
- try {
159
- const signUpResponse = await c.var.auth.api.signUpEmail({
160
- body: {
161
- name,
162
- email,
163
- password
164
- }
165
- });
166
- if (!signUpResponse || "error" in signUpResponse) {
167
- return dsToast("Failed to create account", "error");
168
- }
169
- await c.var.services.settings.completeOnboarding();
170
- // Save auto-detected timezone
171
- if (browserTimezone) {
172
- const tz = mapIanaToTimezone(browserTimezone);
173
- if (tz !== "UTC") {
174
- await c.var.services.settings.set("TIME_ZONE", tz);
175
- }
176
- }
177
- // Seed default navigation items
178
- await c.var.services.navItems.create({
179
- type: "link",
180
- label: "Featured",
181
- url: "/featured"
182
- });
183
- await c.var.services.navItems.create({
184
- type: "link",
185
- label: "Collections",
186
- url: "/collections"
187
- });
188
- return dsRedirect("/signin?setup");
189
- } catch (err) {
190
- // eslint-disable-next-line no-console -- Error logging is intentional
191
- console.error("Setup error:", err);
192
- return dsToast("Failed to create account", "error");
193
- }
194
- });
@@ -1,176 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
- /**
3
- * Sign-in / Sign-out Routes
4
- */ import { Hono } from "hono";
5
- import { useLingui as $_useLingui } from "@jant/core/i18n";
6
- import { BaseLayout } from "../../ui/layouts/BaseLayout.js";
7
- import { dsRedirect, dsToast } from "../../lib/sse.js";
8
- import { SigninSchema } from "../../lib/schemas.js";
9
- const SigninContent = ({ demoEmail, demoPassword })=>{
10
- const { i18n: $__i18n, _: $__ } = $_useLingui();
11
- const signals = JSON.stringify({
12
- email: demoEmail || "",
13
- password: demoPassword || ""
14
- }).replace(/</g, "\\u003c");
15
- return /*#__PURE__*/ _jsx("div", {
16
- class: "min-h-screen flex items-center justify-center",
17
- children: /*#__PURE__*/ _jsxs("div", {
18
- class: "card max-w-md w-full",
19
- children: [
20
- /*#__PURE__*/ _jsx("header", {
21
- children: /*#__PURE__*/ _jsx("h2", {
22
- children: $__i18n._({
23
- id: "n1ekoW",
24
- message: "Sign In"
25
- })
26
- })
27
- }),
28
- /*#__PURE__*/ _jsxs("section", {
29
- children: [
30
- demoEmail && demoPassword && /*#__PURE__*/ _jsx("p", {
31
- class: "text-muted-foreground text-sm mb-4",
32
- children: $__i18n._({
33
- id: "er8+x7",
34
- message: "Demo account pre-filled. Just click Sign In."
35
- })
36
- }),
37
- /*#__PURE__*/ _jsxs("form", {
38
- "data-signals": signals,
39
- "data-on:submit__prevent": "@post('/signin')",
40
- "data-indicator": "_loading",
41
- class: "flex flex-col gap-4",
42
- children: [
43
- /*#__PURE__*/ _jsxs("div", {
44
- class: "field",
45
- children: [
46
- /*#__PURE__*/ _jsx("label", {
47
- class: "label",
48
- children: $__i18n._({
49
- id: "O3oNi5",
50
- message: "Email"
51
- })
52
- }),
53
- /*#__PURE__*/ _jsx("input", {
54
- type: "email",
55
- "data-bind": "email",
56
- class: "input",
57
- required: true
58
- })
59
- ]
60
- }),
61
- /*#__PURE__*/ _jsxs("div", {
62
- class: "field",
63
- children: [
64
- /*#__PURE__*/ _jsx("label", {
65
- class: "label",
66
- children: $__i18n._({
67
- id: "8ZsakT",
68
- message: "Password"
69
- })
70
- }),
71
- /*#__PURE__*/ _jsx("input", {
72
- type: "password",
73
- "data-bind": "password",
74
- class: "input",
75
- required: true
76
- })
77
- ]
78
- }),
79
- /*#__PURE__*/ _jsxs("button", {
80
- type: "submit",
81
- class: "btn",
82
- "data-attr:disabled": "$_loading",
83
- children: [
84
- /*#__PURE__*/ _jsx("svg", {
85
- "data-show": "$_loading",
86
- style: "display:none",
87
- class: "animate-spin size-4",
88
- xmlns: "http://www.w3.org/2000/svg",
89
- viewBox: "0 0 24 24",
90
- fill: "none",
91
- stroke: "currentColor",
92
- "stroke-width": "2",
93
- "stroke-linecap": "round",
94
- "stroke-linejoin": "round",
95
- role: "status",
96
- children: /*#__PURE__*/ _jsx("path", {
97
- d: "M21 12a9 9 0 1 1-6.219-8.56"
98
- })
99
- }),
100
- $__i18n._({
101
- id: "n1ekoW",
102
- message: "Sign In"
103
- })
104
- ]
105
- })
106
- ]
107
- })
108
- ]
109
- })
110
- ]
111
- })
112
- });
113
- };
114
- export const signinRoutes = new Hono();
115
- signinRoutes.get("/signin", async (c)=>{
116
- const isSetup = c.req.query("setup") !== undefined;
117
- const isReset = c.req.query("reset") !== undefined;
118
- let toast;
119
- if (isSetup) {
120
- toast = {
121
- message: "Account created successfully. Please sign in."
122
- };
123
- } else if (isReset) {
124
- toast = {
125
- message: "Password reset successfully. Please sign in."
126
- };
127
- }
128
- return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
129
- title: "Sign In - Jant",
130
- c: c,
131
- toast: toast,
132
- children: /*#__PURE__*/ _jsx(SigninContent, {
133
- demoEmail: c.env.DEMO_EMAIL,
134
- demoPassword: c.env.DEMO_PASSWORD
135
- })
136
- }));
137
- });
138
- signinRoutes.post("/signin", async (c)=>{
139
- if (!c.var.auth) {
140
- return dsToast("Auth not configured", "error");
141
- }
142
- const body = await c.req.json();
143
- const parsed = SigninSchema.safeParse(body);
144
- if (!parsed.success) {
145
- const msg = parsed.error.errors[0]?.message ?? "Invalid input";
146
- return dsToast(msg, "error");
147
- }
148
- const { email, password } = parsed.data;
149
- try {
150
- const { headers } = await c.var.auth.api.signInEmail({
151
- returnHeaders: true,
152
- body: {
153
- email,
154
- password
155
- },
156
- headers: c.req.raw.headers
157
- });
158
- return dsRedirect("/dash", {
159
- headers
160
- });
161
- } catch {
162
- return dsToast("Invalid email or password", "error");
163
- }
164
- });
165
- signinRoutes.get("/signout", async (c)=>{
166
- if (c.var.auth) {
167
- try {
168
- await c.var.auth.api.signOut({
169
- headers: c.req.raw.headers
170
- });
171
- } catch {
172
- // Ignore signout errors
173
- }
174
- }
175
- return c.redirect("/");
176
- });
@@ -1,48 +0,0 @@
1
- /**
2
- * Compose Route
3
- *
4
- * Handles post creation from the public-site compose dialog.
5
- * Returns dsRedirect to the new post's permalink (Datastar form pattern).
6
- */ import { Hono } from "hono";
7
- import { requireAuth } from "../middleware/auth.js";
8
- import { CreatePostSchema, validateMediaCount } from "../lib/schemas.js";
9
- import * as sqid from "../lib/sqid.js";
10
- import { dsRedirect, dsToast } from "../lib/sse.js";
11
- export const composeRoutes = new Hono();
12
- // All compose routes require authentication
13
- composeRoutes.use("*", requireAuth());
14
- composeRoutes.post("/", async (c)=>{
15
- const raw = await c.req.json();
16
- const result = CreatePostSchema.safeParse(raw);
17
- if (!result.success) {
18
- const firstError = result.error.issues[0]?.message ?? "Invalid input";
19
- return dsToast(firstError, "error");
20
- }
21
- const data = result.data;
22
- // Validate media count
23
- if (data.mediaIds) {
24
- const mediaError = validateMediaCount(data.mediaIds);
25
- if (mediaError) {
26
- return dsToast(mediaError, "error");
27
- }
28
- }
29
- const post = await c.var.services.posts.create({
30
- format: data.format,
31
- title: data.title || undefined,
32
- body: data.body || undefined,
33
- status: data.status ?? "published",
34
- featured: data.featured,
35
- pinned: data.pinned,
36
- url: data.url || undefined,
37
- quoteText: data.quoteText || undefined,
38
- rating: data.rating || undefined,
39
- collectionId: data.collectionId || undefined
40
- });
41
- // Attach media if provided
42
- if (data.mediaIds && data.mediaIds.length > 0) {
43
- await c.var.services.media.attachToPost(post.id, data.mediaIds);
44
- }
45
- // Redirect to the new post's permalink
46
- const permalink = post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`;
47
- return dsRedirect(permalink);
48
- });