@jant/core 0.1.2 → 0.2.0

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 (288) hide show
  1. package/dist/app.d.ts +34 -0
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +474 -0
  4. package/dist/assets/datastar.min.js +1775 -0
  5. package/dist/auth.d.ts +23 -0
  6. package/dist/auth.d.ts.map +1 -0
  7. package/dist/auth.js +34 -0
  8. package/{src/client.ts → dist/client.d.ts} +1 -1
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +4 -0
  11. package/dist/db/index.d.ts +10 -0
  12. package/dist/db/index.d.ts.map +1 -0
  13. package/dist/db/index.js +10 -0
  14. package/dist/db/schema.d.ts +1507 -0
  15. package/dist/db/schema.d.ts.map +1 -0
  16. package/dist/db/schema.js +183 -0
  17. package/{src/i18n/Trans.tsx → dist/i18n/Trans.d.ts} +4 -10
  18. package/dist/i18n/Trans.d.ts.map +1 -0
  19. package/dist/i18n/Trans.js +24 -0
  20. package/dist/i18n/context.d.ts +69 -0
  21. package/dist/i18n/context.d.ts.map +1 -0
  22. package/dist/i18n/context.js +61 -0
  23. package/dist/i18n/detect.d.ts +31 -0
  24. package/dist/i18n/detect.d.ts.map +1 -0
  25. package/dist/i18n/detect.js +77 -0
  26. package/{src/i18n/i18n.ts → dist/i18n/i18n.d.ts} +5 -25
  27. package/dist/i18n/i18n.d.ts.map +1 -0
  28. package/dist/i18n/i18n.js +55 -0
  29. package/dist/i18n/index.d.ts +41 -0
  30. package/dist/i18n/index.d.ts.map +1 -0
  31. package/{src/i18n/index.ts → dist/i18n/index.js} +3 -24
  32. package/dist/i18n/locales/en.d.ts +3 -0
  33. package/dist/i18n/locales/en.d.ts.map +1 -0
  34. package/dist/i18n/locales/en.js +1 -0
  35. package/dist/i18n/locales/zh-Hans.d.ts +3 -0
  36. package/dist/i18n/locales/zh-Hans.d.ts.map +1 -0
  37. package/dist/i18n/locales/zh-Hans.js +1 -0
  38. package/dist/i18n/locales/zh-Hant.d.ts +3 -0
  39. package/dist/i18n/locales/zh-Hant.d.ts.map +1 -0
  40. package/dist/i18n/locales/zh-Hant.js +1 -0
  41. package/dist/i18n/locales.d.ts +11 -0
  42. package/dist/i18n/locales.d.ts.map +1 -0
  43. package/dist/i18n/locales.js +13 -0
  44. package/dist/i18n/middleware.d.ts +24 -0
  45. package/dist/i18n/middleware.d.ts.map +1 -0
  46. package/dist/i18n/middleware.js +41 -0
  47. package/dist/index.d.ts +16 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/{src/index.ts → dist/index.js} +1 -28
  50. package/dist/lib/assets.d.ts +19 -0
  51. package/dist/lib/assets.d.ts.map +1 -0
  52. package/dist/lib/assets.js +33 -0
  53. package/dist/lib/constants.d.ts +36 -0
  54. package/dist/lib/constants.d.ts.map +1 -0
  55. package/dist/lib/constants.js +50 -0
  56. package/{src/lib/image.ts → dist/lib/image.d.ts} +13 -47
  57. package/dist/lib/image.d.ts.map +1 -0
  58. package/dist/lib/image.js +77 -0
  59. package/{src/lib/index.ts → dist/lib/index.d.ts} +1 -1
  60. package/dist/lib/index.d.ts.map +1 -0
  61. package/dist/lib/index.js +7 -0
  62. package/dist/lib/markdown.d.ts +60 -0
  63. package/dist/lib/markdown.d.ts.map +1 -0
  64. package/{src/lib/markdown.ts → dist/lib/markdown.js} +16 -26
  65. package/dist/lib/schemas.d.ts +113 -0
  66. package/dist/lib/schemas.d.ts.map +1 -0
  67. package/dist/lib/schemas.js +71 -0
  68. package/dist/lib/sqid.d.ts +60 -0
  69. package/dist/lib/sqid.d.ts.map +1 -0
  70. package/{src/lib/sqid.ts → dist/lib/sqid.js} +15 -22
  71. package/dist/lib/sse.d.ts +95 -0
  72. package/dist/lib/sse.d.ts.map +1 -0
  73. package/dist/lib/sse.js +81 -0
  74. package/dist/lib/time.d.ts +90 -0
  75. package/dist/lib/time.d.ts.map +1 -0
  76. package/{src/lib/time.ts → dist/lib/time.js} +20 -33
  77. package/{src/lib/url.ts → dist/lib/url.d.ts} +5 -30
  78. package/dist/lib/url.d.ts.map +1 -0
  79. package/dist/lib/url.js +89 -0
  80. package/dist/middleware/auth.d.ts +24 -0
  81. package/dist/middleware/auth.d.ts.map +1 -0
  82. package/dist/middleware/auth.js +52 -0
  83. package/dist/routes/api/posts.d.ts +13 -0
  84. package/dist/routes/api/posts.d.ts.map +1 -0
  85. package/dist/routes/api/posts.js +124 -0
  86. package/dist/routes/api/search.d.ts +13 -0
  87. package/dist/routes/api/search.d.ts.map +1 -0
  88. package/dist/routes/api/search.js +49 -0
  89. package/dist/routes/api/upload.d.ts +16 -0
  90. package/dist/routes/api/upload.d.ts.map +1 -0
  91. package/dist/routes/api/upload.js +227 -0
  92. package/dist/routes/dash/collections.d.ts +13 -0
  93. package/dist/routes/dash/collections.d.ts.map +1 -0
  94. package/dist/routes/dash/collections.js +512 -0
  95. package/dist/routes/dash/index.d.ts +15 -0
  96. package/dist/routes/dash/index.d.ts.map +1 -0
  97. package/dist/routes/dash/index.js +117 -0
  98. package/dist/routes/dash/media.d.ts +16 -0
  99. package/dist/routes/dash/media.d.ts.map +1 -0
  100. package/dist/routes/dash/media.js +589 -0
  101. package/dist/routes/dash/pages.d.ts +15 -0
  102. package/dist/routes/dash/pages.d.ts.map +1 -0
  103. package/dist/routes/dash/pages.js +290 -0
  104. package/dist/routes/dash/posts.d.ts +13 -0
  105. package/dist/routes/dash/posts.d.ts.map +1 -0
  106. package/dist/routes/dash/posts.js +226 -0
  107. package/dist/routes/dash/redirects.d.ts +13 -0
  108. package/dist/routes/dash/redirects.d.ts.map +1 -0
  109. package/dist/routes/dash/redirects.js +237 -0
  110. package/dist/routes/dash/settings.d.ts +13 -0
  111. package/dist/routes/dash/settings.d.ts.map +1 -0
  112. package/dist/routes/dash/settings.js +154 -0
  113. package/dist/routes/feed/rss.d.ts +13 -0
  114. package/dist/routes/feed/rss.d.ts.map +1 -0
  115. package/dist/routes/feed/rss.js +95 -0
  116. package/dist/routes/feed/sitemap.d.ts +13 -0
  117. package/dist/routes/feed/sitemap.d.ts.map +1 -0
  118. package/dist/routes/feed/sitemap.js +59 -0
  119. package/dist/routes/pages/archive.d.ts +15 -0
  120. package/dist/routes/pages/archive.d.ts.map +1 -0
  121. package/dist/routes/pages/archive.js +255 -0
  122. package/dist/routes/pages/collection.d.ts +13 -0
  123. package/dist/routes/pages/collection.d.ts.map +1 -0
  124. package/dist/routes/pages/collection.js +93 -0
  125. package/dist/routes/pages/home.d.ts +13 -0
  126. package/dist/routes/pages/home.d.ts.map +1 -0
  127. package/dist/routes/pages/home.js +122 -0
  128. package/dist/routes/pages/page.d.ts +15 -0
  129. package/dist/routes/pages/page.d.ts.map +1 -0
  130. package/dist/routes/pages/page.js +69 -0
  131. package/dist/routes/pages/post.d.ts +13 -0
  132. package/dist/routes/pages/post.d.ts.map +1 -0
  133. package/dist/routes/pages/post.js +90 -0
  134. package/dist/routes/pages/search.d.ts +13 -0
  135. package/dist/routes/pages/search.d.ts.map +1 -0
  136. package/dist/routes/pages/search.js +180 -0
  137. package/dist/services/collection.d.ts +31 -0
  138. package/dist/services/collection.d.ts.map +1 -0
  139. package/dist/services/collection.js +108 -0
  140. package/dist/services/index.d.ts +28 -0
  141. package/dist/services/index.d.ts.map +1 -0
  142. package/dist/services/index.js +20 -0
  143. package/dist/services/media.d.ts +27 -0
  144. package/dist/services/media.d.ts.map +1 -0
  145. package/dist/services/media.js +62 -0
  146. package/dist/services/post.d.ts +31 -0
  147. package/dist/services/post.d.ts.map +1 -0
  148. package/dist/services/post.js +191 -0
  149. package/dist/services/redirect.d.ts +15 -0
  150. package/dist/services/redirect.d.ts.map +1 -0
  151. package/dist/services/redirect.js +48 -0
  152. package/dist/services/search.d.ts +26 -0
  153. package/dist/services/search.d.ts.map +1 -0
  154. package/dist/services/search.js +61 -0
  155. package/dist/services/settings.d.ts +17 -0
  156. package/dist/services/settings.d.ts.map +1 -0
  157. package/dist/services/settings.js +65 -0
  158. package/dist/theme/components/ActionButtons.d.ts +43 -0
  159. package/dist/theme/components/ActionButtons.d.ts.map +1 -0
  160. package/dist/theme/components/ActionButtons.js +50 -0
  161. package/dist/theme/components/CrudPageHeader.d.ts +23 -0
  162. package/dist/theme/components/CrudPageHeader.d.ts.map +1 -0
  163. package/dist/theme/components/CrudPageHeader.js +22 -0
  164. package/dist/theme/components/DangerZone.d.ts +36 -0
  165. package/dist/theme/components/DangerZone.d.ts.map +1 -0
  166. package/dist/theme/components/DangerZone.js +39 -0
  167. package/dist/theme/components/EmptyState.d.ts +27 -0
  168. package/dist/theme/components/EmptyState.d.ts.map +1 -0
  169. package/dist/theme/components/EmptyState.js +27 -0
  170. package/dist/theme/components/ListItemRow.d.ts +15 -0
  171. package/dist/theme/components/ListItemRow.d.ts.map +1 -0
  172. package/dist/theme/components/ListItemRow.js +21 -0
  173. package/dist/theme/components/PageForm.d.ts +14 -0
  174. package/dist/theme/components/PageForm.d.ts.map +1 -0
  175. package/dist/theme/components/PageForm.js +173 -0
  176. package/dist/theme/components/Pagination.d.ts +46 -0
  177. package/dist/theme/components/Pagination.d.ts.map +1 -0
  178. package/dist/theme/components/Pagination.js +159 -0
  179. package/dist/theme/components/PostForm.d.ts +12 -0
  180. package/dist/theme/components/PostForm.d.ts.map +1 -0
  181. package/dist/theme/components/PostForm.js +230 -0
  182. package/dist/theme/components/PostList.d.ts +10 -0
  183. package/dist/theme/components/PostList.d.ts.map +1 -0
  184. package/dist/theme/components/PostList.js +73 -0
  185. package/dist/theme/components/ThreadView.d.ts +15 -0
  186. package/dist/theme/components/ThreadView.d.ts.map +1 -0
  187. package/dist/theme/components/ThreadView.js +111 -0
  188. package/dist/theme/components/TypeBadge.d.ts +12 -0
  189. package/dist/theme/components/TypeBadge.d.ts.map +1 -0
  190. package/dist/theme/components/TypeBadge.js +39 -0
  191. package/dist/theme/components/VisibilityBadge.d.ts +12 -0
  192. package/dist/theme/components/VisibilityBadge.d.ts.map +1 -0
  193. package/dist/theme/components/VisibilityBadge.js +37 -0
  194. package/{src/theme/components/index.ts → dist/theme/components/index.d.ts} +1 -0
  195. package/dist/theme/components/index.d.ts.map +1 -0
  196. package/dist/theme/components/index.js +12 -0
  197. package/dist/theme/index.d.ts +21 -0
  198. package/dist/theme/index.d.ts.map +1 -0
  199. package/{src/theme/index.ts → dist/theme/index.js} +1 -4
  200. package/dist/theme/layouts/BaseLayout.d.ts +16 -0
  201. package/dist/theme/layouts/BaseLayout.d.ts.map +1 -0
  202. package/dist/theme/layouts/BaseLayout.js +58 -0
  203. package/dist/theme/layouts/DashLayout.d.ts +15 -0
  204. package/dist/theme/layouts/DashLayout.d.ts.map +1 -0
  205. package/dist/theme/layouts/DashLayout.js +139 -0
  206. package/{src/theme/layouts/index.ts → dist/theme/layouts/index.d.ts} +1 -0
  207. package/dist/theme/layouts/index.d.ts.map +1 -0
  208. package/dist/theme/layouts/index.js +2 -0
  209. package/dist/theme/styles/main.css +2 -0
  210. package/dist/types.d.ts +179 -0
  211. package/dist/types.d.ts.map +1 -0
  212. package/dist/types.js +19 -0
  213. package/package.json +26 -26
  214. package/drizzle.config.ts +0 -10
  215. package/lingui.config.ts +0 -16
  216. package/src/app.tsx +0 -377
  217. package/src/assets/datastar.min.js +0 -8
  218. package/src/auth.ts +0 -38
  219. package/src/db/index.ts +0 -14
  220. package/src/db/migrations/0000_solid_moon_knight.sql +0 -118
  221. package/src/db/migrations/0001_add_search_fts.sql +0 -40
  222. package/src/db/migrations/0002_collection_path.sql +0 -2
  223. package/src/db/migrations/0003_collection_path_nullable.sql +0 -21
  224. package/src/db/migrations/0004_media_uuid.sql +0 -35
  225. package/src/db/migrations/meta/0000_snapshot.json +0 -784
  226. package/src/db/migrations/meta/_journal.json +0 -41
  227. package/src/db/schema.ts +0 -159
  228. package/src/i18n/EXAMPLES.md +0 -235
  229. package/src/i18n/README.md +0 -296
  230. package/src/i18n/context.tsx +0 -101
  231. package/src/i18n/detect.ts +0 -100
  232. package/src/i18n/locales/en.po +0 -875
  233. package/src/i18n/locales/en.ts +0 -1
  234. package/src/i18n/locales/zh-Hans.po +0 -875
  235. package/src/i18n/locales/zh-Hans.ts +0 -1
  236. package/src/i18n/locales/zh-Hant.po +0 -875
  237. package/src/i18n/locales/zh-Hant.ts +0 -1
  238. package/src/i18n/locales.ts +0 -14
  239. package/src/i18n/middleware.ts +0 -59
  240. package/src/lib/assets.ts +0 -47
  241. package/src/lib/constants.ts +0 -67
  242. package/src/lib/schemas.ts +0 -92
  243. package/src/lib/sse.ts +0 -152
  244. package/src/middleware/auth.ts +0 -59
  245. package/src/routes/api/posts.ts +0 -127
  246. package/src/routes/api/search.ts +0 -53
  247. package/src/routes/api/upload.ts +0 -240
  248. package/src/routes/dash/collections.tsx +0 -341
  249. package/src/routes/dash/index.tsx +0 -89
  250. package/src/routes/dash/media.tsx +0 -551
  251. package/src/routes/dash/pages.tsx +0 -245
  252. package/src/routes/dash/posts.tsx +0 -202
  253. package/src/routes/dash/redirects.tsx +0 -155
  254. package/src/routes/dash/settings.tsx +0 -93
  255. package/src/routes/feed/rss.ts +0 -119
  256. package/src/routes/feed/sitemap.ts +0 -75
  257. package/src/routes/pages/archive.tsx +0 -223
  258. package/src/routes/pages/collection.tsx +0 -79
  259. package/src/routes/pages/home.tsx +0 -93
  260. package/src/routes/pages/page.tsx +0 -64
  261. package/src/routes/pages/post.tsx +0 -81
  262. package/src/routes/pages/search.tsx +0 -162
  263. package/src/services/collection.ts +0 -180
  264. package/src/services/index.ts +0 -40
  265. package/src/services/media.ts +0 -97
  266. package/src/services/post.ts +0 -279
  267. package/src/services/redirect.ts +0 -74
  268. package/src/services/search.ts +0 -117
  269. package/src/services/settings.ts +0 -76
  270. package/src/theme/components/ActionButtons.tsx +0 -98
  271. package/src/theme/components/CrudPageHeader.tsx +0 -48
  272. package/src/theme/components/DangerZone.tsx +0 -77
  273. package/src/theme/components/EmptyState.tsx +0 -56
  274. package/src/theme/components/ListItemRow.tsx +0 -24
  275. package/src/theme/components/PageForm.tsx +0 -114
  276. package/src/theme/components/Pagination.tsx +0 -196
  277. package/src/theme/components/PostForm.tsx +0 -122
  278. package/src/theme/components/PostList.tsx +0 -68
  279. package/src/theme/components/ThreadView.tsx +0 -118
  280. package/src/theme/components/TypeBadge.tsx +0 -28
  281. package/src/theme/components/VisibilityBadge.tsx +0 -33
  282. package/src/theme/layouts/BaseLayout.tsx +0 -49
  283. package/src/theme/layouts/DashLayout.tsx +0 -108
  284. package/src/theme/styles/main.css +0 -52
  285. package/src/types.ts +0 -222
  286. package/tsconfig.json +0 -16
  287. package/vite.config.ts +0 -82
  288. package/wrangler.toml +0 -21
package/dist/app.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Jant App Factory
3
+ */
4
+ import { Hono } from "hono";
5
+ import { type Services } from "./services/index.js";
6
+ import { type Auth } from "./auth.js";
7
+ import type { Bindings, JantConfig } from "./types.js";
8
+ export interface AppVariables {
9
+ services: Services;
10
+ auth: Auth;
11
+ config: JantConfig;
12
+ }
13
+ export type App = Hono<{
14
+ Bindings: Bindings;
15
+ Variables: AppVariables;
16
+ }>;
17
+ /**
18
+ * Create a Jant application
19
+ *
20
+ * @param config - Optional configuration
21
+ * @returns Hono app instance
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { createApp } from "@jant/core";
26
+ *
27
+ * export default createApp({
28
+ * site: { name: "My Blog" },
29
+ * theme: { components: { PostCard: MyPostCard } },
30
+ * });
31
+ * ```
32
+ */
33
+ export declare function createApp(config?: JantConfig): App;
34
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAmCvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,MAAM,GAAE,UAAe,GAAG,GAAG,CAmTtD"}
package/dist/app.js ADDED
@@ -0,0 +1,474 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
+ /**
3
+ * Jant App Factory
4
+ */ import { Hono } from "hono";
5
+ import { createDatabase } from "./db/index.js";
6
+ import { createServices } from "./services/index.js";
7
+ import { createAuth } from "./auth.js";
8
+ import { i18nMiddleware, useLingui } from "./i18n/index.js";
9
+ // Routes - Pages
10
+ import { homeRoutes } from "./routes/pages/home.js";
11
+ import { postRoutes } from "./routes/pages/post.js";
12
+ import { pageRoutes } from "./routes/pages/page.js";
13
+ import { collectionRoutes } from "./routes/pages/collection.js";
14
+ import { archiveRoutes } from "./routes/pages/archive.js";
15
+ import { searchRoutes } from "./routes/pages/search.js";
16
+ // Routes - Dashboard
17
+ import { dashIndexRoutes } from "./routes/dash/index.js";
18
+ import { postsRoutes as dashPostsRoutes } from "./routes/dash/posts.js";
19
+ import { pagesRoutes as dashPagesRoutes } from "./routes/dash/pages.js";
20
+ import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
21
+ import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
22
+ import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
23
+ import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
24
+ // Routes - API
25
+ import { postsApiRoutes } from "./routes/api/posts.js";
26
+ import { uploadApiRoutes } from "./routes/api/upload.js";
27
+ import { searchApiRoutes } from "./routes/api/search.js";
28
+ // Routes - Feed
29
+ import { rssRoutes } from "./routes/feed/rss.js";
30
+ import { sitemapRoutes } from "./routes/feed/sitemap.js";
31
+ // Middleware
32
+ import { requireAuth } from "./middleware/auth.js";
33
+ // Layouts for auth pages
34
+ import { BaseLayout } from "./theme/layouts/index.js";
35
+ /**
36
+ * Create a Jant application
37
+ *
38
+ * @param config - Optional configuration
39
+ * @returns Hono app instance
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { createApp } from "@jant/core";
44
+ *
45
+ * export default createApp({
46
+ * site: { name: "My Blog" },
47
+ * theme: { components: { PostCard: MyPostCard } },
48
+ * });
49
+ * ```
50
+ */ export function createApp(config = {}) {
51
+ const app = new Hono();
52
+ // Initialize services, auth, and config middleware
53
+ app.use("*", async (c, next)=>{
54
+ const db = createDatabase(c.env.DB);
55
+ const services = createServices(db, c.env.DB);
56
+ c.set("services", services);
57
+ c.set("config", config);
58
+ if (c.env.AUTH_SECRET) {
59
+ const auth = createAuth(c.env.DB, {
60
+ secret: c.env.AUTH_SECRET,
61
+ baseURL: c.env.SITE_URL
62
+ });
63
+ c.set("auth", auth);
64
+ }
65
+ await next();
66
+ });
67
+ // i18n middleware
68
+ app.use("*", i18nMiddleware());
69
+ // Trailing slash redirect (redirect /foo/ to /foo)
70
+ app.use("*", async (c, next)=>{
71
+ const url = new URL(c.req.url);
72
+ if (url.pathname !== "/" && url.pathname.endsWith("/")) {
73
+ const newUrl = url.pathname.slice(0, -1) + url.search;
74
+ return c.redirect(newUrl, 301);
75
+ }
76
+ await next();
77
+ });
78
+ // Redirect middleware
79
+ app.use("*", async (c, next)=>{
80
+ const path = new URL(c.req.url).pathname;
81
+ // Skip redirect check for API routes and static assets
82
+ if (path.startsWith("/api/") || path.startsWith("/assets/")) {
83
+ return next();
84
+ }
85
+ const redirect = await c.var.services.redirects.getByPath(path);
86
+ if (redirect) {
87
+ return c.redirect(redirect.toPath, redirect.type);
88
+ }
89
+ await next();
90
+ });
91
+ // Health check
92
+ app.get("/health", (c)=>c.json({
93
+ status: "ok",
94
+ auth: c.env.AUTH_SECRET ? "configured" : "missing",
95
+ authSecretLength: c.env.AUTH_SECRET?.length ?? 0
96
+ }));
97
+ // better-auth handler
98
+ app.all("/api/auth/*", async (c)=>{
99
+ if (!c.var.auth) {
100
+ return c.json({
101
+ error: "Auth not configured. Set AUTH_SECRET."
102
+ }, 500);
103
+ }
104
+ return c.var.auth.handler(c.req.raw);
105
+ });
106
+ // API Routes
107
+ app.route("/api/posts", postsApiRoutes);
108
+ // Setup page component
109
+ const SetupContent = ({ error })=>{
110
+ const { t } = useLingui();
111
+ return /*#__PURE__*/ _jsx("div", {
112
+ class: "min-h-screen flex items-center justify-center",
113
+ children: /*#__PURE__*/ _jsxs("div", {
114
+ class: "card max-w-md w-full",
115
+ children: [
116
+ /*#__PURE__*/ _jsxs("header", {
117
+ children: [
118
+ /*#__PURE__*/ _jsx("h2", {
119
+ children: t({
120
+ message: "Welcome to Jant",
121
+ comment: "@context: Setup page welcome heading"
122
+ })
123
+ }),
124
+ /*#__PURE__*/ _jsx("p", {
125
+ children: t({
126
+ message: "Let's set up your site.",
127
+ comment: "@context: Setup page description"
128
+ })
129
+ })
130
+ ]
131
+ }),
132
+ /*#__PURE__*/ _jsxs("section", {
133
+ children: [
134
+ error && /*#__PURE__*/ _jsx("p", {
135
+ class: "text-destructive text-sm mb-4",
136
+ children: error
137
+ }),
138
+ /*#__PURE__*/ _jsxs("form", {
139
+ method: "post",
140
+ action: "/setup",
141
+ class: "flex flex-col gap-4",
142
+ children: [
143
+ /*#__PURE__*/ _jsxs("div", {
144
+ class: "field",
145
+ children: [
146
+ /*#__PURE__*/ _jsx("label", {
147
+ class: "label",
148
+ children: t({
149
+ message: "Site Name",
150
+ comment: "@context: Setup form field - site name"
151
+ })
152
+ }),
153
+ /*#__PURE__*/ _jsx("input", {
154
+ type: "text",
155
+ name: "siteName",
156
+ class: "input",
157
+ required: true,
158
+ placeholder: t({
159
+ message: "My Blog",
160
+ comment: "@context: Setup site name placeholder"
161
+ })
162
+ })
163
+ ]
164
+ }),
165
+ /*#__PURE__*/ _jsxs("div", {
166
+ class: "field",
167
+ children: [
168
+ /*#__PURE__*/ _jsx("label", {
169
+ class: "label",
170
+ children: t({
171
+ message: "Your Name",
172
+ comment: "@context: Setup form field - user name"
173
+ })
174
+ }),
175
+ /*#__PURE__*/ _jsx("input", {
176
+ type: "text",
177
+ name: "name",
178
+ class: "input",
179
+ required: true,
180
+ placeholder: "John Doe"
181
+ })
182
+ ]
183
+ }),
184
+ /*#__PURE__*/ _jsxs("div", {
185
+ class: "field",
186
+ children: [
187
+ /*#__PURE__*/ _jsx("label", {
188
+ class: "label",
189
+ children: t({
190
+ message: "Email",
191
+ comment: "@context: Setup/signin form field - email"
192
+ })
193
+ }),
194
+ /*#__PURE__*/ _jsx("input", {
195
+ type: "email",
196
+ name: "email",
197
+ class: "input",
198
+ required: true,
199
+ placeholder: "you@example.com"
200
+ })
201
+ ]
202
+ }),
203
+ /*#__PURE__*/ _jsxs("div", {
204
+ class: "field",
205
+ children: [
206
+ /*#__PURE__*/ _jsx("label", {
207
+ class: "label",
208
+ children: t({
209
+ message: "Password",
210
+ comment: "@context: Setup/signin form field - password"
211
+ })
212
+ }),
213
+ /*#__PURE__*/ _jsx("input", {
214
+ type: "password",
215
+ name: "password",
216
+ class: "input",
217
+ required: true,
218
+ minLength: 8
219
+ })
220
+ ]
221
+ }),
222
+ /*#__PURE__*/ _jsx("button", {
223
+ type: "submit",
224
+ class: "btn",
225
+ children: t({
226
+ message: "Complete Setup",
227
+ comment: "@context: Setup form submit button"
228
+ })
229
+ })
230
+ ]
231
+ })
232
+ ]
233
+ })
234
+ ]
235
+ })
236
+ });
237
+ };
238
+ // Setup page
239
+ app.get("/setup", async (c)=>{
240
+ const isComplete = await c.var.services.settings.isOnboardingComplete();
241
+ if (isComplete) return c.redirect("/");
242
+ const error = c.req.query("error");
243
+ return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
244
+ title: "Setup - Jant",
245
+ c: c,
246
+ children: /*#__PURE__*/ _jsx(SetupContent, {
247
+ error: error
248
+ })
249
+ }));
250
+ });
251
+ app.post("/setup", async (c)=>{
252
+ const isComplete = await c.var.services.settings.isOnboardingComplete();
253
+ if (isComplete) return c.redirect("/");
254
+ const formData = await c.req.formData();
255
+ const siteName = formData.get("siteName");
256
+ const name = formData.get("name");
257
+ const email = formData.get("email");
258
+ const password = formData.get("password");
259
+ if (!siteName || !name || !email || !password) {
260
+ return c.redirect("/setup?error=All fields are required");
261
+ }
262
+ if (password.length < 8) {
263
+ return c.redirect("/setup?error=Password must be at least 8 characters");
264
+ }
265
+ if (!c.var.auth) {
266
+ return c.redirect("/setup?error=AUTH_SECRET not configured");
267
+ }
268
+ try {
269
+ const signUpResponse = await c.var.auth.api.signUpEmail({
270
+ body: {
271
+ name,
272
+ email,
273
+ password
274
+ }
275
+ });
276
+ if (!signUpResponse || "error" in signUpResponse) {
277
+ return c.redirect("/setup?error=Failed to create account");
278
+ }
279
+ await c.var.services.settings.setMany({
280
+ SITE_NAME: siteName,
281
+ SITE_LANGUAGE: "en"
282
+ });
283
+ await c.var.services.settings.completeOnboarding();
284
+ return c.redirect("/signin");
285
+ } catch (err) {
286
+ // eslint-disable-next-line no-console -- Error logging is intentional
287
+ console.error("Setup error:", err);
288
+ return c.redirect("/setup?error=Failed to create account");
289
+ }
290
+ });
291
+ // Signin page component
292
+ const SigninContent = ({ error })=>{
293
+ const { t } = useLingui();
294
+ return /*#__PURE__*/ _jsx("div", {
295
+ class: "min-h-screen flex items-center justify-center",
296
+ children: /*#__PURE__*/ _jsxs("div", {
297
+ class: "card max-w-md w-full",
298
+ children: [
299
+ /*#__PURE__*/ _jsx("header", {
300
+ children: /*#__PURE__*/ _jsx("h2", {
301
+ children: t({
302
+ message: "Sign In",
303
+ comment: "@context: Sign in page heading"
304
+ })
305
+ })
306
+ }),
307
+ /*#__PURE__*/ _jsxs("section", {
308
+ children: [
309
+ error && /*#__PURE__*/ _jsx("p", {
310
+ class: "text-destructive text-sm mb-4",
311
+ children: error
312
+ }),
313
+ /*#__PURE__*/ _jsxs("form", {
314
+ method: "post",
315
+ action: "/signin",
316
+ class: "flex flex-col gap-4",
317
+ children: [
318
+ /*#__PURE__*/ _jsxs("div", {
319
+ class: "field",
320
+ children: [
321
+ /*#__PURE__*/ _jsx("label", {
322
+ class: "label",
323
+ children: t({
324
+ message: "Email",
325
+ comment: "@context: Setup/signin form field - email"
326
+ })
327
+ }),
328
+ /*#__PURE__*/ _jsx("input", {
329
+ type: "email",
330
+ name: "email",
331
+ class: "input",
332
+ required: true
333
+ })
334
+ ]
335
+ }),
336
+ /*#__PURE__*/ _jsxs("div", {
337
+ class: "field",
338
+ children: [
339
+ /*#__PURE__*/ _jsx("label", {
340
+ class: "label",
341
+ children: t({
342
+ message: "Password",
343
+ comment: "@context: Setup/signin form field - password"
344
+ })
345
+ }),
346
+ /*#__PURE__*/ _jsx("input", {
347
+ type: "password",
348
+ name: "password",
349
+ class: "input",
350
+ required: true
351
+ })
352
+ ]
353
+ }),
354
+ /*#__PURE__*/ _jsx("button", {
355
+ type: "submit",
356
+ class: "btn",
357
+ children: t({
358
+ message: "Sign In",
359
+ comment: "@context: Sign in form submit button"
360
+ })
361
+ })
362
+ ]
363
+ })
364
+ ]
365
+ })
366
+ ]
367
+ })
368
+ });
369
+ };
370
+ // Signin page
371
+ app.get("/signin", async (c)=>{
372
+ const error = c.req.query("error");
373
+ return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
374
+ title: "Sign In - Jant",
375
+ c: c,
376
+ children: /*#__PURE__*/ _jsx(SigninContent, {
377
+ error: error
378
+ })
379
+ }));
380
+ });
381
+ app.post("/signin", async (c)=>{
382
+ if (!c.var.auth) {
383
+ return c.redirect("/signin?error=Auth not configured");
384
+ }
385
+ const formData = await c.req.formData();
386
+ const email = formData.get("email");
387
+ const password = formData.get("password");
388
+ try {
389
+ const signInRequest = new Request(`${c.env.SITE_URL}/api/auth/sign-in/email`, {
390
+ method: "POST",
391
+ headers: {
392
+ "Content-Type": "application/json"
393
+ },
394
+ body: JSON.stringify({
395
+ email,
396
+ password
397
+ })
398
+ });
399
+ const response = await c.var.auth.handler(signInRequest);
400
+ if (!response.ok) {
401
+ return c.redirect("/signin?error=Invalid email or password");
402
+ }
403
+ const headers = new Headers(response.headers);
404
+ headers.set("Location", "/dash");
405
+ return new Response(null, {
406
+ status: 302,
407
+ headers
408
+ });
409
+ } catch (err) {
410
+ // eslint-disable-next-line no-console -- Error logging is intentional
411
+ console.error("Signin error:", err);
412
+ return c.redirect("/signin?error=Invalid email or password");
413
+ }
414
+ });
415
+ app.get("/signout", async (c)=>{
416
+ if (c.var.auth) {
417
+ try {
418
+ await c.var.auth.api.signOut({
419
+ headers: c.req.raw.headers
420
+ });
421
+ } catch {
422
+ // Ignore signout errors
423
+ }
424
+ }
425
+ return c.redirect("/");
426
+ });
427
+ // Dashboard routes (protected)
428
+ app.use("/dash/*", requireAuth());
429
+ app.route("/dash", dashIndexRoutes);
430
+ app.route("/dash/posts", dashPostsRoutes);
431
+ app.route("/dash/pages", dashPagesRoutes);
432
+ app.route("/dash/media", dashMediaRoutes);
433
+ app.route("/dash/settings", dashSettingsRoutes);
434
+ app.route("/dash/redirects", dashRedirectsRoutes);
435
+ app.route("/dash/collections", dashCollectionsRoutes);
436
+ // API routes
437
+ app.route("/api/upload", uploadApiRoutes);
438
+ app.route("/api/search", searchApiRoutes);
439
+ // Media files from R2 (UUIDv7-based URLs with extension)
440
+ app.get("/media/:idWithExt", async (c)=>{
441
+ if (!c.env.R2) {
442
+ return c.notFound();
443
+ }
444
+ // Extract ID from "uuid.ext" format
445
+ const idWithExt = c.req.param("idWithExt");
446
+ const mediaId = idWithExt.replace(/\.[^.]+$/, "");
447
+ const media = await c.var.services.media.getById(mediaId);
448
+ if (!media) {
449
+ return c.notFound();
450
+ }
451
+ const object = await c.env.R2.get(media.r2Key);
452
+ if (!object) {
453
+ return c.notFound();
454
+ }
455
+ const headers = new Headers();
456
+ headers.set("Content-Type", object.httpMetadata?.contentType || media.mimeType);
457
+ headers.set("Cache-Control", "public, max-age=31536000, immutable");
458
+ return new Response(object.body, {
459
+ headers
460
+ });
461
+ });
462
+ // Feed routes
463
+ app.route("/feed", rssRoutes);
464
+ app.route("/", sitemapRoutes);
465
+ // Frontend routes
466
+ app.route("/search", searchRoutes);
467
+ app.route("/archive", archiveRoutes);
468
+ app.route("/c", collectionRoutes);
469
+ app.route("/p", postRoutes);
470
+ app.route("/", homeRoutes);
471
+ // Custom page catch-all (must be last)
472
+ app.route("/", pageRoutes);
473
+ return app;
474
+ }