@jant/core 0.3.27 → 0.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/client.css +1 -0
- package/dist/client/client.js +31561 -0
- package/dist/index.js +15209 -15
- package/package.json +21 -15
- package/src/__tests__/helpers/app.ts +19 -3
- package/src/__tests__/helpers/db.ts +44 -0
- package/src/__tests__/helpers/lingui-core-macro-mock.ts +33 -0
- package/src/app.tsx +111 -174
- package/src/client.ts +13 -0
- package/src/db/migrations/0007_post_collections_m2m.sql +94 -0
- package/src/db/migrations/0008_add_collection_dividers.sql +8 -0
- package/src/db/migrations/0009_drop_collection_show_divider.sql +2 -0
- package/src/db/migrations/0010_add_performance_indexes.sql +16 -0
- package/src/db/schema.ts +24 -4
- package/src/i18n/locales/en.po +810 -385
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +733 -522
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +733 -522
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +7 -11
- package/src/index.ts +1 -1
- package/src/lib/__tests__/icons.test.ts +178 -0
- package/src/lib/__tests__/resolve-config.test.ts +184 -0
- package/src/lib/__tests__/schemas.test.ts +12 -6
- package/src/lib/__tests__/theme.test.ts +62 -0
- package/src/lib/__tests__/timezones.test.ts +1 -1
- package/src/lib/__tests__/url.test.ts +12 -0
- package/src/lib/__tests__/view.test.ts +1 -5
- package/src/lib/avatar-upload.ts +18 -10
- package/src/lib/collection-form-bridge.ts +52 -0
- package/src/lib/collections-reorder.ts +28 -0
- package/src/lib/compose-bridge.ts +251 -0
- package/src/lib/errors.ts +116 -0
- package/src/lib/excerpt.ts +1 -1
- package/src/lib/favicon.ts +3 -5
- package/src/lib/html.ts +22 -0
- package/src/lib/icon-catalog.ts +181 -0
- package/src/lib/icons.ts +202 -0
- package/src/lib/navigation.ts +18 -33
- package/src/lib/pagination.ts +3 -2
- package/src/lib/post-form-bridge.ts +136 -0
- package/src/lib/render.tsx +11 -4
- package/src/lib/resolve-config.ts +157 -0
- package/src/lib/schemas.ts +76 -12
- package/src/lib/settings-bridge.ts +139 -0
- package/src/lib/storage.ts +37 -16
- package/src/lib/theme.ts +5 -7
- package/src/lib/timeline.ts +4 -8
- package/src/lib/toast.ts +134 -0
- package/src/lib/upload.ts +71 -0
- package/src/lib/url.ts +9 -1
- package/src/lib/version.ts +16 -0
- package/src/lib/view.ts +9 -10
- package/src/middleware/__tests__/auth.test.ts +6 -28
- package/src/middleware/__tests__/onboarding.test.ts +1 -1
- package/src/middleware/auth.ts +6 -12
- package/src/middleware/config.ts +51 -0
- package/src/middleware/error-handler.ts +56 -0
- package/src/middleware/onboarding.ts +1 -1
- package/src/preset.css +6 -0
- package/src/routes/__tests__/compose.test.ts +104 -17
- package/src/routes/api/__tests__/collections.test.ts +93 -2
- package/src/routes/api/__tests__/posts.test.ts +2 -1
- package/src/routes/api/__tests__/settings.test.ts +1 -1
- package/src/routes/api/collections.ts +64 -68
- package/src/routes/api/nav-items.ts +21 -59
- package/src/routes/api/pages.ts +18 -46
- package/src/routes/api/posts.ts +64 -86
- package/src/routes/api/search.ts +6 -4
- package/src/routes/api/settings.ts +8 -24
- package/src/routes/api/upload.ts +55 -53
- package/src/routes/auth/__tests__/setup.test.ts +118 -0
- package/src/routes/auth/reset.tsx +17 -66
- package/src/routes/auth/setup.tsx +67 -11
- package/src/routes/auth/signin.tsx +44 -8
- package/src/routes/compose.tsx +194 -0
- package/src/routes/dash/__tests__/font-theme.test.ts +110 -0
- package/src/routes/dash/__tests__/pages.test.ts +2 -2
- package/src/routes/dash/__tests__/settings-avatar.test.ts +23 -12
- package/src/routes/dash/appearance.tsx +173 -0
- package/src/routes/dash/collections.tsx +80 -14
- package/src/routes/dash/index.tsx +12 -14
- package/src/routes/dash/media.tsx +46 -49
- package/src/routes/dash/pages.tsx +85 -37
- package/src/routes/dash/posts.tsx +60 -23
- package/src/routes/dash/redirects.tsx +43 -33
- package/src/routes/dash/settings.tsx +234 -214
- package/src/routes/feed/__tests__/rss.test.ts +7 -3
- package/src/routes/feed/rss.ts +11 -16
- package/src/routes/feed/sitemap.ts +15 -9
- package/src/routes/pages/__tests__/collections.test.ts +9 -8
- package/src/routes/pages/archive.tsx +2 -2
- package/src/routes/pages/collection.tsx +76 -9
- package/src/routes/pages/collections.tsx +3 -1
- package/src/routes/pages/featured.tsx +2 -2
- package/src/routes/pages/home.tsx +3 -3
- package/src/routes/pages/latest.tsx +2 -2
- package/src/routes/pages/page.tsx +2 -2
- package/src/routes/pages/post.tsx +2 -2
- package/src/routes/pages/search.tsx +2 -2
- package/src/services/__tests__/collection.test.ts +324 -34
- package/src/services/__tests__/media.test.ts +1 -1
- package/src/services/__tests__/page.test.ts +116 -1
- package/src/services/auth.ts +88 -0
- package/src/services/collection.ts +169 -30
- package/src/services/index.ts +8 -3
- package/src/services/media.ts +39 -12
- package/src/services/navigation.ts +17 -5
- package/src/services/page.ts +24 -4
- package/src/services/post.ts +87 -19
- package/src/services/search.ts +0 -1
- package/src/services/settings.ts +21 -13
- package/src/style.css +3 -0
- package/src/styles/components.css +42 -1
- package/src/styles/tokens.css +4 -0
- package/src/styles/ui.css +902 -73
- package/src/types/app-context.ts +25 -0
- package/src/types/bindings.ts +1 -0
- package/src/types/config.ts +60 -23
- package/src/types/entities.ts +12 -2
- package/src/types/lingui-react-macro.d.ts +3 -3
- package/src/types/operations.ts +2 -4
- package/src/types/views.ts +1 -3
- package/src/ui/__tests__/font-themes.test.ts +27 -8
- package/src/ui/color-themes.ts +1 -1
- package/src/ui/components/__tests__/jant-collection-form.test.ts +153 -0
- package/src/ui/components/__tests__/jant-compose-dialog.test.ts +512 -0
- package/src/ui/components/__tests__/jant-compose-editor.test.ts +272 -0
- package/src/ui/components/__tests__/jant-post-form.test.ts +172 -0
- package/src/ui/components/__tests__/jant-settings-avatar.test.ts +235 -0
- package/src/ui/components/__tests__/jant-settings-general.test.ts +319 -0
- package/src/ui/components/collection-types.ts +45 -0
- package/src/ui/components/compose-types.ts +75 -0
- package/src/ui/components/jant-collection-form.ts +512 -0
- package/src/ui/components/jant-compose-dialog.ts +494 -0
- package/src/ui/components/jant-compose-editor.ts +799 -0
- package/src/ui/components/jant-post-form.ts +290 -0
- package/src/ui/components/jant-settings-avatar.ts +231 -0
- package/src/ui/components/jant-settings-general.ts +436 -0
- package/src/ui/components/post-form-template.ts +260 -0
- package/src/ui/components/post-form-types.ts +87 -0
- package/src/ui/components/settings-types.ts +62 -0
- package/src/ui/compose/ComposeDialog.tsx +141 -385
- package/src/ui/compose/ComposePrompt.tsx +3 -3
- package/src/ui/dash/PostList.tsx +55 -61
- package/src/ui/dash/appearance/AdvancedContent.tsx +80 -0
- package/src/ui/dash/appearance/AppearanceNav.tsx +56 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +129 -0
- package/src/ui/dash/appearance/FontThemeContent.tsx +98 -0
- package/src/ui/dash/collections/CollectionForm.tsx +130 -117
- package/src/ui/dash/collections/CollectionsListContent.tsx +102 -41
- package/src/ui/dash/collections/IconPickerGrid.tsx +50 -0
- package/src/ui/dash/collections/ViewCollectionContent.tsx +14 -3
- package/src/ui/dash/index.ts +1 -1
- package/src/ui/dash/posts/PostForm.tsx +248 -0
- package/src/ui/dash/settings/AccountContent.tsx +69 -80
- package/src/ui/dash/settings/GeneralContent.tsx +159 -478
- package/src/ui/dash/settings/SettingsNav.tsx +4 -4
- package/src/ui/font-themes.ts +115 -32
- package/src/ui/layouts/BaseLayout.tsx +49 -19
- package/src/ui/layouts/DashLayout.tsx +14 -9
- package/src/ui/layouts/SiteLayout.tsx +38 -23
- package/src/ui/pages/CollectionPage.tsx +12 -2
- package/src/ui/pages/CollectionsPage.tsx +27 -27
- package/src/ui/pages/HomePage.tsx +15 -6
- package/src/ui/pages/SearchPage.tsx +1 -2
- package/src/ui/shared/CollectionsSidebar.tsx +59 -0
- package/src/ui/shared/Pagination.tsx +2 -2
- package/dist/app.js +0 -267
- package/dist/auth.js +0 -39
- package/dist/client.js +0 -13
- package/dist/db/index.js +0 -10
- package/dist/db/schema.js +0 -224
- package/dist/i18n/Trans.js +0 -24
- package/dist/i18n/context.js +0 -58
- package/dist/i18n/detect.js +0 -26
- package/dist/i18n/i18n.js +0 -49
- package/dist/i18n/index.js +0 -44
- package/dist/i18n/locales/en.js +0 -1
- package/dist/i18n/locales/zh-Hans.js +0 -1
- package/dist/i18n/locales/zh-Hant.js +0 -1
- package/dist/i18n/locales.js +0 -13
- package/dist/i18n/middleware.js +0 -30
- package/dist/lib/avatar-upload.js +0 -134
- package/dist/lib/config.js +0 -143
- package/dist/lib/constants.js +0 -50
- package/dist/lib/excerpt.js +0 -76
- package/dist/lib/favicon.js +0 -102
- package/dist/lib/feed.js +0 -123
- package/dist/lib/image-processor.js +0 -187
- package/dist/lib/image.js +0 -97
- package/dist/lib/index.js +0 -7
- package/dist/lib/markdown.js +0 -83
- package/dist/lib/media-helpers.js +0 -49
- package/dist/lib/media-upload.js +0 -104
- package/dist/lib/nav-reorder.js +0 -27
- package/dist/lib/navigation.js +0 -79
- package/dist/lib/pagination.js +0 -44
- package/dist/lib/render.js +0 -53
- package/dist/lib/schemas.js +0 -174
- package/dist/lib/sqid.js +0 -72
- package/dist/lib/sse.js +0 -218
- package/dist/lib/storage.js +0 -164
- package/dist/lib/theme.js +0 -65
- package/dist/lib/time.js +0 -159
- package/dist/lib/timeline.js +0 -95
- package/dist/lib/timezones.js +0 -388
- package/dist/lib/url.js +0 -89
- package/dist/lib/view.js +0 -217
- package/dist/middleware/auth.js +0 -52
- package/dist/middleware/onboarding.js +0 -41
- package/dist/routes/api/collections.js +0 -124
- package/dist/routes/api/nav-items.js +0 -104
- package/dist/routes/api/pages.js +0 -91
- package/dist/routes/api/posts.js +0 -218
- package/dist/routes/api/search.js +0 -48
- package/dist/routes/api/settings.js +0 -68
- package/dist/routes/api/upload.js +0 -246
- package/dist/routes/auth/reset.js +0 -221
- package/dist/routes/auth/setup.js +0 -194
- package/dist/routes/auth/signin.js +0 -176
- package/dist/routes/compose.js +0 -48
- package/dist/routes/dash/collections.js +0 -115
- package/dist/routes/dash/index.js +0 -118
- package/dist/routes/dash/media.js +0 -106
- package/dist/routes/dash/pages.js +0 -294
- package/dist/routes/dash/posts.js +0 -244
- package/dist/routes/dash/redirects.js +0 -257
- package/dist/routes/dash/settings.js +0 -379
- package/dist/routes/feed/rss.js +0 -62
- package/dist/routes/feed/sitemap.js +0 -49
- package/dist/routes/pages/archive.js +0 -62
- package/dist/routes/pages/collection.js +0 -34
- package/dist/routes/pages/collections.js +0 -28
- package/dist/routes/pages/featured.js +0 -36
- package/dist/routes/pages/home.js +0 -64
- package/dist/routes/pages/latest.js +0 -45
- package/dist/routes/pages/page.js +0 -68
- package/dist/routes/pages/post.js +0 -44
- package/dist/routes/pages/search.js +0 -54
- package/dist/services/collection.js +0 -109
- package/dist/services/index.js +0 -24
- package/dist/services/media.js +0 -117
- package/dist/services/navigation.js +0 -91
- package/dist/services/page.js +0 -84
- package/dist/services/post.js +0 -229
- package/dist/services/redirect.js +0 -48
- package/dist/services/search.js +0 -67
- package/dist/services/settings.js +0 -68
- package/dist/types/bindings.js +0 -3
- package/dist/types/config.js +0 -147
- package/dist/types/constants.js +0 -27
- package/dist/types/entities.js +0 -3
- package/dist/types/lingui-react-macro.d.js +0 -9
- package/dist/types/operations.js +0 -3
- package/dist/types/props.js +0 -3
- package/dist/types/sortablejs.d.js +0 -5
- package/dist/types/views.js +0 -5
- package/dist/types.js +0 -11
- package/dist/ui/color-themes.js +0 -268
- package/dist/ui/compose/ComposeDialog.js +0 -467
- package/dist/ui/compose/ComposePrompt.js +0 -55
- package/dist/ui/dash/ActionButtons.js +0 -46
- package/dist/ui/dash/CrudPageHeader.js +0 -22
- package/dist/ui/dash/DangerZone.js +0 -36
- package/dist/ui/dash/FormatBadge.js +0 -27
- package/dist/ui/dash/ListItemRow.js +0 -21
- package/dist/ui/dash/PageForm.js +0 -195
- package/dist/ui/dash/PostForm.js +0 -395
- package/dist/ui/dash/PostList.js +0 -83
- package/dist/ui/dash/StatusBadge.js +0 -46
- package/dist/ui/dash/collections/CollectionForm.js +0 -152
- package/dist/ui/dash/collections/CollectionsListContent.js +0 -68
- package/dist/ui/dash/collections/ViewCollectionContent.js +0 -96
- package/dist/ui/dash/index.js +0 -10
- package/dist/ui/dash/media/MediaListContent.js +0 -166
- package/dist/ui/dash/media/ViewMediaContent.js +0 -212
- package/dist/ui/dash/pages/LinkFormContent.js +0 -130
- package/dist/ui/dash/pages/UnifiedPagesContent.js +0 -193
- package/dist/ui/dash/settings/AccountContent.js +0 -209
- package/dist/ui/dash/settings/AppearanceContent.js +0 -259
- package/dist/ui/dash/settings/GeneralContent.js +0 -536
- package/dist/ui/dash/settings/SettingsNav.js +0 -41
- package/dist/ui/feed/LinkCard.js +0 -72
- package/dist/ui/feed/NoteCard.js +0 -58
- package/dist/ui/feed/QuoteCard.js +0 -63
- package/dist/ui/feed/ThreadPreview.js +0 -48
- package/dist/ui/feed/TimelineFeed.js +0 -41
- package/dist/ui/feed/TimelineItem.js +0 -27
- package/dist/ui/font-themes.js +0 -36
- package/dist/ui/layouts/BaseLayout.js +0 -153
- package/dist/ui/layouts/DashLayout.js +0 -141
- package/dist/ui/layouts/SiteLayout.js +0 -169
- package/dist/ui/pages/ArchivePage.js +0 -143
- package/dist/ui/pages/CollectionPage.js +0 -70
- package/dist/ui/pages/CollectionsPage.js +0 -76
- package/dist/ui/pages/FeaturedPage.js +0 -24
- package/dist/ui/pages/HomePage.js +0 -24
- package/dist/ui/pages/PostPage.js +0 -55
- package/dist/ui/pages/SearchPage.js +0 -122
- package/dist/ui/pages/SinglePage.js +0 -23
- package/dist/ui/shared/EmptyState.js +0 -27
- package/dist/ui/shared/MediaGallery.js +0 -35
- package/dist/ui/shared/Pagination.js +0 -195
- package/dist/ui/shared/ThreadView.js +0 -108
- package/dist/ui/shared/index.js +0 -5
- package/dist/vendor/datastar.js +0 -1606
- package/src/lib/__tests__/config.test.ts +0 -192
- package/src/lib/config.ts +0 -167
- package/src/routes/compose.ts +0 -63
- package/src/ui/dash/PostForm.tsx +0 -360
- package/src/ui/dash/settings/AppearanceContent.tsx +0 -254
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import { createTestDatabase } from "../../__tests__/helpers/db.js";
|
|
3
|
-
import { createSettingsService } from "../../services/settings.js";
|
|
4
|
-
import type { Database } from "../../db/index.js";
|
|
5
|
-
import {
|
|
6
|
-
getConfig,
|
|
7
|
-
getHomeDefaultView,
|
|
8
|
-
getConfigFallback,
|
|
9
|
-
getTimeZone,
|
|
10
|
-
getSiteFooter,
|
|
11
|
-
isNoIndex,
|
|
12
|
-
} from "../config.js";
|
|
13
|
-
import type { Context } from "hono";
|
|
14
|
-
|
|
15
|
-
function createMockContext(
|
|
16
|
-
services: { settings: ReturnType<typeof createSettingsService> },
|
|
17
|
-
env: Record<string, string> = {},
|
|
18
|
-
): Context {
|
|
19
|
-
return {
|
|
20
|
-
env,
|
|
21
|
-
var: { services },
|
|
22
|
-
} as unknown as Context;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("getConfig", () => {
|
|
26
|
-
let db: Database;
|
|
27
|
-
let settingsService: ReturnType<typeof createSettingsService>;
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
const testDb = createTestDatabase();
|
|
31
|
-
db = testDb.db as unknown as Database;
|
|
32
|
-
settingsService = createSettingsService(db);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("returns default value when no DB or ENV value exists", async () => {
|
|
36
|
-
const c = createMockContext({ settings: settingsService });
|
|
37
|
-
const result = await getConfig(c, "HOME_DEFAULT_VIEW");
|
|
38
|
-
expect(result).toBe("latest");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("returns DB value when set", async () => {
|
|
42
|
-
await settingsService.set("HOME_DEFAULT_VIEW", "featured");
|
|
43
|
-
const c = createMockContext({ settings: settingsService });
|
|
44
|
-
const result = await getConfig(c, "HOME_DEFAULT_VIEW");
|
|
45
|
-
expect(result).toBe("featured");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("returns env value when DB is empty", async () => {
|
|
49
|
-
const c = createMockContext(
|
|
50
|
-
{ settings: settingsService },
|
|
51
|
-
{
|
|
52
|
-
HOME_DEFAULT_VIEW: "featured",
|
|
53
|
-
},
|
|
54
|
-
);
|
|
55
|
-
const result = await getConfig(c, "HOME_DEFAULT_VIEW");
|
|
56
|
-
expect(result).toBe("featured");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("DB value takes precedence over env value", async () => {
|
|
60
|
-
await settingsService.set("HOME_DEFAULT_VIEW", "featured");
|
|
61
|
-
const c = createMockContext(
|
|
62
|
-
{ settings: settingsService },
|
|
63
|
-
{
|
|
64
|
-
HOME_DEFAULT_VIEW: "latest",
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
const result = await getConfig(c, "HOME_DEFAULT_VIEW");
|
|
68
|
-
expect(result).toBe("featured");
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe("getHomeDefaultView", () => {
|
|
73
|
-
let db: Database;
|
|
74
|
-
let settingsService: ReturnType<typeof createSettingsService>;
|
|
75
|
-
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
const testDb = createTestDatabase();
|
|
78
|
-
db = testDb.db as unknown as Database;
|
|
79
|
-
settingsService = createSettingsService(db);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("returns 'latest' by default", async () => {
|
|
83
|
-
const c = createMockContext({ settings: settingsService });
|
|
84
|
-
const result = await getHomeDefaultView(c);
|
|
85
|
-
expect(result).toBe("latest");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("returns 'featured' when set in DB", async () => {
|
|
89
|
-
await settingsService.set("HOME_DEFAULT_VIEW", "featured");
|
|
90
|
-
const c = createMockContext({ settings: settingsService });
|
|
91
|
-
const result = await getHomeDefaultView(c);
|
|
92
|
-
expect(result).toBe("featured");
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe("getTimeZone", () => {
|
|
97
|
-
let db: Database;
|
|
98
|
-
let settingsService: ReturnType<typeof createSettingsService>;
|
|
99
|
-
|
|
100
|
-
beforeEach(() => {
|
|
101
|
-
const testDb = createTestDatabase();
|
|
102
|
-
db = testDb.db as unknown as Database;
|
|
103
|
-
settingsService = createSettingsService(db);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("returns 'UTC' by default", async () => {
|
|
107
|
-
const c = createMockContext({ settings: settingsService });
|
|
108
|
-
const result = await getTimeZone(c);
|
|
109
|
-
expect(result).toBe("UTC");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("returns DB value when set", async () => {
|
|
113
|
-
await settingsService.set("TIME_ZONE", "Beijing");
|
|
114
|
-
const c = createMockContext({ settings: settingsService });
|
|
115
|
-
const result = await getTimeZone(c);
|
|
116
|
-
expect(result).toBe("Beijing");
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe("getSiteFooter", () => {
|
|
121
|
-
let db: Database;
|
|
122
|
-
let settingsService: ReturnType<typeof createSettingsService>;
|
|
123
|
-
|
|
124
|
-
beforeEach(() => {
|
|
125
|
-
const testDb = createTestDatabase();
|
|
126
|
-
db = testDb.db as unknown as Database;
|
|
127
|
-
settingsService = createSettingsService(db);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("returns empty string by default", async () => {
|
|
131
|
-
const c = createMockContext({ settings: settingsService });
|
|
132
|
-
const result = await getSiteFooter(c);
|
|
133
|
-
expect(result).toBe("");
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("returns DB value when set", async () => {
|
|
137
|
-
await settingsService.set("SITE_FOOTER", "**Footer text**");
|
|
138
|
-
const c = createMockContext({ settings: settingsService });
|
|
139
|
-
const result = await getSiteFooter(c);
|
|
140
|
-
expect(result).toBe("**Footer text**");
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("isNoIndex", () => {
|
|
145
|
-
let db: Database;
|
|
146
|
-
let settingsService: ReturnType<typeof createSettingsService>;
|
|
147
|
-
|
|
148
|
-
beforeEach(() => {
|
|
149
|
-
const testDb = createTestDatabase();
|
|
150
|
-
db = testDb.db as unknown as Database;
|
|
151
|
-
settingsService = createSettingsService(db);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("returns false by default", async () => {
|
|
155
|
-
const c = createMockContext({ settings: settingsService });
|
|
156
|
-
const result = await isNoIndex(c);
|
|
157
|
-
expect(result).toBe(false);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("returns true when NOINDEX is set to 'true'", async () => {
|
|
161
|
-
await settingsService.set("NOINDEX", "true");
|
|
162
|
-
const c = createMockContext({ settings: settingsService });
|
|
163
|
-
const result = await isNoIndex(c);
|
|
164
|
-
expect(result).toBe(true);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("returns false when NOINDEX is set to other value", async () => {
|
|
168
|
-
await settingsService.set("NOINDEX", "false");
|
|
169
|
-
const c = createMockContext({ settings: settingsService });
|
|
170
|
-
const result = await isNoIndex(c);
|
|
171
|
-
expect(result).toBe(false);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe("getConfigFallback", () => {
|
|
176
|
-
it("returns default when no env value", () => {
|
|
177
|
-
const c = createMockContext({
|
|
178
|
-
settings: {} as ReturnType<typeof createSettingsService>,
|
|
179
|
-
});
|
|
180
|
-
const result = getConfigFallback(c, "HOME_DEFAULT_VIEW");
|
|
181
|
-
expect(result).toBe("latest");
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("returns env value when set", () => {
|
|
185
|
-
const c = createMockContext(
|
|
186
|
-
{ settings: {} as ReturnType<typeof createSettingsService> },
|
|
187
|
-
{ HOME_DEFAULT_VIEW: "featured" },
|
|
188
|
-
);
|
|
189
|
-
const result = getConfigFallback(c, "HOME_DEFAULT_VIEW");
|
|
190
|
-
expect(result).toBe("featured");
|
|
191
|
-
});
|
|
192
|
-
});
|
package/src/lib/config.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified Configuration System
|
|
3
|
-
*
|
|
4
|
-
* Provides a flexible configuration system with two priority modes:
|
|
5
|
-
* - User-configurable (envOnly: false): Database > Environment > Default
|
|
6
|
-
* - Environment-only (envOnly: true): Environment > Default
|
|
7
|
-
*
|
|
8
|
-
* All configuration fields are defined in CONFIG_FIELDS (types.ts).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { Context } from "hono";
|
|
12
|
-
import { CONFIG_FIELDS, type ConfigKey } from "../types.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get the fallback value for a config key (ENV > Default), skipping the database.
|
|
16
|
-
* Used for placeholder values in forms where the DB value is shown separately.
|
|
17
|
-
*
|
|
18
|
-
* @param c - Hono context
|
|
19
|
-
* @param key - Configuration key from CONFIG_FIELDS
|
|
20
|
-
* @returns Fallback value from environment or default
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```typescript
|
|
24
|
-
* const placeholder = getConfigFallback(c, "SITE_NAME");
|
|
25
|
-
* // Returns: c.env.SITE_NAME ?? "Jant"
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function getConfigFallback(c: Context, key: ConfigKey): string {
|
|
29
|
-
const field = CONFIG_FIELDS[key];
|
|
30
|
-
const envValue = c.env[key as keyof typeof c.env];
|
|
31
|
-
if (envValue && typeof envValue === "string") return envValue;
|
|
32
|
-
return field.defaultValue;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Generic configuration getter that respects priority settings
|
|
37
|
-
*
|
|
38
|
-
* @param c - Hono context
|
|
39
|
-
* @param key - Configuration key from CONFIG_FIELDS
|
|
40
|
-
* @returns Configuration value following the defined priority
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```typescript
|
|
44
|
-
* // For user-configurable configs (SITE_NAME):
|
|
45
|
-
* // Returns: (DB: SITE_NAME) ?? c.env.SITE_NAME ?? "Jant"
|
|
46
|
-
*
|
|
47
|
-
* // For environment-only configs (SITE_URL):
|
|
48
|
-
* // Returns: c.env.SITE_URL ?? ""
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
export async function getConfig(c: Context, key: ConfigKey): Promise<string> {
|
|
52
|
-
const field = CONFIG_FIELDS[key];
|
|
53
|
-
|
|
54
|
-
if (!field.envOnly) {
|
|
55
|
-
// User-configurable: DB > ENV > Default
|
|
56
|
-
// 1. Check database setting first
|
|
57
|
-
const dbValue = await c.var.services.settings.get(key);
|
|
58
|
-
if (dbValue) {
|
|
59
|
-
return dbValue;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ENV > Default
|
|
64
|
-
// 2. Check environment variable
|
|
65
|
-
const envValue = c.env[key as keyof typeof c.env];
|
|
66
|
-
if (envValue && typeof envValue === "string") {
|
|
67
|
-
return envValue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 3. Default value
|
|
71
|
-
return field.defaultValue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Get site name with fallback chain: DB > ENV > Default
|
|
76
|
-
*
|
|
77
|
-
* @param c - Hono context
|
|
78
|
-
* @returns Site name
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```typescript
|
|
82
|
-
* const siteName = await getSiteName(c);
|
|
83
|
-
* // Returns: (DB: SITE_NAME) ?? c.env.SITE_NAME ?? "Jant"
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
|
-
export async function getSiteName(c: Context): Promise<string> {
|
|
87
|
-
return getConfig(c, "SITE_NAME");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Get site description with fallback chain: DB > ENV > Default
|
|
92
|
-
*
|
|
93
|
-
* @param c - Hono context
|
|
94
|
-
* @returns Site description
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```typescript
|
|
98
|
-
* const description = await getSiteDescription(c);
|
|
99
|
-
* // Returns: (DB: SITE_DESCRIPTION) ?? c.env.SITE_DESCRIPTION ?? "A microblog powered by Jant"
|
|
100
|
-
* ```
|
|
101
|
-
*/
|
|
102
|
-
export async function getSiteDescription(c: Context): Promise<string> {
|
|
103
|
-
return getConfig(c, "SITE_DESCRIPTION");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get site language with fallback chain: DB > ENV > Default
|
|
108
|
-
*
|
|
109
|
-
* @param c - Hono context
|
|
110
|
-
* @returns Site language code
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```typescript
|
|
114
|
-
* const lang = await getSiteLanguage(c);
|
|
115
|
-
* // Returns: (DB: SITE_LANGUAGE) ?? c.env.SITE_LANGUAGE ?? "en"
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
export async function getSiteLanguage(c: Context): Promise<string> {
|
|
119
|
-
return getConfig(c, "SITE_LANGUAGE");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get home default view with fallback chain: DB > ENV > Default
|
|
124
|
-
*
|
|
125
|
-
* @param c - Hono context
|
|
126
|
-
* @returns Home default view ("latest" or "featured")
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```typescript
|
|
130
|
-
* const view = await getHomeDefaultView(c);
|
|
131
|
-
* // Returns: (DB: HOME_DEFAULT_VIEW) ?? "latest"
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
export async function getHomeDefaultView(c: Context): Promise<string> {
|
|
135
|
-
return getConfig(c, "HOME_DEFAULT_VIEW");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Get timezone with fallback chain: DB > ENV > Default
|
|
140
|
-
*
|
|
141
|
-
* @param c - Hono context
|
|
142
|
-
* @returns Timezone string (e.g. "Beijing", "UTC")
|
|
143
|
-
*/
|
|
144
|
-
export async function getTimeZone(c: Context): Promise<string> {
|
|
145
|
-
return getConfig(c, "TIME_ZONE");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Get site footer markdown with fallback chain: DB > ENV > Default
|
|
150
|
-
*
|
|
151
|
-
* @param c - Hono context
|
|
152
|
-
* @returns Footer markdown string (empty string if not set)
|
|
153
|
-
*/
|
|
154
|
-
export async function getSiteFooter(c: Context): Promise<string> {
|
|
155
|
-
return getConfig(c, "SITE_FOOTER");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Check if search engine indexing is disabled
|
|
160
|
-
*
|
|
161
|
-
* @param c - Hono context
|
|
162
|
-
* @returns true if NOINDEX is set to "true"
|
|
163
|
-
*/
|
|
164
|
-
export async function isNoIndex(c: Context): Promise<boolean> {
|
|
165
|
-
const value = await getConfig(c, "NOINDEX");
|
|
166
|
-
return value === "true";
|
|
167
|
-
}
|
package/src/routes/compose.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compose Route
|
|
3
|
-
*
|
|
4
|
-
* Handles post creation from the public-site compose dialog.
|
|
5
|
-
* Returns dsRedirect to the new post's permalink (Datastar form pattern).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Hono } from "hono";
|
|
9
|
-
import type { Bindings } from "../types.js";
|
|
10
|
-
import type { AppVariables } from "../app.js";
|
|
11
|
-
import { requireAuth } from "../middleware/auth.js";
|
|
12
|
-
import { CreatePostSchema, validateMediaCount } from "../lib/schemas.js";
|
|
13
|
-
import * as sqid from "../lib/sqid.js";
|
|
14
|
-
import { dsRedirect, dsToast } from "../lib/sse.js";
|
|
15
|
-
|
|
16
|
-
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
17
|
-
|
|
18
|
-
export const composeRoutes = new Hono<Env>();
|
|
19
|
-
|
|
20
|
-
// All compose routes require authentication
|
|
21
|
-
composeRoutes.use("*", requireAuth());
|
|
22
|
-
|
|
23
|
-
composeRoutes.post("/", async (c) => {
|
|
24
|
-
const raw = await c.req.json();
|
|
25
|
-
|
|
26
|
-
const result = CreatePostSchema.safeParse(raw);
|
|
27
|
-
if (!result.success) {
|
|
28
|
-
const firstError = result.error.issues[0]?.message ?? "Invalid input";
|
|
29
|
-
return dsToast(firstError, "error");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const data = result.data;
|
|
33
|
-
|
|
34
|
-
// Validate media count
|
|
35
|
-
if (data.mediaIds) {
|
|
36
|
-
const mediaError = validateMediaCount(data.mediaIds);
|
|
37
|
-
if (mediaError) {
|
|
38
|
-
return dsToast(mediaError, "error");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const post = await c.var.services.posts.create({
|
|
43
|
-
format: data.format,
|
|
44
|
-
title: data.title || undefined,
|
|
45
|
-
body: data.body || undefined,
|
|
46
|
-
status: data.status ?? "published",
|
|
47
|
-
featured: data.featured,
|
|
48
|
-
pinned: data.pinned,
|
|
49
|
-
url: data.url || undefined,
|
|
50
|
-
quoteText: data.quoteText || undefined,
|
|
51
|
-
rating: data.rating || undefined,
|
|
52
|
-
collectionId: data.collectionId || undefined,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Attach media if provided
|
|
56
|
-
if (data.mediaIds && data.mediaIds.length > 0) {
|
|
57
|
-
await c.var.services.media.attachToPost(post.id, data.mediaIds);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Redirect to the new post's permalink
|
|
61
|
-
const permalink = post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`;
|
|
62
|
-
return dsRedirect(permalink);
|
|
63
|
-
});
|