@leadertechie/personal-site-kit 0.1.0-alpha.2 → 0.1.0-alpha.21
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/README.md +94 -17
- package/dist/api/content-utils.d.ts +27 -0
- package/dist/api/content-utils.d.ts.map +1 -0
- package/dist/api/handlers/about-me.d.ts.map +1 -1
- package/dist/api/handlers/auth-handler.d.ts +2 -0
- package/dist/api/handlers/auth-handler.d.ts.map +1 -0
- package/dist/api/handlers/auth.d.ts +23 -0
- package/dist/api/handlers/auth.d.ts.map +1 -0
- package/dist/api/handlers/content-api.d.ts +0 -1
- package/dist/api/handlers/content-api.d.ts.map +1 -1
- package/dist/api/handlers/content.d.ts.map +1 -1
- package/dist/api/handlers/home.d.ts.map +1 -1
- package/dist/api/handlers/static-details.d.ts +1 -1
- package/dist/api/handlers/static-details.d.ts.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/website-api.d.ts +1 -1
- package/dist/api/website-api.d.ts.map +1 -1
- package/dist/api.js +17 -2
- package/dist/assets/logo-placeholder.svg +21 -0
- package/dist/chunks/index-C1krnvU3.js +211 -0
- package/dist/chunks/index-DsRjL9Uy.js +2727 -0
- package/dist/chunks/site-store-CGV9c2DI.js +89 -0
- package/dist/chunks/{template-gGTkeOcA.js → template-Lmx7Dxoc.js} +132 -91
- package/dist/chunks/website-api-CFRUPu0X.js +958 -0
- package/dist/index.js +42 -14
- package/dist/prerender/data-fetcher.d.ts +19 -0
- package/dist/prerender/data-fetcher.d.ts.map +1 -0
- package/dist/prerender/page-content.d.ts.map +1 -1
- package/dist/prerender/page-generators/about.d.ts +16 -0
- package/dist/prerender/page-generators/about.d.ts.map +1 -0
- package/dist/prerender/page-generators/base.d.ts +25 -0
- package/dist/prerender/page-generators/base.d.ts.map +1 -0
- package/dist/prerender/page-generators/blog-detail.d.ts +15 -0
- package/dist/prerender/page-generators/blog-detail.d.ts.map +1 -0
- package/dist/prerender/page-generators/blogs-list.d.ts +17 -0
- package/dist/prerender/page-generators/blogs-list.d.ts.map +1 -0
- package/dist/prerender/page-generators/home.d.ts +19 -0
- package/dist/prerender/page-generators/home.d.ts.map +1 -0
- package/dist/prerender/page-generators/index.d.ts +9 -0
- package/dist/prerender/page-generators/index.d.ts.map +1 -0
- package/dist/prerender/page-generators/not-found.d.ts +14 -0
- package/dist/prerender/page-generators/not-found.d.ts.map +1 -0
- package/dist/prerender/page-generators/stories-list.d.ts +17 -0
- package/dist/prerender/page-generators/stories-list.d.ts.map +1 -0
- package/dist/prerender/page-generators/story-detail.d.ts +15 -0
- package/dist/prerender/page-generators/story-detail.d.ts.map +1 -0
- package/dist/prerender/template.d.ts +3 -1
- package/dist/prerender/template.d.ts.map +1 -1
- package/dist/prerender/website-prerender.d.ts +6 -0
- package/dist/prerender/website-prerender.d.ts.map +1 -1
- package/dist/prerender.js +291 -145
- package/dist/shared/config/index.d.ts +1 -0
- package/dist/shared/config/index.d.ts.map +1 -1
- package/dist/shared/core/site-store.d.ts +1 -0
- package/dist/shared/core/site-store.d.ts.map +1 -1
- package/dist/shared/core/theme-toggle.d.ts.map +1 -1
- package/dist/shared/page-content.d.ts.map +1 -1
- package/dist/shared/router.d.ts +9 -3
- package/dist/shared/router.d.ts.map +1 -1
- package/dist/shared/website-ui.d.ts +23 -0
- package/dist/shared/website-ui.d.ts.map +1 -1
- package/dist/shared.js +6 -4
- package/dist/ui/about-me/index.d.ts +2 -10
- package/dist/ui/about-me/index.d.ts.map +1 -1
- package/dist/ui/about-me/styles.d.ts.map +1 -1
- package/dist/ui/admin/api.d.ts +16 -0
- package/dist/ui/admin/api.d.ts.map +1 -0
- package/dist/ui/admin/components/AboutMeSection.d.ts +7 -0
- package/dist/ui/admin/components/AboutMeSection.d.ts.map +1 -0
- package/dist/ui/admin/components/AdminSection.d.ts +13 -0
- package/dist/ui/admin/components/AdminSection.d.ts.map +1 -0
- package/dist/ui/admin/components/BlogsSection.d.ts +7 -0
- package/dist/ui/admin/components/BlogsSection.d.ts.map +1 -0
- package/dist/ui/admin/components/HomeSection.d.ts +7 -0
- package/dist/ui/admin/components/HomeSection.d.ts.map +1 -0
- package/dist/ui/admin/components/ImagesSection.d.ts +7 -0
- package/dist/ui/admin/components/ImagesSection.d.ts.map +1 -0
- package/dist/ui/admin/components/LoginForm.d.ts +9 -0
- package/dist/ui/admin/components/LoginForm.d.ts.map +1 -0
- package/dist/ui/admin/components/LogoSection.d.ts +7 -0
- package/dist/ui/admin/components/LogoSection.d.ts.map +1 -0
- package/dist/ui/admin/components/ProfileSection.d.ts +7 -0
- package/dist/ui/admin/components/ProfileSection.d.ts.map +1 -0
- package/dist/ui/admin/components/StaticSection.d.ts +9 -0
- package/dist/ui/admin/components/StaticSection.d.ts.map +1 -0
- package/dist/ui/admin/components/StoriesSection.d.ts +7 -0
- package/dist/ui/admin/components/StoriesSection.d.ts.map +1 -0
- package/dist/ui/admin/components/index.d.ts +11 -0
- package/dist/ui/admin/components/index.d.ts.map +1 -0
- package/dist/ui/admin/index.d.ts +27 -26
- package/dist/ui/admin/index.d.ts.map +1 -1
- package/dist/ui/admin/styles.d.ts.map +1 -1
- package/dist/ui/admin/types.d.ts +24 -0
- package/dist/ui/admin/types.d.ts.map +1 -0
- package/dist/ui/banner/index.d.ts.map +1 -1
- package/dist/ui/banner/styles.d.ts.map +1 -1
- package/dist/ui/blog-viewer/__tests__/blogviewer.test.d.ts +2 -0
- package/dist/ui/blog-viewer/__tests__/blogviewer.test.d.ts.map +1 -0
- package/dist/ui/blog-viewer/index.d.ts +25 -0
- package/dist/ui/blog-viewer/index.d.ts.map +1 -0
- package/dist/ui/blog-viewer/styles.d.ts +2 -0
- package/dist/ui/blog-viewer/styles.d.ts.map +1 -0
- package/dist/ui/footer/index.d.ts.map +1 -1
- package/dist/ui/footer/styles.d.ts.map +1 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/story-viewer/__tests__/storyviewer.test.d.ts +2 -0
- package/dist/ui/story-viewer/__tests__/storyviewer.test.d.ts.map +1 -0
- package/dist/ui/story-viewer/index.d.ts +25 -0
- package/dist/ui/story-viewer/index.d.ts.map +1 -0
- package/dist/ui/story-viewer/styles.d.ts +2 -0
- package/dist/ui/story-viewer/styles.d.ts.map +1 -0
- package/dist/ui.js +15 -3
- package/package.json +37 -13
- package/public/assets/logo-placeholder.svg +21 -0
- package/dist/chunks/index-BqixlS-2.js +0 -1157
- package/dist/chunks/website-api-CVsi-OLc.js +0 -596
- package/dist/ui/about-me/renderer.d.ts +0 -5
- package/dist/ui/about-me/renderer.d.ts.map +0 -1
- package/src/api/__tests__/info.test.ts +0 -44
- package/src/api/__tests__/utils.test.ts +0 -78
- package/src/api/handlers/about-me.ts +0 -99
- package/src/api/handlers/content-api.ts +0 -268
- package/src/api/handlers/content.ts +0 -72
- package/src/api/handlers/home.ts +0 -79
- package/src/api/handlers/info.ts +0 -12
- package/src/api/handlers/logo.ts +0 -55
- package/src/api/handlers/static-details.ts +0 -48
- package/src/api/index.ts +0 -7
- package/src/api/utils.ts +0 -16
- package/src/api/website-api.ts +0 -124
- package/src/index.ts +0 -4
- package/src/prerender/__tests__/page-content.test.ts +0 -54
- package/src/prerender/__tests__/template.test.ts +0 -54
- package/src/prerender/index.ts +0 -7
- package/src/prerender/page-content.ts +0 -263
- package/src/prerender/prerender.ts +0 -25
- package/src/prerender/template.ts +0 -65
- package/src/prerender/website-prerender.ts +0 -152
- package/src/shared/config/api.ts +0 -16
- package/src/shared/config/index.ts +0 -41
- package/src/shared/config/types.ts +0 -16
- package/src/shared/core/__tests__/theme-toggle.test.ts +0 -204
- package/src/shared/core/site-store.ts +0 -38
- package/src/shared/core/theme-toggle.ts +0 -118
- package/src/shared/index.ts +0 -17
- package/src/shared/interfaces/ifooter-link.ts +0 -4
- package/src/shared/interfaces/iroute.ts +0 -4
- package/src/shared/models/theme-variables.css +0 -25
- package/src/shared/page-content.ts +0 -210
- package/src/shared/router.ts +0 -241
- package/src/shared/runtime.ts +0 -11
- package/src/shared/template.ts +0 -35
- package/src/shared/website-ui.ts +0 -92
- package/src/styles/markdown.css +0 -129
- package/src/ui/about-me/api.ts +0 -12
- package/src/ui/about-me/index.ts +0 -155
- package/src/ui/about-me/renderer.ts +0 -7
- package/src/ui/about-me/styles.ts +0 -10
- package/src/ui/admin/index.ts +0 -492
- package/src/ui/admin/styles.ts +0 -317
- package/src/ui/banner/index.ts +0 -38
- package/src/ui/banner/styles.ts +0 -10
- package/src/ui/footer/index.ts +0 -37
- package/src/ui/footer/styles.ts +0 -9
- package/src/ui/index.ts +0 -4
- /package/{src/shared → dist}/styles/markdown.css +0 -0
- /package/{src → dist}/styles/theme.css +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @leadertechie/personal-site-kit
|
|
2
2
|
|
|
3
|
-
A high-performance, modular engine for building personal websites and professional portfolios. Powered by Cloudflare Workers, R2 Storage,
|
|
3
|
+
A high-performance, modular engine for building personal websites and professional portfolios. Powered by Cloudflare Workers, R2 Storage, Lit Web Components, and the **@leadertechie** ecosystem.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -8,18 +8,21 @@ A high-performance, modular engine for building personal websites and profession
|
|
|
8
8
|
- **🛠️ 4-Section Engine**: Fully integrated logic for **API**, **UI**, **Prerender**, and **Admin**.
|
|
9
9
|
- **🎨 Modern Layout**: Semantic grid system with responsive `main-column` and `sidebar-column` components.
|
|
10
10
|
- **🌓 Theme Engine**: Built-in light/dark mode support with persistent user preferences.
|
|
11
|
-
- **📝 Content Driven**: Dynamic Markdown rendering with frontmatter support via `@leadertechie/md2html
|
|
11
|
+
- **📝 Content Driven**: Dynamic Markdown rendering with frontmatter support via `@leadertechie/md2html` (v2).
|
|
12
12
|
- **⚙️ Remote Config**: Manage site settings (title, copyright, social links) directly from R2 without re-deploying.
|
|
13
13
|
- **🔍 SEO Optimized**: Server-side prerendering ensures search engines see your content perfectly.
|
|
14
|
+
- **🔄 SWR Caching**: Stale-while-revalidate caching via `@leadertechie/r2tohtml` for optimal performance.
|
|
15
|
+
- **⚡ Client-Side Interactivity**: DOM interaction patterns (poll, live-update, click-toggle, infinite-scroll, form-live) via `@leadertechie/md2interact`.
|
|
16
|
+
- **🎯 CSS Hydration**: Inline critical CSS, layer injection, and theme toggle support.
|
|
14
17
|
|
|
15
18
|
## Architecture
|
|
16
19
|
|
|
17
20
|
The kit is divided into four logical subpaths:
|
|
18
21
|
|
|
19
|
-
1. **/shared**: Common interfaces, types,
|
|
20
|
-
2. **/ui**: Web components for the Banner, Footer, About Me, and Admin Portal.
|
|
21
|
-
3. **/api**: Standard handlers for Cloudflare Workers to manage R2 content.
|
|
22
|
-
4. **/prerender**: SEO engine to generate static HTML for crawlers.
|
|
22
|
+
1. **/shared**: Common interfaces, types, the reactive `SiteStore`, and the `WebsiteUI` bootstrap.
|
|
23
|
+
2. **/ui**: Web components for the Banner, Footer, About Me, Blog Viewer, Story Viewer, and Admin Portal.
|
|
24
|
+
3. **/api**: Standard handlers for Cloudflare Workers to manage R2 content with v2 caching.
|
|
25
|
+
4. **/prerender**: SEO engine to generate static HTML for crawlers with SWR caching.
|
|
23
26
|
|
|
24
27
|
## Installation
|
|
25
28
|
|
|
@@ -29,26 +32,100 @@ npm install @leadertechie/personal-site-kit
|
|
|
29
32
|
|
|
30
33
|
## Quick Start (Usage)
|
|
31
34
|
|
|
32
|
-
### 1. API Worker (`api.ts`)
|
|
35
|
+
### 1. API Worker (`api/index.ts`)
|
|
36
|
+
|
|
33
37
|
```typescript
|
|
34
|
-
import {
|
|
38
|
+
import { WebsiteAPI } from '@leadertechie/personal-site-kit/api';
|
|
39
|
+
|
|
40
|
+
export default new WebsiteAPI();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. UI Entry (`ui/index.ts`)
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
```typescript
|
|
46
|
+
import { WebsiteUI } from '@leadertechie/personal-site-kit/shared';
|
|
47
|
+
import '@leadertechie/personal-site-kit/styles/theme.css';
|
|
48
|
+
import '@leadertechie/personal-site-kit/ui';
|
|
49
|
+
|
|
50
|
+
// Bootstrap the UI — md2interact is automatically initialized
|
|
51
|
+
WebsiteUI.getInstance({
|
|
52
|
+
apiUrl: window.location.origin,
|
|
53
|
+
// Optional: configure md2interact interactions
|
|
54
|
+
interactConfig: {
|
|
55
|
+
interactions: {
|
|
56
|
+
'poll': { selector: '[data-interact="poll"]' },
|
|
57
|
+
'live-update': { selector: '[data-interact="live-update"]' },
|
|
58
|
+
'click-toggle': { selector: '[data-interact="click-toggle"]' },
|
|
59
|
+
'infinite-scroll': { selector: '[data-interact="infinite-scroll"]' },
|
|
60
|
+
'form-live': { selector: '[data-interact="form-live"]' }
|
|
61
|
+
},
|
|
62
|
+
cssHydration: {
|
|
63
|
+
inlineCritical: true,
|
|
64
|
+
layerInjection: true,
|
|
65
|
+
themeToggle: true
|
|
66
|
+
}
|
|
39
67
|
}
|
|
40
|
-
};
|
|
68
|
+
}).bootstrap();
|
|
41
69
|
```
|
|
42
70
|
|
|
43
|
-
###
|
|
71
|
+
### 3. Prerender (`prerender/index.ts`)
|
|
72
|
+
|
|
44
73
|
```typescript
|
|
45
|
-
import {
|
|
46
|
-
import '@leadertechie/personal-site-kit/ui/banner';
|
|
74
|
+
import { WebsitePrerender } from '@leadertechie/personal-site-kit/prerender';
|
|
47
75
|
|
|
48
|
-
|
|
49
|
-
await store.init({ apiUrl: 'https://api.yourdomain.com' });
|
|
76
|
+
export default new WebsitePrerender();
|
|
50
77
|
```
|
|
51
78
|
|
|
79
|
+
## Client-Side Interactivity with md2interact
|
|
80
|
+
|
|
81
|
+
The kit automatically initializes `@leadertechie/md2interact` during `WebsiteUI.bootstrap()`. This enables:
|
|
82
|
+
|
|
83
|
+
### DOM Interaction Patterns
|
|
84
|
+
|
|
85
|
+
Markdown content with frontmatter can declare interaction patterns:
|
|
86
|
+
|
|
87
|
+
```markdown
|
|
88
|
+
---
|
|
89
|
+
interaction: poll
|
|
90
|
+
interactConfig:
|
|
91
|
+
url: /api/data
|
|
92
|
+
interval: 5000
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# Live Poll Data
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Supported patterns: `poll`, `live-update`, `click-toggle`, `infinite-scroll`, `form-live`, `mfe`, `custom`
|
|
99
|
+
|
|
100
|
+
### CSS Hydration
|
|
101
|
+
|
|
102
|
+
- **inlineCritical**: Injects critical CSS directly into the DOM
|
|
103
|
+
- **layerInjection**: Uses CSS `@layer` for organized style injection
|
|
104
|
+
- **themeToggle**: Enables light/dark theme toggling
|
|
105
|
+
|
|
106
|
+
### SPA Navigation
|
|
107
|
+
|
|
108
|
+
After dynamic content loads (e.g., blog detail pages), call `reinitInteract()` to re-scan the DOM:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const ui = WebsiteUI.getInstance();
|
|
112
|
+
ui.reinitInteract();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Caching Strategy (v2)
|
|
116
|
+
|
|
117
|
+
The kit uses `@leadertechie/r2tohtml` with a multi-tier caching strategy:
|
|
118
|
+
|
|
119
|
+
| Tier | Cache | TTL |
|
|
120
|
+
|------|-------|-----|
|
|
121
|
+
| In-Memory | `ContentCacheV2` with SWR | 5 min fresh, 30 min stale |
|
|
122
|
+
| Cloudflare Edge | `cfCache` | 5 min |
|
|
123
|
+
|
|
124
|
+
This means:
|
|
125
|
+
- **Fresh**: Served instantly from memory
|
|
126
|
+
- **Stale**: Served from memory while revalidating in background (SWR)
|
|
127
|
+
- **Miss**: Fetched from R2, cached at edge and in-memory
|
|
128
|
+
|
|
52
129
|
## Deployment
|
|
53
130
|
|
|
54
131
|
This kit is designed to be deployed to **Cloudflare**. Ensure your `wrangler.toml` is configured with an R2 bucket named `CONTENT_BUCKET`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ContentMetadata {
|
|
2
|
+
slug: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
summary?: string;
|
|
6
|
+
date: string;
|
|
7
|
+
imageUrl?: string;
|
|
8
|
+
tags?: string[];
|
|
9
|
+
author?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface BlogPost extends ContentMetadata {
|
|
12
|
+
content: string;
|
|
13
|
+
}
|
|
14
|
+
export interface StoryPost extends ContentMetadata {
|
|
15
|
+
content: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function getCachedOrFetch<T>(key: string, fetchFn: () => Promise<T>): Promise<T>;
|
|
18
|
+
export declare function clearContentCache(prefix?: string): void;
|
|
19
|
+
export declare function parseFrontmatter(content: string): {
|
|
20
|
+
metadata: ContentMetadata;
|
|
21
|
+
content: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function checkContentBucket(env: any): Response | null;
|
|
24
|
+
export declare function fetchContentItem(bucket: any, type: 'blogs' | 'stories', slug: string): Promise<BlogPost | StoryPost>;
|
|
25
|
+
export declare function fetchContentList(bucket: any, type: 'blogs' | 'stories', latest?: number): Promise<ContentMetadata[]>;
|
|
26
|
+
export declare function searchContent(bucket: any, query: string): Promise<(BlogPost | StoryPost)[]>;
|
|
27
|
+
//# sourceMappingURL=content-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-utils.d.ts","sourceRoot":"","sources":["../../src/api/content-utils.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAS,SAAQ,eAAe;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,eAAe;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AASD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAoBtF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAMhG;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,GAAG,IAAI,CAK5D;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAqB1H;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAiB1H;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC,CA2BjG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"about-me.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/about-me.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"about-me.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/about-me.ts"],"names":[],"mappings":"AAmDA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4DhE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-handler.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/auth-handler.ts"],"names":[],"mappings":"AAyBA,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgC/F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare const AUTH_KV = "auth_store";
|
|
2
|
+
declare const RATE_LIMIT_KV = "rate_limit";
|
|
3
|
+
declare const MAX_ATTEMPTS = 5;
|
|
4
|
+
declare const BASE_DELAY_MS = 1000;
|
|
5
|
+
interface AuthStore {
|
|
6
|
+
username: string;
|
|
7
|
+
passwordHash: string;
|
|
8
|
+
salt: string;
|
|
9
|
+
}
|
|
10
|
+
declare function hashPassword(password: string, salt: string): Promise<string>;
|
|
11
|
+
declare function generateSalt(): Promise<string>;
|
|
12
|
+
declare function checkRateLimit(env: any, ip: string): Promise<{
|
|
13
|
+
allowed: boolean;
|
|
14
|
+
delayMs: number;
|
|
15
|
+
}>;
|
|
16
|
+
declare function recordFailedAttempt(env: any, ip: string): Promise<void>;
|
|
17
|
+
declare function clearRateLimit(env: any, ip: string): Promise<void>;
|
|
18
|
+
declare function getAuthStore(env: any): Promise<AuthStore | null>;
|
|
19
|
+
declare function setupAuth(env: any, username: string, password: string): Promise<void>;
|
|
20
|
+
declare function verifyCredentials(env: any, username: string, password: string): Promise<boolean>;
|
|
21
|
+
declare function getClientIP(request: Request): string;
|
|
22
|
+
export { hashPassword, generateSalt, checkRateLimit, recordFailedAttempt, clearRateLimit, getAuthStore, setupAuth, verifyCredentials, getClientIP, AUTH_KV, RATE_LIMIT_KV, MAX_ATTEMPTS, BASE_DELAY_MS };
|
|
23
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/auth.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,OAAO,eAAe,CAAC;AAC7B,QAAA,MAAM,aAAa,eAAe,CAAC;AACnC,QAAA,MAAM,YAAY,IAAI,CAAC;AACvB,QAAA,MAAM,aAAa,OAAO,CAAC;AAG3B,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,iBAAe,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB3E;AAED,iBAAe,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAG7C;AAED,iBAAe,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA2BlG;AAED,iBAAe,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtE;AAED,iBAAe,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED,iBAAe,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAG/D;AAED,iBAAe,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF;AAED,iBAAe,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa/F;AAED,iBAAS,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAI7C;AAED,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,aAAa,EACd,CAAC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export declare function handleBlogs(env?: any, slug?: string, latest?: number): Promise<Response>;
|
|
2
2
|
export declare function handleStories(env?: any, slug?: string, latest?: number): Promise<Response>;
|
|
3
3
|
export declare function handleSearch(env?: any, query?: string): Promise<Response>;
|
|
4
|
-
export declare function clearContentCache(): void;
|
|
5
4
|
//# sourceMappingURL=content-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-api.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/content-api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"content-api.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/content-api.ts"],"names":[],"mappings":"AAWA,wBAAsB,WAAW,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmB9F;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmBhG;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkB/E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/content.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/content.ts"],"names":[],"mappings":"AAqBA,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqElG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/home.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/home.ts"],"names":[],"mappings":"AAwCA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwC7D"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function handleStaticDetails(env?: any, method?: string,
|
|
1
|
+
export declare function handleStaticDetails(env?: any, method?: string, _body?: any): Promise<Response>;
|
|
2
2
|
//# sourceMappingURL=static-details.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-details.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/static-details.ts"],"names":[],"mappings":"AAQA,wBAAsB,mBAAmB,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"static-details.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/static-details.ts"],"names":[],"mappings":"AAQA,wBAAsB,mBAAmB,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuCpG"}
|
package/dist/api/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { WebsiteAPI } from './website-api';
|
|
2
2
|
export { WebsiteAPI };
|
|
3
3
|
export type { APIHandler } from './website-api';
|
|
4
|
+
export * from './handlers/auth';
|
|
5
|
+
export * from './handlers/auth-handler';
|
|
4
6
|
declare const defaultAPI: WebsiteAPI;
|
|
5
7
|
export default defaultAPI;
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/api/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AAGxC,QAAA,MAAM,UAAU,YAAmB,CAAC;AACpC,eAAe,UAAU,CAAC"}
|
|
@@ -3,8 +3,8 @@ export declare class WebsiteAPI {
|
|
|
3
3
|
private customHandlers;
|
|
4
4
|
registerHandler(route: string, handler: APIHandler): void;
|
|
5
5
|
private addCORSHeaders;
|
|
6
|
+
private addAdminCORSHeaders;
|
|
6
7
|
private handleCORS;
|
|
7
|
-
private requireAuth;
|
|
8
8
|
fetch(request: Request, env: any): Promise<Response>;
|
|
9
9
|
}
|
|
10
10
|
//# sourceMappingURL=website-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"website-api.d.ts","sourceRoot":"","sources":["../../src/api/website-api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"website-api.d.ts","sourceRoot":"","sources":["../../src/api/website-api.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE3E,qBAAa,UAAU;IACrB,OAAO,CAAC,cAAc,CAAiC;IAEhD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;IAIzD,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,UAAU;IAqBL,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;CAkGlE"}
|
package/dist/api.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
import { W as WebsiteAPI } from "./chunks/website-api-
|
|
1
|
+
import { W as WebsiteAPI } from "./chunks/website-api-CFRUPu0X.js";
|
|
2
|
+
import { A, B, M, R, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-CFRUPu0X.js";
|
|
2
3
|
const defaultAPI = new WebsiteAPI();
|
|
3
4
|
export {
|
|
5
|
+
A as AUTH_KV,
|
|
6
|
+
B as BASE_DELAY_MS,
|
|
7
|
+
M as MAX_ATTEMPTS,
|
|
8
|
+
R as RATE_LIMIT_KV,
|
|
4
9
|
WebsiteAPI,
|
|
5
|
-
|
|
10
|
+
c as checkRateLimit,
|
|
11
|
+
a as clearRateLimit,
|
|
12
|
+
defaultAPI as default,
|
|
13
|
+
g as generateSalt,
|
|
14
|
+
b as getAuthStore,
|
|
15
|
+
d as getClientIP,
|
|
16
|
+
h as handleAuth,
|
|
17
|
+
e as hashPassword,
|
|
18
|
+
r as recordFailedAttempt,
|
|
19
|
+
s as setupAuth,
|
|
20
|
+
v as verifyCredentials
|
|
6
21
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<svg width="600" height="320" viewBox="0 0 600 320" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<style>
|
|
3
|
+
.text-main {
|
|
4
|
+
fill: light-dark(#4A5568, #E2E8F0);
|
|
5
|
+
font-family: 'Segoe UI', 'Arial Black', sans-serif;
|
|
6
|
+
font-weight: 900;
|
|
7
|
+
font-size: 46px;
|
|
8
|
+
letter-spacing: -0.01em;
|
|
9
|
+
text-anchor: middle;
|
|
10
|
+
}
|
|
11
|
+
</style>
|
|
12
|
+
|
|
13
|
+
<!-- Big circle placeholder -->
|
|
14
|
+
<circle cx="300" cy="160" r="100" fill="none" stroke="light-dark(#A0AEC0, #718096)" stroke-width="2" stroke-dasharray="8 4" opacity="0.5" />
|
|
15
|
+
|
|
16
|
+
<!-- Plus sign in circle -->
|
|
17
|
+
<line x1="300" y1="100" x2="300" y2="220" stroke="light-dark(#A0AEC0, #718096)" stroke-width="2" opacity="0.3" />
|
|
18
|
+
<line x1="240" y1="160" x2="360" y2="160" stroke="light-dark(#A0AEC0, #718096)" stroke-width="2" opacity="0.3" />
|
|
19
|
+
|
|
20
|
+
<text x="300" y="290" class="text-main">YOUR LOGO</text>
|
|
21
|
+
</svg>
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
class BasePageGenerator {
|
|
2
|
+
generateBanner(routes, siteTitle, logo) {
|
|
3
|
+
const navLinks = routes.map((r) => `<a href="${r.link}" class="nav-link" data-route="${r.link === "/" ? "home" : r.text.toLowerCase()}">${r.text}</a>`).join("");
|
|
4
|
+
return `
|
|
5
|
+
<my-banner header="${siteTitle}" logo="${logo}">
|
|
6
|
+
<theme-toggle slot="theme-switcher"></theme-toggle>
|
|
7
|
+
<nav slot="nav-links">
|
|
8
|
+
${navLinks}
|
|
9
|
+
</nav>
|
|
10
|
+
</my-banner>`;
|
|
11
|
+
}
|
|
12
|
+
generateFooter(footerLinks, copyright) {
|
|
13
|
+
return `
|
|
14
|
+
<my-footer
|
|
15
|
+
copyright="${copyright}"
|
|
16
|
+
footerlinks='${JSON.stringify(footerLinks)}'>
|
|
17
|
+
</my-footer>`;
|
|
18
|
+
}
|
|
19
|
+
wrapContent(banner, mainContent, footer) {
|
|
20
|
+
return `${banner}${mainContent}${footer}`;
|
|
21
|
+
}
|
|
22
|
+
generatePage(pathname, routes, footerLinks, staticDetails, apiUrl, baseUrl, mainContent, title, description) {
|
|
23
|
+
const logo = "/api/logo";
|
|
24
|
+
const banner = this.generateBanner(routes, staticDetails.siteTitle || "My Personal Website", logo);
|
|
25
|
+
const footer = this.generateFooter(footerLinks, staticDetails.copyright || "2026 My Personal Website");
|
|
26
|
+
const canonicalUrl = new URL(pathname, baseUrl).toString();
|
|
27
|
+
const content = this.wrapContent(banner, mainContent, footer);
|
|
28
|
+
return {
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
canonicalUrl,
|
|
32
|
+
content
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class HomePageGenerator extends BasePageGenerator {
|
|
37
|
+
generate(data) {
|
|
38
|
+
const { profile, homeContent, latestBlogs, latestStories, staticDetails, ...baseData } = data;
|
|
39
|
+
const name = profile?.name || "User";
|
|
40
|
+
const title = profile?.title || "Professional";
|
|
41
|
+
const homeHtml = homeContent || `<h1>Welcome to ${name}</h1><p>Upload home.md to customize this page.</p>`;
|
|
42
|
+
const blogGists = latestBlogs.map((b) => `<div class="gist-card"><a href="/blogs/${b.slug}"><h4>${b.title}</h4></a><p>${b.summary}</p><small>${b.date}</small></div>`).join("");
|
|
43
|
+
const storyGists = latestStories.map((s) => `<div class="gist-card"><a href="/stories/${s.slug}"><h4>${s.title}</h4></a><p>${s.summary}</p><small>${s.date}</small></div>`).join("");
|
|
44
|
+
const mainContent = `
|
|
45
|
+
<main class="container container-wide column-layout">
|
|
46
|
+
<div class="main-column">
|
|
47
|
+
${homeHtml}
|
|
48
|
+
</div>
|
|
49
|
+
<div class="sidebar-column">
|
|
50
|
+
<h3>Recent Blogs</h3>
|
|
51
|
+
${blogGists || "<p>No blogs yet.</p>"}
|
|
52
|
+
<h3 class="mt-2">Recent Stories</h3>
|
|
53
|
+
${storyGists || "<p>No stories yet.</p>"}
|
|
54
|
+
</div>
|
|
55
|
+
</main>`;
|
|
56
|
+
return this.generatePage(
|
|
57
|
+
baseData.pathname,
|
|
58
|
+
baseData.routes,
|
|
59
|
+
baseData.footerLinks,
|
|
60
|
+
staticDetails,
|
|
61
|
+
baseData.apiUrl,
|
|
62
|
+
baseData.baseUrl,
|
|
63
|
+
mainContent,
|
|
64
|
+
`${name} – ${title}`,
|
|
65
|
+
`Welcome to ${name}'s personal website. Professional portfolio and content.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class AboutPageGenerator extends BasePageGenerator {
|
|
70
|
+
generate(data) {
|
|
71
|
+
const { profile, staticDetails, ...baseData } = data;
|
|
72
|
+
const name = profile?.name || "User";
|
|
73
|
+
const mainContent = `
|
|
74
|
+
<main class="container container-narrow">
|
|
75
|
+
<my-aboutme base-url="${baseData.apiUrl}"></my-aboutme>
|
|
76
|
+
</main>`;
|
|
77
|
+
return this.generatePage(
|
|
78
|
+
baseData.pathname,
|
|
79
|
+
baseData.routes,
|
|
80
|
+
baseData.footerLinks,
|
|
81
|
+
staticDetails,
|
|
82
|
+
baseData.apiUrl,
|
|
83
|
+
baseData.baseUrl,
|
|
84
|
+
mainContent,
|
|
85
|
+
`About - ${name}`,
|
|
86
|
+
`Learn more about ${name}'s experience and skills.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
class BlogsListPageGenerator extends BasePageGenerator {
|
|
91
|
+
generate(data) {
|
|
92
|
+
const { latestBlogs, name, staticDetails, ...baseData } = data;
|
|
93
|
+
const blogGists = latestBlogs.map((b) => `<div class="gist-card"><a href="/blogs/${b.slug}"><h4>${b.title}</h4></a><p>${b.summary}</p><small>${b.date}</small></div>`).join("");
|
|
94
|
+
const mainContent = `
|
|
95
|
+
<main class="container container-wide">
|
|
96
|
+
<h1>Blogs</h1>
|
|
97
|
+
<input type="text" placeholder="Search blogs..." class="search-input" />
|
|
98
|
+
<div class="blog-list">
|
|
99
|
+
${blogGists || "<p>No blogs yet.</p>"}
|
|
100
|
+
</div>
|
|
101
|
+
</main>`;
|
|
102
|
+
return this.generatePage(
|
|
103
|
+
baseData.pathname,
|
|
104
|
+
baseData.routes,
|
|
105
|
+
baseData.footerLinks,
|
|
106
|
+
staticDetails,
|
|
107
|
+
baseData.apiUrl,
|
|
108
|
+
baseData.baseUrl,
|
|
109
|
+
mainContent,
|
|
110
|
+
`Blogs – ${name}`,
|
|
111
|
+
"Read the latest blog posts."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
class StoriesListPageGenerator extends BasePageGenerator {
|
|
116
|
+
generate(data) {
|
|
117
|
+
const { latestStories, name, staticDetails, ...baseData } = data;
|
|
118
|
+
const storyGists = latestStories.map((s) => `<div class="gist-card"><a href="/stories/${s.slug}"><h4>${s.title}</h4></a><p>${s.summary}</p><small>${s.date}</small></div>`).join("");
|
|
119
|
+
const mainContent = `
|
|
120
|
+
<main class="container container-wide">
|
|
121
|
+
<h1>Stories</h1>
|
|
122
|
+
<input type="text" placeholder="Search stories..." class="search-input" />
|
|
123
|
+
<div class="story-list">
|
|
124
|
+
${storyGists || "<p>No stories yet.</p>"}
|
|
125
|
+
</div>
|
|
126
|
+
</main>`;
|
|
127
|
+
return this.generatePage(
|
|
128
|
+
baseData.pathname,
|
|
129
|
+
baseData.routes,
|
|
130
|
+
baseData.footerLinks,
|
|
131
|
+
staticDetails,
|
|
132
|
+
baseData.apiUrl,
|
|
133
|
+
baseData.baseUrl,
|
|
134
|
+
mainContent,
|
|
135
|
+
`Stories – ${name}`,
|
|
136
|
+
"Read the latest stories."
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
class BlogDetailPageGenerator extends BasePageGenerator {
|
|
141
|
+
generate(data) {
|
|
142
|
+
const { slug, staticDetails, ...baseData } = data;
|
|
143
|
+
const mainContent = `
|
|
144
|
+
<main class="container container-narrow">
|
|
145
|
+
<my-blog-viewer slug="${slug}"></my-blog-viewer>
|
|
146
|
+
</main>`;
|
|
147
|
+
return this.generatePage(
|
|
148
|
+
baseData.pathname,
|
|
149
|
+
baseData.routes,
|
|
150
|
+
baseData.footerLinks,
|
|
151
|
+
staticDetails,
|
|
152
|
+
baseData.apiUrl,
|
|
153
|
+
baseData.baseUrl,
|
|
154
|
+
mainContent,
|
|
155
|
+
`Blog: ${slug}`,
|
|
156
|
+
"Blog post"
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
class StoryDetailPageGenerator extends BasePageGenerator {
|
|
161
|
+
generate(data) {
|
|
162
|
+
const { slug, staticDetails, ...baseData } = data;
|
|
163
|
+
const mainContent = `
|
|
164
|
+
<main class="container container-narrow">
|
|
165
|
+
<my-story-viewer slug="${slug}"></my-story-viewer>
|
|
166
|
+
</main>`;
|
|
167
|
+
return this.generatePage(
|
|
168
|
+
baseData.pathname,
|
|
169
|
+
baseData.routes,
|
|
170
|
+
baseData.footerLinks,
|
|
171
|
+
staticDetails,
|
|
172
|
+
baseData.apiUrl,
|
|
173
|
+
baseData.baseUrl,
|
|
174
|
+
mainContent,
|
|
175
|
+
`Story: ${slug}`,
|
|
176
|
+
"Story post"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
class NotFoundPageGenerator extends BasePageGenerator {
|
|
181
|
+
generate(data) {
|
|
182
|
+
const { staticDetails, ...baseData } = data;
|
|
183
|
+
const mainContent = `
|
|
184
|
+
<main class="container container-narrow text-center">
|
|
185
|
+
<h1>Page Not Found</h1>
|
|
186
|
+
<p>The page you're looking for doesn't exist.</p>
|
|
187
|
+
<p><a href="/">Return to home</a></p>
|
|
188
|
+
</main>`;
|
|
189
|
+
return this.generatePage(
|
|
190
|
+
baseData.pathname,
|
|
191
|
+
baseData.routes,
|
|
192
|
+
baseData.footerLinks,
|
|
193
|
+
staticDetails,
|
|
194
|
+
baseData.apiUrl,
|
|
195
|
+
baseData.baseUrl,
|
|
196
|
+
mainContent,
|
|
197
|
+
"404 Not Found",
|
|
198
|
+
"The page you requested could not be found."
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
AboutPageGenerator,
|
|
204
|
+
BasePageGenerator,
|
|
205
|
+
BlogDetailPageGenerator,
|
|
206
|
+
BlogsListPageGenerator,
|
|
207
|
+
HomePageGenerator,
|
|
208
|
+
NotFoundPageGenerator,
|
|
209
|
+
StoriesListPageGenerator,
|
|
210
|
+
StoryDetailPageGenerator
|
|
211
|
+
};
|