@jant/core 0.3.23 → 0.3.25
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.js +50 -26
- package/dist/db/schema.js +72 -47
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.js +5 -11
- package/dist/lib/constants.js +2 -4
- package/dist/lib/excerpt.js +76 -0
- package/dist/lib/feed.js +18 -7
- package/dist/lib/nav-reorder.js +1 -1
- package/dist/lib/navigation.js +30 -6
- package/dist/lib/pagination.js +44 -0
- package/dist/lib/render.js +7 -11
- package/dist/lib/schemas.js +80 -38
- package/dist/lib/theme.js +4 -4
- package/dist/lib/time.js +56 -1
- package/dist/lib/timeline.js +95 -0
- package/dist/lib/view.js +61 -72
- package/dist/routes/api/collections.js +124 -0
- package/dist/routes/api/nav-items.js +104 -0
- package/dist/routes/api/pages.js +91 -0
- package/dist/routes/api/posts.js +27 -33
- package/dist/routes/api/search.js +4 -5
- package/dist/routes/api/settings.js +68 -0
- package/dist/routes/api/upload.js +13 -13
- package/dist/routes/compose.js +48 -0
- package/dist/routes/dash/collections.js +24 -42
- package/dist/routes/dash/index.js +3 -3
- package/dist/routes/dash/media.js +2 -2
- package/dist/routes/dash/pages.js +440 -106
- package/dist/routes/dash/posts.js +27 -37
- package/dist/routes/dash/redirects.js +2 -2
- package/dist/routes/dash/settings.js +79 -5
- package/dist/routes/feed/rss.js +4 -6
- package/dist/routes/feed/sitemap.js +11 -8
- package/dist/routes/pages/archive.js +13 -15
- package/dist/routes/pages/collection.js +12 -9
- package/dist/routes/pages/collections.js +28 -0
- package/dist/routes/pages/featured.js +32 -0
- package/dist/routes/pages/home.js +19 -68
- package/dist/routes/pages/page.js +57 -29
- package/dist/routes/pages/post.js +7 -17
- package/dist/routes/pages/search.js +5 -9
- package/dist/services/collection.js +52 -64
- package/dist/services/index.js +5 -3
- package/dist/services/navigation.js +29 -53
- package/dist/services/page.js +84 -0
- package/dist/services/post.js +102 -69
- package/dist/services/search.js +24 -18
- package/dist/types.js +24 -40
- package/dist/ui/compose/ComposeDialog.js +452 -0
- package/dist/ui/compose/ComposePrompt.js +55 -0
- package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +3 -15
- package/dist/{theme/components → ui/dash}/PageForm.js +15 -15
- package/dist/{theme/components → ui/dash}/PostForm.js +117 -137
- package/dist/{theme/components → ui/dash}/PostList.js +18 -13
- package/dist/ui/dash/StatusBadge.js +46 -0
- package/dist/{theme/components → ui/dash}/index.js +3 -6
- package/dist/ui/feed/LinkCard.js +72 -0
- package/dist/ui/feed/NoteCard.js +58 -0
- package/dist/{themes/minimal/timeline → ui/feed}/QuoteCard.js +29 -14
- package/dist/{themes/minimal/timeline → ui/feed}/ThreadPreview.js +20 -18
- package/dist/ui/feed/TimelineFeed.js +41 -0
- package/dist/ui/feed/TimelineItem.js +27 -0
- package/dist/{theme → ui}/layouts/BaseLayout.js +10 -0
- package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
- package/dist/ui/layouts/SiteLayout.js +141 -0
- package/dist/{themes/minimal → ui}/pages/ArchivePage.js +37 -50
- package/dist/ui/pages/CollectionPage.js +70 -0
- package/dist/ui/pages/CollectionsPage.js +76 -0
- package/dist/ui/pages/FeaturedPage.js +24 -0
- package/dist/ui/pages/HomePage.js +24 -0
- package/dist/{themes/minimal → ui}/pages/PostPage.js +20 -12
- package/dist/{themes/minimal → ui}/pages/SearchPage.js +19 -18
- package/dist/{themes/minimal → ui}/pages/SinglePage.js +5 -4
- package/dist/ui/shared/MediaGallery.js +35 -0
- package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
- package/dist/{theme/components → ui/shared}/ThreadView.js +3 -3
- package/dist/ui/shared/index.js +5 -0
- package/package.json +2 -9
- package/src/__tests__/helpers/app.ts +4 -0
- package/src/__tests__/helpers/db.ts +53 -73
- package/src/app.tsx +56 -28
- package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
- package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
- package/src/db/migrations/meta/_journal.json +14 -0
- package/src/db/schema.ts +63 -46
- package/src/i18n/locales/en.po +443 -240
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +443 -240
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +443 -240
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +29 -42
- package/src/lib/__tests__/excerpt.test.ts +125 -0
- package/src/lib/__tests__/schemas.test.ts +201 -99
- package/src/lib/__tests__/time.test.ts +62 -0
- package/src/{routes/api → lib}/__tests__/timeline.test.ts +81 -75
- package/src/lib/__tests__/view.test.ts +204 -50
- package/src/lib/constants.ts +2 -4
- package/src/lib/excerpt.ts +87 -0
- package/src/lib/feed.ts +22 -7
- package/src/lib/nav-reorder.ts +1 -1
- package/src/lib/navigation.ts +45 -8
- package/src/lib/pagination.ts +50 -0
- package/src/lib/render.tsx +7 -14
- package/src/lib/schemas.ts +119 -51
- package/src/lib/theme.ts +5 -5
- package/src/lib/time.ts +64 -0
- package/src/lib/timeline.ts +141 -0
- package/src/lib/view.ts +80 -82
- package/src/preset.css +46 -0
- package/src/routes/__tests__/compose.test.ts +199 -0
- package/src/routes/api/__tests__/collections.test.ts +249 -0
- package/src/routes/api/__tests__/nav-items.test.ts +222 -0
- package/src/routes/api/__tests__/pages.test.ts +218 -0
- package/src/routes/api/__tests__/posts.test.ts +50 -108
- package/src/routes/api/__tests__/search.test.ts +2 -3
- package/src/routes/api/__tests__/settings.test.ts +132 -0
- package/src/routes/api/collections.ts +143 -0
- package/src/routes/api/nav-items.ts +115 -0
- package/src/routes/api/pages.ts +101 -0
- package/src/routes/api/posts.ts +28 -28
- package/src/routes/api/search.ts +3 -3
- package/src/routes/api/settings.ts +91 -0
- package/src/routes/api/upload.ts +16 -6
- package/src/routes/compose.ts +63 -0
- package/src/routes/dash/__tests__/pages.test.ts +225 -0
- package/src/routes/dash/collections.tsx +20 -42
- package/src/routes/dash/index.tsx +3 -3
- package/src/routes/dash/media.tsx +2 -2
- package/src/routes/dash/pages.tsx +480 -122
- package/src/routes/dash/posts.tsx +42 -54
- package/src/routes/dash/redirects.tsx +2 -2
- package/src/routes/dash/settings.tsx +83 -5
- package/src/routes/feed/rss.ts +4 -3
- package/src/routes/feed/sitemap.ts +15 -5
- package/src/routes/pages/__tests__/collections.test.ts +94 -0
- package/src/routes/pages/__tests__/featured.test.ts +94 -0
- package/src/routes/pages/archive.tsx +15 -15
- package/src/routes/pages/collection.tsx +16 -9
- package/src/routes/pages/collections.tsx +36 -0
- package/src/routes/pages/featured.tsx +38 -0
- package/src/routes/pages/home.tsx +21 -92
- package/src/routes/pages/page.tsx +62 -27
- package/src/routes/pages/post.tsx +6 -18
- package/src/routes/pages/search.tsx +3 -7
- package/src/services/__tests__/collection.test.ts +257 -158
- package/src/services/__tests__/media.test.ts +18 -18
- package/src/services/__tests__/navigation.test.ts +161 -87
- package/src/services/__tests__/page.test.ts +106 -0
- package/src/services/__tests__/post-timeline.test.ts +92 -88
- package/src/services/__tests__/post.test.ts +432 -197
- package/src/services/__tests__/search.test.ts +19 -25
- package/src/services/collection.ts +71 -113
- package/src/services/index.ts +9 -8
- package/src/services/navigation.ts +38 -71
- package/src/services/page.ts +136 -0
- package/src/services/post.ts +141 -101
- package/src/services/search.ts +38 -27
- package/src/styles/tokens.css +47 -0
- package/src/styles/ui.css +491 -0
- package/src/types.ts +212 -198
- package/src/ui/compose/ComposeDialog.tsx +395 -0
- package/src/ui/compose/ComposePrompt.tsx +55 -0
- package/src/ui/dash/FormatBadge.tsx +28 -0
- package/src/{theme/components → ui/dash}/PageForm.tsx +21 -21
- package/src/{theme/components → ui/dash}/PostForm.tsx +110 -131
- package/src/ui/dash/PostList.tsx +101 -0
- package/src/ui/dash/StatusBadge.tsx +61 -0
- package/src/ui/dash/index.ts +10 -0
- package/src/ui/feed/LinkCard.tsx +72 -0
- package/src/ui/feed/NoteCard.tsx +63 -0
- package/src/ui/feed/QuoteCard.tsx +68 -0
- package/src/ui/feed/ThreadPreview.tsx +48 -0
- package/src/ui/feed/TimelineFeed.tsx +49 -0
- package/src/ui/feed/TimelineItem.tsx +45 -0
- package/src/{theme → ui}/layouts/BaseLayout.tsx +11 -1
- package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
- package/src/ui/layouts/SiteLayout.tsx +150 -0
- package/src/ui/pages/ArchivePage.tsx +162 -0
- package/src/ui/pages/CollectionPage.tsx +70 -0
- package/src/ui/pages/CollectionsPage.tsx +73 -0
- package/src/ui/pages/FeaturedPage.tsx +31 -0
- package/src/ui/pages/HomePage.tsx +37 -0
- package/src/ui/pages/PostPage.tsx +56 -0
- package/src/{themes/minimal → ui}/pages/SearchPage.tsx +24 -20
- package/src/{themes/minimal → ui}/pages/SinglePage.tsx +5 -5
- package/src/ui/shared/MediaGallery.tsx +59 -0
- package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
- package/src/{theme/components → ui/shared}/ThreadView.tsx +6 -3
- package/src/ui/shared/__tests__/pagination.test.ts +46 -0
- package/src/ui/shared/index.ts +12 -0
- package/bin/jant.js +0 -185
- package/dist/lib/theme-components.js +0 -49
- package/dist/routes/api/timeline.js +0 -120
- package/dist/routes/dash/navigation.js +0 -288
- package/dist/theme/components/MediaGallery.js +0 -107
- package/dist/theme/components/VisibilityBadge.js +0 -37
- package/dist/theme/index.js +0 -18
- package/dist/theme/layouts/index.js +0 -2
- package/dist/themes/minimal/MinimalSiteLayout.js +0 -83
- package/dist/themes/minimal/index.js +0 -65
- package/dist/themes/minimal/pages/CollectionPage.js +0 -65
- package/dist/themes/minimal/pages/HomePage.js +0 -25
- package/dist/themes/minimal/timeline/ArticleCard.js +0 -36
- package/dist/themes/minimal/timeline/ImageCard.js +0 -67
- package/dist/themes/minimal/timeline/LinkCard.js +0 -47
- package/dist/themes/minimal/timeline/NoteCard.js +0 -34
- package/dist/themes/minimal/timeline/TimelineFeed.js +0 -48
- package/dist/themes/minimal/timeline/TimelineItem.js +0 -44
- package/src/lib/__tests__/theme-components.test.ts +0 -126
- package/src/lib/theme-components.ts +0 -68
- package/src/routes/api/timeline.tsx +0 -159
- package/src/routes/dash/navigation.tsx +0 -316
- package/src/theme/components/MediaGallery.tsx +0 -128
- package/src/theme/components/PostList.tsx +0 -92
- package/src/theme/components/TypeBadge.tsx +0 -37
- package/src/theme/components/VisibilityBadge.tsx +0 -45
- package/src/theme/components/index.ts +0 -23
- package/src/theme/index.ts +0 -22
- package/src/theme/layouts/index.ts +0 -7
- package/src/themes/minimal/MinimalSiteLayout.tsx +0 -100
- package/src/themes/minimal/index.ts +0 -83
- package/src/themes/minimal/pages/ArchivePage.tsx +0 -157
- package/src/themes/minimal/pages/CollectionPage.tsx +0 -60
- package/src/themes/minimal/pages/HomePage.tsx +0 -41
- package/src/themes/minimal/pages/PostPage.tsx +0 -43
- package/src/themes/minimal/timeline/ArticleCard.tsx +0 -37
- package/src/themes/minimal/timeline/ImageCard.tsx +0 -63
- package/src/themes/minimal/timeline/LinkCard.tsx +0 -48
- package/src/themes/minimal/timeline/NoteCard.tsx +0 -35
- package/src/themes/minimal/timeline/QuoteCard.tsx +0 -49
- package/src/themes/minimal/timeline/ThreadPreview.tsx +0 -47
- package/src/themes/minimal/timeline/TimelineFeed.tsx +0 -57
- package/src/themes/minimal/timeline/TimelineItem.tsx +0 -75
- /package/dist/{theme → ui}/color-themes.js +0 -0
- /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
- /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
- /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
- /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
- /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
- /package/src/{theme → ui}/color-themes.ts +0 -0
- /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
- /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
- /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
- /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
- /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
package/src/app.tsx
CHANGED
|
@@ -11,7 +11,6 @@ import { i18nMiddleware } from "./i18n/index.js";
|
|
|
11
11
|
import { useLingui } from "@lingui/react/macro";
|
|
12
12
|
import type { Bindings, JantConfig } from "./types.js";
|
|
13
13
|
import { SETTINGS_KEYS } from "./lib/constants.js";
|
|
14
|
-
import { theme as minimalTheme } from "./themes/minimal/index.js";
|
|
15
14
|
import { hashPassword } from "better-auth/crypto";
|
|
16
15
|
|
|
17
16
|
// Routes - Pages
|
|
@@ -21,6 +20,8 @@ import { pageRoutes } from "./routes/pages/page.js";
|
|
|
21
20
|
import { collectionRoutes } from "./routes/pages/collection.js";
|
|
22
21
|
import { archiveRoutes } from "./routes/pages/archive.js";
|
|
23
22
|
import { searchRoutes } from "./routes/pages/search.js";
|
|
23
|
+
import { featuredRoutes } from "./routes/pages/featured.js";
|
|
24
|
+
import { collectionsPageRoutes } from "./routes/pages/collections.js";
|
|
24
25
|
|
|
25
26
|
// Routes - Dashboard
|
|
26
27
|
import { dashIndexRoutes } from "./routes/dash/index.js";
|
|
@@ -30,13 +31,17 @@ import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
|
|
|
30
31
|
import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
|
|
31
32
|
import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
|
|
32
33
|
import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
|
|
33
|
-
import { navigationRoutes as dashNavigationRoutes } from "./routes/dash/navigation.js";
|
|
34
34
|
|
|
35
35
|
// Routes - API
|
|
36
36
|
import { postsApiRoutes } from "./routes/api/posts.js";
|
|
37
|
+
import { pagesApiRoutes } from "./routes/api/pages.js";
|
|
38
|
+
import { navItemsApiRoutes } from "./routes/api/nav-items.js";
|
|
39
|
+
import { collectionsApiRoutes } from "./routes/api/collections.js";
|
|
40
|
+
import { settingsApiRoutes } from "./routes/api/settings.js";
|
|
37
41
|
import { uploadApiRoutes } from "./routes/api/upload.js";
|
|
38
42
|
import { searchApiRoutes } from "./routes/api/search.js";
|
|
39
|
-
|
|
43
|
+
// Routes - Compose
|
|
44
|
+
import { composeRoutes } from "./routes/compose.js";
|
|
40
45
|
|
|
41
46
|
// Routes - Feed
|
|
42
47
|
import { rssRoutes } from "./routes/feed/rss.js";
|
|
@@ -47,7 +52,7 @@ import { requireAuth } from "./middleware/auth.js";
|
|
|
47
52
|
import { requireOnboarding } from "./middleware/onboarding.js";
|
|
48
53
|
|
|
49
54
|
// Layouts for auth pages
|
|
50
|
-
import { BaseLayout } from "./
|
|
55
|
+
import { BaseLayout } from "./ui/layouts/BaseLayout.js";
|
|
51
56
|
import { dsRedirect, dsToast } from "./lib/sse.js";
|
|
52
57
|
import { getAvailableThemes, buildThemeStyle } from "./lib/theme.js";
|
|
53
58
|
import { createStorageDriver, type StorageDriver } from "./lib/storage.js";
|
|
@@ -58,6 +63,8 @@ export interface AppVariables {
|
|
|
58
63
|
auth: Auth;
|
|
59
64
|
config: JantConfig;
|
|
60
65
|
themeStyle: string;
|
|
66
|
+
customCSS: string;
|
|
67
|
+
isAuthenticated: boolean;
|
|
61
68
|
storage: StorageDriver | null;
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -78,29 +85,12 @@ export type App = Hono<{ Bindings: Bindings; Variables: AppVariables }>;
|
|
|
78
85
|
* import { createApp } from "@jant/core";
|
|
79
86
|
*
|
|
80
87
|
* export default createApp({
|
|
81
|
-
*
|
|
88
|
+
* cssVariables: { "--card-radius": "0" },
|
|
82
89
|
* });
|
|
83
90
|
* ```
|
|
84
91
|
*/
|
|
85
92
|
export function createApp(config: JantConfig = {}): App {
|
|
86
|
-
|
|
87
|
-
const defaultTheme = minimalTheme();
|
|
88
|
-
const resolvedConfig: JantConfig = {
|
|
89
|
-
...config,
|
|
90
|
-
theme: {
|
|
91
|
-
name: config.theme?.name ?? defaultTheme.name,
|
|
92
|
-
components: {
|
|
93
|
-
...defaultTheme.components,
|
|
94
|
-
...config.theme?.components,
|
|
95
|
-
},
|
|
96
|
-
cssVariables: {
|
|
97
|
-
...defaultTheme.cssVariables,
|
|
98
|
-
...config.theme?.cssVariables,
|
|
99
|
-
},
|
|
100
|
-
colorThemes: config.theme?.colorThemes ?? defaultTheme.colorThemes,
|
|
101
|
-
feed: config.theme?.feed,
|
|
102
|
-
},
|
|
103
|
-
};
|
|
93
|
+
const resolvedConfig: JantConfig = { ...config };
|
|
104
94
|
|
|
105
95
|
const app = new Hono<{ Bindings: Bindings; Variables: AppVariables }>();
|
|
106
96
|
|
|
@@ -134,18 +124,37 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
134
124
|
// Onboarding gate — redirect to /setup if not yet initialized
|
|
135
125
|
app.use("*", requireOnboarding());
|
|
136
126
|
|
|
137
|
-
// Theme middleware - resolve active color theme and
|
|
127
|
+
// Theme middleware - resolve active color theme, custom CSS, and auth state
|
|
138
128
|
app.use("*", async (c, next) => {
|
|
139
|
-
const themeId = await
|
|
129
|
+
const [themeId, customCSS] = await Promise.all([
|
|
130
|
+
c.var.services.settings.get(SETTINGS_KEYS.THEME),
|
|
131
|
+
c.var.services.settings.get(SETTINGS_KEYS.CUSTOM_CSS),
|
|
132
|
+
]);
|
|
140
133
|
const themes = getAvailableThemes(resolvedConfig);
|
|
141
134
|
const activeTheme = themeId
|
|
142
135
|
? themes.find((t) => t.id === themeId)
|
|
143
136
|
: undefined;
|
|
144
137
|
const themeStyle = buildThemeStyle(
|
|
145
138
|
activeTheme,
|
|
146
|
-
resolvedConfig.
|
|
139
|
+
resolvedConfig.cssVariables,
|
|
147
140
|
);
|
|
148
141
|
c.set("themeStyle", themeStyle);
|
|
142
|
+
c.set("customCSS", customCSS ?? "");
|
|
143
|
+
|
|
144
|
+
// Check auth state for data-authenticated attribute on <body>
|
|
145
|
+
let isAuthenticated = false;
|
|
146
|
+
if (c.var.auth) {
|
|
147
|
+
try {
|
|
148
|
+
const session = await c.var.auth.api.getSession({
|
|
149
|
+
headers: c.req.raw.headers,
|
|
150
|
+
});
|
|
151
|
+
isAuthenticated = !!session;
|
|
152
|
+
} catch {
|
|
153
|
+
// Not authenticated
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
c.set("isAuthenticated", isAuthenticated);
|
|
157
|
+
|
|
149
158
|
await next();
|
|
150
159
|
});
|
|
151
160
|
|
|
@@ -197,7 +206,10 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
197
206
|
|
|
198
207
|
// API Routes
|
|
199
208
|
app.route("/api/posts", postsApiRoutes);
|
|
200
|
-
app.route("/api/
|
|
209
|
+
app.route("/api/pages", pagesApiRoutes);
|
|
210
|
+
app.route("/api/nav-items", navItemsApiRoutes);
|
|
211
|
+
app.route("/api/collections", collectionsApiRoutes);
|
|
212
|
+
app.route("/api/settings", settingsApiRoutes);
|
|
201
213
|
|
|
202
214
|
// Setup page component
|
|
203
215
|
const SetupContent: FC = () => {
|
|
@@ -340,6 +352,18 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
340
352
|
|
|
341
353
|
await c.var.services.settings.completeOnboarding();
|
|
342
354
|
|
|
355
|
+
// Seed default navigation items
|
|
356
|
+
await c.var.services.navItems.create({
|
|
357
|
+
type: "link",
|
|
358
|
+
label: "Featured",
|
|
359
|
+
url: "/featured",
|
|
360
|
+
});
|
|
361
|
+
await c.var.services.navItems.create({
|
|
362
|
+
type: "link",
|
|
363
|
+
label: "Collections",
|
|
364
|
+
url: "/collections",
|
|
365
|
+
});
|
|
366
|
+
|
|
343
367
|
return dsRedirect("/signin?setup");
|
|
344
368
|
} catch (err) {
|
|
345
369
|
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
@@ -720,7 +744,6 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
720
744
|
app.route("/dash/settings", dashSettingsRoutes);
|
|
721
745
|
app.route("/dash/redirects", dashRedirectsRoutes);
|
|
722
746
|
app.route("/dash/collections", dashCollectionsRoutes);
|
|
723
|
-
app.route("/dash/navigation", dashNavigationRoutes);
|
|
724
747
|
// API routes
|
|
725
748
|
app.route("/api/upload", uploadApiRoutes);
|
|
726
749
|
app.route("/api/search", searchApiRoutes);
|
|
@@ -753,6 +776,9 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
753
776
|
return new Response(object.body, { headers });
|
|
754
777
|
});
|
|
755
778
|
|
|
779
|
+
// Compose route (auth enforced in route middleware)
|
|
780
|
+
app.route("/compose", composeRoutes);
|
|
781
|
+
|
|
756
782
|
// Feed routes
|
|
757
783
|
app.route("/feed", rssRoutes);
|
|
758
784
|
app.route("/", sitemapRoutes);
|
|
@@ -760,6 +786,8 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
760
786
|
// Frontend routes
|
|
761
787
|
app.route("/search", searchRoutes);
|
|
762
788
|
app.route("/archive", archiveRoutes);
|
|
789
|
+
app.route("/featured", featuredRoutes);
|
|
790
|
+
app.route("/collections", collectionsPageRoutes);
|
|
763
791
|
app.route("/c", collectionRoutes);
|
|
764
792
|
app.route("/p", postRoutes);
|
|
765
793
|
app.route("/", homeRoutes);
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
-- v2 Schema Migration
|
|
2
|
+
-- Restructures posts, creates pages, updates collections, replaces navigation_links with nav_items
|
|
3
|
+
|
|
4
|
+
-- Disable FK checks for migration (dropping/recreating tables with cross-references)
|
|
5
|
+
PRAGMA foreign_keys = OFF;
|
|
6
|
+
--> statement-breakpoint
|
|
7
|
+
|
|
8
|
+
-- =============================================================================
|
|
9
|
+
-- 1. Create pages table (before modifying posts, migrate type='page' data)
|
|
10
|
+
-- =============================================================================
|
|
11
|
+
|
|
12
|
+
CREATE TABLE `pages` (
|
|
13
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
14
|
+
`slug` text NOT NULL,
|
|
15
|
+
`title` text,
|
|
16
|
+
`body` text,
|
|
17
|
+
`body_html` text,
|
|
18
|
+
`status` text DEFAULT 'published' NOT NULL,
|
|
19
|
+
`created_at` integer NOT NULL,
|
|
20
|
+
`updated_at` integer NOT NULL
|
|
21
|
+
);
|
|
22
|
+
--> statement-breakpoint
|
|
23
|
+
CREATE UNIQUE INDEX `pages_slug_unique` ON `pages` (`slug`);
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
|
|
26
|
+
-- Migrate type='page' posts into pages table
|
|
27
|
+
INSERT INTO `pages` (`slug`, `title`, `body`, `body_html`, `status`, `created_at`, `updated_at`)
|
|
28
|
+
SELECT
|
|
29
|
+
CASE
|
|
30
|
+
WHEN `path` IS NOT NULL AND `path` != '' THEN REPLACE(`path`, '/', '')
|
|
31
|
+
ELSE 'page-' || `id`
|
|
32
|
+
END,
|
|
33
|
+
`title`,
|
|
34
|
+
`content`,
|
|
35
|
+
`content_html`,
|
|
36
|
+
CASE WHEN `visibility` = 'draft' THEN 'draft' ELSE 'published' END,
|
|
37
|
+
`created_at`,
|
|
38
|
+
`updated_at`
|
|
39
|
+
FROM `posts`
|
|
40
|
+
WHERE `type` = 'page';
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
|
|
43
|
+
-- =============================================================================
|
|
44
|
+
-- 2. Restructure posts table (create new → migrate → drop old → rename)
|
|
45
|
+
-- =============================================================================
|
|
46
|
+
|
|
47
|
+
CREATE TABLE `posts_new` (
|
|
48
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
49
|
+
`format` text DEFAULT 'note' NOT NULL,
|
|
50
|
+
`status` text DEFAULT 'published' NOT NULL,
|
|
51
|
+
`featured` integer DEFAULT 0 NOT NULL,
|
|
52
|
+
`pinned` integer DEFAULT 0 NOT NULL,
|
|
53
|
+
`slug` text,
|
|
54
|
+
`title` text,
|
|
55
|
+
`url` text,
|
|
56
|
+
`body` text,
|
|
57
|
+
`body_html` text,
|
|
58
|
+
`quote_text` text,
|
|
59
|
+
`rating` integer,
|
|
60
|
+
`collection_id` integer,
|
|
61
|
+
`reply_to_id` integer,
|
|
62
|
+
`thread_id` integer,
|
|
63
|
+
`deleted_at` integer,
|
|
64
|
+
`published_at` integer NOT NULL,
|
|
65
|
+
`created_at` integer NOT NULL,
|
|
66
|
+
`updated_at` integer NOT NULL,
|
|
67
|
+
FOREIGN KEY (`collection_id`) REFERENCES `collections`(`id`) ON UPDATE no action ON DELETE set null
|
|
68
|
+
);
|
|
69
|
+
--> statement-breakpoint
|
|
70
|
+
|
|
71
|
+
-- Migrate data from old posts to new posts (excluding type='page')
|
|
72
|
+
INSERT INTO `posts_new` (
|
|
73
|
+
`id`, `format`, `status`, `featured`, `pinned`,
|
|
74
|
+
`slug`, `title`, `url`, `body`, `body_html`, `quote_text`, `rating`,
|
|
75
|
+
`collection_id`, `reply_to_id`, `thread_id`,
|
|
76
|
+
`deleted_at`, `published_at`, `created_at`, `updated_at`
|
|
77
|
+
)
|
|
78
|
+
SELECT
|
|
79
|
+
`id`,
|
|
80
|
+
-- format mapping: article→note, image→note, note→note, link→link, quote→quote
|
|
81
|
+
CASE
|
|
82
|
+
WHEN `type` IN ('article', 'image', 'note') THEN 'note'
|
|
83
|
+
WHEN `type` = 'link' THEN 'link'
|
|
84
|
+
WHEN `type` = 'quote' THEN 'quote'
|
|
85
|
+
ELSE 'note'
|
|
86
|
+
END,
|
|
87
|
+
-- status mapping from visibility
|
|
88
|
+
CASE WHEN `visibility` = 'draft' THEN 'draft' ELSE 'published' END,
|
|
89
|
+
-- featured mapping from visibility
|
|
90
|
+
CASE WHEN `visibility` = 'featured' THEN 1 ELSE 0 END,
|
|
91
|
+
-- pinned: default 0
|
|
92
|
+
0,
|
|
93
|
+
-- slug: migrate from path (strip leading /)
|
|
94
|
+
CASE
|
|
95
|
+
WHEN `path` IS NOT NULL AND `path` != '' THEN REPLACE(`path`, '/', '')
|
|
96
|
+
ELSE NULL
|
|
97
|
+
END,
|
|
98
|
+
`title`,
|
|
99
|
+
`source_url`,
|
|
100
|
+
`content`,
|
|
101
|
+
`content_html`,
|
|
102
|
+
-- quote_text: for quote type, content was the quote; set to null (manual fix may be needed)
|
|
103
|
+
NULL,
|
|
104
|
+
-- rating: null
|
|
105
|
+
NULL,
|
|
106
|
+
-- collection_id: migrate from post_collections (take first collection for each post)
|
|
107
|
+
(SELECT `collection_id` FROM `post_collections` WHERE `post_collections`.`post_id` = `posts`.`id` LIMIT 1),
|
|
108
|
+
`reply_to_id`,
|
|
109
|
+
`thread_id`,
|
|
110
|
+
`deleted_at`,
|
|
111
|
+
`published_at`,
|
|
112
|
+
`created_at`,
|
|
113
|
+
`updated_at`
|
|
114
|
+
FROM `posts`
|
|
115
|
+
WHERE `type` != 'page';
|
|
116
|
+
--> statement-breakpoint
|
|
117
|
+
|
|
118
|
+
-- Update media references to point at new table (foreign keys reference posts)
|
|
119
|
+
-- media.post_id still works since IDs are preserved
|
|
120
|
+
--> statement-breakpoint
|
|
121
|
+
|
|
122
|
+
-- Drop old posts table and rename new one
|
|
123
|
+
DROP TABLE `posts`;
|
|
124
|
+
--> statement-breakpoint
|
|
125
|
+
ALTER TABLE `posts_new` RENAME TO `posts`;
|
|
126
|
+
--> statement-breakpoint
|
|
127
|
+
CREATE UNIQUE INDEX `posts_slug_unique` ON `posts` (`slug`);
|
|
128
|
+
--> statement-breakpoint
|
|
129
|
+
|
|
130
|
+
-- =============================================================================
|
|
131
|
+
-- 3. Update collections table (add new columns, rename path→slug)
|
|
132
|
+
-- =============================================================================
|
|
133
|
+
|
|
134
|
+
CREATE TABLE `collections_new` (
|
|
135
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
136
|
+
`slug` text NOT NULL,
|
|
137
|
+
`title` text NOT NULL,
|
|
138
|
+
`description` text,
|
|
139
|
+
`icon` text,
|
|
140
|
+
`sort_order` text DEFAULT 'newest' NOT NULL,
|
|
141
|
+
`position` integer DEFAULT 0 NOT NULL,
|
|
142
|
+
`show_divider` integer DEFAULT 0 NOT NULL,
|
|
143
|
+
`created_at` integer NOT NULL,
|
|
144
|
+
`updated_at` integer NOT NULL
|
|
145
|
+
);
|
|
146
|
+
--> statement-breakpoint
|
|
147
|
+
CREATE UNIQUE INDEX `collections_new_slug_unique` ON `collections_new` (`slug`);
|
|
148
|
+
--> statement-breakpoint
|
|
149
|
+
|
|
150
|
+
INSERT INTO `collections_new` (`id`, `slug`, `title`, `description`, `icon`, `sort_order`, `position`, `show_divider`, `created_at`, `updated_at`)
|
|
151
|
+
SELECT
|
|
152
|
+
`id`,
|
|
153
|
+
COALESCE(`path`, 'collection-' || `id`),
|
|
154
|
+
`title`,
|
|
155
|
+
`description`,
|
|
156
|
+
NULL,
|
|
157
|
+
'newest',
|
|
158
|
+
0,
|
|
159
|
+
0,
|
|
160
|
+
`created_at`,
|
|
161
|
+
`updated_at`
|
|
162
|
+
FROM `collections`;
|
|
163
|
+
--> statement-breakpoint
|
|
164
|
+
|
|
165
|
+
DROP TABLE `collections`;
|
|
166
|
+
--> statement-breakpoint
|
|
167
|
+
ALTER TABLE `collections_new` RENAME TO `collections`;
|
|
168
|
+
--> statement-breakpoint
|
|
169
|
+
CREATE UNIQUE INDEX `collections_slug_unique` ON `collections` (`slug`);
|
|
170
|
+
--> statement-breakpoint
|
|
171
|
+
|
|
172
|
+
-- =============================================================================
|
|
173
|
+
-- 4. Replace navigation_links with nav_items
|
|
174
|
+
-- =============================================================================
|
|
175
|
+
|
|
176
|
+
CREATE TABLE `nav_items` (
|
|
177
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
178
|
+
`type` text DEFAULT 'link' NOT NULL,
|
|
179
|
+
`label` text NOT NULL,
|
|
180
|
+
`url` text NOT NULL,
|
|
181
|
+
`page_id` integer,
|
|
182
|
+
`position` integer DEFAULT 0 NOT NULL,
|
|
183
|
+
`created_at` integer NOT NULL,
|
|
184
|
+
`updated_at` integer NOT NULL,
|
|
185
|
+
FOREIGN KEY (`page_id`) REFERENCES `pages`(`id`) ON UPDATE no action ON DELETE cascade
|
|
186
|
+
);
|
|
187
|
+
--> statement-breakpoint
|
|
188
|
+
|
|
189
|
+
-- Migrate existing navigation_links as type='link'
|
|
190
|
+
INSERT INTO `nav_items` (`type`, `label`, `url`, `page_id`, `position`, `created_at`, `updated_at`)
|
|
191
|
+
SELECT 'link', `label`, `url`, NULL, `position`, `created_at`, `updated_at`
|
|
192
|
+
FROM `navigation_links`;
|
|
193
|
+
--> statement-breakpoint
|
|
194
|
+
|
|
195
|
+
DROP TABLE `navigation_links`;
|
|
196
|
+
--> statement-breakpoint
|
|
197
|
+
|
|
198
|
+
-- =============================================================================
|
|
199
|
+
-- 5. Drop post_collections table (replaced by posts.collection_id)
|
|
200
|
+
-- =============================================================================
|
|
201
|
+
|
|
202
|
+
DROP TABLE `post_collections`;
|
|
203
|
+
--> statement-breakpoint
|
|
204
|
+
|
|
205
|
+
-- =============================================================================
|
|
206
|
+
-- 6. Rebuild FTS5 (column rename: content→body, add quote_text)
|
|
207
|
+
-- =============================================================================
|
|
208
|
+
|
|
209
|
+
-- Drop old FTS triggers
|
|
210
|
+
DROP TRIGGER IF EXISTS posts_fts_insert;
|
|
211
|
+
--> statement-breakpoint
|
|
212
|
+
DROP TRIGGER IF EXISTS posts_fts_update;
|
|
213
|
+
--> statement-breakpoint
|
|
214
|
+
DROP TRIGGER IF EXISTS posts_fts_delete;
|
|
215
|
+
--> statement-breakpoint
|
|
216
|
+
|
|
217
|
+
-- Drop old FTS table
|
|
218
|
+
DROP TABLE IF EXISTS posts_fts;
|
|
219
|
+
--> statement-breakpoint
|
|
220
|
+
|
|
221
|
+
-- Create new FTS table with updated columns
|
|
222
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
|
|
223
|
+
title,
|
|
224
|
+
body,
|
|
225
|
+
quote_text,
|
|
226
|
+
content=posts,
|
|
227
|
+
content_rowid=id,
|
|
228
|
+
tokenize='trigram'
|
|
229
|
+
);
|
|
230
|
+
--> statement-breakpoint
|
|
231
|
+
|
|
232
|
+
-- Populate FTS with migrated data
|
|
233
|
+
INSERT INTO posts_fts(rowid, title, body, quote_text)
|
|
234
|
+
SELECT id, COALESCE(title, ''), COALESCE(body, ''), COALESCE(quote_text, '')
|
|
235
|
+
FROM posts WHERE deleted_at IS NULL;
|
|
236
|
+
--> statement-breakpoint
|
|
237
|
+
|
|
238
|
+
-- Trigger: sync FTS on INSERT
|
|
239
|
+
CREATE TRIGGER posts_fts_insert AFTER INSERT ON posts
|
|
240
|
+
WHEN NEW.deleted_at IS NULL
|
|
241
|
+
BEGIN
|
|
242
|
+
INSERT INTO posts_fts(rowid, title, body, quote_text)
|
|
243
|
+
VALUES (NEW.id, COALESCE(NEW.title, ''), COALESCE(NEW.body, ''), COALESCE(NEW.quote_text, ''));
|
|
244
|
+
END;
|
|
245
|
+
--> statement-breakpoint
|
|
246
|
+
|
|
247
|
+
-- Trigger: sync FTS on UPDATE
|
|
248
|
+
CREATE TRIGGER posts_fts_update AFTER UPDATE ON posts BEGIN
|
|
249
|
+
DELETE FROM posts_fts WHERE rowid = OLD.id;
|
|
250
|
+
INSERT INTO posts_fts(rowid, title, body, quote_text)
|
|
251
|
+
SELECT NEW.id, COALESCE(NEW.title, ''), COALESCE(NEW.body, ''), COALESCE(NEW.quote_text, '')
|
|
252
|
+
WHERE NEW.deleted_at IS NULL;
|
|
253
|
+
END;
|
|
254
|
+
--> statement-breakpoint
|
|
255
|
+
|
|
256
|
+
-- Trigger: sync FTS on DELETE
|
|
257
|
+
CREATE TRIGGER posts_fts_delete AFTER DELETE ON posts BEGIN
|
|
258
|
+
DELETE FROM posts_fts WHERE rowid = OLD.id;
|
|
259
|
+
END;
|
|
260
|
+
--> statement-breakpoint
|
|
261
|
+
|
|
262
|
+
-- =============================================================================
|
|
263
|
+
-- 7. Re-enable FK checks and verify integrity
|
|
264
|
+
-- =============================================================================
|
|
265
|
+
|
|
266
|
+
PRAGMA foreign_keys = ON;
|
|
267
|
+
--> statement-breakpoint
|
|
268
|
+
PRAGMA foreign_key_check;
|
|
@@ -36,6 +36,20 @@
|
|
|
36
36
|
"when": 1770946168874,
|
|
37
37
|
"tag": "0004_add_storage_provider",
|
|
38
38
|
"breakpoints": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"idx": 5,
|
|
42
|
+
"version": "6",
|
|
43
|
+
"when": 1771346168875,
|
|
44
|
+
"tag": "0005_v2_schema_migration",
|
|
45
|
+
"breakpoints": true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"idx": 6,
|
|
49
|
+
"version": "6",
|
|
50
|
+
"when": 1771746168876,
|
|
51
|
+
"tag": "0006_rename_slug_to_path",
|
|
52
|
+
"breakpoints": true
|
|
39
53
|
}
|
|
40
54
|
]
|
|
41
55
|
}
|
package/src/db/schema.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Drizzle Schema
|
|
3
3
|
*
|
|
4
|
-
* Database schema for Jant
|
|
4
|
+
* Database schema for Jant v2
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
sqliteTable,
|
|
9
|
-
text,
|
|
10
|
-
integer,
|
|
11
|
-
primaryKey,
|
|
12
|
-
} from "drizzle-orm/sqlite-core";
|
|
7
|
+
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
13
8
|
|
|
14
9
|
// =============================================================================
|
|
15
10
|
// Posts
|
|
@@ -17,21 +12,26 @@ import {
|
|
|
17
12
|
|
|
18
13
|
export const posts = sqliteTable("posts", {
|
|
19
14
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
20
|
-
|
|
21
|
-
enum: ["note", "
|
|
15
|
+
format: text("format", {
|
|
16
|
+
enum: ["note", "link", "quote"],
|
|
22
17
|
}).notNull(),
|
|
23
|
-
|
|
24
|
-
enum: ["
|
|
18
|
+
status: text("status", {
|
|
19
|
+
enum: ["draft", "published"],
|
|
25
20
|
})
|
|
26
21
|
.notNull()
|
|
27
|
-
.default("
|
|
22
|
+
.default("published"),
|
|
23
|
+
featured: integer("featured").notNull().default(0),
|
|
24
|
+
pinned: integer("pinned").notNull().default(0),
|
|
25
|
+
path: text("path").unique(),
|
|
28
26
|
title: text("title"),
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
url: text("url"),
|
|
28
|
+
body: text("body"),
|
|
29
|
+
bodyHtml: text("body_html"),
|
|
30
|
+
quoteText: text("quote_text"),
|
|
31
|
+
rating: integer("rating"),
|
|
32
|
+
collectionId: integer("collection_id").references(() => collections.id, {
|
|
33
|
+
onDelete: "set null",
|
|
34
|
+
}),
|
|
35
35
|
replyToId: integer("reply_to_id"),
|
|
36
36
|
threadId: integer("thread_id"),
|
|
37
37
|
deletedAt: integer("deleted_at"),
|
|
@@ -40,6 +40,25 @@ export const posts = sqliteTable("posts", {
|
|
|
40
40
|
updatedAt: integer("updated_at").notNull(),
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
// =============================================================================
|
|
44
|
+
// Pages
|
|
45
|
+
// =============================================================================
|
|
46
|
+
|
|
47
|
+
export const pages = sqliteTable("pages", {
|
|
48
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
49
|
+
slug: text("slug").notNull().unique(),
|
|
50
|
+
title: text("title"),
|
|
51
|
+
body: text("body"),
|
|
52
|
+
bodyHtml: text("body_html"),
|
|
53
|
+
status: text("status", {
|
|
54
|
+
enum: ["draft", "published"],
|
|
55
|
+
})
|
|
56
|
+
.notNull()
|
|
57
|
+
.default("published"),
|
|
58
|
+
createdAt: integer("created_at").notNull(),
|
|
59
|
+
updatedAt: integer("updated_at").notNull(),
|
|
60
|
+
});
|
|
61
|
+
|
|
43
62
|
// =============================================================================
|
|
44
63
|
// Media
|
|
45
64
|
// =============================================================================
|
|
@@ -67,30 +86,41 @@ export const media = sqliteTable("media", {
|
|
|
67
86
|
|
|
68
87
|
export const collections = sqliteTable("collections", {
|
|
69
88
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
70
|
-
|
|
89
|
+
slug: text("slug").notNull().unique(),
|
|
71
90
|
title: text("title").notNull(),
|
|
72
91
|
description: text("description"),
|
|
92
|
+
icon: text("icon"),
|
|
93
|
+
sortOrder: text("sort_order", {
|
|
94
|
+
enum: ["newest", "oldest", "rating_desc", "rating_asc"],
|
|
95
|
+
})
|
|
96
|
+
.notNull()
|
|
97
|
+
.default("newest"),
|
|
98
|
+
position: integer("position").notNull().default(0),
|
|
99
|
+
showDivider: integer("show_divider").notNull().default(0),
|
|
73
100
|
createdAt: integer("created_at").notNull(),
|
|
74
101
|
updatedAt: integer("updated_at").notNull(),
|
|
75
102
|
});
|
|
76
103
|
|
|
77
104
|
// =============================================================================
|
|
78
|
-
//
|
|
105
|
+
// Navigation Items
|
|
79
106
|
// =============================================================================
|
|
80
107
|
|
|
81
|
-
export const
|
|
82
|
-
"
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
108
|
+
export const navItems = sqliteTable("nav_items", {
|
|
109
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
110
|
+
type: text("type", {
|
|
111
|
+
enum: ["page", "link"],
|
|
112
|
+
})
|
|
113
|
+
.notNull()
|
|
114
|
+
.default("link"),
|
|
115
|
+
label: text("label").notNull(),
|
|
116
|
+
url: text("url").notNull(),
|
|
117
|
+
pageId: integer("page_id").references(() => pages.id, {
|
|
118
|
+
onDelete: "cascade",
|
|
119
|
+
}),
|
|
120
|
+
position: integer("position").notNull().default(0),
|
|
121
|
+
createdAt: integer("created_at").notNull(),
|
|
122
|
+
updatedAt: integer("updated_at").notNull(),
|
|
123
|
+
});
|
|
94
124
|
|
|
95
125
|
// =============================================================================
|
|
96
126
|
// Redirects
|
|
@@ -104,19 +134,6 @@ export const redirects = sqliteTable("redirects", {
|
|
|
104
134
|
createdAt: integer("created_at").notNull(),
|
|
105
135
|
});
|
|
106
136
|
|
|
107
|
-
// =============================================================================
|
|
108
|
-
// Navigation Links
|
|
109
|
-
// =============================================================================
|
|
110
|
-
|
|
111
|
-
export const navigationLinks = sqliteTable("navigation_links", {
|
|
112
|
-
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
113
|
-
label: text("label").notNull(),
|
|
114
|
-
url: text("url").notNull(),
|
|
115
|
-
position: integer("position").notNull().default(0),
|
|
116
|
-
createdAt: integer("created_at").notNull(),
|
|
117
|
-
updatedAt: integer("updated_at").notNull(),
|
|
118
|
-
});
|
|
119
|
-
|
|
120
137
|
// =============================================================================
|
|
121
138
|
// Settings (Key-Value)
|
|
122
139
|
// =============================================================================
|