@leadertechie/personal-site-kit 0.1.0-alpha.7 → 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 (129) 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/about-me.d.ts.map +1 -1
  4. package/dist/api/handlers/content-api.d.ts +0 -1
  5. package/dist/api/handlers/content-api.d.ts.map +1 -1
  6. package/dist/api/website-api.d.ts.map +1 -1
  7. package/dist/api.js +2 -2
  8. package/dist/chunks/index-CGvOrVf8.js +213 -0
  9. package/dist/chunks/{index-Bq8WDk9L.js → index-CnSEOZse.js} +1285 -477
  10. package/dist/chunks/{template-Boh_MKY5.js → template-DWcsZW22.js} +8 -7
  11. package/dist/chunks/{website-api-XoeLwo_N.js → website-api-BEYGOsT3.js} +137 -156
  12. package/dist/index.js +19 -9
  13. package/dist/prerender/data-fetcher.d.ts +19 -0
  14. package/dist/prerender/data-fetcher.d.ts.map +1 -0
  15. package/dist/prerender/page-content.d.ts.map +1 -1
  16. package/dist/prerender/page-generators/about.d.ts +16 -0
  17. package/dist/prerender/page-generators/about.d.ts.map +1 -0
  18. package/dist/prerender/page-generators/base.d.ts +26 -0
  19. package/dist/prerender/page-generators/base.d.ts.map +1 -0
  20. package/dist/prerender/page-generators/blog-detail.d.ts +15 -0
  21. package/dist/prerender/page-generators/blog-detail.d.ts.map +1 -0
  22. package/dist/prerender/page-generators/blogs-list.d.ts +17 -0
  23. package/dist/prerender/page-generators/blogs-list.d.ts.map +1 -0
  24. package/dist/prerender/page-generators/home.d.ts +19 -0
  25. package/dist/prerender/page-generators/home.d.ts.map +1 -0
  26. package/dist/prerender/page-generators/index.d.ts +9 -0
  27. package/dist/prerender/page-generators/index.d.ts.map +1 -0
  28. package/dist/prerender/page-generators/not-found.d.ts +14 -0
  29. package/dist/prerender/page-generators/not-found.d.ts.map +1 -0
  30. package/dist/prerender/page-generators/stories-list.d.ts +17 -0
  31. package/dist/prerender/page-generators/stories-list.d.ts.map +1 -0
  32. package/dist/prerender/page-generators/story-detail.d.ts +15 -0
  33. package/dist/prerender/page-generators/story-detail.d.ts.map +1 -0
  34. package/dist/prerender.js +109 -102
  35. package/dist/shared.js +1 -1
  36. package/dist/ui/about-me/index.d.ts +2 -10
  37. package/dist/ui/about-me/index.d.ts.map +1 -1
  38. package/dist/ui/admin/api.d.ts +16 -0
  39. package/dist/ui/admin/api.d.ts.map +1 -0
  40. package/dist/ui/admin/components/AboutMeSection.d.ts +7 -0
  41. package/dist/ui/admin/components/AboutMeSection.d.ts.map +1 -0
  42. package/dist/ui/admin/components/AdminSection.d.ts +13 -0
  43. package/dist/ui/admin/components/AdminSection.d.ts.map +1 -0
  44. package/dist/ui/admin/components/BlogsSection.d.ts +7 -0
  45. package/dist/ui/admin/components/BlogsSection.d.ts.map +1 -0
  46. package/dist/ui/admin/components/HomeSection.d.ts +7 -0
  47. package/dist/ui/admin/components/HomeSection.d.ts.map +1 -0
  48. package/dist/ui/admin/components/ImagesSection.d.ts +7 -0
  49. package/dist/ui/admin/components/ImagesSection.d.ts.map +1 -0
  50. package/dist/ui/admin/components/LoginForm.d.ts +9 -0
  51. package/dist/ui/admin/components/LoginForm.d.ts.map +1 -0
  52. package/dist/ui/admin/components/LogoSection.d.ts +7 -0
  53. package/dist/ui/admin/components/LogoSection.d.ts.map +1 -0
  54. package/dist/ui/admin/components/ProfileSection.d.ts +7 -0
  55. package/dist/ui/admin/components/ProfileSection.d.ts.map +1 -0
  56. package/dist/ui/admin/components/StaticSection.d.ts +13 -0
  57. package/dist/ui/admin/components/StaticSection.d.ts.map +1 -0
  58. package/dist/ui/admin/components/StoriesSection.d.ts +7 -0
  59. package/dist/ui/admin/components/StoriesSection.d.ts.map +1 -0
  60. package/dist/ui/admin/components/index.d.ts +11 -0
  61. package/dist/ui/admin/components/index.d.ts.map +1 -0
  62. package/dist/ui/admin/index.d.ts +18 -26
  63. package/dist/ui/admin/index.d.ts.map +1 -1
  64. package/dist/ui/admin/types.d.ts +24 -0
  65. package/dist/ui/admin/types.d.ts.map +1 -0
  66. package/dist/ui/blog-viewer/index.d.ts.map +1 -1
  67. package/dist/ui/index.d.ts.map +1 -1
  68. package/dist/ui/story-viewer/index.d.ts.map +1 -1
  69. package/dist/ui.js +14 -4
  70. package/package.json +4 -4
  71. package/dist/ui/about-me/renderer.d.ts +0 -6
  72. package/dist/ui/about-me/renderer.d.ts.map +0 -1
  73. package/src/api/__tests__/info.test.ts +0 -44
  74. package/src/api/__tests__/utils.test.ts +0 -78
  75. package/src/api/handlers/about-me.ts +0 -99
  76. package/src/api/handlers/auth-handler.ts +0 -194
  77. package/src/api/handlers/auth.ts +0 -157
  78. package/src/api/handlers/content-api.ts +0 -268
  79. package/src/api/handlers/content.ts +0 -139
  80. package/src/api/handlers/home.ts +0 -79
  81. package/src/api/handlers/info.ts +0 -12
  82. package/src/api/handlers/logo.ts +0 -55
  83. package/src/api/handlers/static-details.ts +0 -48
  84. package/src/api/index.ts +0 -9
  85. package/src/api/utils.ts +0 -16
  86. package/src/api/website-api.ts +0 -138
  87. package/src/index.ts +0 -4
  88. package/src/prerender/__tests__/page-content.test.ts +0 -54
  89. package/src/prerender/__tests__/template.test.ts +0 -54
  90. package/src/prerender/index.ts +0 -7
  91. package/src/prerender/page-content.ts +0 -263
  92. package/src/prerender/prerender.ts +0 -25
  93. package/src/prerender/template.ts +0 -65
  94. package/src/prerender/website-prerender.ts +0 -152
  95. package/src/shared/config/api.ts +0 -16
  96. package/src/shared/config/index.ts +0 -43
  97. package/src/shared/config/types.ts +0 -16
  98. package/src/shared/core/__tests__/theme-toggle.test.ts +0 -204
  99. package/src/shared/core/site-store.ts +0 -38
  100. package/src/shared/core/theme-toggle.ts +0 -118
  101. package/src/shared/index.ts +0 -17
  102. package/src/shared/interfaces/ifooter-link.ts +0 -4
  103. package/src/shared/interfaces/iroute.ts +0 -4
  104. package/src/shared/models/theme-variables.css +0 -25
  105. package/src/shared/page-content.ts +0 -210
  106. package/src/shared/router.ts +0 -250
  107. package/src/shared/runtime.ts +0 -11
  108. package/src/shared/template.ts +0 -35
  109. package/src/shared/website-ui.ts +0 -92
  110. package/src/styles/markdown.css +0 -129
  111. package/src/ui/about-me/api.ts +0 -12
  112. package/src/ui/about-me/index.ts +0 -164
  113. package/src/ui/about-me/renderer.ts +0 -23
  114. package/src/ui/about-me/styles.ts +0 -85
  115. package/src/ui/admin/index.ts +0 -655
  116. package/src/ui/admin/styles.ts +0 -270
  117. package/src/ui/banner/index.ts +0 -38
  118. package/src/ui/banner/styles.ts +0 -95
  119. package/src/ui/blog-viewer/__tests__/blogviewer.test.ts +0 -7
  120. package/src/ui/blog-viewer/index.ts +0 -124
  121. package/src/ui/blog-viewer/styles.ts +0 -23
  122. package/src/ui/footer/index.ts +0 -37
  123. package/src/ui/footer/styles.ts +0 -50
  124. package/src/ui/index.ts +0 -6
  125. package/src/ui/story-viewer/__tests__/storyviewer.test.ts +0 -7
  126. package/src/ui/story-viewer/index.ts +0 -120
  127. package/src/ui/story-viewer/styles.ts +0 -54
  128. /package/{src/shared → dist}/styles/markdown.css +0 -0
  129. /package/{src → dist}/styles/theme.css +0 -0
@@ -1,7 +1,8 @@
1
1
  import { MarkdownPipeline } from "@leadertechie/md2html";
2
+ const __vite_import_meta_env__ = {};
2
3
  const DEFAULT_INFRA = {
3
4
  baseUrl: typeof window !== "undefined" ? window.location.origin : "http://localhost:5173",
4
- apiUrl: typeof window !== "undefined" && window.__VITE_API_URL__ || "http://localhost:8787"
5
+ apiUrl: typeof window !== "undefined" && (window.__VITE_API_URL__ || __vite_import_meta_env__?.VITE_API_URL) || (typeof window !== "undefined" ? window.location.origin : "http://localhost:8787")
5
6
  };
6
7
  const DEFAULT_STATIC = {
7
8
  siteTitle: "My Personal Website",
@@ -92,7 +93,7 @@ const generatePageContent = (pathname, routes, footerLinks, data) => {
92
93
  const footerTemplate = `
93
94
  <my-footer
94
95
  copyright="${copyright}"
95
- footerlinks='\${JSON.stringify(footerLinks)}'>
96
+ footerLinks='${JSON.stringify(footerLinks)}'>
96
97
  </my-footer>`;
97
98
  const renderContentGists = (items = [], title, type) => {
98
99
  const listHtml = items.length > 0 ? items.map((item) => `
@@ -231,10 +232,10 @@ class Router {
231
232
  return url;
232
233
  };
233
234
  this.footerLinks = [
234
- { text: "LinkedIn", link: normalizeUrl(config.linkedin) },
235
- { text: "GitHub", link: normalizeUrl(config.github) },
236
- { text: "Email", link: config.email ? `mailto:${config.email}` : "" }
237
- ].filter((link) => link.link !== "");
235
+ { text: "LinkedIn", link: normalizeUrl(config.linkedin) || "https://linkedin.com" },
236
+ { text: "GitHub", link: normalizeUrl(config.github) || "https://github.com" },
237
+ { text: "Email", link: config.email ? `mailto:${config.email}` : "mailto:hello@example.com" }
238
+ ];
238
239
  }
239
240
  init(appElementId = "app") {
240
241
  this.appElement = document.getElementById(appElementId);
@@ -426,7 +427,7 @@ class Router {
426
427
  <main class="container container-medium">
427
428
  <admin-portal></admin-portal>
428
429
  </main>
429
- <my-footer copyright="${this.copyright}" footerlinks='[]'></my-footer>
430
+ <my-footer copyright="${this.copyright}" footerLinks='[]'></my-footer>
430
431
  `;
431
432
  }
432
433
  }
@@ -52,20 +52,30 @@ async function handleAboutMe(env) {
52
52
  });
53
53
  }
54
54
  console.log("handleAboutMe: r2 created, fetching data");
55
- const [profileObj, astResult] = await Promise.all([
55
+ const [profileObj, rendered] = await Promise.all([
56
56
  r2.getObject("profile.json"),
57
- r2.getWithAST("about-me.md")
57
+ r2.getRendered("about-me.md")
58
58
  ]);
59
- console.log("handleAboutMe: profileObj =", !!profileObj, "astResult =", !!astResult);
60
- if (!profileObj || !astResult) {
61
- throw new Error("Content not found in R2");
59
+ if (!rendered) {
60
+ return new Response(JSON.stringify({ error: "About-me content not found. Please run seed." }), {
61
+ status: 404,
62
+ headers: { "Content-Type": "application/json" }
63
+ });
64
+ }
65
+ let profile = {
66
+ name: "Your Name",
67
+ title: "Professional",
68
+ experience: "Experienced",
69
+ profileImageUrl: ""
70
+ };
71
+ if (profileObj) {
72
+ profile = await profileObj.json();
62
73
  }
63
- const profile = await profileObj.json();
64
74
  console.log("handleAboutMe: profile loaded:", profile.name);
65
75
  const responseData = {
66
76
  profile,
67
- contentNodes: astResult.contentNodes,
68
- processedMarkdown: ""
77
+ contentNodes: [],
78
+ processedMarkdown: rendered.content
69
79
  };
70
80
  return new Response(JSON.stringify(responseData), {
71
81
  headers: { "Content-Type": "application/json" }
@@ -109,8 +119,8 @@ async function handleHome(env) {
109
119
  }
110
120
  const r2 = getLoader(env);
111
121
  const [astResult, renderedResult] = await Promise.all([
112
- r2.getWithAST("home.md"),
113
- r2.getRendered("home.md")
122
+ r2.getWithAST("pages/home.md"),
123
+ r2.getRendered("pages/home.md")
114
124
  ]);
115
125
  if (!astResult || !renderedResult) {
116
126
  return new Response(JSON.stringify({
@@ -281,7 +291,7 @@ async function handleContent(request, env, subpath) {
281
291
  }
282
292
  return createErrorResponse("Admin not configured. Use POST /auth/setup to configure.", 401);
283
293
  }
284
- const sessionToken = getSessionToken(request);
294
+ const sessionToken = getSessionToken(request) || request.headers.get("X-Session-Token");
285
295
  let isAuthenticated = false;
286
296
  if (sessionToken) {
287
297
  const session = await env.KV.get(`session:${sessionToken}`, "json");
@@ -403,32 +413,40 @@ async function handleAuth(request, env, subpath) {
403
413
  }
404
414
  }
405
415
  async function handleSetup(request, env, clientIP, origin) {
416
+ console.log("handleSetup: starting setup, env.KV exists:", !!env.KV);
406
417
  if (request.method !== "POST") {
407
418
  return createErrorResponse("Method not allowed", 405);
408
419
  }
409
- const existing = await getAuthStore(env);
410
- if (existing) {
411
- return createErrorResponse("Admin already configured. Use /auth/login to login.", 400);
412
- }
413
420
  try {
421
+ const existing = await getAuthStore(env);
422
+ console.log("handleSetup: existing store check:", !!existing);
423
+ if (existing) {
424
+ return createErrorResponse("Admin already configured. Use /auth/login to login.", 400);
425
+ }
414
426
  const body = await request.json();
415
427
  const { username, password } = body;
428
+ console.log("handleSetup: body parsed, username:", username);
416
429
  if (!username || !password) {
417
430
  return createErrorResponse("Username and password required", 400);
418
431
  }
419
432
  if (username.length < 3 || password.length < 8) {
420
433
  return createErrorResponse("Username must be 3+ chars, password must be 8+ chars", 400);
421
434
  }
435
+ console.log("handleSetup: calling setupAuth");
422
436
  await setupAuth(env, username, password);
437
+ console.log("handleSetup: setupAuth successful");
423
438
  await clearRateLimit(env, clientIP);
424
439
  const token = crypto.randomUUID();
440
+ console.log("handleSetup: session token generated");
425
441
  await env.KV.put(`session:${token}`, JSON.stringify({
426
442
  createdAt: Date.now(),
427
443
  expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1e3
428
444
  }), { expirationTtl: 7 * 24 * 60 * 60 });
445
+ console.log("handleSetup: session stored in KV");
429
446
  const headers = {
430
447
  "Content-Type": "application/json",
431
- "Set-Cookie": createSessionCookie(token, origin)
448
+ "Set-Cookie": createSessionCookie(token, origin),
449
+ "X-Session-Token": token
432
450
  };
433
451
  return new Response(JSON.stringify({
434
452
  success: true,
@@ -438,7 +456,8 @@ async function handleSetup(request, env, clientIP, origin) {
438
456
  headers
439
457
  });
440
458
  } catch (e) {
441
- return createErrorResponse("Invalid request body", 400);
459
+ console.error("handleSetup: error occurred:", e);
460
+ return createErrorResponse("Internal server error: " + e.message, 500);
442
461
  }
443
462
  }
444
463
  async function handleStatus(env) {
@@ -471,7 +490,8 @@ async function handleLogin(request, env, clientIP, origin) {
471
490
  }), { expirationTtl: 7 * 24 * 60 * 60 });
472
491
  const headers = {
473
492
  "Content-Type": "application/json",
474
- "Set-Cookie": createSessionCookie(token, origin)
493
+ "Set-Cookie": createSessionCookie(token, origin),
494
+ "X-Session-Token": token
475
495
  };
476
496
  return new Response(JSON.stringify({
477
497
  success: true,
@@ -512,14 +532,15 @@ async function handleLogout(request, env) {
512
532
  }
513
533
  const contentCache = /* @__PURE__ */ new Map();
514
534
  const CACHE_TTL = 5 * 60 * 1e3;
515
- async function getCachedOrFetch(key, fetchFn) {
535
+ function getCachedOrFetch(key, fetchFn) {
516
536
  const cached = contentCache.get(key);
517
537
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
518
- return cached.data;
538
+ return Promise.resolve(cached.data);
519
539
  }
520
- const data = await fetchFn();
521
- contentCache.set(key, { data, timestamp: Date.now() });
522
- return data;
540
+ return fetchFn().then((data) => {
541
+ contentCache.set(key, { data, timestamp: Date.now() });
542
+ return data;
543
+ });
523
544
  }
524
545
  function parseFrontmatter(content) {
525
546
  const lines = content.split("\n");
@@ -549,159 +570,117 @@ function parseFrontmatter(content) {
549
570
  content: lines.slice(contentStart).join("\n").trim()
550
571
  };
551
572
  }
573
+ function checkContentBucket(env) {
574
+ if (!env?.CONTENT_BUCKET) {
575
+ return createErrorResponse("Content bucket not configured", 500);
576
+ }
577
+ return null;
578
+ }
579
+ async function fetchContentItem(bucket, type, slug) {
580
+ const mdObj = await bucket.get(`${type}/${slug}.md`);
581
+ const jsonObj = await bucket.get(`${type}/${slug}.json`);
582
+ if (!mdObj && !jsonObj) throw new Error(`${type.slice(0, -1)} not found`);
583
+ let metadata = {};
584
+ if (jsonObj) {
585
+ metadata = await jsonObj.json();
586
+ }
587
+ let content = "";
588
+ if (mdObj) {
589
+ const text = await mdObj.text();
590
+ const parsed = parseFrontmatter(text);
591
+ content = parsed.content;
592
+ metadata = { ...parsed.metadata, ...metadata };
593
+ }
594
+ return { ...metadata, slug, content };
595
+ }
596
+ async function fetchContentList(bucket, type, latest) {
597
+ const list = await bucket.list({ prefix: `${type}/` });
598
+ const items = [];
599
+ for (const item of list.objects) {
600
+ if (item.key.endsWith(".json")) {
601
+ const obj = await bucket.get(item.key);
602
+ if (obj) {
603
+ const metadata = await obj.json();
604
+ const slug = item.key.replace(`${type}/`, "").replace(".json", "");
605
+ items.push({ ...metadata, slug });
606
+ }
607
+ }
608
+ }
609
+ const sorted = items.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
610
+ return latest ? sorted.slice(0, latest) : sorted;
611
+ }
612
+ async function searchContent(bucket, query) {
613
+ const q = query.toLowerCase();
614
+ const results = [];
615
+ const [blogsList, storiesList] = await Promise.all([
616
+ bucket.list({ prefix: "blogs/" }),
617
+ bucket.list({ prefix: "stories/" })
618
+ ]);
619
+ for (const item of [...blogsList.objects, ...storiesList.objects]) {
620
+ if (item.key.endsWith(".md")) {
621
+ const obj = await bucket.get(item.key);
622
+ if (obj) {
623
+ const text = await obj.text();
624
+ const { metadata } = parseFrontmatter(text);
625
+ const matchTitle = metadata.title?.toLowerCase().includes(q);
626
+ const matchDesc = metadata.description?.toLowerCase().includes(q);
627
+ const matchTags = metadata.tags?.some((t) => t.toLowerCase().includes(q));
628
+ if (matchTitle || matchDesc || matchTags) {
629
+ results.push(metadata);
630
+ }
631
+ }
632
+ }
633
+ }
634
+ return results;
635
+ }
552
636
  async function handleBlogs(env, slug, latest) {
637
+ const bucketCheck = checkContentBucket(env);
638
+ if (bucketCheck) return bucketCheck;
553
639
  try {
554
- if (!env?.CONTENT_BUCKET) {
555
- return new Response(JSON.stringify({ error: "Content bucket not configured" }), {
556
- status: 500,
557
- headers: { "Content-Type": "application/json" }
558
- });
559
- }
560
640
  const cacheKey = slug ? `blog-${slug}` : `blogs-list-${latest || "all"}`;
561
641
  const result = await getCachedOrFetch(cacheKey, async () => {
562
642
  if (slug) {
563
- const mdObj = await env.CONTENT_BUCKET.get(`blogs/${slug}.md`);
564
- const jsonObj = await env.CONTENT_BUCKET.get(`blogs/${slug}.json`);
565
- if (!mdObj && !jsonObj) throw new Error("Blog not found");
566
- let metadata = {};
567
- if (jsonObj) {
568
- metadata = await jsonObj.json();
569
- }
570
- let content = "";
571
- if (mdObj) {
572
- const text = await mdObj.text();
573
- const parsed = parseFrontmatter(text);
574
- content = parsed.content;
575
- metadata = { ...parsed.metadata, ...metadata };
576
- }
577
- return { ...metadata, slug, content };
578
- }
579
- const list = await env.CONTENT_BUCKET.list({ prefix: "blogs/" });
580
- const blogs = [];
581
- for (const item of list.objects) {
582
- if (item.key.endsWith(".json")) {
583
- const obj = await env.CONTENT_BUCKET.get(item.key);
584
- if (obj) {
585
- const metadata = await obj.json();
586
- const slug2 = item.key.replace("blogs/", "").replace(".json", "");
587
- blogs.push({ ...metadata, slug: slug2 });
588
- }
589
- }
643
+ return await fetchContentItem(env.CONTENT_BUCKET, "blogs", slug);
590
644
  }
591
- const sorted = blogs.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
592
- return latest ? sorted.slice(0, latest) : sorted;
593
- });
594
- return new Response(JSON.stringify(result), {
595
- headers: { "Content-Type": "application/json" }
645
+ return await fetchContentList(env.CONTENT_BUCKET, "blogs", latest);
596
646
  });
647
+ return createJSONResponse(result);
597
648
  } catch (error) {
598
649
  console.error("Error serving blogs:", error);
599
- return new Response(JSON.stringify({ error: "Blog not found" }), {
600
- status: 404,
601
- headers: { "Content-Type": "application/json" }
602
- });
650
+ return createErrorResponse("Blog not found", 404);
603
651
  }
604
652
  }
605
653
  async function handleStories(env, slug, latest) {
654
+ const bucketCheck = checkContentBucket(env);
655
+ if (bucketCheck) return bucketCheck;
606
656
  try {
607
- if (!env?.CONTENT_BUCKET) {
608
- return new Response(JSON.stringify({ error: "Content bucket not configured" }), {
609
- status: 500,
610
- headers: { "Content-Type": "application/json" }
611
- });
612
- }
613
657
  const cacheKey = slug ? `story-${slug}` : `stories-list-${latest || "all"}`;
614
658
  const result = await getCachedOrFetch(cacheKey, async () => {
615
659
  if (slug) {
616
- const mdObj = await env.CONTENT_BUCKET.get(`stories/${slug}.md`);
617
- const jsonObj = await env.CONTENT_BUCKET.get(`stories/${slug}.json`);
618
- if (!mdObj && !jsonObj) throw new Error("Story not found");
619
- let metadata = {};
620
- if (jsonObj) {
621
- metadata = await jsonObj.json();
622
- }
623
- let content = "";
624
- if (mdObj) {
625
- const text = await mdObj.text();
626
- const parsed = parseFrontmatter(text);
627
- content = parsed.content;
628
- metadata = { ...parsed.metadata, ...metadata };
629
- }
630
- return { ...metadata, slug, content };
631
- }
632
- const list = await env.CONTENT_BUCKET.list({ prefix: "stories/" });
633
- const stories = [];
634
- for (const item of list.objects) {
635
- if (item.key.endsWith(".json")) {
636
- const obj = await env.CONTENT_BUCKET.get(item.key);
637
- if (obj) {
638
- const metadata = await obj.json();
639
- const slug2 = item.key.replace("stories/", "").replace(".json", "");
640
- stories.push({ ...metadata, slug: slug2 });
641
- }
642
- }
660
+ return await fetchContentItem(env.CONTENT_BUCKET, "stories", slug);
643
661
  }
644
- const sorted = stories.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
645
- return latest ? sorted.slice(0, latest) : sorted;
646
- });
647
- return new Response(JSON.stringify(result), {
648
- headers: { "Content-Type": "application/json" }
662
+ return await fetchContentList(env.CONTENT_BUCKET, "stories", latest);
649
663
  });
664
+ return createJSONResponse(result);
650
665
  } catch (error) {
651
666
  console.error("Error serving stories:", error);
652
- return new Response(JSON.stringify({ error: "Story not found" }), {
653
- status: 404,
654
- headers: { "Content-Type": "application/json" }
655
- });
667
+ return createErrorResponse("Story not found", 404);
656
668
  }
657
669
  }
658
670
  async function handleSearch(env, query) {
671
+ const bucketCheck = checkContentBucket(env);
672
+ if (bucketCheck) return bucketCheck;
673
+ if (!query) {
674
+ return createErrorResponse("Search query required", 400);
675
+ }
659
676
  try {
660
- if (!env?.CONTENT_BUCKET) {
661
- return new Response(JSON.stringify({ error: "Content bucket not configured" }), {
662
- status: 500,
663
- headers: { "Content-Type": "application/json" }
664
- });
665
- }
666
- if (!query) {
667
- return new Response(JSON.stringify({ error: "Search query required" }), {
668
- status: 400,
669
- headers: { "Content-Type": "application/json" }
670
- });
671
- }
672
677
  const searchResults = await getCachedOrFetch(`search-${query}`, async () => {
673
- const q = query.toLowerCase();
674
- const results = [];
675
- const [blogsList, storiesList] = await Promise.all([
676
- env.CONTENT_BUCKET.list({ prefix: "blogs/" }),
677
- env.CONTENT_BUCKET.list({ prefix: "stories/" })
678
- ]);
679
- for (const item of [...blogsList.objects, ...storiesList.objects]) {
680
- if (item.key.endsWith(".md")) {
681
- const obj = await env.CONTENT_BUCKET.get(item.key);
682
- if (obj) {
683
- const text = await obj.text();
684
- const { metadata } = parseFrontmatter(text);
685
- const matchTitle = metadata.title?.toLowerCase().includes(q);
686
- const matchDesc = metadata.description?.toLowerCase().includes(q);
687
- const matchTags = metadata.tags?.some((t) => t.toLowerCase().includes(q));
688
- if (matchTitle || matchDesc || matchTags) {
689
- results.push(metadata);
690
- }
691
- }
692
- }
693
- }
694
- return results;
695
- });
696
- return new Response(JSON.stringify(searchResults), {
697
- headers: { "Content-Type": "application/json" }
678
+ return await searchContent(env.CONTENT_BUCKET, query);
698
679
  });
680
+ return createJSONResponse(searchResults);
699
681
  } catch (error) {
700
682
  console.error("Error serving search:", error);
701
- return new Response(JSON.stringify({ error: "Search failed" }), {
702
- status: 500,
703
- headers: { "Content-Type": "application/json" }
704
- });
683
+ return createErrorResponse("Search failed", 500);
705
684
  }
706
685
  }
707
686
  const PLACEHOLDER_LOGO = `<svg width="600" height="320" viewBox="0 0 600 320" xmlns="http://www.w3.org/2000/svg">
@@ -770,7 +749,7 @@ async function handleStaticDetails(env, method, body) {
770
749
  });
771
750
  }
772
751
  if (!method || method === "GET") {
773
- const staticDetails = await env.CONTENT_BUCKET.get("staticdetails.json");
752
+ const staticDetails = await env.CONTENT_BUCKET.get("static-details.json");
774
753
  if (staticDetails) {
775
754
  const data = await staticDetails.json();
776
755
  return new Response(JSON.stringify({ ...DEFAULT_STATIC_DETAILS, ...data }), {
@@ -808,25 +787,25 @@ class WebsiteAPI {
808
787
  addCORSHeaders(response) {
809
788
  response.headers.set("Access-Control-Allow-Origin", "*");
810
789
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
811
- response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
790
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Session-Token");
812
791
  return response;
813
792
  }
814
793
  addAdminCORSHeaders(response, origin) {
815
- const allowOrigin = origin.includes("localhost") || origin.includes("127.0.0.1") ? origin : "same-origin";
794
+ const allowOrigin = origin && (origin.includes("localhost") || origin.includes("127.0.0.1")) ? origin : "same-origin";
816
795
  response.headers.set("Access-Control-Allow-Origin", allowOrigin);
817
796
  response.headers.set("Access-Control-Allow-Credentials", "true");
818
797
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
819
- response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
798
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Session-Token");
820
799
  return response;
821
800
  }
822
801
  handleCORS(origin) {
823
- const allowOrigin = origin.includes("localhost") || origin.includes("127.0.0.1") ? origin : "*";
802
+ const allowOrigin = origin && (origin.includes("localhost") || origin.includes("127.0.0.1")) ? origin : "*";
824
803
  return new Response(null, {
825
804
  status: 200,
826
805
  headers: {
827
806
  "Access-Control-Allow-Origin": allowOrigin,
828
807
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
829
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
808
+ "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Session-Token",
830
809
  "Access-Control-Allow-Credentials": "true",
831
810
  "Access-Control-Max-Age": "86400"
832
811
  }
@@ -849,6 +828,10 @@ class WebsiteAPI {
849
828
  const subpath = route.replace(/^content\/?/, "");
850
829
  return this.addAdminCORSHeaders(await handleContent(request, env, subpath), origin);
851
830
  }
831
+ if (route === "auth" || route.startsWith("auth/")) {
832
+ const subpath = route.replace(/^auth\/?/, "");
833
+ return this.addAdminCORSHeaders(await handleAuth(request, env, subpath || "/"), origin);
834
+ }
852
835
  switch (route) {
853
836
  case "info":
854
837
  return this.addCORSHeaders(await handleInfo());
@@ -869,8 +852,6 @@ class WebsiteAPI {
869
852
  return this.addCORSHeaders(await handleLogo(env));
870
853
  case "static":
871
854
  return this.addCORSHeaders(await handleStaticDetails(env));
872
- case "auth":
873
- return this.addAdminCORSHeaders(await handleAuth(request, env, "/auth"), origin);
874
855
  case "blogs":
875
856
  return this.addCORSHeaders(await handleBlogs(env));
876
857
  case "blogs/latest":
package/dist/index.js CHANGED
@@ -1,16 +1,25 @@
1
- import { A, B, M, R, W, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-XoeLwo_N.js";
1
+ import { A, B, M, R, W, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-BEYGOsT3.js";
2
2
  import { WebsitePrerender } from "./prerender.js";
3
- import { A as A2, B as B2, F, M as M2, a as a2, S } from "./chunks/index-Bq8WDk9L.js";
4
- import { R as R2, S as S2, T, W as W2, b as b2, c as c2, g as g2, a as a3, i, r as r2 } from "./chunks/template-Boh_MKY5.js";
3
+ import { A as A2, a as a2, b as b2, c as c2, d as d2, e as e2, f, g as g2, h as h2, i, B as B2, F, M as M2, j, S, k } from "./chunks/index-CnSEOZse.js";
4
+ import { R as R2, S as S2, T, W as W2, b as b3, c as c3, g as g3, a as a3, i as i2, r as r2 } from "./chunks/template-DWcsZW22.js";
5
5
  export {
6
6
  A as AUTH_KV,
7
- A2 as AdminPortal,
7
+ A2 as AdminAboutMeSection,
8
+ a2 as AdminBlogsSection,
9
+ b2 as AdminHomeSection,
10
+ c2 as AdminImagesSection,
11
+ d2 as AdminLoginForm,
12
+ e2 as AdminLogoSection,
13
+ f as AdminPortal,
14
+ g2 as AdminProfileSection,
15
+ h2 as AdminStaticSection,
16
+ i as AdminStoriesSection,
8
17
  B as BASE_DELAY_MS,
9
18
  B2 as BlogViewer,
10
19
  F as FooterComponent,
11
20
  M as MAX_ATTEMPTS,
12
21
  M2 as MyAboutme,
13
- a2 as MyBanner,
22
+ j as MyBanner,
14
23
  R as RATE_LIMIT_KV,
15
24
  R2 as Router,
16
25
  S2 as SiteStore,
@@ -19,18 +28,19 @@ export {
19
28
  W as WebsiteAPI,
20
29
  WebsitePrerender,
21
30
  W2 as WebsiteUI,
22
- b2 as bootstrap,
31
+ k as adminLoaded,
32
+ b3 as bootstrap,
23
33
  c as checkRateLimit,
24
34
  a as clearRateLimit,
25
- c2 as createHtmlTemplate,
26
- g2 as generatePageContent,
35
+ c3 as createHtmlTemplate,
36
+ g3 as generatePageContent,
27
37
  g as generateSalt,
28
38
  b as getAuthStore,
29
39
  d as getClientIP,
30
40
  a3 as getConfig,
31
41
  h as handleAuth,
32
42
  e as hashPassword,
33
- i as initializeConfig,
43
+ i2 as initializeConfig,
34
44
  r as recordFailedAttempt,
35
45
  r2 as renderMarkdown,
36
46
  s as setupAuth,
@@ -0,0 +1,19 @@
1
+ export interface Profile {
2
+ name: string;
3
+ title: string;
4
+ experience: string;
5
+ profileImageUrl: string;
6
+ }
7
+ export interface BlogMeta {
8
+ slug: string;
9
+ title: string;
10
+ summary: string;
11
+ tags: string[];
12
+ date: string;
13
+ }
14
+ export declare function fetchProfile(env: any): Promise<Profile | null>;
15
+ export declare function fetchAboutMe(env: any): Promise<string>;
16
+ export declare function fetchHome(env: any): Promise<string>;
17
+ export declare function fetchLatestBlogSummaries(env: any, count?: number): Promise<BlogMeta[]>;
18
+ export declare function fetchLatestStorySummaries(env: any, count?: number): Promise<BlogMeta[]>;
19
+ //# sourceMappingURL=data-fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-fetcher.d.ts","sourceRoot":"","sources":["../../src/prerender/data-fetcher.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAeD,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAQpE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAO5D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAOzD;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgB/F;AAED,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgBhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../../src/prerender/page-content.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAuFD,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,MAAM,GAAG,KACR,OAAO,CAAC,WAAW,CAkJrB,CAAC"}
1
+ {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../../src/prerender/page-content.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAuFD,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,MAAM,GAAG,KACR,OAAO,CAAC,WAAW,CAqJrB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { BasePageGenerator, StaticDetails } from './base';
2
+ import { IRoute, IFooterLink, PageContent } from '../page-content';
3
+ import { Profile } from '../data-fetcher';
4
+ export interface AboutPageData {
5
+ routes: IRoute[];
6
+ footerLinks: IFooterLink[];
7
+ staticDetails: StaticDetails;
8
+ apiUrl: string;
9
+ baseUrl: string;
10
+ pathname: string;
11
+ profile: Profile | null;
12
+ }
13
+ export declare class AboutPageGenerator extends BasePageGenerator {
14
+ generate(data: AboutPageData): PageContent;
15
+ }
16
+ //# sourceMappingURL=about.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about.d.ts","sourceRoot":"","sources":["../../../src/prerender/page-generators/about.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB;AAED,qBAAa,kBAAmB,SAAQ,iBAAiB;IAChD,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,WAAW;CAsBlD"}
@@ -0,0 +1,26 @@
1
+ import { IRoute, IFooterLink, PageContent } from '../page-content';
2
+ export interface StaticDetails {
3
+ siteTitle?: string;
4
+ copyright?: string;
5
+ linkedin?: string;
6
+ github?: string;
7
+ email?: string;
8
+ }
9
+ export interface BasePageData {
10
+ routes: IRoute[];
11
+ footerLinks: IFooterLink[];
12
+ staticDetails: StaticDetails;
13
+ apiUrl: string;
14
+ baseUrl: string;
15
+ pathname: string;
16
+ name?: string;
17
+ title?: string;
18
+ }
19
+ export declare class BasePageGenerator {
20
+ protected generateBanner(routes: IRoute[], siteTitle: string, logo: string): string;
21
+ protected generateFooter(footerLinks: IFooterLink[], copyright: string): string;
22
+ protected generateMeta(title: string, description: string, canonicalUrl: string): void;
23
+ protected wrapContent(banner: string, mainContent: string, footer: string): string;
24
+ generatePage(pathname: string, routes: IRoute[], footerLinks: IFooterLink[], staticDetails: StaticDetails, apiUrl: string, baseUrl: string, mainContent: string, title: string, description: string): PageContent;
25
+ }
26
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/prerender/page-generators/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,iBAAiB;IAC5B,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAcnF,SAAS,CAAC,cAAc,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAQ/E,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAItF,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAI3E,YAAY,CACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,WAAW,EAAE,EAC1B,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,WAAW;CAcf"}
@@ -0,0 +1,15 @@
1
+ import { BasePageGenerator, StaticDetails } from './base';
2
+ import { IRoute, IFooterLink, PageContent } from '../page-content';
3
+ export interface BlogDetailPageData {
4
+ routes: IRoute[];
5
+ footerLinks: IFooterLink[];
6
+ staticDetails: StaticDetails;
7
+ apiUrl: string;
8
+ baseUrl: string;
9
+ pathname: string;
10
+ slug: string;
11
+ }
12
+ export declare class BlogDetailPageGenerator extends BasePageGenerator {
13
+ generate(data: BlogDetailPageData): PageContent;
14
+ }
15
+ //# sourceMappingURL=blog-detail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog-detail.d.ts","sourceRoot":"","sources":["../../../src/prerender/page-generators/blog-detail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,uBAAwB,SAAQ,iBAAiB;IACrD,QAAQ,CAAC,IAAI,EAAE,kBAAkB,GAAG,WAAW;CAoBvD"}
@@ -0,0 +1,17 @@
1
+ import { BasePageGenerator, StaticDetails } from './base';
2
+ import { IRoute, IFooterLink, PageContent } from '../page-content';
3
+ import { BlogMeta } from '../data-fetcher';
4
+ export interface BlogsListPageData {
5
+ routes: IRoute[];
6
+ footerLinks: IFooterLink[];
7
+ staticDetails: StaticDetails;
8
+ apiUrl: string;
9
+ baseUrl: string;
10
+ pathname: string;
11
+ latestBlogs: BlogMeta[];
12
+ name: string;
13
+ }
14
+ export declare class BlogsListPageGenerator extends BasePageGenerator {
15
+ generate(data: BlogsListPageData): PageContent;
16
+ }
17
+ //# sourceMappingURL=blogs-list.d.ts.map