@leadertechie/personal-site-kit 0.1.0-alpha.8 → 0.1.0-alpha.9

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.
Files changed (93) hide show
  1. package/dist/api/content-utils.d.ts +27 -0
  2. package/dist/api/content-utils.d.ts.map +1 -0
  3. package/dist/api/handlers/content-api.d.ts +0 -1
  4. package/dist/api/handlers/content-api.d.ts.map +1 -1
  5. package/dist/api.js +2 -2
  6. package/dist/chunks/{index-CYd_Pe2U.js → index-CnSEOZse.js} +81 -121
  7. package/dist/chunks/{template-D1uGvdWZ.js → template-DWcsZW22.js} +1 -1
  8. package/dist/chunks/{website-api-FLejlWxJ.js → website-api-BEYGOsT3.js} +90 -131
  9. package/dist/index.js +3 -3
  10. package/dist/shared.js +1 -1
  11. package/dist/ui/admin/index.d.ts +8 -0
  12. package/dist/ui/admin/index.d.ts.map +1 -1
  13. package/dist/ui.js +1 -1
  14. package/package.json +4 -4
  15. package/src/api/__tests__/info.test.ts +0 -44
  16. package/src/api/__tests__/utils.test.ts +0 -78
  17. package/src/api/handlers/about-me.ts +0 -109
  18. package/src/api/handlers/auth-handler.ts +0 -204
  19. package/src/api/handlers/auth.ts +0 -157
  20. package/src/api/handlers/content-api.ts +0 -268
  21. package/src/api/handlers/content.ts +0 -139
  22. package/src/api/handlers/home.ts +0 -79
  23. package/src/api/handlers/info.ts +0 -12
  24. package/src/api/handlers/logo.ts +0 -55
  25. package/src/api/handlers/static-details.ts +0 -48
  26. package/src/api/index.ts +0 -9
  27. package/src/api/utils.ts +0 -16
  28. package/src/api/website-api.ts +0 -142
  29. package/src/index.ts +0 -4
  30. package/src/prerender/__tests__/page-content.test.ts +0 -44
  31. package/src/prerender/__tests__/template.test.ts +0 -54
  32. package/src/prerender/data-fetcher.ts +0 -93
  33. package/src/prerender/index.ts +0 -7
  34. package/src/prerender/page-content.ts +0 -266
  35. package/src/prerender/page-generators/about.ts +0 -38
  36. package/src/prerender/page-generators/base.ts +0 -77
  37. package/src/prerender/page-generators/blog-detail.ts +0 -35
  38. package/src/prerender/page-generators/blogs-list.ts +0 -43
  39. package/src/prerender/page-generators/home.ts +0 -54
  40. package/src/prerender/page-generators/index.ts +0 -8
  41. package/src/prerender/page-generators/not-found.ts +0 -36
  42. package/src/prerender/page-generators/stories-list.ts +0 -43
  43. package/src/prerender/page-generators/story-detail.ts +0 -35
  44. package/src/prerender/prerender.ts +0 -25
  45. package/src/prerender/template.ts +0 -65
  46. package/src/prerender/website-prerender.ts +0 -152
  47. package/src/shared/config/api.ts +0 -16
  48. package/src/shared/config/index.ts +0 -43
  49. package/src/shared/config/types.ts +0 -16
  50. package/src/shared/core/__tests__/theme-toggle.test.ts +0 -204
  51. package/src/shared/core/site-store.ts +0 -38
  52. package/src/shared/core/theme-toggle.ts +0 -118
  53. package/src/shared/index.ts +0 -17
  54. package/src/shared/interfaces/ifooter-link.ts +0 -4
  55. package/src/shared/interfaces/iroute.ts +0 -4
  56. package/src/shared/models/theme-variables.css +0 -25
  57. package/src/shared/page-content.ts +0 -210
  58. package/src/shared/router.ts +0 -250
  59. package/src/shared/runtime.ts +0 -11
  60. package/src/shared/template.ts +0 -35
  61. package/src/shared/website-ui.ts +0 -92
  62. package/src/styles/markdown.css +0 -129
  63. package/src/ui/about-me/api.ts +0 -12
  64. package/src/ui/about-me/index.ts +0 -121
  65. package/src/ui/about-me/styles.ts +0 -85
  66. package/src/ui/admin/api.ts +0 -93
  67. package/src/ui/admin/components/AboutMeSection.ts +0 -47
  68. package/src/ui/admin/components/AdminSection.ts +0 -134
  69. package/src/ui/admin/components/BlogsSection.ts +0 -62
  70. package/src/ui/admin/components/HomeSection.ts +0 -47
  71. package/src/ui/admin/components/ImagesSection.ts +0 -54
  72. package/src/ui/admin/components/LoginForm.ts +0 -116
  73. package/src/ui/admin/components/LogoSection.ts +0 -51
  74. package/src/ui/admin/components/ProfileSection.ts +0 -47
  75. package/src/ui/admin/components/StaticSection.ts +0 -67
  76. package/src/ui/admin/components/StoriesSection.ts +0 -62
  77. package/src/ui/admin/components/index.ts +0 -10
  78. package/src/ui/admin/index.ts +0 -413
  79. package/src/ui/admin/styles.ts +0 -270
  80. package/src/ui/admin/types.ts +0 -26
  81. package/src/ui/banner/index.ts +0 -38
  82. package/src/ui/banner/styles.ts +0 -95
  83. package/src/ui/blog-viewer/__tests__/blogviewer.test.ts +0 -7
  84. package/src/ui/blog-viewer/index.ts +0 -127
  85. package/src/ui/blog-viewer/styles.ts +0 -23
  86. package/src/ui/footer/index.ts +0 -37
  87. package/src/ui/footer/styles.ts +0 -50
  88. package/src/ui/index.ts +0 -13
  89. package/src/ui/story-viewer/__tests__/storyviewer.test.ts +0 -7
  90. package/src/ui/story-viewer/index.ts +0 -123
  91. package/src/ui/story-viewer/styles.ts +0 -54
  92. /package/{src/shared → dist}/styles/markdown.css +0 -0
  93. /package/{src → dist}/styles/theme.css +0 -0
@@ -1,142 +0,0 @@
1
- import { createErrorResponse } from './utils';
2
- import { handleAboutMe, clearContentCache } from './handlers/about-me';
3
- import { handleHome } from './handlers/home';
4
- import { handleInfo } from './handlers/info';
5
- import { handleContent } from './handlers/content';
6
- import { handleAuth } from './handlers/auth-handler';
7
- import { handleBlogs, handleStories, handleSearch } from './handlers/content-api';
8
- import { handleLogo } from './handlers/logo';
9
- import { handleStaticDetails } from './handlers/static-details';
10
- import { getAuthStore } from './handlers/auth';
11
-
12
- export type APIHandler = (request: Request, env: any) => Promise<Response>;
13
-
14
- export class WebsiteAPI {
15
- private customHandlers = new Map<string, APIHandler>();
16
-
17
- public registerHandler(route: string, handler: APIHandler) {
18
- this.customHandlers.set(route, handler);
19
- }
20
-
21
- private addCORSHeaders(response: Response): Response {
22
- response.headers.set('Access-Control-Allow-Origin', '*' );
23
- response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS' );
24
- response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Session-Token');
25
- return response;
26
- }
27
-
28
- private addAdminCORSHeaders(response: Response, origin: string): Response {
29
- const allowOrigin = origin && (origin.includes('localhost') || origin.includes('127.0.0.1'))
30
- ? origin
31
- : 'same-origin';
32
- response.headers.set('Access-Control-Allow-Origin', allowOrigin);
33
- response.headers.set('Access-Control-Allow-Credentials', 'true');
34
- response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
35
- response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Session-Token');
36
- return response;
37
- }
38
-
39
- private handleCORS(origin: string): Response {
40
- const allowOrigin = origin && (origin.includes('localhost') || origin.includes('127.0.0.1'))
41
- ? origin
42
- : '*';
43
- return new Response(null, {
44
- status: 200,
45
- headers: {
46
- 'Access-Control-Allow-Origin': allowOrigin ,
47
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS' ,
48
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Session-Token',
49
- 'Access-Control-Allow-Credentials': 'true',
50
- 'Access-Control-Max-Age': '86400',
51
- },
52
- });
53
- }
54
-
55
- public async fetch(request: Request, env: any): Promise<Response> {
56
- const url = new URL(request.url);
57
- const origin = request.headers.get('Origin') || url.origin;
58
-
59
- if (request.method === 'OPTIONS') {
60
- return this.handleCORS(origin);
61
- }
62
-
63
- const pathname = url.pathname;
64
- const route = pathname
65
- .replace(/^\/api\//, '')
66
- .replace(/^\//, '')
67
- .replace(/\/+$/, '');
68
-
69
- // Check custom handlers first
70
- if (this.customHandlers.has(route)) {
71
- const handler = this.customHandlers.get(route)!;
72
- return this.addCORSHeaders(await handler(request, env));
73
- }
74
-
75
- try {
76
- // Check for content route first (content/*)
77
- if (route === 'content' || route.startsWith('content/')) {
78
- const subpath = route.replace(/^content\/?/, '');
79
- return this.addAdminCORSHeaders(await handleContent(request, env, subpath), origin);
80
- }
81
-
82
- // Check for auth route (auth/*)
83
- if (route === 'auth' || route.startsWith('auth/')) {
84
- const subpath = route.replace(/^auth\/?/, '');
85
- return this.addAdminCORSHeaders(await handleAuth(request, env, subpath || '/'), origin);
86
- }
87
-
88
- switch (route) {
89
- case 'info':
90
- return this.addCORSHeaders(await handleInfo());
91
- case 'home':
92
- return this.addCORSHeaders(await handleHome(env));
93
- case 'cache-clear':
94
- const cookieHeader = request.headers.get('Cookie');
95
- const sessionToken = cookieHeader?.split(';')
96
- .find(c => c.trim().startsWith('session='))
97
- ?.split('=')[1];
98
- const session = sessionToken ? await env.KV.get(`session:${sessionToken}`, 'json') : null;
99
- if (!session || session.expiresAt < Date.now()) {
100
- return this.addAdminCORSHeaders(createErrorResponse('Unauthorized', 401), origin);
101
- }
102
- clearContentCache();
103
- return this.addAdminCORSHeaders(new Response(JSON.stringify({ success: true, message: 'Cache cleared' }), { status: 200 }), origin);
104
- case 'aboutme':
105
- return this.addCORSHeaders(await handleAboutMe(env));
106
- case 'logo':
107
- return this.addCORSHeaders(await handleLogo(env));
108
- case 'static':
109
- return this.addCORSHeaders(await handleStaticDetails(env));
110
- case 'blogs':
111
- return this.addCORSHeaders(await handleBlogs(env));
112
- case 'blogs/latest':
113
- const latestCount = url.searchParams.get('count');
114
- return this.addCORSHeaders(await handleBlogs(env, undefined, latestCount ? parseInt(latestCount) : 5));
115
- default:
116
- if (route.startsWith('blogs/')) {
117
- const slug = route.replace('blogs/', '');
118
- return this.addCORSHeaders(await handleBlogs(env, slug));
119
- }
120
- if (route.startsWith('stories')) {
121
- if (route === 'stories') {
122
- return this.addCORSHeaders(await handleStories(env));
123
- }
124
- if (route === 'stories/latest') {
125
- const latestCount = url.searchParams.get('count');
126
- return this.addCORSHeaders(await handleStories(env, undefined, latestCount ? parseInt(latestCount) : 5));
127
- }
128
- const slug = route.replace('stories/', '');
129
- return this.addCORSHeaders(await handleStories(env, slug));
130
- }
131
- if (route === 'search') {
132
- const query = url.searchParams.get('q');
133
- return this.addCORSHeaders(await handleSearch(env, query || undefined));
134
- }
135
- return this.addCORSHeaders(createErrorResponse('Route not found', 404));
136
- }
137
- } catch (error) {
138
- console.error('API Error:', error);
139
- return this.addCORSHeaders(createErrorResponse('Internal server error', 500));
140
- }
141
- }
142
- }
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from './api';
2
- export * from './prerender';
3
- export * from './ui';
4
- export * from './shared';
@@ -1,44 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { generatePageContent } from '../page-content';
3
-
4
- describe('generatePageContent', () => {
5
- const mockRoutes = [
6
- { link: '/', text: 'Home' },
7
- { link: '/about-me', text: 'About' }
8
- ];
9
- const mockFooterLinks = [
10
- { text: 'Link', link: '#' }
11
- ];
12
- const mockEnv = {
13
- apiUrl: 'https://api.example.com',
14
- baseUrl: 'https://www.example.com'
15
- };
16
-
17
- beforeEach(() => {
18
- vi.clearAllMocks();
19
- });
20
-
21
- it('should generate home page content', async () => {
22
- const result = await generatePageContent('/', mockRoutes, mockFooterLinks, mockEnv);
23
-
24
- expect(result.title).toContain('User');
25
- expect(result.canonicalUrl).toBe('https://www.example.com/');
26
- expect(result.content).toContain('<my-banner');
27
- });
28
-
29
- it('should generate about-me page content', async () => {
30
- const result = await generatePageContent('/about-me', mockRoutes, mockFooterLinks, mockEnv);
31
-
32
- expect(result.title).toContain('About');
33
- expect(result.canonicalUrl).toBe('https://www.example.com/about-me');
34
- expect(result.content).toContain('<my-aboutme');
35
- });
36
-
37
- it('should generate 404 page content', async () => {
38
- const result = await generatePageContent('/non-existent', mockRoutes, mockFooterLinks, mockEnv);
39
-
40
- expect(result.title).toContain('Not Found');
41
- expect(result.canonicalUrl).toBe('https://www.example.com/non-existent');
42
- expect(result.content).toContain('Page Not Found');
43
- });
44
- });
@@ -1,54 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createHtmlTemplate } from '../template';
3
-
4
- describe('createHtmlTemplate', () => {
5
- it('should generate complete HTML template', async () => {
6
- const props = {
7
- title: 'Test Title',
8
- description: 'Test Description',
9
- canonicalUrl: 'https://example.com/test',
10
- content: '<main>Test Content</main>'
11
- };
12
-
13
- const html = await createHtmlTemplate(props);
14
-
15
- expect(html).toContain('<!doctype html>');
16
- expect(html).toContain('<title>Test Title</title>');
17
- expect(html).toContain('<meta name="description" content="Test Description" />');
18
- expect(html).toContain('<meta property="og:title" content="Test Title" />');
19
- expect(html).toContain('<link rel="canonical" href="https://example.com/test" />');
20
- expect(html).toContain('<main>Test Content</main>');
21
- });
22
-
23
- it('should escape HTML entities properly', async () => {
24
- const props = {
25
- title: 'Test & Title',
26
- description: 'Test "Description"',
27
- canonicalUrl: 'https://example.com/test',
28
- content: '<main>Test Content</main>'
29
- };
30
-
31
- const html = await createHtmlTemplate(props);
32
-
33
- expect(html).toContain('<title>Test & Title</title>');
34
- expect(html).toContain('<meta name="description" content="Test "Description"" />');
35
- });
36
-
37
- it('should have proper HTML structure', async () => {
38
- const props = {
39
- title: 'Test',
40
- description: 'Test',
41
- canonicalUrl: 'https://example.com',
42
- content: '<div>Content</div>'
43
- };
44
-
45
- const html = await createHtmlTemplate(props);
46
-
47
- expect(html).toMatch(/^<!doctype html>/);
48
- expect(html).toContain('<html lang="en" data-theme="light">');
49
- expect(html).toContain('<head>');
50
- expect(html).toContain('<body>');
51
- expect(html).toContain('<div id="app">');
52
- expect(html).toContain('</html>');
53
- });
54
- });
@@ -1,93 +0,0 @@
1
- import { R2ContentLoader } from "@leadertechie/r2tohtml";
2
-
3
- export interface Profile {
4
- name: string;
5
- title: string;
6
- experience: string;
7
- profileImageUrl: string;
8
- }
9
-
10
- export interface BlogMeta {
11
- slug: string;
12
- title: string;
13
- summary: string;
14
- tags: string[];
15
- date: string;
16
- }
17
-
18
- let loader: R2ContentLoader | null = null;
19
-
20
- function getLoader(env: any): R2ContentLoader | null {
21
- if (!loader) {
22
- if (!env?.CONTENT_BUCKET) return null;
23
- loader = new R2ContentLoader(
24
- { bucket: env.CONTENT_BUCKET, cacheTTL: 5 * 60 * 1000 },
25
- { md2html: { imagePathPrefix: "images/", styleOptions: { classPrefix: "md-", addHeadingIds: true } } }
26
- );
27
- }
28
- return loader;
29
- }
30
-
31
- export async function fetchProfile(env: any): Promise<Profile | null> {
32
- try {
33
- const r2 = getLoader(env);
34
- if (!r2) return null;
35
- const obj = await r2.getObject("profile.json");
36
- if (!obj) return null;
37
- return await obj.json() as Profile;
38
- } catch { return null; }
39
- }
40
-
41
- export async function fetchAboutMe(env: any): Promise<string> {
42
- try {
43
- const r2 = getLoader(env);
44
- if (!r2) return "";
45
- const result = await r2.getRendered("about-me.md");
46
- return result?.content || "";
47
- } catch { return ""; }
48
- }
49
-
50
- export async function fetchHome(env: any): Promise<string> {
51
- try {
52
- const r2 = getLoader(env);
53
- if (!r2) return "";
54
- const result = await r2.getRendered("pages/home.md");
55
- return result?.content || "";
56
- } catch { return ""; }
57
- }
58
-
59
- export async function fetchLatestBlogSummaries(env: any, count: number = 3): Promise<BlogMeta[]> {
60
- try {
61
- const r2 = getLoader(env);
62
- if (!r2) return [];
63
- const list = await r2.list("blogs/");
64
- const metas: BlogMeta[] = [];
65
- for (const obj of list.objects) {
66
- if (obj.key.endsWith(".json")) {
67
- try {
68
- const metaObj = await r2.getObject(obj.key);
69
- if (metaObj) metas.push(await metaObj.json() as BlogMeta);
70
- } catch {}
71
- }
72
- }
73
- return metas.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()).slice(0, count);
74
- } catch { return []; }
75
- }
76
-
77
- export async function fetchLatestStorySummaries(env: any, count: number = 3): Promise<BlogMeta[]> {
78
- try {
79
- const r2 = getLoader(env);
80
- if (!r2) return [];
81
- const list = await r2.list("stories/");
82
- const metas: BlogMeta[] = [];
83
- for (const obj of list.objects) {
84
- if (obj.key.endsWith(".json")) {
85
- try {
86
- const metaObj = await r2.getObject(obj.key);
87
- if (metaObj) metas.push(await metaObj.json() as BlogMeta);
88
- } catch {}
89
- }
90
- }
91
- return metas.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()).slice(0, count);
92
- } catch { return []; }
93
- }
@@ -1,7 +0,0 @@
1
- import { WebsitePrerender } from './website-prerender';
2
- export { WebsitePrerender };
3
- export type { PrerenderOptions } from './website-prerender';
4
-
5
- // Default worker export using WebsitePrerender
6
- const defaultPrerender = new WebsitePrerender();
7
- export default defaultPrerender;
@@ -1,266 +0,0 @@
1
- import { R2ContentLoader } from "@leadertechie/r2tohtml";
2
-
3
- interface Profile {
4
- name: string;
5
- title: string;
6
- experience: string;
7
- profileImageUrl: string;
8
- }
9
-
10
- export interface IRoute {
11
- text: string;
12
- link: string;
13
- }
14
-
15
- export interface IFooterLink {
16
- text: string;
17
- link: string;
18
- }
19
-
20
- export interface PageContent {
21
- title: string;
22
- description: string;
23
- canonicalUrl: string;
24
- content: string;
25
- }
26
-
27
- interface BlogMeta {
28
- slug: string;
29
- title: string;
30
- summary: string;
31
- tags: string[];
32
- date: string;
33
- }
34
-
35
- let loader: R2ContentLoader | null = null;
36
-
37
- function getLoader(env: any): R2ContentLoader | null {
38
- if (!loader) {
39
- if (!env?.CONTENT_BUCKET) return null;
40
- loader = new R2ContentLoader(
41
- { bucket: env.CONTENT_BUCKET, cacheTTL: 5 * 60 * 1000 },
42
- { md2html: { imagePathPrefix: "images/", styleOptions: { classPrefix: "md-", addHeadingIds: true } } }
43
- );
44
- }
45
- return loader;
46
- }
47
-
48
- async function fetchProfile(env: any): Promise<Profile | null> {
49
- try {
50
- const r2 = getLoader(env);
51
- if (!r2) return null;
52
- const obj = await r2.getObject("profile.json");
53
- if (!obj) return null;
54
- return await obj.json() as Profile;
55
- } catch { return null; }
56
- }
57
-
58
- async function fetchAboutMe(env: any): Promise<string> {
59
- try {
60
- const r2 = getLoader(env);
61
- if (!r2) return "";
62
- const result = await r2.getRendered("about-me.md");
63
- return result?.content || "";
64
- } catch { return ""; }
65
- }
66
-
67
- async function fetchHome(env: any): Promise<string> {
68
- try {
69
- const r2 = getLoader(env);
70
- if (!r2) return "";
71
- const result = await r2.getRendered("pages/home.md");
72
- return result?.content || "";
73
- } catch { return ""; }
74
- }
75
-
76
- async function fetchLatestBlogSummaries(env: any, count: number = 3): Promise<BlogMeta[]> {
77
- try {
78
- const r2 = getLoader(env);
79
- if (!r2) return [];
80
- const list = await r2.list("blogs/");
81
- const metas: BlogMeta[] = [];
82
- for (const obj of list.objects) {
83
- if (obj.key.endsWith(".json")) {
84
- try {
85
- const metaObj = await r2.getObject(obj.key);
86
- if (metaObj) metas.push(await metaObj.json() as BlogMeta);
87
- } catch {}
88
- }
89
- }
90
- return metas.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()).slice(0, count);
91
- } catch { return []; }
92
- }
93
-
94
- async function fetchLatestStorySummaries(env: any, count: number = 3): Promise<BlogMeta[]> {
95
- try {
96
- const r2 = getLoader(env);
97
- if (!r2) return [];
98
- const list = await r2.list("stories/");
99
- const metas: BlogMeta[] = [];
100
- for (const obj of list.objects) {
101
- if (obj.key.endsWith(".json")) {
102
- try {
103
- const metaObj = await r2.getObject(obj.key);
104
- if (metaObj) metas.push(await metaObj.json() as BlogMeta);
105
- } catch {}
106
- }
107
- }
108
- return metas.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()).slice(0, count);
109
- } catch { return []; }
110
- }
111
-
112
- export const generatePageContent = async (
113
- pathname: string,
114
- routes: IRoute[],
115
- footerLinks: IFooterLink[],
116
- env?: any
117
- ): Promise<PageContent> => {
118
- const apiUrl = env?.API_URL || env?.apiUrl || "https://api.example.com";
119
- const baseUrl = env?.BASE_URL || env?.baseUrl || "https://www.example.com";
120
-
121
- let staticDetails = {
122
- siteTitle: "My Personal Website",
123
- copyright: "2026 My Personal Website",
124
- linkedin: "https://linkedin.com/in/yourname",
125
- github: "https://github.com/yourname",
126
- email: "yourname@domain.com"
127
- };
128
-
129
- try {
130
- const res = await fetch(`${apiUrl}/api/static`);
131
- if (res.ok) staticDetails = await res.json();
132
- } catch (e) {}
133
-
134
- let profile: Profile | null = null;
135
- let aboutMeContent = "";
136
- let homeContent = "";
137
- let latestBlogs: BlogMeta[] = [];
138
- let latestStories: BlogMeta[] = [];
139
-
140
- if (env?.CONTENT_BUCKET) {
141
- [profile, aboutMeContent, homeContent, latestBlogs, latestStories] = await Promise.all([
142
- fetchProfile(env), fetchAboutMe(env), fetchHome(env), fetchLatestBlogSummaries(env, 3), fetchLatestStorySummaries(env, 3)
143
- ]);
144
- }
145
-
146
- const name = profile?.name || "User";
147
- const title = profile?.title || "Professional";
148
- const canonicalUrl = new URL(pathname, baseUrl).toString();
149
-
150
- // Strategy pattern: map pathname patterns to generators
151
- const strategies = {
152
- home: async () => {
153
- const { HomePageGenerator } = await import('./page-generators');
154
- const generator = new HomePageGenerator();
155
- return generator.generate({
156
- routes,
157
- footerLinks,
158
- staticDetails,
159
- apiUrl,
160
- baseUrl,
161
- pathname,
162
- profile,
163
- homeContent,
164
- latestBlogs,
165
- latestStories
166
- });
167
- },
168
- about: async () => {
169
- const { AboutPageGenerator } = await import('./page-generators');
170
- const generator = new AboutPageGenerator();
171
- return generator.generate({
172
- routes,
173
- footerLinks,
174
- staticDetails,
175
- apiUrl,
176
- baseUrl,
177
- pathname,
178
- profile
179
- });
180
- },
181
- blogsList: async () => {
182
- const { BlogsListPageGenerator } = await import('./page-generators');
183
- const generator = new BlogsListPageGenerator();
184
- return generator.generate({
185
- routes,
186
- footerLinks,
187
- staticDetails,
188
- apiUrl,
189
- baseUrl,
190
- pathname,
191
- latestBlogs,
192
- name
193
- });
194
- },
195
- storiesList: async () => {
196
- const { StoriesListPageGenerator } = await import('./page-generators');
197
- const generator = new StoriesListPageGenerator();
198
- return generator.generate({
199
- routes,
200
- footerLinks,
201
- staticDetails,
202
- apiUrl,
203
- baseUrl,
204
- pathname,
205
- latestStories,
206
- name
207
- });
208
- },
209
- blogDetail: async (slug: string) => {
210
- const { BlogDetailPageGenerator } = await import('./page-generators');
211
- const generator = new BlogDetailPageGenerator();
212
- return generator.generate({
213
- routes,
214
- footerLinks,
215
- staticDetails,
216
- apiUrl,
217
- baseUrl,
218
- pathname,
219
- slug
220
- });
221
- },
222
- storyDetail: async (slug: string) => {
223
- const { StoryDetailPageGenerator } = await import('./page-generators');
224
- const generator = new StoryDetailPageGenerator();
225
- return generator.generate({
226
- routes,
227
- footerLinks,
228
- staticDetails,
229
- apiUrl,
230
- baseUrl,
231
- pathname,
232
- slug
233
- });
234
- },
235
- notFound: async () => {
236
- const { NotFoundPageGenerator } = await import('./page-generators');
237
- const generator = new NotFoundPageGenerator();
238
- return generator.generate({
239
- routes,
240
- footerLinks,
241
- staticDetails,
242
- apiUrl,
243
- baseUrl,
244
- pathname
245
- });
246
- }
247
- };
248
-
249
- if (pathname === "/" || pathname === "") {
250
- return strategies.home();
251
- } else if (pathname === "/about-me") {
252
- return strategies.about();
253
- } else if (pathname === "/blogs" || pathname === "/blogs/") {
254
- return strategies.blogsList();
255
- } else if (pathname === "/stories" || pathname === "/stories/") {
256
- return strategies.storiesList();
257
- } else if (pathname.startsWith("/blogs/")) {
258
- const slug = pathname.replace("/blogs/", "").replace("/", "");
259
- return strategies.blogDetail(slug);
260
- } else if (pathname.startsWith("/stories/")) {
261
- const slug = pathname.replace("/stories/", "").replace("/", "");
262
- return strategies.storyDetail(slug);
263
- } else {
264
- return strategies.notFound();
265
- }
266
- };
@@ -1,38 +0,0 @@
1
- import { BasePageGenerator, StaticDetails } from './base';
2
- import { IRoute, IFooterLink, PageContent } from '../page-content';
3
- import { Profile } from '../data-fetcher';
4
-
5
- export interface AboutPageData {
6
- routes: IRoute[];
7
- footerLinks: IFooterLink[];
8
- staticDetails: StaticDetails;
9
- apiUrl: string;
10
- baseUrl: string;
11
- pathname: string;
12
- profile: Profile | null;
13
- }
14
-
15
- export class AboutPageGenerator extends BasePageGenerator {
16
- public generate(data: AboutPageData): PageContent {
17
- const { profile, staticDetails, ...baseData } = data;
18
-
19
- const name = profile?.name || "User";
20
-
21
- const mainContent = `
22
- <main class="container container-narrow">
23
- <my-aboutme base-url="${baseData.apiUrl}"></my-aboutme>
24
- </main>`;
25
-
26
- return this.generatePage(
27
- baseData.pathname,
28
- baseData.routes,
29
- baseData.footerLinks,
30
- staticDetails,
31
- baseData.apiUrl,
32
- baseData.baseUrl,
33
- mainContent,
34
- `About - ${name}`,
35
- `Learn more about ${name}'s experience and skills.`
36
- );
37
- }
38
- }