@jant/core 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +112 -85
- package/dist/auth.d.ts +1 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -1
- package/dist/client.js +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/i18n/context.d.ts.map +1 -1
- package/dist/i18n/context.js +0 -3
- package/dist/i18n/detect.d.ts +0 -11
- package/dist/i18n/detect.d.ts.map +1 -1
- package/dist/i18n/detect.js +1 -52
- package/dist/i18n/i18n.d.ts +4 -14
- package/dist/i18n/i18n.d.ts.map +1 -1
- package/dist/i18n/i18n.js +19 -25
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/middleware.d.ts +2 -5
- package/dist/i18n/middleware.d.ts.map +1 -1
- package/dist/i18n/middleware.js +12 -23
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/sse.d.ts +45 -17
- package/dist/lib/sse.d.ts.map +1 -1
- package/dist/lib/sse.js +77 -37
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/routes/api/posts.js +0 -1
- package/dist/routes/api/upload.js +13 -3
- package/dist/routes/dash/collections.d.ts.map +1 -1
- package/dist/routes/dash/collections.js +134 -142
- package/dist/routes/dash/index.js +25 -25
- package/dist/routes/dash/media.d.ts.map +1 -1
- package/dist/routes/dash/media.js +60 -56
- package/dist/routes/dash/pages.d.ts.map +1 -1
- package/dist/routes/dash/pages.js +64 -66
- package/dist/routes/dash/posts.d.ts.map +1 -1
- package/dist/routes/dash/posts.js +50 -59
- package/dist/routes/dash/redirects.d.ts.map +1 -1
- package/dist/routes/dash/redirects.js +63 -60
- package/dist/routes/dash/settings.d.ts.map +1 -1
- package/dist/routes/dash/settings.js +249 -93
- package/dist/routes/feed/rss.js +6 -4
- package/dist/routes/pages/archive.js +60 -62
- package/dist/routes/pages/collection.js +8 -8
- package/dist/routes/pages/home.js +14 -14
- package/dist/routes/pages/page.js +7 -6
- package/dist/routes/pages/post.js +8 -8
- package/dist/routes/pages/search.js +25 -27
- package/dist/services/collection.d.ts.map +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/media.d.ts.map +1 -1
- package/dist/services/post.d.ts.map +1 -1
- package/dist/services/redirect.d.ts.map +1 -1
- package/dist/services/settings.d.ts.map +1 -1
- package/dist/theme/components/ActionButtons.d.ts +1 -1
- package/dist/theme/components/ActionButtons.d.ts.map +1 -1
- package/dist/theme/components/ActionButtons.js +17 -21
- package/dist/theme/components/CrudPageHeader.d.ts.map +1 -1
- package/dist/theme/components/DangerZone.d.ts.map +1 -1
- package/dist/theme/components/DangerZone.js +12 -15
- package/dist/theme/components/EmptyState.d.ts.map +1 -1
- package/dist/theme/components/PageForm.d.ts.map +1 -1
- package/dist/theme/components/PageForm.js +58 -56
- package/dist/theme/components/Pagination.d.ts.map +1 -1
- package/dist/theme/components/Pagination.js +22 -25
- package/dist/theme/components/PostForm.d.ts +0 -1
- package/dist/theme/components/PostForm.d.ts.map +1 -1
- package/dist/theme/components/PostForm.js +85 -77
- package/dist/theme/components/PostList.d.ts.map +1 -1
- package/dist/theme/components/PostList.js +17 -17
- package/dist/theme/components/ThreadView.d.ts.map +1 -1
- package/dist/theme/components/ThreadView.js +15 -18
- package/dist/theme/components/TypeBadge.d.ts.map +1 -1
- package/dist/theme/components/TypeBadge.js +20 -20
- package/dist/theme/components/VisibilityBadge.d.ts.map +1 -1
- package/dist/theme/components/VisibilityBadge.js +14 -14
- package/dist/theme/components/index.d.ts +2 -2
- package/dist/theme/components/index.d.ts.map +1 -1
- package/dist/theme/layouts/BaseLayout.d.ts.map +1 -1
- package/dist/theme/layouts/BaseLayout.js +4 -2
- package/dist/theme/layouts/DashLayout.d.ts.map +1 -1
- package/dist/theme/layouts/DashLayout.js +29 -29
- package/dist/types/lingui-react-macro.d.js +9 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vendor/datastar.js +1606 -0
- package/package.json +7 -15
- package/src/app.tsx +222 -59
- package/src/auth.ts +5 -1
- package/src/client.ts +1 -1
- package/src/db/migrations/meta/0000_snapshot.json +16 -47
- package/src/db/migrations/meta/_journal.json +1 -1
- package/src/db/schema.ts +22 -7
- package/src/i18n/EXAMPLES.md +45 -23
- package/src/i18n/README.md +39 -25
- package/src/i18n/context.tsx +1 -4
- package/src/i18n/detect.ts +1 -67
- package/src/i18n/i18n.ts +15 -19
- package/src/i18n/index.ts +0 -3
- package/src/i18n/middleware.ts +12 -24
- package/src/lib/constants.ts +2 -1
- package/src/lib/image-processor.ts +14 -6
- package/src/lib/image.ts +2 -2
- package/src/lib/schemas.ts +7 -3
- package/src/lib/sse.ts +133 -51
- package/src/middleware/auth.ts +6 -2
- package/src/routes/api/posts.ts +9 -9
- package/src/routes/api/upload.ts +39 -10
- package/src/routes/dash/collections.tsx +249 -81
- package/src/routes/dash/index.tsx +22 -7
- package/src/routes/dash/media.tsx +94 -24
- package/src/routes/dash/pages.tsx +132 -54
- package/src/routes/dash/posts.tsx +99 -57
- package/src/routes/dash/redirects.tsx +117 -36
- package/src/routes/dash/settings.tsx +268 -55
- package/src/routes/feed/rss.ts +6 -4
- package/src/routes/pages/archive.tsx +78 -24
- package/src/routes/pages/collection.tsx +32 -8
- package/src/routes/pages/home.tsx +38 -10
- package/src/routes/pages/page.tsx +15 -5
- package/src/routes/pages/post.tsx +17 -6
- package/src/routes/pages/search.tsx +50 -13
- package/src/services/collection.ts +29 -8
- package/src/services/index.ts +4 -1
- package/src/services/media.ts +15 -3
- package/src/services/post.ts +37 -10
- package/src/services/redirect.ts +4 -1
- package/src/services/settings.ts +14 -3
- package/src/theme/components/ActionButtons.tsx +31 -15
- package/src/theme/components/CrudPageHeader.tsx +3 -4
- package/src/theme/components/DangerZone.tsx +19 -13
- package/src/theme/components/EmptyState.tsx +1 -5
- package/src/theme/components/PageForm.tsx +80 -25
- package/src/theme/components/Pagination.tsx +34 -31
- package/src/theme/components/PostForm.tsx +91 -27
- package/src/theme/components/PostList.tsx +23 -6
- package/src/theme/components/ThreadView.tsx +25 -10
- package/src/theme/components/TypeBadge.tsx +13 -4
- package/src/theme/components/VisibilityBadge.tsx +17 -5
- package/src/theme/components/index.ts +12 -2
- package/src/theme/layouts/BaseLayout.tsx +6 -5
- package/src/theme/layouts/DashLayout.tsx +71 -18
- package/src/types/lingui-react-macro.d.ts +34 -0
- package/src/types.ts +16 -4
- package/src/vendor/datastar.js +9 -0
- package/src/vendor/datastar.js.map +7 -0
- package/dist/plugin.d.ts +0 -3
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -20
- package/dist/tailwind.d.ts +0 -12
- package/dist/tailwind.d.ts.map +0 -1
- package/dist/tailwind.js +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jant/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "A modern, open-source microblogging platform built on Cloudflare Workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
"types": "./dist/theme/index.d.ts",
|
|
16
16
|
"default": "./dist/theme/index.js"
|
|
17
17
|
},
|
|
18
|
+
"./i18n": {
|
|
19
|
+
"types": "./dist/i18n/index.d.ts",
|
|
20
|
+
"default": "./dist/i18n/index.js"
|
|
21
|
+
},
|
|
18
22
|
"./preset.css": "./src/preset.css",
|
|
19
|
-
"./client": "./dist/client.js"
|
|
20
|
-
"./*": "./*"
|
|
23
|
+
"./client": "./dist/client.js"
|
|
21
24
|
},
|
|
22
25
|
"files": [
|
|
23
26
|
"bin",
|
|
@@ -30,7 +33,6 @@
|
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
35
|
"@lingui/core": "^5.9.0",
|
|
33
|
-
"@sudodevnull/datastar": "^0.19.9",
|
|
34
36
|
"better-auth": "^1.4.18",
|
|
35
37
|
"drizzle-orm": "^0.45.1",
|
|
36
38
|
"hono": "^4.11.7",
|
|
@@ -45,23 +47,15 @@
|
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|
|
47
49
|
"@cloudflare/workers-types": "^4.20260131.0",
|
|
48
|
-
"@eslint/js": "^9.39.2",
|
|
49
50
|
"@lingui/cli": "^5.9.0",
|
|
50
51
|
"@lingui/format-po": "^5.9.0",
|
|
51
52
|
"@lingui/swc-plugin": "^5.10.1",
|
|
52
53
|
"@swc/cli": "^0.6.0",
|
|
53
54
|
"@swc/core": "^1.15.11",
|
|
54
55
|
"@types/node": "^25.1.0",
|
|
55
|
-
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
56
|
-
"@typescript-eslint/parser": "^8.54.0",
|
|
57
56
|
"basecoat-css": "^0.3.10",
|
|
58
57
|
"drizzle-kit": "^0.31.8",
|
|
59
|
-
"eslint": "^9.39.2",
|
|
60
|
-
"eslint-plugin-react": "^7.37.5",
|
|
61
58
|
"glob": "^13.0.0",
|
|
62
|
-
"husky": "^9.1.7",
|
|
63
|
-
"lint-staged": "^16.2.7",
|
|
64
|
-
"prettier": "^3.8.1",
|
|
65
59
|
"tailwindcss": "^4.1.18",
|
|
66
60
|
"tsx": "^4.21.0",
|
|
67
61
|
"typescript": "^5.9.3"
|
|
@@ -90,12 +84,10 @@
|
|
|
90
84
|
"node": ">=24.0.0"
|
|
91
85
|
},
|
|
92
86
|
"scripts": {
|
|
87
|
+
"build": "pnpm build:lib",
|
|
93
88
|
"build:lib": "swc src -d dist --strip-leading-paths && pnpm build:types",
|
|
94
89
|
"build:types": "tsc -p tsconfig.build.json",
|
|
95
90
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.client.json",
|
|
96
|
-
"lint": "eslint src/",
|
|
97
|
-
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
98
|
-
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
99
91
|
"db:generate": "drizzle-kit generate",
|
|
100
92
|
"i18n:extract": "lingui extract",
|
|
101
93
|
"i18n:compile": "lingui compile --typescript",
|
package/src/app.tsx
CHANGED
|
@@ -7,7 +7,8 @@ import type { FC } from "hono/jsx";
|
|
|
7
7
|
import { createDatabase } from "./db/index.js";
|
|
8
8
|
import { createServices, type Services } from "./services/index.js";
|
|
9
9
|
import { createAuth, type Auth } from "./auth.js";
|
|
10
|
-
import { i18nMiddleware
|
|
10
|
+
import { i18nMiddleware } from "./i18n/index.js";
|
|
11
|
+
import { useLingui } from "@lingui/react/macro";
|
|
11
12
|
import type { Bindings, JantConfig } from "./types.js";
|
|
12
13
|
|
|
13
14
|
// Routes - Pages
|
|
@@ -41,6 +42,7 @@ import { requireAuth } from "./middleware/auth.js";
|
|
|
41
42
|
|
|
42
43
|
// Layouts for auth pages
|
|
43
44
|
import { BaseLayout } from "./theme/layouts/index.js";
|
|
45
|
+
import { sse } from "./lib/sse.js";
|
|
44
46
|
|
|
45
47
|
// Extend Hono's context variables
|
|
46
48
|
export interface AppVariables {
|
|
@@ -123,7 +125,7 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
123
125
|
status: "ok",
|
|
124
126
|
auth: c.env.AUTH_SECRET ? "configured" : "missing",
|
|
125
127
|
authSecretLength: c.env.AUTH_SECRET?.length ?? 0,
|
|
126
|
-
})
|
|
128
|
+
}),
|
|
127
129
|
);
|
|
128
130
|
|
|
129
131
|
// better-auth handler
|
|
@@ -138,36 +140,102 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
138
140
|
app.route("/api/posts", postsApiRoutes);
|
|
139
141
|
|
|
140
142
|
// Setup page component
|
|
141
|
-
const SetupContent: FC
|
|
143
|
+
const SetupContent: FC = () => {
|
|
142
144
|
const { t } = useLingui();
|
|
143
145
|
|
|
144
146
|
return (
|
|
145
147
|
<div class="min-h-screen flex items-center justify-center">
|
|
146
148
|
<div class="card max-w-md w-full">
|
|
147
149
|
<header>
|
|
148
|
-
<h2>
|
|
149
|
-
|
|
150
|
+
<h2>
|
|
151
|
+
{t({
|
|
152
|
+
message: "Welcome to Jant",
|
|
153
|
+
comment: "@context: Setup page welcome heading",
|
|
154
|
+
})}
|
|
155
|
+
</h2>
|
|
156
|
+
<p>
|
|
157
|
+
{t({
|
|
158
|
+
message: "Let's set up your site.",
|
|
159
|
+
comment: "@context: Setup page description",
|
|
160
|
+
})}
|
|
161
|
+
</p>
|
|
150
162
|
</header>
|
|
151
163
|
<section>
|
|
152
|
-
|
|
153
|
-
<form
|
|
164
|
+
<div id="setup-message"></div>
|
|
165
|
+
<form
|
|
166
|
+
data-signals="{siteName: '', name: '', email: '', password: ''}"
|
|
167
|
+
data-on:submit__prevent="@post('/setup')"
|
|
168
|
+
class="flex flex-col gap-4"
|
|
169
|
+
>
|
|
154
170
|
<div class="field">
|
|
155
|
-
<label class="label">
|
|
156
|
-
|
|
171
|
+
<label class="label">
|
|
172
|
+
{t({
|
|
173
|
+
message: "Site Name",
|
|
174
|
+
comment: "@context: Setup form field - site name",
|
|
175
|
+
})}
|
|
176
|
+
</label>
|
|
177
|
+
<input
|
|
178
|
+
type="text"
|
|
179
|
+
data-bind="siteName"
|
|
180
|
+
class="input"
|
|
181
|
+
required
|
|
182
|
+
placeholder={t({
|
|
183
|
+
message: "My Blog",
|
|
184
|
+
comment: "@context: Setup site name placeholder",
|
|
185
|
+
})}
|
|
186
|
+
/>
|
|
157
187
|
</div>
|
|
158
188
|
<div class="field">
|
|
159
|
-
<label class="label">
|
|
160
|
-
|
|
189
|
+
<label class="label">
|
|
190
|
+
{t({
|
|
191
|
+
message: "Your Name",
|
|
192
|
+
comment: "@context: Setup form field - user name",
|
|
193
|
+
})}
|
|
194
|
+
</label>
|
|
195
|
+
<input
|
|
196
|
+
type="text"
|
|
197
|
+
data-bind="name"
|
|
198
|
+
class="input"
|
|
199
|
+
required
|
|
200
|
+
placeholder="John Doe"
|
|
201
|
+
/>
|
|
161
202
|
</div>
|
|
162
203
|
<div class="field">
|
|
163
|
-
<label class="label">
|
|
164
|
-
|
|
204
|
+
<label class="label">
|
|
205
|
+
{t({
|
|
206
|
+
message: "Email",
|
|
207
|
+
comment: "@context: Setup/signin form field - email",
|
|
208
|
+
})}
|
|
209
|
+
</label>
|
|
210
|
+
<input
|
|
211
|
+
type="email"
|
|
212
|
+
data-bind="email"
|
|
213
|
+
class="input"
|
|
214
|
+
required
|
|
215
|
+
placeholder="you@example.com"
|
|
216
|
+
/>
|
|
165
217
|
</div>
|
|
166
218
|
<div class="field">
|
|
167
|
-
<label class="label">
|
|
168
|
-
|
|
219
|
+
<label class="label">
|
|
220
|
+
{t({
|
|
221
|
+
message: "Password",
|
|
222
|
+
comment: "@context: Setup/signin form field - password",
|
|
223
|
+
})}
|
|
224
|
+
</label>
|
|
225
|
+
<input
|
|
226
|
+
type="password"
|
|
227
|
+
data-bind="password"
|
|
228
|
+
class="input"
|
|
229
|
+
required
|
|
230
|
+
minLength={8}
|
|
231
|
+
/>
|
|
169
232
|
</div>
|
|
170
|
-
<button type="submit" class="btn">
|
|
233
|
+
<button type="submit" class="btn">
|
|
234
|
+
{t({
|
|
235
|
+
message: "Complete Setup",
|
|
236
|
+
comment: "@context: Setup form submit button",
|
|
237
|
+
})}
|
|
238
|
+
</button>
|
|
171
239
|
</form>
|
|
172
240
|
</section>
|
|
173
241
|
</div>
|
|
@@ -180,12 +248,10 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
180
248
|
const isComplete = await c.var.services.settings.isOnboardingComplete();
|
|
181
249
|
if (isComplete) return c.redirect("/");
|
|
182
250
|
|
|
183
|
-
const error = c.req.query("error");
|
|
184
|
-
|
|
185
251
|
return c.html(
|
|
186
252
|
<BaseLayout title="Setup - Jant" c={c}>
|
|
187
|
-
<SetupContent
|
|
188
|
-
</BaseLayout
|
|
253
|
+
<SetupContent />
|
|
254
|
+
</BaseLayout>,
|
|
189
255
|
);
|
|
190
256
|
});
|
|
191
257
|
|
|
@@ -193,22 +259,36 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
193
259
|
const isComplete = await c.var.services.settings.isOnboardingComplete();
|
|
194
260
|
if (isComplete) return c.redirect("/");
|
|
195
261
|
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
262
|
+
const body = await c.req.json<{
|
|
263
|
+
siteName: string;
|
|
264
|
+
name: string;
|
|
265
|
+
email: string;
|
|
266
|
+
password: string;
|
|
267
|
+
}>();
|
|
268
|
+
const { siteName, name, email, password } = body;
|
|
201
269
|
|
|
202
270
|
if (!siteName || !name || !email || !password) {
|
|
203
|
-
return c
|
|
271
|
+
return sse(c, async (stream) => {
|
|
272
|
+
await stream.patchElements(
|
|
273
|
+
'<div id="setup-message"><p class="text-destructive text-sm mb-4">All fields are required</p></div>',
|
|
274
|
+
);
|
|
275
|
+
});
|
|
204
276
|
}
|
|
205
277
|
|
|
206
278
|
if (password.length < 8) {
|
|
207
|
-
return c
|
|
279
|
+
return sse(c, async (stream) => {
|
|
280
|
+
await stream.patchElements(
|
|
281
|
+
'<div id="setup-message"><p class="text-destructive text-sm mb-4">Password must be at least 8 characters</p></div>',
|
|
282
|
+
);
|
|
283
|
+
});
|
|
208
284
|
}
|
|
209
285
|
|
|
210
286
|
if (!c.var.auth) {
|
|
211
|
-
return c
|
|
287
|
+
return sse(c, async (stream) => {
|
|
288
|
+
await stream.patchElements(
|
|
289
|
+
'<div id="setup-message"><p class="text-destructive text-sm mb-4">AUTH_SECRET not configured</p></div>',
|
|
290
|
+
);
|
|
291
|
+
});
|
|
212
292
|
}
|
|
213
293
|
|
|
214
294
|
try {
|
|
@@ -217,7 +297,11 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
217
297
|
});
|
|
218
298
|
|
|
219
299
|
if (!signUpResponse || "error" in signUpResponse) {
|
|
220
|
-
return c
|
|
300
|
+
return sse(c, async (stream) => {
|
|
301
|
+
await stream.patchElements(
|
|
302
|
+
'<div id="setup-message"><p class="text-destructive text-sm mb-4">Failed to create account</p></div>',
|
|
303
|
+
);
|
|
304
|
+
});
|
|
221
305
|
}
|
|
222
306
|
|
|
223
307
|
await c.var.services.settings.setMany({
|
|
@@ -226,36 +310,87 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
226
310
|
});
|
|
227
311
|
await c.var.services.settings.completeOnboarding();
|
|
228
312
|
|
|
229
|
-
return c
|
|
313
|
+
return sse(c, async (stream) => {
|
|
314
|
+
await stream.redirect("/signin");
|
|
315
|
+
});
|
|
230
316
|
} catch (err) {
|
|
231
317
|
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
232
318
|
console.error("Setup error:", err);
|
|
233
|
-
return c
|
|
319
|
+
return sse(c, async (stream) => {
|
|
320
|
+
await stream.patchElements(
|
|
321
|
+
'<div id="setup-message"><p class="text-destructive text-sm mb-4">Failed to create account</p></div>',
|
|
322
|
+
);
|
|
323
|
+
});
|
|
234
324
|
}
|
|
235
325
|
});
|
|
236
326
|
|
|
237
327
|
// Signin page component
|
|
238
|
-
const SigninContent: FC<{
|
|
328
|
+
const SigninContent: FC<{
|
|
329
|
+
demoEmail?: string;
|
|
330
|
+
demoPassword?: string;
|
|
331
|
+
}> = ({ demoEmail, demoPassword }) => {
|
|
239
332
|
const { t } = useLingui();
|
|
333
|
+
const signals = JSON.stringify({
|
|
334
|
+
email: demoEmail || "",
|
|
335
|
+
password: demoPassword || "",
|
|
336
|
+
}).replace(/</g, "\\u003c");
|
|
240
337
|
|
|
241
338
|
return (
|
|
242
339
|
<div class="min-h-screen flex items-center justify-center">
|
|
243
340
|
<div class="card max-w-md w-full">
|
|
244
341
|
<header>
|
|
245
|
-
<h2>
|
|
342
|
+
<h2>
|
|
343
|
+
{t({
|
|
344
|
+
message: "Sign In",
|
|
345
|
+
comment: "@context: Sign in page heading",
|
|
346
|
+
})}
|
|
347
|
+
</h2>
|
|
246
348
|
</header>
|
|
247
349
|
<section>
|
|
248
|
-
|
|
249
|
-
|
|
350
|
+
<div id="signin-message"></div>
|
|
351
|
+
{demoEmail && demoPassword && (
|
|
352
|
+
<p class="text-muted-foreground text-sm mb-4">
|
|
353
|
+
{t({
|
|
354
|
+
message: "Demo account pre-filled. Just click Sign In.",
|
|
355
|
+
comment:
|
|
356
|
+
"@context: Hint shown on signin page when demo credentials are pre-filled",
|
|
357
|
+
})}
|
|
358
|
+
</p>
|
|
359
|
+
)}
|
|
360
|
+
<form
|
|
361
|
+
data-signals={signals}
|
|
362
|
+
data-on:submit__prevent="@post('/signin')"
|
|
363
|
+
class="flex flex-col gap-4"
|
|
364
|
+
>
|
|
250
365
|
<div class="field">
|
|
251
|
-
<label class="label">
|
|
252
|
-
|
|
366
|
+
<label class="label">
|
|
367
|
+
{t({
|
|
368
|
+
message: "Email",
|
|
369
|
+
comment: "@context: Setup/signin form field - email",
|
|
370
|
+
})}
|
|
371
|
+
</label>
|
|
372
|
+
<input type="email" data-bind="email" class="input" required />
|
|
253
373
|
</div>
|
|
254
374
|
<div class="field">
|
|
255
|
-
<label class="label">
|
|
256
|
-
|
|
375
|
+
<label class="label">
|
|
376
|
+
{t({
|
|
377
|
+
message: "Password",
|
|
378
|
+
comment: "@context: Setup/signin form field - password",
|
|
379
|
+
})}
|
|
380
|
+
</label>
|
|
381
|
+
<input
|
|
382
|
+
type="password"
|
|
383
|
+
data-bind="password"
|
|
384
|
+
class="input"
|
|
385
|
+
required
|
|
386
|
+
/>
|
|
257
387
|
</div>
|
|
258
|
-
<button type="submit" class="btn">
|
|
388
|
+
<button type="submit" class="btn">
|
|
389
|
+
{t({
|
|
390
|
+
message: "Sign In",
|
|
391
|
+
comment: "@context: Sign in form submit button",
|
|
392
|
+
})}
|
|
393
|
+
</button>
|
|
259
394
|
</form>
|
|
260
395
|
</section>
|
|
261
396
|
</div>
|
|
@@ -265,45 +400,70 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
265
400
|
|
|
266
401
|
// Signin page
|
|
267
402
|
app.get("/signin", async (c) => {
|
|
268
|
-
const error = c.req.query("error");
|
|
269
|
-
|
|
270
403
|
return c.html(
|
|
271
404
|
<BaseLayout title="Sign In - Jant" c={c}>
|
|
272
|
-
<SigninContent
|
|
273
|
-
|
|
405
|
+
<SigninContent
|
|
406
|
+
demoEmail={c.env.DEMO_EMAIL}
|
|
407
|
+
demoPassword={c.env.DEMO_PASSWORD}
|
|
408
|
+
/>
|
|
409
|
+
</BaseLayout>,
|
|
274
410
|
);
|
|
275
411
|
});
|
|
276
412
|
|
|
277
413
|
app.post("/signin", async (c) => {
|
|
278
414
|
if (!c.var.auth) {
|
|
279
|
-
return c
|
|
415
|
+
return sse(c, async (stream) => {
|
|
416
|
+
await stream.patchElements(
|
|
417
|
+
'<div id="signin-message"><p class="text-destructive text-sm mb-4">Auth not configured</p></div>',
|
|
418
|
+
);
|
|
419
|
+
});
|
|
280
420
|
}
|
|
281
421
|
|
|
282
|
-
const
|
|
283
|
-
const email
|
|
284
|
-
const password = formData.get("password") as string;
|
|
422
|
+
const body = await c.req.json<{ email: string; password: string }>();
|
|
423
|
+
const { email, password } = body;
|
|
285
424
|
|
|
286
425
|
try {
|
|
287
|
-
const signInRequest = new Request(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
426
|
+
const signInRequest = new Request(
|
|
427
|
+
`${c.env.SITE_URL}/api/auth/sign-in/email`,
|
|
428
|
+
{
|
|
429
|
+
method: "POST",
|
|
430
|
+
headers: { "Content-Type": "application/json" },
|
|
431
|
+
body: JSON.stringify({ email, password }),
|
|
432
|
+
},
|
|
433
|
+
);
|
|
292
434
|
|
|
293
435
|
const response = await c.var.auth.handler(signInRequest);
|
|
294
436
|
|
|
295
437
|
if (!response.ok) {
|
|
296
|
-
return c
|
|
438
|
+
return sse(c, async (stream) => {
|
|
439
|
+
await stream.patchElements(
|
|
440
|
+
'<div id="signin-message"><p class="text-destructive text-sm mb-4">Invalid email or password</p></div>',
|
|
441
|
+
);
|
|
442
|
+
});
|
|
297
443
|
}
|
|
298
444
|
|
|
299
|
-
|
|
300
|
-
|
|
445
|
+
// Forward Set-Cookie headers from auth response
|
|
446
|
+
const cookieHeaders: Record<string, string> = {};
|
|
447
|
+
const setCookie = response.headers.get("set-cookie");
|
|
448
|
+
if (setCookie) {
|
|
449
|
+
cookieHeaders["Set-Cookie"] = setCookie;
|
|
450
|
+
}
|
|
301
451
|
|
|
302
|
-
return
|
|
452
|
+
return sse(
|
|
453
|
+
c,
|
|
454
|
+
async (stream) => {
|
|
455
|
+
await stream.redirect("/dash");
|
|
456
|
+
},
|
|
457
|
+
{ headers: cookieHeaders },
|
|
458
|
+
);
|
|
303
459
|
} catch (err) {
|
|
304
460
|
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
305
461
|
console.error("Signin error:", err);
|
|
306
|
-
return c
|
|
462
|
+
return sse(c, async (stream) => {
|
|
463
|
+
await stream.patchElements(
|
|
464
|
+
'<div id="signin-message"><p class="text-destructive text-sm mb-4">Invalid email or password</p></div>',
|
|
465
|
+
);
|
|
466
|
+
});
|
|
307
467
|
}
|
|
308
468
|
});
|
|
309
469
|
|
|
@@ -353,7 +513,10 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
353
513
|
}
|
|
354
514
|
|
|
355
515
|
const headers = new Headers();
|
|
356
|
-
headers.set(
|
|
516
|
+
headers.set(
|
|
517
|
+
"Content-Type",
|
|
518
|
+
object.httpMetadata?.contentType || media.mimeType,
|
|
519
|
+
);
|
|
357
520
|
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
358
521
|
|
|
359
522
|
return new Response(object.body, { headers });
|
package/src/auth.ts
CHANGED
|
@@ -7,7 +7,10 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
|
7
7
|
import { drizzle } from "drizzle-orm/d1";
|
|
8
8
|
import * as schema from "./db/schema.js";
|
|
9
9
|
|
|
10
|
-
export function createAuth(
|
|
10
|
+
export function createAuth(
|
|
11
|
+
d1: D1Database,
|
|
12
|
+
options: { secret: string; baseURL: string },
|
|
13
|
+
) {
|
|
11
14
|
const db = drizzle(d1, { schema });
|
|
12
15
|
|
|
13
16
|
return betterAuth({
|
|
@@ -25,6 +28,7 @@ export function createAuth(d1: D1Database, options: { secret: string; baseURL: s
|
|
|
25
28
|
emailAndPassword: {
|
|
26
29
|
enabled: true,
|
|
27
30
|
autoSignIn: true,
|
|
31
|
+
minPasswordLength: 8,
|
|
28
32
|
},
|
|
29
33
|
session: {
|
|
30
34
|
cookieCache: {
|
package/src/client.ts
CHANGED
|
@@ -105,12 +105,8 @@
|
|
|
105
105
|
"name": "account_user_id_user_id_fk",
|
|
106
106
|
"tableFrom": "account",
|
|
107
107
|
"tableTo": "user",
|
|
108
|
-
"columnsFrom": [
|
|
109
|
-
|
|
110
|
-
],
|
|
111
|
-
"columnsTo": [
|
|
112
|
-
"id"
|
|
113
|
-
],
|
|
108
|
+
"columnsFrom": ["user_id"],
|
|
109
|
+
"columnsTo": ["id"],
|
|
114
110
|
"onDelete": "no action",
|
|
115
111
|
"onUpdate": "no action"
|
|
116
112
|
}
|
|
@@ -168,9 +164,7 @@
|
|
|
168
164
|
"indexes": {
|
|
169
165
|
"collections_slug_unique": {
|
|
170
166
|
"name": "collections_slug_unique",
|
|
171
|
-
"columns": [
|
|
172
|
-
"slug"
|
|
173
|
-
],
|
|
167
|
+
"columns": ["slug"],
|
|
174
168
|
"isUnique": true
|
|
175
169
|
}
|
|
176
170
|
},
|
|
@@ -266,12 +260,8 @@
|
|
|
266
260
|
"name": "media_post_id_posts_id_fk",
|
|
267
261
|
"tableFrom": "media",
|
|
268
262
|
"tableTo": "posts",
|
|
269
|
-
"columnsFrom": [
|
|
270
|
-
|
|
271
|
-
],
|
|
272
|
-
"columnsTo": [
|
|
273
|
-
"id"
|
|
274
|
-
],
|
|
263
|
+
"columnsFrom": ["post_id"],
|
|
264
|
+
"columnsTo": ["id"],
|
|
275
265
|
"onDelete": "no action",
|
|
276
266
|
"onUpdate": "no action"
|
|
277
267
|
}
|
|
@@ -311,12 +301,8 @@
|
|
|
311
301
|
"name": "post_collections_post_id_posts_id_fk",
|
|
312
302
|
"tableFrom": "post_collections",
|
|
313
303
|
"tableTo": "posts",
|
|
314
|
-
"columnsFrom": [
|
|
315
|
-
|
|
316
|
-
],
|
|
317
|
-
"columnsTo": [
|
|
318
|
-
"id"
|
|
319
|
-
],
|
|
304
|
+
"columnsFrom": ["post_id"],
|
|
305
|
+
"columnsTo": ["id"],
|
|
320
306
|
"onDelete": "no action",
|
|
321
307
|
"onUpdate": "no action"
|
|
322
308
|
},
|
|
@@ -324,22 +310,15 @@
|
|
|
324
310
|
"name": "post_collections_collection_id_collections_id_fk",
|
|
325
311
|
"tableFrom": "post_collections",
|
|
326
312
|
"tableTo": "collections",
|
|
327
|
-
"columnsFrom": [
|
|
328
|
-
|
|
329
|
-
],
|
|
330
|
-
"columnsTo": [
|
|
331
|
-
"id"
|
|
332
|
-
],
|
|
313
|
+
"columnsFrom": ["collection_id"],
|
|
314
|
+
"columnsTo": ["id"],
|
|
333
315
|
"onDelete": "no action",
|
|
334
316
|
"onUpdate": "no action"
|
|
335
317
|
}
|
|
336
318
|
},
|
|
337
319
|
"compositePrimaryKeys": {
|
|
338
320
|
"post_collections_post_id_collection_id_pk": {
|
|
339
|
-
"columns": [
|
|
340
|
-
"post_id",
|
|
341
|
-
"collection_id"
|
|
342
|
-
],
|
|
321
|
+
"columns": ["post_id", "collection_id"],
|
|
343
322
|
"name": "post_collections_post_id_collection_id_pk"
|
|
344
323
|
}
|
|
345
324
|
},
|
|
@@ -512,9 +491,7 @@
|
|
|
512
491
|
"indexes": {
|
|
513
492
|
"redirects_from_path_unique": {
|
|
514
493
|
"name": "redirects_from_path_unique",
|
|
515
|
-
"columns": [
|
|
516
|
-
"from_path"
|
|
517
|
-
],
|
|
494
|
+
"columns": ["from_path"],
|
|
518
495
|
"isUnique": true
|
|
519
496
|
}
|
|
520
497
|
},
|
|
@@ -586,9 +563,7 @@
|
|
|
586
563
|
"indexes": {
|
|
587
564
|
"session_token_unique": {
|
|
588
565
|
"name": "session_token_unique",
|
|
589
|
-
"columns": [
|
|
590
|
-
"token"
|
|
591
|
-
],
|
|
566
|
+
"columns": ["token"],
|
|
592
567
|
"isUnique": true
|
|
593
568
|
}
|
|
594
569
|
},
|
|
@@ -597,12 +572,8 @@
|
|
|
597
572
|
"name": "session_user_id_user_id_fk",
|
|
598
573
|
"tableFrom": "session",
|
|
599
574
|
"tableTo": "user",
|
|
600
|
-
"columnsFrom": [
|
|
601
|
-
|
|
602
|
-
],
|
|
603
|
-
"columnsTo": [
|
|
604
|
-
"id"
|
|
605
|
-
],
|
|
575
|
+
"columnsFrom": ["user_id"],
|
|
576
|
+
"columnsTo": ["id"],
|
|
606
577
|
"onDelete": "no action",
|
|
607
578
|
"onUpdate": "no action"
|
|
608
579
|
}
|
|
@@ -707,9 +678,7 @@
|
|
|
707
678
|
"indexes": {
|
|
708
679
|
"user_email_unique": {
|
|
709
680
|
"name": "user_email_unique",
|
|
710
|
-
"columns": [
|
|
711
|
-
"email"
|
|
712
|
-
],
|
|
681
|
+
"columns": ["email"],
|
|
713
682
|
"isUnique": true
|
|
714
683
|
}
|
|
715
684
|
},
|
|
@@ -781,4 +750,4 @@
|
|
|
781
750
|
"internal": {
|
|
782
751
|
"indexes": {}
|
|
783
752
|
}
|
|
784
|
-
}
|
|
753
|
+
}
|