@jant/core 0.2.17 → 0.2.18
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 +1 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +319 -115
- package/dist/i18n/context.d.ts +2 -2
- package/dist/i18n/context.js +1 -1
- package/dist/i18n/i18n.d.ts +1 -1
- package/dist/i18n/i18n.js +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/lib/config.d.ts +44 -10
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +69 -44
- package/dist/lib/constants.d.ts +2 -1
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +5 -2
- package/dist/lib/sse.d.ts +15 -0
- package/dist/lib/sse.d.ts.map +1 -1
- package/dist/lib/sse.js +13 -0
- package/dist/lib/theme.d.ts +44 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +65 -0
- package/dist/routes/dash/appearance.d.ts +13 -0
- package/dist/routes/dash/appearance.d.ts.map +1 -0
- package/dist/routes/dash/appearance.js +164 -0
- package/dist/routes/dash/settings.d.ts.map +1 -1
- package/dist/routes/dash/settings.js +38 -37
- package/dist/services/settings.d.ts +1 -0
- package/dist/services/settings.d.ts.map +1 -1
- package/dist/services/settings.js +3 -0
- package/dist/theme/color-themes.d.ts +30 -0
- package/dist/theme/color-themes.d.ts.map +1 -0
- package/dist/theme/color-themes.js +268 -0
- package/dist/theme/layouts/BaseLayout.d.ts +5 -0
- package/dist/theme/layouts/BaseLayout.d.ts.map +1 -1
- package/dist/theme/layouts/BaseLayout.js +70 -3
- package/dist/theme/layouts/DashLayout.d.ts +2 -0
- package/dist/theme/layouts/DashLayout.d.ts.map +1 -1
- package/dist/theme/layouts/DashLayout.js +10 -1
- package/dist/theme/layouts/index.d.ts +1 -1
- package/dist/theme/layouts/index.d.ts.map +1 -1
- package/dist/types.d.ts +53 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +52 -0
- package/package.json +1 -1
- package/src/app.tsx +272 -55
- package/src/db/migrations/{0000_solid_moon_knight.sql → 0000_square_wallflower.sql} +3 -3
- package/src/db/migrations/meta/0000_snapshot.json +9 -9
- package/src/db/migrations/meta/_journal.json +2 -30
- package/src/i18n/context.tsx +2 -2
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/index.ts +1 -1
- package/src/i18n/locales/en.po +328 -252
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +315 -278
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +315 -278
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/lib/config.ts +73 -47
- package/src/lib/constants.ts +3 -0
- package/src/lib/sse.ts +38 -0
- package/src/lib/theme.ts +86 -0
- package/src/preset.css +9 -0
- package/src/routes/dash/appearance.tsx +180 -0
- package/src/routes/dash/settings.tsx +50 -52
- package/src/services/settings.ts +5 -0
- package/src/styles/components.css +93 -0
- package/src/theme/color-themes.ts +321 -0
- package/src/theme/layouts/BaseLayout.tsx +61 -1
- package/src/theme/layouts/DashLayout.tsx +13 -2
- package/src/theme/layouts/index.ts +5 -1
- package/src/types.ts +62 -1
- package/src/db/migrations/0001_add_search_fts.sql +0 -40
- package/src/db/migrations/0002_collection_path.sql +0 -2
- package/src/db/migrations/0003_collection_path_nullable.sql +0 -21
- package/src/db/migrations/0004_media_uuid.sql +0 -35
package/dist/app.d.ts
CHANGED
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +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;AAGlD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
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;AAGlD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAwCvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,MAAM,GAAE,UAAe,GAAG,GAAG,CA+qBtD"}
|
package/dist/app.js
CHANGED
|
@@ -7,6 +7,8 @@ import { createServices } from "./services/index.js";
|
|
|
7
7
|
import { createAuth } from "./auth.js";
|
|
8
8
|
import { i18nMiddleware } from "./i18n/index.js";
|
|
9
9
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
10
|
+
import { SETTINGS_KEYS } from "./lib/constants.js";
|
|
11
|
+
import { hashPassword } from "better-auth/crypto";
|
|
10
12
|
// Routes - Pages
|
|
11
13
|
import { homeRoutes } from "./routes/pages/home.js";
|
|
12
14
|
import { postRoutes } from "./routes/pages/post.js";
|
|
@@ -22,6 +24,7 @@ import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
|
|
|
22
24
|
import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
|
|
23
25
|
import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
|
|
24
26
|
import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
|
|
27
|
+
import { appearanceRoutes as dashAppearanceRoutes } from "./routes/dash/appearance.js";
|
|
25
28
|
// Routes - API
|
|
26
29
|
import { postsApiRoutes } from "./routes/api/posts.js";
|
|
27
30
|
import { uploadApiRoutes } from "./routes/api/upload.js";
|
|
@@ -34,6 +37,7 @@ import { requireAuth } from "./middleware/auth.js";
|
|
|
34
37
|
// Layouts for auth pages
|
|
35
38
|
import { BaseLayout } from "./theme/layouts/index.js";
|
|
36
39
|
import { sse } from "./lib/sse.js";
|
|
40
|
+
import { getAvailableThemes, buildThemeStyle } from "./lib/theme.js";
|
|
37
41
|
/**
|
|
38
42
|
* Create a Jant application
|
|
39
43
|
*
|
|
@@ -75,6 +79,15 @@ import { sse } from "./lib/sse.js";
|
|
|
75
79
|
}
|
|
76
80
|
await next();
|
|
77
81
|
});
|
|
82
|
+
// Theme middleware - resolve active color theme and build CSS
|
|
83
|
+
app.use("*", async (c, next)=>{
|
|
84
|
+
const themeId = await c.var.services.settings.get(SETTINGS_KEYS.THEME);
|
|
85
|
+
const themes = getAvailableThemes(config);
|
|
86
|
+
const activeTheme = themeId ? themes.find((t)=>t.id === themeId) : undefined;
|
|
87
|
+
const themeStyle = buildThemeStyle(activeTheme, config.theme?.cssVariables);
|
|
88
|
+
c.set("themeStyle", themeStyle);
|
|
89
|
+
await next();
|
|
90
|
+
});
|
|
78
91
|
// i18n middleware
|
|
79
92
|
app.use("*", i18nMiddleware());
|
|
80
93
|
// Trailing slash redirect (redirect /foo/ to /foo)
|
|
@@ -134,112 +147,85 @@ import { sse } from "./lib/sse.js";
|
|
|
134
147
|
}),
|
|
135
148
|
/*#__PURE__*/ _jsx("p", {
|
|
136
149
|
children: $__i18n._({
|
|
137
|
-
id: "
|
|
138
|
-
message: "
|
|
150
|
+
id: "GX2VMa",
|
|
151
|
+
message: "Create your admin account."
|
|
139
152
|
})
|
|
140
153
|
})
|
|
141
154
|
]
|
|
142
155
|
}),
|
|
143
|
-
/*#__PURE__*/
|
|
144
|
-
children:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class: "label",
|
|
158
|
-
children: $__i18n._({
|
|
159
|
-
id: "SJmfuf",
|
|
160
|
-
message: "Site Name"
|
|
161
|
-
})
|
|
162
|
-
}),
|
|
163
|
-
/*#__PURE__*/ _jsx("input", {
|
|
164
|
-
type: "text",
|
|
165
|
-
"data-bind": "siteName",
|
|
166
|
-
class: "input",
|
|
167
|
-
required: true,
|
|
168
|
-
placeholder: $__i18n._({
|
|
169
|
-
id: "HfyyXl",
|
|
170
|
-
message: "My Blog"
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
]
|
|
174
|
-
}),
|
|
175
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
176
|
-
class: "field",
|
|
177
|
-
children: [
|
|
178
|
-
/*#__PURE__*/ _jsx("label", {
|
|
179
|
-
class: "label",
|
|
180
|
-
children: $__i18n._({
|
|
181
|
-
id: "/Rj5P4",
|
|
182
|
-
message: "Your Name"
|
|
183
|
-
})
|
|
184
|
-
}),
|
|
185
|
-
/*#__PURE__*/ _jsx("input", {
|
|
186
|
-
type: "text",
|
|
187
|
-
"data-bind": "name",
|
|
188
|
-
class: "input",
|
|
189
|
-
required: true,
|
|
190
|
-
placeholder: "John Doe"
|
|
156
|
+
/*#__PURE__*/ _jsx("section", {
|
|
157
|
+
children: /*#__PURE__*/ _jsxs("form", {
|
|
158
|
+
"data-signals": "{name: '', email: '', password: ''}",
|
|
159
|
+
"data-on:submit__prevent": "@post('/setup')",
|
|
160
|
+
class: "flex flex-col gap-4",
|
|
161
|
+
children: [
|
|
162
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
163
|
+
class: "field",
|
|
164
|
+
children: [
|
|
165
|
+
/*#__PURE__*/ _jsx("label", {
|
|
166
|
+
class: "label",
|
|
167
|
+
children: $__i18n._({
|
|
168
|
+
id: "/Rj5P4",
|
|
169
|
+
message: "Your Name"
|
|
191
170
|
})
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
171
|
+
}),
|
|
172
|
+
/*#__PURE__*/ _jsx("input", {
|
|
173
|
+
type: "text",
|
|
174
|
+
"data-bind": "name",
|
|
175
|
+
class: "input",
|
|
176
|
+
required: true,
|
|
177
|
+
placeholder: "John Doe"
|
|
178
|
+
})
|
|
179
|
+
]
|
|
180
|
+
}),
|
|
181
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
182
|
+
class: "field",
|
|
183
|
+
children: [
|
|
184
|
+
/*#__PURE__*/ _jsx("label", {
|
|
185
|
+
class: "label",
|
|
186
|
+
children: $__i18n._({
|
|
187
|
+
id: "O3oNi5",
|
|
188
|
+
message: "Email"
|
|
210
189
|
})
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
190
|
+
}),
|
|
191
|
+
/*#__PURE__*/ _jsx("input", {
|
|
192
|
+
type: "email",
|
|
193
|
+
"data-bind": "email",
|
|
194
|
+
class: "input",
|
|
195
|
+
required: true,
|
|
196
|
+
placeholder: "you@example.com"
|
|
197
|
+
})
|
|
198
|
+
]
|
|
199
|
+
}),
|
|
200
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
201
|
+
class: "field",
|
|
202
|
+
children: [
|
|
203
|
+
/*#__PURE__*/ _jsx("label", {
|
|
204
|
+
class: "label",
|
|
205
|
+
children: $__i18n._({
|
|
206
|
+
id: "8ZsakT",
|
|
207
|
+
message: "Password"
|
|
229
208
|
})
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
message: "Complete Setup"
|
|
209
|
+
}),
|
|
210
|
+
/*#__PURE__*/ _jsx("input", {
|
|
211
|
+
type: "password",
|
|
212
|
+
"data-bind": "password",
|
|
213
|
+
class: "input",
|
|
214
|
+
required: true,
|
|
215
|
+
minLength: 8
|
|
238
216
|
})
|
|
217
|
+
]
|
|
218
|
+
}),
|
|
219
|
+
/*#__PURE__*/ _jsx("button", {
|
|
220
|
+
type: "submit",
|
|
221
|
+
class: "btn",
|
|
222
|
+
children: $__i18n._({
|
|
223
|
+
id: "EGwzOK",
|
|
224
|
+
message: "Complete Setup"
|
|
239
225
|
})
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
})
|
|
243
229
|
})
|
|
244
230
|
]
|
|
245
231
|
})
|
|
@@ -259,20 +245,20 @@ import { sse } from "./lib/sse.js";
|
|
|
259
245
|
const isComplete = await c.var.services.settings.isOnboardingComplete();
|
|
260
246
|
if (isComplete) return c.redirect("/");
|
|
261
247
|
const body = await c.req.json();
|
|
262
|
-
const {
|
|
263
|
-
if (!
|
|
248
|
+
const { name, email, password } = body;
|
|
249
|
+
if (!name || !email || !password) {
|
|
264
250
|
return sse(c, async (stream)=>{
|
|
265
|
-
await stream.
|
|
251
|
+
await stream.toast("All fields are required", "error");
|
|
266
252
|
});
|
|
267
253
|
}
|
|
268
254
|
if (password.length < 8) {
|
|
269
255
|
return sse(c, async (stream)=>{
|
|
270
|
-
await stream.
|
|
256
|
+
await stream.toast("Password must be at least 8 characters", "error");
|
|
271
257
|
});
|
|
272
258
|
}
|
|
273
259
|
if (!c.var.auth) {
|
|
274
260
|
return sse(c, async (stream)=>{
|
|
275
|
-
await stream.
|
|
261
|
+
await stream.toast("AUTH_SECRET not configured", "error");
|
|
276
262
|
});
|
|
277
263
|
}
|
|
278
264
|
try {
|
|
@@ -285,22 +271,18 @@ import { sse } from "./lib/sse.js";
|
|
|
285
271
|
});
|
|
286
272
|
if (!signUpResponse || "error" in signUpResponse) {
|
|
287
273
|
return sse(c, async (stream)=>{
|
|
288
|
-
await stream.
|
|
274
|
+
await stream.toast("Failed to create account", "error");
|
|
289
275
|
});
|
|
290
276
|
}
|
|
291
|
-
await c.var.services.settings.setMany({
|
|
292
|
-
SITE_NAME: siteName,
|
|
293
|
-
SITE_LANGUAGE: "en"
|
|
294
|
-
});
|
|
295
277
|
await c.var.services.settings.completeOnboarding();
|
|
296
278
|
return sse(c, async (stream)=>{
|
|
297
|
-
await stream.redirect("/signin");
|
|
279
|
+
await stream.redirect("/signin?setup");
|
|
298
280
|
});
|
|
299
281
|
} catch (err) {
|
|
300
282
|
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
301
283
|
console.error("Setup error:", err);
|
|
302
284
|
return sse(c, async (stream)=>{
|
|
303
|
-
await stream.
|
|
285
|
+
await stream.toast("Failed to create account", "error");
|
|
304
286
|
});
|
|
305
287
|
}
|
|
306
288
|
});
|
|
@@ -326,9 +308,6 @@ import { sse } from "./lib/sse.js";
|
|
|
326
308
|
}),
|
|
327
309
|
/*#__PURE__*/ _jsxs("section", {
|
|
328
310
|
children: [
|
|
329
|
-
/*#__PURE__*/ _jsx("div", {
|
|
330
|
-
id: "signin-message"
|
|
331
|
-
}),
|
|
332
311
|
demoEmail && demoPassword && /*#__PURE__*/ _jsx("p", {
|
|
333
312
|
class: "text-muted-foreground text-sm mb-4",
|
|
334
313
|
children: $__i18n._({
|
|
@@ -395,9 +374,22 @@ import { sse } from "./lib/sse.js";
|
|
|
395
374
|
};
|
|
396
375
|
// Signin page
|
|
397
376
|
app.get("/signin", async (c)=>{
|
|
377
|
+
const isSetup = c.req.query("setup") !== undefined;
|
|
378
|
+
const isReset = c.req.query("reset") !== undefined;
|
|
379
|
+
let toast;
|
|
380
|
+
if (isSetup) {
|
|
381
|
+
toast = {
|
|
382
|
+
message: "Account created successfully. Please sign in."
|
|
383
|
+
};
|
|
384
|
+
} else if (isReset) {
|
|
385
|
+
toast = {
|
|
386
|
+
message: "Password reset successfully. Please sign in."
|
|
387
|
+
};
|
|
388
|
+
}
|
|
398
389
|
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
399
390
|
title: "Sign In - Jant",
|
|
400
391
|
c: c,
|
|
392
|
+
toast: toast,
|
|
401
393
|
children: /*#__PURE__*/ _jsx(SigninContent, {
|
|
402
394
|
demoEmail: c.env.DEMO_EMAIL,
|
|
403
395
|
demoPassword: c.env.DEMO_PASSWORD
|
|
@@ -407,7 +399,7 @@ import { sse } from "./lib/sse.js";
|
|
|
407
399
|
app.post("/signin", async (c)=>{
|
|
408
400
|
if (!c.var.auth) {
|
|
409
401
|
return sse(c, async (stream)=>{
|
|
410
|
-
await stream.
|
|
402
|
+
await stream.toast("Auth not configured", "error");
|
|
411
403
|
});
|
|
412
404
|
}
|
|
413
405
|
const body = await c.req.json();
|
|
@@ -426,7 +418,7 @@ import { sse } from "./lib/sse.js";
|
|
|
426
418
|
const response = await c.var.auth.handler(signInRequest);
|
|
427
419
|
if (!response.ok) {
|
|
428
420
|
return sse(c, async (stream)=>{
|
|
429
|
-
await stream.
|
|
421
|
+
await stream.toast("Invalid email or password", "error");
|
|
430
422
|
});
|
|
431
423
|
}
|
|
432
424
|
// Forward Set-Cookie headers from auth response
|
|
@@ -444,7 +436,7 @@ import { sse } from "./lib/sse.js";
|
|
|
444
436
|
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
445
437
|
console.error("Signin error:", err);
|
|
446
438
|
return sse(c, async (stream)=>{
|
|
447
|
-
await stream.
|
|
439
|
+
await stream.toast("Invalid email or password", "error");
|
|
448
440
|
});
|
|
449
441
|
}
|
|
450
442
|
});
|
|
@@ -460,6 +452,217 @@ import { sse } from "./lib/sse.js";
|
|
|
460
452
|
}
|
|
461
453
|
return c.redirect("/");
|
|
462
454
|
});
|
|
455
|
+
// Password reset via one-time token
|
|
456
|
+
const ResetContent = ({ token })=>{
|
|
457
|
+
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
458
|
+
const signals = JSON.stringify({
|
|
459
|
+
password: "",
|
|
460
|
+
confirmPassword: "",
|
|
461
|
+
token
|
|
462
|
+
}).replace(/</g, "\\u003c");
|
|
463
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
464
|
+
class: "min-h-screen flex items-center justify-center",
|
|
465
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
466
|
+
class: "card max-w-md w-full",
|
|
467
|
+
children: [
|
|
468
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
469
|
+
children: [
|
|
470
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
471
|
+
children: $__i18n._({
|
|
472
|
+
id: "KbS2K9",
|
|
473
|
+
message: "Reset Password"
|
|
474
|
+
})
|
|
475
|
+
}),
|
|
476
|
+
/*#__PURE__*/ _jsx("p", {
|
|
477
|
+
children: $__i18n._({
|
|
478
|
+
id: "hWOZIv",
|
|
479
|
+
message: "Enter your new password."
|
|
480
|
+
})
|
|
481
|
+
})
|
|
482
|
+
]
|
|
483
|
+
}),
|
|
484
|
+
/*#__PURE__*/ _jsx("section", {
|
|
485
|
+
children: /*#__PURE__*/ _jsxs("form", {
|
|
486
|
+
"data-signals": signals,
|
|
487
|
+
"data-on:submit__prevent": "@post('/reset')",
|
|
488
|
+
class: "flex flex-col gap-4",
|
|
489
|
+
children: [
|
|
490
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
491
|
+
class: "field",
|
|
492
|
+
children: [
|
|
493
|
+
/*#__PURE__*/ _jsx("label", {
|
|
494
|
+
class: "label",
|
|
495
|
+
children: $__i18n._({
|
|
496
|
+
id: "7vhWI8",
|
|
497
|
+
message: "New Password"
|
|
498
|
+
})
|
|
499
|
+
}),
|
|
500
|
+
/*#__PURE__*/ _jsx("input", {
|
|
501
|
+
type: "password",
|
|
502
|
+
"data-bind": "password",
|
|
503
|
+
class: "input",
|
|
504
|
+
required: true,
|
|
505
|
+
minLength: 8,
|
|
506
|
+
autocomplete: "new-password"
|
|
507
|
+
})
|
|
508
|
+
]
|
|
509
|
+
}),
|
|
510
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
511
|
+
class: "field",
|
|
512
|
+
children: [
|
|
513
|
+
/*#__PURE__*/ _jsx("label", {
|
|
514
|
+
class: "label",
|
|
515
|
+
children: $__i18n._({
|
|
516
|
+
id: "p2/GCq",
|
|
517
|
+
message: "Confirm Password"
|
|
518
|
+
})
|
|
519
|
+
}),
|
|
520
|
+
/*#__PURE__*/ _jsx("input", {
|
|
521
|
+
type: "password",
|
|
522
|
+
"data-bind": "confirmPassword",
|
|
523
|
+
class: "input",
|
|
524
|
+
required: true,
|
|
525
|
+
minLength: 8,
|
|
526
|
+
autocomplete: "new-password"
|
|
527
|
+
})
|
|
528
|
+
]
|
|
529
|
+
}),
|
|
530
|
+
/*#__PURE__*/ _jsx("button", {
|
|
531
|
+
type: "submit",
|
|
532
|
+
class: "btn",
|
|
533
|
+
children: $__i18n._({
|
|
534
|
+
id: "KbS2K9",
|
|
535
|
+
message: "Reset Password"
|
|
536
|
+
})
|
|
537
|
+
})
|
|
538
|
+
]
|
|
539
|
+
})
|
|
540
|
+
})
|
|
541
|
+
]
|
|
542
|
+
})
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
const ResetErrorContent = ()=>{
|
|
546
|
+
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
547
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
548
|
+
class: "min-h-screen flex items-center justify-center",
|
|
549
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
550
|
+
class: "card max-w-md w-full",
|
|
551
|
+
children: [
|
|
552
|
+
/*#__PURE__*/ _jsx("header", {
|
|
553
|
+
children: /*#__PURE__*/ _jsx("h2", {
|
|
554
|
+
children: $__i18n._({
|
|
555
|
+
id: "7aECQB",
|
|
556
|
+
message: "Invalid or Expired Link"
|
|
557
|
+
})
|
|
558
|
+
})
|
|
559
|
+
}),
|
|
560
|
+
/*#__PURE__*/ _jsx("section", {
|
|
561
|
+
children: /*#__PURE__*/ _jsx("p", {
|
|
562
|
+
class: "text-muted-foreground",
|
|
563
|
+
children: $__i18n._({
|
|
564
|
+
id: "GbVAnd",
|
|
565
|
+
message: "This password reset link is invalid or has expired. Please generate a new one."
|
|
566
|
+
})
|
|
567
|
+
})
|
|
568
|
+
})
|
|
569
|
+
]
|
|
570
|
+
})
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
app.get("/reset", async (c)=>{
|
|
574
|
+
const token = c.req.query("token");
|
|
575
|
+
if (!token) {
|
|
576
|
+
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
577
|
+
title: "Reset Password - Jant",
|
|
578
|
+
c: c,
|
|
579
|
+
children: /*#__PURE__*/ _jsx(ResetErrorContent, {})
|
|
580
|
+
}));
|
|
581
|
+
}
|
|
582
|
+
const stored = await c.var.services.settings.get(SETTINGS_KEYS.PASSWORD_RESET_TOKEN);
|
|
583
|
+
if (!stored) {
|
|
584
|
+
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
585
|
+
title: "Reset Password - Jant",
|
|
586
|
+
c: c,
|
|
587
|
+
children: /*#__PURE__*/ _jsx(ResetErrorContent, {})
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
const separatorIndex = stored.lastIndexOf(":");
|
|
591
|
+
const storedToken = stored.substring(0, separatorIndex);
|
|
592
|
+
const expiry = parseInt(stored.substring(separatorIndex + 1), 10);
|
|
593
|
+
const now = Math.floor(Date.now() / 1000);
|
|
594
|
+
if (token !== storedToken || now > expiry) {
|
|
595
|
+
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
596
|
+
title: "Reset Password - Jant",
|
|
597
|
+
c: c,
|
|
598
|
+
children: /*#__PURE__*/ _jsx(ResetErrorContent, {})
|
|
599
|
+
}));
|
|
600
|
+
}
|
|
601
|
+
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
602
|
+
title: "Reset Password - Jant",
|
|
603
|
+
c: c,
|
|
604
|
+
children: /*#__PURE__*/ _jsx(ResetContent, {
|
|
605
|
+
token: token
|
|
606
|
+
})
|
|
607
|
+
}));
|
|
608
|
+
});
|
|
609
|
+
app.post("/reset", async (c)=>{
|
|
610
|
+
const body = await c.req.json();
|
|
611
|
+
const { password, confirmPassword, token } = body;
|
|
612
|
+
// Validate token
|
|
613
|
+
const stored = await c.var.services.settings.get(SETTINGS_KEYS.PASSWORD_RESET_TOKEN);
|
|
614
|
+
if (!stored) {
|
|
615
|
+
return sse(c, async (stream)=>{
|
|
616
|
+
await stream.toast("Invalid or expired reset link.", "error");
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
const separatorIndex = stored.lastIndexOf(":");
|
|
620
|
+
const storedToken = stored.substring(0, separatorIndex);
|
|
621
|
+
const expiry = parseInt(stored.substring(separatorIndex + 1), 10);
|
|
622
|
+
const now = Math.floor(Date.now() / 1000);
|
|
623
|
+
if (token !== storedToken || now > expiry) {
|
|
624
|
+
return sse(c, async (stream)=>{
|
|
625
|
+
await stream.toast("Invalid or expired reset link.", "error");
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
// Validate passwords
|
|
629
|
+
if (!password || password.length < 8) {
|
|
630
|
+
return sse(c, async (stream)=>{
|
|
631
|
+
await stream.toast("Password must be at least 8 characters.", "error");
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
if (password !== confirmPassword) {
|
|
635
|
+
return sse(c, async (stream)=>{
|
|
636
|
+
await stream.toast("Passwords do not match.", "error");
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
try {
|
|
640
|
+
const hashedPassword = await hashPassword(password);
|
|
641
|
+
const db = c.env.DB.withSession();
|
|
642
|
+
// Get admin user
|
|
643
|
+
const userResult = await db.prepare("SELECT id FROM user LIMIT 1").first();
|
|
644
|
+
if (!userResult) {
|
|
645
|
+
return sse(c, async (stream)=>{
|
|
646
|
+
await stream.toast("No user account found.", "error");
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
// Update password
|
|
650
|
+
await db.prepare("UPDATE account SET password = ? WHERE user_id = ? AND provider_id = 'credential'").bind(hashedPassword, userResult.id).run();
|
|
651
|
+
// Delete all sessions
|
|
652
|
+
await db.prepare("DELETE FROM session WHERE user_id = ?").bind(userResult.id).run();
|
|
653
|
+
// Delete the reset token
|
|
654
|
+
await c.var.services.settings.remove(SETTINGS_KEYS.PASSWORD_RESET_TOKEN);
|
|
655
|
+
return sse(c, async (stream)=>{
|
|
656
|
+
await stream.redirect("/signin?reset");
|
|
657
|
+
});
|
|
658
|
+
} catch (err) {
|
|
659
|
+
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
660
|
+
console.error("Password reset error:", err);
|
|
661
|
+
return sse(c, async (stream)=>{
|
|
662
|
+
await stream.toast("Failed to reset password.", "error");
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
});
|
|
463
666
|
// Dashboard routes (protected)
|
|
464
667
|
app.use("/dash/*", requireAuth());
|
|
465
668
|
app.route("/dash", dashIndexRoutes);
|
|
@@ -469,6 +672,7 @@ import { sse } from "./lib/sse.js";
|
|
|
469
672
|
app.route("/dash/settings", dashSettingsRoutes);
|
|
470
673
|
app.route("/dash/redirects", dashRedirectsRoutes);
|
|
471
674
|
app.route("/dash/collections", dashCollectionsRoutes);
|
|
675
|
+
app.route("/dash/appearance", dashAppearanceRoutes);
|
|
472
676
|
// API routes
|
|
473
677
|
app.route("/api/upload", uploadApiRoutes);
|
|
474
678
|
app.route("/api/search", searchApiRoutes);
|
package/dist/i18n/context.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ type TranslationDescriptor = {
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* ```tsx
|
|
24
|
-
* import { I18nProvider } from "
|
|
24
|
+
* import { I18nProvider } from "../i18n/index.js";
|
|
25
25
|
*
|
|
26
26
|
* return c.html(
|
|
27
27
|
* <I18nProvider c={c}>
|
|
@@ -41,7 +41,7 @@ export declare const I18nProvider: FC<I18nProviderProps>;
|
|
|
41
41
|
* @example
|
|
42
42
|
* ```tsx
|
|
43
43
|
* import { t } from "@lingui/core/macro";
|
|
44
|
-
* import { useLingui } from "
|
|
44
|
+
* import { useLingui } from "../i18n/index.js";
|
|
45
45
|
*
|
|
46
46
|
* function MyComponent() {
|
|
47
47
|
* const { t: _ } = useLingui(); // Use _ to avoid conflict with macro
|
package/dist/i18n/context.js
CHANGED
|
@@ -22,7 +22,7 @@ export const I18nProvider = ({ c, children })=>{
|
|
|
22
22
|
* @example
|
|
23
23
|
* ```tsx
|
|
24
24
|
* import { t } from "@lingui/core/macro";
|
|
25
|
-
* import { useLingui } from "
|
|
25
|
+
* import { useLingui } from "../i18n/index.js";
|
|
26
26
|
*
|
|
27
27
|
* function MyComponent() {
|
|
28
28
|
* const { t: _ } = useLingui(); // Use _ to avoid conflict with macro
|
package/dist/i18n/i18n.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare function createI18n(locale: Locale): I18n;
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* import { msg } from "@lingui/core/macro";
|
|
24
|
-
* import { getI18n } from "
|
|
24
|
+
* import { getI18n } from "../i18n/index.js";
|
|
25
25
|
*
|
|
26
26
|
* const i18n = getI18n(c);
|
|
27
27
|
* const title = i18n._(msg({ message: "Dashboard", comment: "@context: Page title" }));
|
package/dist/i18n/i18n.js
CHANGED
|
@@ -40,7 +40,7 @@ const catalogZhHant = {
|
|
|
40
40
|
*
|
|
41
41
|
* @example
|
|
42
42
|
* import { msg } from "@lingui/core/macro";
|
|
43
|
-
* import { getI18n } from "
|
|
43
|
+
* import { getI18n } from "../i18n/index.js";
|
|
44
44
|
*
|
|
45
45
|
* const i18n = getI18n(c);
|
|
46
46
|
* const title = i18n._(msg({ message: "Dashboard", comment: "@context: Page title" }));
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
11
|
* ```tsx
|
|
12
|
-
* import { useLingui, Trans, I18nProvider } from "
|
|
12
|
+
* import { useLingui, Trans, I18nProvider } from "../i18n/index.js";
|
|
13
13
|
*
|
|
14
14
|
* // Wrap your app in I18nProvider (automatically done by BaseLayout when c is provided)
|
|
15
15
|
* c.html(
|
package/dist/i18n/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
11
|
* ```tsx
|
|
12
|
-
* import { useLingui, Trans, I18nProvider } from "
|
|
12
|
+
* import { useLingui, Trans, I18nProvider } from "../i18n/index.js";
|
|
13
13
|
*
|
|
14
14
|
* // Wrap your app in I18nProvider (automatically done by BaseLayout when c is provided)
|
|
15
15
|
* c.html(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/i18n/locales/en.ts"],"names":[],"mappings":"AAAkB,OAAO,KAAI,EAAC,QAAQ,EAAC,MAAI,cAAc,CAAC;AAAA,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/i18n/locales/en.ts"],"names":[],"mappings":"AAAkB,OAAO,KAAI,EAAC,QAAQ,EAAC,MAAI,cAAc,CAAC;AAAA,eAAO,MAAM,QAAQ,EAAs1K,QAAQ,CAAC"}
|