@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
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Hono } from "hono";
|
|
6
|
-
import {
|
|
7
|
-
import { useLingui } from "../../i18n/index.js";
|
|
6
|
+
import { useLingui } from "@lingui/react/macro";
|
|
8
7
|
import type { Bindings, Post } from "../../types.js";
|
|
9
8
|
import type { AppVariables } from "../../app.js";
|
|
10
9
|
import { DashLayout } from "../../theme/layouts/index.js";
|
|
11
|
-
import { PostForm, PostList, CrudPageHeader, ActionButtons } from "../../theme/components/index.js";
|
|
12
|
-
import * as sqid from "../../lib/sqid.js";
|
|
13
10
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from "../../
|
|
11
|
+
PostForm,
|
|
12
|
+
PostList,
|
|
13
|
+
CrudPageHeader,
|
|
14
|
+
ActionButtons,
|
|
15
|
+
} from "../../theme/components/index.js";
|
|
16
|
+
import * as sqid from "../../lib/sqid.js";
|
|
17
|
+
import { sse } from "../../lib/sse.js";
|
|
19
18
|
|
|
20
19
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
21
20
|
|
|
@@ -27,7 +26,10 @@ function PostsListContent({ posts }: { posts: Post[] }) {
|
|
|
27
26
|
<>
|
|
28
27
|
<CrudPageHeader
|
|
29
28
|
title={t({ message: "Posts", comment: "@context: Dashboard heading" })}
|
|
30
|
-
ctaLabel={t({
|
|
29
|
+
ctaLabel={t({
|
|
30
|
+
message: "New Post",
|
|
31
|
+
comment: "@context: Button to create new post",
|
|
32
|
+
})}
|
|
31
33
|
ctaHref="/dash/posts/new"
|
|
32
34
|
/>
|
|
33
35
|
<PostList posts={posts} />
|
|
@@ -39,7 +41,9 @@ function NewPostContent() {
|
|
|
39
41
|
const { t } = useLingui();
|
|
40
42
|
return (
|
|
41
43
|
<>
|
|
42
|
-
<h1 class="text-2xl font-semibold mb-6">
|
|
44
|
+
<h1 class="text-2xl font-semibold mb-6">
|
|
45
|
+
{t({ message: "New Post", comment: "@context: Page heading" })}
|
|
46
|
+
</h1>
|
|
43
47
|
<PostForm action="/dash/posts" />
|
|
44
48
|
</>
|
|
45
49
|
);
|
|
@@ -53,9 +57,14 @@ postsRoutes.get("/", async (c) => {
|
|
|
53
57
|
const siteName = (await c.var.services.settings.get("SITE_NAME")) ?? "Jant";
|
|
54
58
|
|
|
55
59
|
return c.html(
|
|
56
|
-
<DashLayout
|
|
60
|
+
<DashLayout
|
|
61
|
+
c={c}
|
|
62
|
+
title="Posts"
|
|
63
|
+
siteName={siteName}
|
|
64
|
+
currentPath="/dash/posts"
|
|
65
|
+
>
|
|
57
66
|
<PostsListContent posts={posts} />
|
|
58
|
-
</DashLayout
|
|
67
|
+
</DashLayout>,
|
|
59
68
|
);
|
|
60
69
|
});
|
|
61
70
|
|
|
@@ -64,39 +73,48 @@ postsRoutes.get("/new", async (c) => {
|
|
|
64
73
|
const siteName = (await c.var.services.settings.get("SITE_NAME")) ?? "Jant";
|
|
65
74
|
|
|
66
75
|
return c.html(
|
|
67
|
-
<DashLayout
|
|
76
|
+
<DashLayout
|
|
77
|
+
c={c}
|
|
78
|
+
title="New Post"
|
|
79
|
+
siteName={siteName}
|
|
80
|
+
currentPath="/dash/posts"
|
|
81
|
+
>
|
|
68
82
|
<NewPostContent />
|
|
69
|
-
</DashLayout
|
|
83
|
+
</DashLayout>,
|
|
70
84
|
);
|
|
71
85
|
});
|
|
72
86
|
|
|
73
87
|
// Create post
|
|
74
88
|
postsRoutes.post("/", async (c) => {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const path = parseFormDataOptional(formData, "path", z.string());
|
|
89
|
+
const body = await c.req.json<{
|
|
90
|
+
type: string;
|
|
91
|
+
title?: string;
|
|
92
|
+
content: string;
|
|
93
|
+
visibility: string;
|
|
94
|
+
sourceUrl?: string;
|
|
95
|
+
path?: string;
|
|
96
|
+
}>();
|
|
84
97
|
|
|
85
98
|
const post = await c.var.services.posts.create({
|
|
86
|
-
type,
|
|
87
|
-
title,
|
|
88
|
-
content,
|
|
89
|
-
visibility,
|
|
90
|
-
sourceUrl,
|
|
91
|
-
path,
|
|
99
|
+
type: body.type as Post["type"],
|
|
100
|
+
title: body.title || undefined,
|
|
101
|
+
content: body.content,
|
|
102
|
+
visibility: body.visibility as Post["visibility"],
|
|
103
|
+
sourceUrl: body.sourceUrl || undefined,
|
|
104
|
+
path: body.path || undefined,
|
|
92
105
|
});
|
|
93
106
|
|
|
94
|
-
return c
|
|
107
|
+
return sse(c, async (stream) => {
|
|
108
|
+
await stream.redirect(`/dash/posts/${sqid.encode(post.id)}`);
|
|
109
|
+
});
|
|
95
110
|
});
|
|
96
111
|
|
|
97
112
|
function ViewPostContent({ post }: { post: Post }) {
|
|
98
113
|
const { t } = useLingui();
|
|
99
|
-
const defaultTitle = t({
|
|
114
|
+
const defaultTitle = t({
|
|
115
|
+
message: "Post",
|
|
116
|
+
comment: "@context: Default post title",
|
|
117
|
+
});
|
|
100
118
|
|
|
101
119
|
return (
|
|
102
120
|
<>
|
|
@@ -104,15 +122,24 @@ function ViewPostContent({ post }: { post: Post }) {
|
|
|
104
122
|
<h1 class="text-2xl font-semibold">{post.title || defaultTitle}</h1>
|
|
105
123
|
<ActionButtons
|
|
106
124
|
editHref={`/dash/posts/${sqid.encode(post.id)}/edit`}
|
|
107
|
-
editLabel={t({
|
|
125
|
+
editLabel={t({
|
|
126
|
+
message: "Edit",
|
|
127
|
+
comment: "@context: Button to edit post",
|
|
128
|
+
})}
|
|
108
129
|
viewHref={`/p/${sqid.encode(post.id)}`}
|
|
109
|
-
viewLabel={t({
|
|
130
|
+
viewLabel={t({
|
|
131
|
+
message: "View",
|
|
132
|
+
comment: "@context: Button to view post",
|
|
133
|
+
})}
|
|
110
134
|
/>
|
|
111
135
|
</div>
|
|
112
136
|
|
|
113
137
|
<div class="card">
|
|
114
138
|
<section>
|
|
115
|
-
<div
|
|
139
|
+
<div
|
|
140
|
+
class="prose"
|
|
141
|
+
dangerouslySetInnerHTML={{ __html: post.contentHtml || "" }}
|
|
142
|
+
/>
|
|
116
143
|
</section>
|
|
117
144
|
</div>
|
|
118
145
|
</>
|
|
@@ -123,7 +150,9 @@ function EditPostContent({ post }: { post: Post }) {
|
|
|
123
150
|
const { t } = useLingui();
|
|
124
151
|
return (
|
|
125
152
|
<>
|
|
126
|
-
<h1 class="text-2xl font-semibold mb-6">
|
|
153
|
+
<h1 class="text-2xl font-semibold mb-6">
|
|
154
|
+
{t({ message: "Edit Post", comment: "@context: Page heading" })}
|
|
155
|
+
</h1>
|
|
127
156
|
<PostForm post={post} action={`/dash/posts/${sqid.encode(post.id)}`} />
|
|
128
157
|
</>
|
|
129
158
|
);
|
|
@@ -141,9 +170,14 @@ postsRoutes.get("/:id", async (c) => {
|
|
|
141
170
|
const pageTitle = post.title || "Post";
|
|
142
171
|
|
|
143
172
|
return c.html(
|
|
144
|
-
<DashLayout
|
|
173
|
+
<DashLayout
|
|
174
|
+
c={c}
|
|
175
|
+
title={pageTitle}
|
|
176
|
+
siteName={siteName}
|
|
177
|
+
currentPath="/dash/posts"
|
|
178
|
+
>
|
|
145
179
|
<ViewPostContent post={post} />
|
|
146
|
-
</DashLayout
|
|
180
|
+
</DashLayout>,
|
|
147
181
|
);
|
|
148
182
|
});
|
|
149
183
|
|
|
@@ -158,9 +192,14 @@ postsRoutes.get("/:id/edit", async (c) => {
|
|
|
158
192
|
const siteName = (await c.var.services.settings.get("SITE_NAME")) ?? "Jant";
|
|
159
193
|
|
|
160
194
|
return c.html(
|
|
161
|
-
<DashLayout
|
|
195
|
+
<DashLayout
|
|
196
|
+
c={c}
|
|
197
|
+
title={`Edit: ${post.title || "Post"}`}
|
|
198
|
+
siteName={siteName}
|
|
199
|
+
currentPath="/dash/posts"
|
|
200
|
+
>
|
|
162
201
|
<EditPostContent post={post} />
|
|
163
|
-
</DashLayout
|
|
202
|
+
</DashLayout>,
|
|
164
203
|
);
|
|
165
204
|
});
|
|
166
205
|
|
|
@@ -169,26 +208,27 @@ postsRoutes.post("/:id", async (c) => {
|
|
|
169
208
|
const id = sqid.decode(c.req.param("id"));
|
|
170
209
|
if (!id) return c.notFound();
|
|
171
210
|
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const path = parseFormDataOptional(formData, "path", z.string()) || null;
|
|
211
|
+
const body = await c.req.json<{
|
|
212
|
+
type: string;
|
|
213
|
+
title?: string;
|
|
214
|
+
content?: string;
|
|
215
|
+
visibility: string;
|
|
216
|
+
sourceUrl?: string;
|
|
217
|
+
path?: string;
|
|
218
|
+
}>();
|
|
181
219
|
|
|
182
220
|
await c.var.services.posts.update(id, {
|
|
183
|
-
type,
|
|
184
|
-
title,
|
|
185
|
-
content,
|
|
186
|
-
visibility,
|
|
187
|
-
sourceUrl,
|
|
188
|
-
path,
|
|
221
|
+
type: body.type as Post["type"],
|
|
222
|
+
title: body.title || null,
|
|
223
|
+
content: body.content || null,
|
|
224
|
+
visibility: body.visibility as Post["visibility"],
|
|
225
|
+
sourceUrl: body.sourceUrl || null,
|
|
226
|
+
path: body.path || null,
|
|
189
227
|
});
|
|
190
228
|
|
|
191
|
-
return c
|
|
229
|
+
return sse(c, async (stream) => {
|
|
230
|
+
await stream.redirect(`/dash/posts/${sqid.encode(id)}`);
|
|
231
|
+
});
|
|
192
232
|
});
|
|
193
233
|
|
|
194
234
|
// Delete post
|
|
@@ -198,5 +238,7 @@ postsRoutes.post("/:id/delete", async (c) => {
|
|
|
198
238
|
|
|
199
239
|
await c.var.services.posts.delete(id);
|
|
200
240
|
|
|
201
|
-
return c
|
|
241
|
+
return sse(c, async (stream) => {
|
|
242
|
+
await stream.redirect("/dash/posts");
|
|
243
|
+
});
|
|
202
244
|
});
|
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Hono } from "hono";
|
|
6
|
-
import { useLingui } from "
|
|
6
|
+
import { useLingui } from "@lingui/react/macro";
|
|
7
7
|
import type { Bindings, Redirect } from "../../types.js";
|
|
8
8
|
import type { AppVariables } from "../../app.js";
|
|
9
9
|
import { DashLayout } from "../../theme/layouts/index.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
EmptyState,
|
|
12
|
+
ListItemRow,
|
|
13
|
+
ActionButtons,
|
|
14
|
+
CrudPageHeader,
|
|
15
|
+
} from "../../theme/components/index.js";
|
|
16
|
+
import { sse } from "../../lib/sse.js";
|
|
11
17
|
|
|
12
18
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
13
19
|
|
|
@@ -19,15 +25,27 @@ function RedirectsListContent({ redirects }: { redirects: Redirect[] }) {
|
|
|
19
25
|
return (
|
|
20
26
|
<>
|
|
21
27
|
<CrudPageHeader
|
|
22
|
-
title={t({
|
|
23
|
-
|
|
28
|
+
title={t({
|
|
29
|
+
message: "Redirects",
|
|
30
|
+
comment: "@context: Dashboard heading",
|
|
31
|
+
})}
|
|
32
|
+
ctaLabel={t({
|
|
33
|
+
message: "New Redirect",
|
|
34
|
+
comment: "@context: Button to create new redirect",
|
|
35
|
+
})}
|
|
24
36
|
ctaHref="/dash/redirects/new"
|
|
25
37
|
/>
|
|
26
38
|
|
|
27
39
|
{redirects.length === 0 ? (
|
|
28
40
|
<EmptyState
|
|
29
|
-
message={t({
|
|
30
|
-
|
|
41
|
+
message={t({
|
|
42
|
+
message: "No redirects configured.",
|
|
43
|
+
comment: "@context: Empty state message",
|
|
44
|
+
})}
|
|
45
|
+
ctaText={t({
|
|
46
|
+
message: "New Redirect",
|
|
47
|
+
comment: "@context: Button to create new redirect",
|
|
48
|
+
})}
|
|
31
49
|
ctaHref="/dash/redirects/new"
|
|
32
50
|
/>
|
|
33
51
|
) : (
|
|
@@ -38,7 +56,10 @@ function RedirectsListContent({ redirects }: { redirects: Redirect[] }) {
|
|
|
38
56
|
actions={
|
|
39
57
|
<ActionButtons
|
|
40
58
|
deleteAction={`/dash/redirects/${r.id}/delete`}
|
|
41
|
-
deleteLabel={t({
|
|
59
|
+
deleteLabel={t({
|
|
60
|
+
message: "Delete",
|
|
61
|
+
comment: "@context: Button to delete redirect",
|
|
62
|
+
})}
|
|
42
63
|
/>
|
|
43
64
|
}
|
|
44
65
|
>
|
|
@@ -61,47 +82,91 @@ function NewRedirectContent() {
|
|
|
61
82
|
|
|
62
83
|
return (
|
|
63
84
|
<>
|
|
64
|
-
<h1 class="text-2xl font-semibold mb-6">
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
<h1 class="text-2xl font-semibold mb-6">
|
|
86
|
+
{t({ message: "New Redirect", comment: "@context: Page heading" })}
|
|
87
|
+
</h1>
|
|
88
|
+
|
|
89
|
+
<form
|
|
90
|
+
data-signals="{fromPath: '', toPath: '', type: '301'}"
|
|
91
|
+
data-on:submit__prevent="@post('/dash/redirects')"
|
|
92
|
+
class="flex flex-col gap-4 max-w-lg"
|
|
93
|
+
>
|
|
67
94
|
<div class="field">
|
|
68
|
-
<label class="label">
|
|
95
|
+
<label class="label">
|
|
96
|
+
{t({
|
|
97
|
+
message: "From Path",
|
|
98
|
+
comment: "@context: Redirect form field",
|
|
99
|
+
})}
|
|
100
|
+
</label>
|
|
69
101
|
<input
|
|
70
102
|
type="text"
|
|
71
|
-
|
|
103
|
+
data-bind="fromPath"
|
|
72
104
|
class="input"
|
|
73
105
|
placeholder="/old-path"
|
|
74
106
|
required
|
|
75
107
|
/>
|
|
76
|
-
<p class="text-xs text-muted-foreground mt-1">
|
|
108
|
+
<p class="text-xs text-muted-foreground mt-1">
|
|
109
|
+
{t({
|
|
110
|
+
message: "The path to redirect from",
|
|
111
|
+
comment: "@context: Redirect from path help text",
|
|
112
|
+
})}
|
|
113
|
+
</p>
|
|
77
114
|
</div>
|
|
78
115
|
|
|
79
116
|
<div class="field">
|
|
80
|
-
<label class="label">
|
|
117
|
+
<label class="label">
|
|
118
|
+
{t({
|
|
119
|
+
message: "To Path",
|
|
120
|
+
comment: "@context: Redirect form field",
|
|
121
|
+
})}
|
|
122
|
+
</label>
|
|
81
123
|
<input
|
|
82
124
|
type="text"
|
|
83
|
-
|
|
125
|
+
data-bind="toPath"
|
|
84
126
|
class="input"
|
|
85
127
|
placeholder="/new-path or https://..."
|
|
86
128
|
required
|
|
87
129
|
/>
|
|
88
|
-
<p class="text-xs text-muted-foreground mt-1">
|
|
130
|
+
<p class="text-xs text-muted-foreground mt-1">
|
|
131
|
+
{t({
|
|
132
|
+
message: "The destination path or URL",
|
|
133
|
+
comment: "@context: Redirect to path help text",
|
|
134
|
+
})}
|
|
135
|
+
</p>
|
|
89
136
|
</div>
|
|
90
137
|
|
|
91
138
|
<div class="field">
|
|
92
|
-
<label class="label">
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
139
|
+
<label class="label">
|
|
140
|
+
{t({ message: "Type", comment: "@context: Redirect form field" })}
|
|
141
|
+
</label>
|
|
142
|
+
<select data-bind="type" class="select">
|
|
143
|
+
<option value="301">
|
|
144
|
+
{t({
|
|
145
|
+
message: "301 (Permanent)",
|
|
146
|
+
comment: "@context: Redirect type option",
|
|
147
|
+
})}
|
|
148
|
+
</option>
|
|
149
|
+
<option value="302">
|
|
150
|
+
{t({
|
|
151
|
+
message: "302 (Temporary)",
|
|
152
|
+
comment: "@context: Redirect type option",
|
|
153
|
+
})}
|
|
154
|
+
</option>
|
|
96
155
|
</select>
|
|
97
156
|
</div>
|
|
98
157
|
|
|
99
158
|
<div class="flex gap-2">
|
|
100
159
|
<button type="submit" class="btn">
|
|
101
|
-
{t({
|
|
160
|
+
{t({
|
|
161
|
+
message: "Create Redirect",
|
|
162
|
+
comment: "@context: Button to save new redirect",
|
|
163
|
+
})}
|
|
102
164
|
</button>
|
|
103
165
|
<a href="/dash/redirects" class="btn-outline">
|
|
104
|
-
{t({
|
|
166
|
+
{t({
|
|
167
|
+
message: "Cancel",
|
|
168
|
+
comment: "@context: Button to cancel form",
|
|
169
|
+
})}
|
|
105
170
|
</a>
|
|
106
171
|
</div>
|
|
107
172
|
</form>
|
|
@@ -115,9 +180,14 @@ redirectsRoutes.get("/", async (c) => {
|
|
|
115
180
|
const redirects = await c.var.services.redirects.list();
|
|
116
181
|
|
|
117
182
|
return c.html(
|
|
118
|
-
<DashLayout
|
|
183
|
+
<DashLayout
|
|
184
|
+
c={c}
|
|
185
|
+
title="Redirects"
|
|
186
|
+
siteName={siteName}
|
|
187
|
+
currentPath="/dash/redirects"
|
|
188
|
+
>
|
|
119
189
|
<RedirectsListContent redirects={redirects} />
|
|
120
|
-
</DashLayout
|
|
190
|
+
</DashLayout>,
|
|
121
191
|
);
|
|
122
192
|
});
|
|
123
193
|
|
|
@@ -126,23 +196,31 @@ redirectsRoutes.get("/new", async (c) => {
|
|
|
126
196
|
const siteName = (await c.var.services.settings.get("SITE_NAME")) ?? "Jant";
|
|
127
197
|
|
|
128
198
|
return c.html(
|
|
129
|
-
<DashLayout
|
|
199
|
+
<DashLayout
|
|
200
|
+
c={c}
|
|
201
|
+
title="New Redirect"
|
|
202
|
+
siteName={siteName}
|
|
203
|
+
currentPath="/dash/redirects"
|
|
204
|
+
>
|
|
130
205
|
<NewRedirectContent />
|
|
131
|
-
</DashLayout
|
|
206
|
+
</DashLayout>,
|
|
132
207
|
);
|
|
133
208
|
});
|
|
134
209
|
|
|
135
210
|
// Create redirect
|
|
136
211
|
redirectsRoutes.post("/", async (c) => {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
212
|
+
const body = await c.req.json<{
|
|
213
|
+
fromPath: string;
|
|
214
|
+
toPath: string;
|
|
215
|
+
type: string;
|
|
216
|
+
}>();
|
|
217
|
+
|
|
218
|
+
const type = parseInt(body.type, 10) as 301 | 302;
|
|
219
|
+
await c.var.services.redirects.create(body.fromPath, body.toPath, type);
|
|
220
|
+
|
|
221
|
+
return sse(c, async (stream) => {
|
|
222
|
+
await stream.redirect("/dash/redirects");
|
|
223
|
+
});
|
|
146
224
|
});
|
|
147
225
|
|
|
148
226
|
// Delete redirect
|
|
@@ -151,5 +229,8 @@ redirectsRoutes.post("/:id/delete", async (c) => {
|
|
|
151
229
|
if (!isNaN(id)) {
|
|
152
230
|
await c.var.services.redirects.delete(id);
|
|
153
231
|
}
|
|
154
|
-
|
|
232
|
+
|
|
233
|
+
return sse(c, async (stream) => {
|
|
234
|
+
await stream.redirect("/dash/redirects");
|
|
235
|
+
});
|
|
155
236
|
});
|