@leadertechie/personal-site-kit 0.0.0 → 0.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/__tests__/info.test.d.ts +2 -0
- package/dist/api/__tests__/info.test.d.ts.map +1 -0
- package/dist/api/__tests__/utils.test.d.ts +2 -0
- package/dist/api/__tests__/utils.test.d.ts.map +1 -0
- package/dist/api/content-utils.d.ts +27 -0
- package/dist/api/content-utils.d.ts.map +1 -0
- package/dist/api/handlers/{aboutme.d.ts → about-me.d.ts} +1 -1
- package/dist/api/handlers/about-me.d.ts.map +1 -0
- 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/{staticdetails.d.ts → static-details.d.ts} +1 -1
- package/dist/api/handlers/static-details.d.ts.map +1 -0
- package/dist/api/index.d.ts +7 -8
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/website-api.d.ts +10 -0
- package/dist/api/website-api.d.ts.map +1 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +19 -589
- package/dist/assets/logo-placeholder.svg +21 -0
- package/dist/chunks/index-CGvOrVf8.js +213 -0
- package/dist/chunks/index-_AMi6ort.js +2690 -0
- package/dist/chunks/site-store-Vqmjjz9c.js +86 -0
- package/dist/chunks/template-C1tMqlPY.js +597 -0
- package/dist/chunks/website-api-CuyeBej-.js +920 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -352
- package/dist/prerender/__tests__/page-content.test.d.ts +2 -0
- package/dist/prerender/__tests__/page-content.test.d.ts.map +1 -0
- package/dist/prerender/__tests__/template.test.d.ts +2 -0
- package/dist/prerender/__tests__/template.test.d.ts.map +1 -0
- package/dist/prerender/data-fetcher.d.ts +19 -0
- package/dist/prerender/data-fetcher.d.ts.map +1 -0
- package/dist/prerender/index.d.ts +5 -4
- package/dist/prerender/index.d.ts.map +1 -1
- package/dist/prerender/{pageContent.d.ts → page-content.d.ts} +1 -1
- package/dist/prerender/page-content.d.ts.map +1 -0
- 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 +26 -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/website-prerender.d.ts +22 -0
- package/dist/prerender/website-prerender.d.ts.map +1 -0
- package/dist/prerender.d.ts +2 -0
- package/dist/prerender.js +163 -151
- package/dist/shared/config/index.d.ts +1 -0
- package/dist/shared/config/index.d.ts.map +1 -1
- package/dist/shared/core/__tests__/theme-toggle.test.d.ts +2 -0
- package/dist/shared/core/__tests__/theme-toggle.test.d.ts.map +1 -0
- package/dist/shared/core/site-store.d.ts +1 -0
- package/dist/shared/core/site-store.d.ts.map +1 -1
- package/dist/shared/index.d.ts +5 -3
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/interfaces/{iFooterLink.d.ts → ifooter-link.d.ts} +1 -1
- package/dist/shared/interfaces/ifooter-link.d.ts.map +1 -0
- package/dist/shared/interfaces/{iRoute.d.ts → iroute.d.ts} +1 -1
- package/dist/shared/interfaces/iroute.d.ts.map +1 -0
- package/dist/shared/{pageContent.d.ts → page-content.d.ts} +4 -3
- package/dist/shared/page-content.d.ts.map +1 -0
- package/dist/shared/router.d.ts +23 -0
- package/dist/shared/router.d.ts.map +1 -0
- package/dist/shared/runtime.d.ts +6 -6
- package/dist/shared/runtime.d.ts.map +1 -1
- package/dist/shared/website-ui.d.ts +32 -0
- package/dist/shared/website-ui.d.ts.map +1 -0
- package/dist/shared.js +13 -8
- package/dist/ui/about-me/api.d.ts.map +1 -0
- package/dist/ui/{aboutme → about-me}/index.d.ts +2 -10
- package/dist/ui/about-me/index.d.ts.map +1 -0
- package/dist/ui/about-me/styles.d.ts.map +1 -0
- 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/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 +1 -1
- 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 +7 -0
- package/dist/ui/index.d.ts.map +1 -0
- 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.d.ts +1 -1
- package/dist/ui.js +17 -818
- package/package.json +35 -12
- package/public/assets/logo-placeholder.svg +21 -0
- package/dist/api/handlers/aboutme.d.ts.map +0 -1
- package/dist/api/handlers/staticdetails.d.ts.map +0 -1
- package/dist/prerender/pageContent.d.ts.map +0 -1
- package/dist/shared/interfaces/iFooterLink.d.ts.map +0 -1
- package/dist/shared/interfaces/iRoute.d.ts.map +0 -1
- package/dist/shared/pageContent.d.ts.map +0 -1
- package/dist/ui/aboutme/api.d.ts.map +0 -1
- package/dist/ui/aboutme/index.d.ts.map +0 -1
- package/dist/ui/aboutme/renderer.d.ts +0 -5
- package/dist/ui/aboutme/renderer.d.ts.map +0 -1
- package/dist/ui/aboutme/styles.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/aboutme.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/staticdetails.ts +0 -48
- package/src/api/index.ts +0 -125
- package/src/api/utils.ts +0 -16
- package/src/prerender/__tests__/pageContent.test.ts +0 -54
- package/src/prerender/__tests__/template.test.ts +0 -54
- package/src/prerender/index.ts +0 -138
- package/src/prerender/pageContent.ts +0 -263
- package/src/prerender/prerender.ts +0 -25
- package/src/prerender/template.ts +0 -65
- 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 -15
- package/src/shared/interfaces/iFooterLink.ts +0 -4
- package/src/shared/interfaces/iRoute.ts +0 -4
- package/src/shared/models/theme-variables.css +0 -25
- package/src/shared/pageContent.ts +0 -209
- package/src/shared/runtime.ts +0 -11
- package/src/shared/template.ts +0 -35
- package/src/styles/markdown.css +0 -129
- package/src/ui/aboutme/api.ts +0 -12
- package/src/ui/aboutme/index.ts +0 -155
- package/src/ui/aboutme/renderer.ts +0 -7
- package/src/ui/aboutme/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/shared → dist}/styles/markdown.css +0 -0
- /package/{src → dist}/styles/theme.css +0 -0
- /package/dist/ui/{aboutme → about-me}/api.d.ts +0 -0
- /package/dist/ui/{aboutme → about-me}/styles.d.ts +0 -0
package/package.json
CHANGED
|
@@ -1,30 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadertechie/personal-site-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.1.0-alpha.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A high-performance personal website engine for Cloudflare Workers and R2",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/leadertechie/personal-site-kit"
|
|
9
|
+
},
|
|
6
10
|
"author": "Techie Leader",
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"files": [
|
|
9
13
|
"dist",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
14
|
+
"README.md",
|
|
15
|
+
"public"
|
|
12
16
|
],
|
|
13
17
|
"exports": {
|
|
14
|
-
".":
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"./
|
|
19
|
-
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./shared": {
|
|
23
|
+
"types": "./dist/shared.d.ts",
|
|
24
|
+
"import": "./dist/shared.js"
|
|
25
|
+
},
|
|
26
|
+
"./ui": {
|
|
27
|
+
"types": "./dist/ui.d.ts",
|
|
28
|
+
"import": "./dist/ui.js"
|
|
29
|
+
},
|
|
30
|
+
"./api": {
|
|
31
|
+
"types": "./dist/api.d.ts",
|
|
32
|
+
"import": "./dist/api.js"
|
|
33
|
+
},
|
|
34
|
+
"./prerender": {
|
|
35
|
+
"types": "./dist/prerender.d.ts",
|
|
36
|
+
"import": "./dist/prerender.js"
|
|
37
|
+
},
|
|
38
|
+
"./styles/*": "./dist/styles/*"
|
|
20
39
|
},
|
|
21
40
|
"scripts": {
|
|
22
|
-
"build": "vite build",
|
|
41
|
+
"build": "vite build && npm run postbuild",
|
|
42
|
+
"postbuild": "cp -r src/styles dist/",
|
|
23
43
|
"test": "vitest run"
|
|
24
44
|
},
|
|
25
45
|
"dependencies": {
|
|
26
|
-
"@leadertechie/md2html": "^0.1.0-alpha.
|
|
27
|
-
"@leadertechie/r2tohtml": "^0.1.0-alpha.
|
|
46
|
+
"@leadertechie/md2html": "^0.1.0-alpha.13",
|
|
47
|
+
"@leadertechie/r2tohtml": "^0.1.0-alpha.12",
|
|
28
48
|
"lit": "^3.2.1"
|
|
29
49
|
},
|
|
30
50
|
"devDependencies": {
|
|
@@ -37,5 +57,8 @@
|
|
|
37
57
|
},
|
|
38
58
|
"publishConfig": {
|
|
39
59
|
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"overrides": {
|
|
62
|
+
"lodash": "^4.18.1"
|
|
40
63
|
}
|
|
41
64
|
}
|
|
@@ -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>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"aboutme.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/aboutme.ts"],"names":[],"mappings":"AA4CA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkDhE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"staticdetails.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/staticdetails.ts"],"names":[],"mappings":"AAQA,wBAAsB,mBAAmB,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuCnG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pageContent.d.ts","sourceRoot":"","sources":["../../src/prerender/pageContent.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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"iFooterLink.d.ts","sourceRoot":"","sources":["../../../src/shared/interfaces/iFooterLink.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"iRoute.d.ts","sourceRoot":"","sources":["../../../src/shared/interfaces/iRoute.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pageContent.d.ts","sourceRoot":"","sources":["../../src/shared/pageContent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,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;AAUD,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAGhD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,OAAO;IACL,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,KACA,WA0JF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/ui/aboutme/api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,GAAG,EAAE,CAAA;CAAE,CAAC,CAIlG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/aboutme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAIhC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAOrC,qBACa,SAAU,SAAQ,UAAU;IACvC,MAAM,CAAC,MAAM,0BAAiB;IAG9B,QAAQ,CAAC,OAAO,SAAM;IAGtB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAQ;IAGxC,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,CAAM;IAG1C,QAAQ,CAAC,OAAO,UAAQ;IAExB,OAAO,CAAC,QAAQ,CAAkB;IAClC;;;;OAIG;IACI,OAAO,sBAAgB;gBAElB,QAAQ,CAAC,EAAE,eAAe;IAKtC,OAAO,KAAK,UAAU,GAErB;IAEK,iBAAiB;IAmBvB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;YAiB7B,WAAW;IAuBzB,OAAO,CAAC,kBAAkB;IAU1B,MAAM;CAoCP"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/ui/aboutme/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,qBAAa,eAAe;IAC1B,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO;CAG7C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/ui/aboutme/styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,yBAOzB,CAAC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { handleInfo } from '../handlers/info';
|
|
4
|
-
|
|
5
|
-
describe('Info Handler', () => {
|
|
6
|
-
it('should return API information', async () => {
|
|
7
|
-
const response = await handleInfo();
|
|
8
|
-
|
|
9
|
-
expect(response.status).toBe(200);
|
|
10
|
-
expect(response.headers.get('Content-Type')).toBe('application/json');
|
|
11
|
-
|
|
12
|
-
const body = await response.text();
|
|
13
|
-
const data = JSON.parse(body);
|
|
14
|
-
|
|
15
|
-
expect(data.name).toBe('TechieLeader');
|
|
16
|
-
expect(data.version).toBe('1.0.0');
|
|
17
|
-
expect(data.description).toBe('TechieLeader API');
|
|
18
|
-
expect(data.endpoints).toBeInstanceOf(Array);
|
|
19
|
-
expect(data.endpoints.length).toBeGreaterThan(0);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should return valid endpoints array', async () => {
|
|
23
|
-
const response = await handleInfo();
|
|
24
|
-
const body = await response.text();
|
|
25
|
-
const data = JSON.parse(body);
|
|
26
|
-
|
|
27
|
-
expect(data.endpoints).toContainEqual({
|
|
28
|
-
path: '/info',
|
|
29
|
-
method: 'GET',
|
|
30
|
-
description: 'Get API information'
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should have correct response structure', async () => {
|
|
35
|
-
const response = await handleInfo();
|
|
36
|
-
const body = await response.text();
|
|
37
|
-
const data = JSON.parse(body);
|
|
38
|
-
|
|
39
|
-
expect(data).toHaveProperty('name');
|
|
40
|
-
expect(data).toHaveProperty('version');
|
|
41
|
-
expect(data).toHaveProperty('description');
|
|
42
|
-
expect(data).toHaveProperty('endpoints');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { createErrorResponse, createJSONResponse } from '../utils';
|
|
4
|
-
|
|
5
|
-
describe('Utils', () => {
|
|
6
|
-
describe('createJSONResponse', () => {
|
|
7
|
-
it('should create JSON response with data', async () => {
|
|
8
|
-
const data = { message: 'Hello' };
|
|
9
|
-
const response = createJSONResponse(data);
|
|
10
|
-
|
|
11
|
-
expect(response.status).toBe(200);
|
|
12
|
-
expect(response.headers.get('Content-Type')).toBe('application/json');
|
|
13
|
-
|
|
14
|
-
const body = await response.text();
|
|
15
|
-
expect(JSON.parse(body)).toEqual(data);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should create JSON response with custom status', async () => {
|
|
19
|
-
const data = { message: 'Not Found' };
|
|
20
|
-
const response = createJSONResponse(data, 404);
|
|
21
|
-
|
|
22
|
-
expect(response.status).toBe(404);
|
|
23
|
-
expect(response.headers.get('Content-Type')).toBe('application/json');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should handle empty data', async () => {
|
|
27
|
-
const response = createJSONResponse(null);
|
|
28
|
-
|
|
29
|
-
expect(response.status).toBe(200);
|
|
30
|
-
const body = await response.text();
|
|
31
|
-
expect(body).toBe('null');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should handle complex data structures', async () => {
|
|
35
|
-
const data = {
|
|
36
|
-
nested: {
|
|
37
|
-
array: [1, 2, 3],
|
|
38
|
-
string: 'test'
|
|
39
|
-
},
|
|
40
|
-
number: 42
|
|
41
|
-
};
|
|
42
|
-
const response = createJSONResponse(data);
|
|
43
|
-
|
|
44
|
-
const body = await response.text();
|
|
45
|
-
expect(JSON.parse(body)).toEqual(data);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('createErrorResponse', () => {
|
|
50
|
-
it('should create error response with message', async () => {
|
|
51
|
-
const response = createErrorResponse('Something went wrong');
|
|
52
|
-
|
|
53
|
-
expect(response.status).toBe(500);
|
|
54
|
-
expect(response.headers.get('Content-Type')).toBe('application/json');
|
|
55
|
-
|
|
56
|
-
const body = await response.text();
|
|
57
|
-
const parsed = JSON.parse(body);
|
|
58
|
-
expect(parsed.error).toBe('Something went wrong');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should create error response with custom status', async () => {
|
|
62
|
-
const response = createErrorResponse('Not found', 404);
|
|
63
|
-
|
|
64
|
-
expect(response.status).toBe(404);
|
|
65
|
-
expect(response.headers.get('Content-Type')).toBe('application/json');
|
|
66
|
-
|
|
67
|
-
const body = await response.text();
|
|
68
|
-
const parsed = JSON.parse(body);
|
|
69
|
-
expect(parsed.error).toBe('Not found');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should default to status 500 if not provided', () => {
|
|
73
|
-
const response = createErrorResponse('Error');
|
|
74
|
-
|
|
75
|
-
expect(response.status).toBe(500);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// Simple JSON response handler for Cloudflare Workers
|
|
2
|
-
|
|
3
|
-
import { R2ContentLoader, type ContentNode } from '@leadertechie/r2tohtml';
|
|
4
|
-
|
|
5
|
-
interface Profile {
|
|
6
|
-
name: string;
|
|
7
|
-
title: string;
|
|
8
|
-
experience: string;
|
|
9
|
-
profileImageUrl: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface AboutMeApiResponse {
|
|
13
|
-
profile: Profile;
|
|
14
|
-
contentNodes: ContentNode[];
|
|
15
|
-
processedMarkdown: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let loader: R2ContentLoader | null = null;
|
|
19
|
-
|
|
20
|
-
function getLoader(env: any): R2ContentLoader | null {
|
|
21
|
-
if (!env?.CONTENT_BUCKET) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!loader) {
|
|
26
|
-
loader = new R2ContentLoader(
|
|
27
|
-
{
|
|
28
|
-
bucket: env.CONTENT_BUCKET,
|
|
29
|
-
cacheTTL: 5 * 60 * 1000,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
md2html: {
|
|
33
|
-
imagePathPrefix: 'images/',
|
|
34
|
-
styleOptions: {
|
|
35
|
-
classPrefix: 'md-',
|
|
36
|
-
addHeadingIds: true
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
return loader;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function clearContentCache() {
|
|
46
|
-
loader?.clearCache();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export async function handleAboutMe(env?: any): Promise<Response> {
|
|
50
|
-
try {
|
|
51
|
-
console.log('handleAboutMe: env?.CONTENT_BUCKET =', !!env?.CONTENT_BUCKET);
|
|
52
|
-
|
|
53
|
-
if (!env?.CONTENT_BUCKET) {
|
|
54
|
-
return new Response(JSON.stringify({ error: 'Content bucket not configured', env: !!env }), {
|
|
55
|
-
status: 500,
|
|
56
|
-
headers: { 'Content-Type': 'application/json' },
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const r2 = getLoader(env);
|
|
61
|
-
if (!r2) {
|
|
62
|
-
return new Response(JSON.stringify({ error: 'Content bucket not configured' }), {
|
|
63
|
-
status: 500,
|
|
64
|
-
headers: { 'Content-Type': 'application/json' },
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
console.log('handleAboutMe: r2 created, fetching data');
|
|
68
|
-
|
|
69
|
-
const [profileObj, astResult] = await Promise.all([
|
|
70
|
-
r2.getObject('profile.json'),
|
|
71
|
-
r2.getWithAST('about-me.md')
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
console.log('handleAboutMe: profileObj =', !!profileObj, 'astResult =', !!astResult);
|
|
75
|
-
|
|
76
|
-
if (!profileObj || !astResult) {
|
|
77
|
-
throw new Error('Content not found in R2');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const profile = await profileObj.json() as Profile;
|
|
81
|
-
console.log('handleAboutMe: profile loaded:', profile.name);
|
|
82
|
-
|
|
83
|
-
const responseData: AboutMeApiResponse = {
|
|
84
|
-
profile,
|
|
85
|
-
contentNodes: astResult.contentNodes,
|
|
86
|
-
processedMarkdown: ''
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
return new Response(JSON.stringify(responseData), {
|
|
90
|
-
headers: { 'Content-Type': 'application/json' },
|
|
91
|
-
});
|
|
92
|
-
} catch (error) {
|
|
93
|
-
console.error('Error serving aboutme content:', error);
|
|
94
|
-
return new Response(JSON.stringify({ error: 'Content not available', message: String(error) }), {
|
|
95
|
-
status: 500,
|
|
96
|
-
headers: { 'Content-Type': 'application/json' },
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
// Content handler for blogs, stories, and search
|
|
2
|
-
|
|
3
|
-
interface ContentMetadata {
|
|
4
|
-
slug: string;
|
|
5
|
-
title: string;
|
|
6
|
-
description: string;
|
|
7
|
-
summary?: string;
|
|
8
|
-
date: string;
|
|
9
|
-
imageUrl?: string;
|
|
10
|
-
tags?: string[];
|
|
11
|
-
author?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface BlogPost extends ContentMetadata {
|
|
15
|
-
content: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface StoryPost extends ContentMetadata {
|
|
19
|
-
content: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// In-memory cache for content (reduces R2 reads)
|
|
23
|
-
const contentCache = new Map<string, { data: any; timestamp: number }>();
|
|
24
|
-
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
25
|
-
|
|
26
|
-
async function getCachedOrFetch<T>(key: string, fetchFn: () => Promise<T>): Promise<T> {
|
|
27
|
-
const cached = contentCache.get(key);
|
|
28
|
-
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
29
|
-
return cached.data as T;
|
|
30
|
-
}
|
|
31
|
-
const data = await fetchFn();
|
|
32
|
-
contentCache.set(key, { data, timestamp: Date.now() });
|
|
33
|
-
return data;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function clearCache(prefix?: string): void {
|
|
37
|
-
if (prefix) {
|
|
38
|
-
for (const key of contentCache.keys()) {
|
|
39
|
-
if (key.startsWith(prefix)) {
|
|
40
|
-
contentCache.delete(key);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
} else {
|
|
44
|
-
contentCache.clear();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function parseFrontmatter(content: string): { metadata: ContentMetadata; content: string } {
|
|
49
|
-
const lines = content.split('\n');
|
|
50
|
-
const metadata: Record<string, string | string[]> = {};
|
|
51
|
-
let contentStart = 0;
|
|
52
|
-
|
|
53
|
-
if (lines[0]?.trim() === '---') {
|
|
54
|
-
for (let i = 1; i < lines.length; i++) {
|
|
55
|
-
if (lines[i]?.trim() === '---') {
|
|
56
|
-
contentStart = i + 1;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
const colonIdx = lines[i].indexOf(':');
|
|
60
|
-
if (colonIdx > 0) {
|
|
61
|
-
const key = lines[i].slice(0, colonIdx).trim();
|
|
62
|
-
let value = lines[i].slice(colonIdx + 1).trim();
|
|
63
|
-
if (value.startsWith('[') && value.endsWith(']')) {
|
|
64
|
-
value = value.slice(1, -1);
|
|
65
|
-
metadata[key] = value.split(',').map(v => v.trim());
|
|
66
|
-
} else {
|
|
67
|
-
metadata[key] = value;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
metadata: metadata as unknown as ContentMetadata,
|
|
75
|
-
content: lines.slice(contentStart).join('\n').trim()
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export async function handleBlogs(env?: any, slug?: string, latest?: number): Promise<Response> {
|
|
80
|
-
try {
|
|
81
|
-
if (!env?.CONTENT_BUCKET) {
|
|
82
|
-
return new Response(JSON.stringify({ error: 'Content bucket not configured' }), {
|
|
83
|
-
status: 500,
|
|
84
|
-
headers: { 'Content-Type': 'application/json' },
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const cacheKey = slug ? `blog-${slug}` : `blogs-list-${latest || 'all'}`;
|
|
89
|
-
|
|
90
|
-
const result = await getCachedOrFetch(cacheKey, async () => {
|
|
91
|
-
if (slug) {
|
|
92
|
-
// Fetch content from .md file
|
|
93
|
-
const mdObj = await env.CONTENT_BUCKET.get(`blogs/${slug}.md`);
|
|
94
|
-
const jsonObj = await env.CONTENT_BUCKET.get(`blogs/${slug}.json`);
|
|
95
|
-
|
|
96
|
-
if (!mdObj && !jsonObj) throw new Error('Blog not found');
|
|
97
|
-
|
|
98
|
-
let metadata: any = {};
|
|
99
|
-
if (jsonObj) {
|
|
100
|
-
metadata = await jsonObj.json();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let content = '';
|
|
104
|
-
if (mdObj) {
|
|
105
|
-
const text = await mdObj.text();
|
|
106
|
-
const parsed = parseFrontmatter(text);
|
|
107
|
-
content = parsed.content;
|
|
108
|
-
// Merge metadata from frontmatter if not in JSON
|
|
109
|
-
metadata = { ...parsed.metadata, ...metadata };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return { ...metadata, slug, content } as BlogPost;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const list = await env.CONTENT_BUCKET.list({ prefix: 'blogs/' });
|
|
116
|
-
const blogs: ContentMetadata[] = [];
|
|
117
|
-
|
|
118
|
-
for (const item of list.objects) {
|
|
119
|
-
if (item.key.endsWith('.json')) {
|
|
120
|
-
const obj = await env.CONTENT_BUCKET.get(item.key);
|
|
121
|
-
if (obj) {
|
|
122
|
-
const metadata = await obj.json() as ContentMetadata;
|
|
123
|
-
const slug = item.key.replace('blogs/', '').replace('.json', '');
|
|
124
|
-
blogs.push({ ...metadata, slug });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const sorted = blogs.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
130
|
-
return latest ? sorted.slice(0, latest) : sorted;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
return new Response(JSON.stringify(result), {
|
|
134
|
-
headers: { 'Content-Type': 'application/json' },
|
|
135
|
-
});
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('Error serving blogs:', error);
|
|
138
|
-
return new Response(JSON.stringify({ error: 'Blog not found' }), {
|
|
139
|
-
status: 404,
|
|
140
|
-
headers: { 'Content-Type': 'application/json' },
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export async function handleStories(env?: any, slug?: string, latest?: number): Promise<Response> {
|
|
146
|
-
try {
|
|
147
|
-
if (!env?.CONTENT_BUCKET) {
|
|
148
|
-
return new Response(JSON.stringify({ error: 'Content bucket not configured' }), {
|
|
149
|
-
status: 500,
|
|
150
|
-
headers: { 'Content-Type': 'application/json' },
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const cacheKey = slug ? `story-${slug}` : `stories-list-${latest || 'all'}`;
|
|
155
|
-
|
|
156
|
-
const result = await getCachedOrFetch(cacheKey, async () => {
|
|
157
|
-
if (slug) {
|
|
158
|
-
const mdObj = await env.CONTENT_BUCKET.get(`stories/${slug}.md`);
|
|
159
|
-
const jsonObj = await env.CONTENT_BUCKET.get(`stories/${slug}.json`);
|
|
160
|
-
|
|
161
|
-
if (!mdObj && !jsonObj) throw new Error('Story not found');
|
|
162
|
-
|
|
163
|
-
let metadata: any = {};
|
|
164
|
-
if (jsonObj) {
|
|
165
|
-
metadata = await jsonObj.json();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let content = '';
|
|
169
|
-
if (mdObj) {
|
|
170
|
-
const text = await mdObj.text();
|
|
171
|
-
const parsed = parseFrontmatter(text);
|
|
172
|
-
content = parsed.content;
|
|
173
|
-
metadata = { ...parsed.metadata, ...metadata };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return { ...metadata, slug, content } as StoryPost;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const list = await env.CONTENT_BUCKET.list({ prefix: 'stories/' });
|
|
180
|
-
const stories: ContentMetadata[] = [];
|
|
181
|
-
|
|
182
|
-
for (const item of list.objects) {
|
|
183
|
-
if (item.key.endsWith('.json')) {
|
|
184
|
-
const obj = await env.CONTENT_BUCKET.get(item.key);
|
|
185
|
-
if (obj) {
|
|
186
|
-
const metadata = await obj.json() as ContentMetadata;
|
|
187
|
-
const slug = item.key.replace('stories/', '').replace('.json', '');
|
|
188
|
-
stories.push({ ...metadata, slug });
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const sorted = stories.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
194
|
-
return latest ? sorted.slice(0, latest) : sorted;
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
return new Response(JSON.stringify(result), {
|
|
198
|
-
headers: { 'Content-Type': 'application/json' },
|
|
199
|
-
});
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.error('Error serving stories:', error);
|
|
202
|
-
return new Response(JSON.stringify({ error: 'Story not found' }), {
|
|
203
|
-
status: 404,
|
|
204
|
-
headers: { 'Content-Type': 'application/json' },
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export async function handleSearch(env?: any, query?: string): Promise<Response> {
|
|
210
|
-
try {
|
|
211
|
-
if (!env?.CONTENT_BUCKET) {
|
|
212
|
-
return new Response(JSON.stringify({ error: 'Content bucket not configured' }), {
|
|
213
|
-
status: 500,
|
|
214
|
-
headers: { 'Content-Type': 'application/json' },
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (!query) {
|
|
219
|
-
return new Response(JSON.stringify({ error: 'Search query required' }), {
|
|
220
|
-
status: 400,
|
|
221
|
-
headers: { 'Content-Type': 'application/json' },
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const searchResults = await getCachedOrFetch(`search-${query}`, async () => {
|
|
226
|
-
const q = query.toLowerCase();
|
|
227
|
-
const results: (BlogPost | StoryPost)[] = [];
|
|
228
|
-
|
|
229
|
-
const [blogsList, storiesList] = await Promise.all([
|
|
230
|
-
env.CONTENT_BUCKET.list({ prefix: 'blogs/' }),
|
|
231
|
-
env.CONTENT_BUCKET.list({ prefix: 'stories/' })
|
|
232
|
-
]);
|
|
233
|
-
|
|
234
|
-
for (const item of [...blogsList.objects, ...storiesList.objects]) {
|
|
235
|
-
if (item.key.endsWith('.md')) {
|
|
236
|
-
const obj = await env.CONTENT_BUCKET.get(item.key);
|
|
237
|
-
if (obj) {
|
|
238
|
-
const text = await obj.text();
|
|
239
|
-
const { metadata } = parseFrontmatter(text);
|
|
240
|
-
const matchTitle = metadata.title?.toLowerCase().includes(q);
|
|
241
|
-
const matchDesc = metadata.description?.toLowerCase().includes(q);
|
|
242
|
-
const matchTags = metadata.tags?.some((t: string) => t.toLowerCase().includes(q));
|
|
243
|
-
|
|
244
|
-
if (matchTitle || matchDesc || matchTags) {
|
|
245
|
-
results.push(metadata as BlogPost | StoryPost);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return results;
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
return new Response(JSON.stringify(searchResults), {
|
|
255
|
-
headers: { 'Content-Type': 'application/json' },
|
|
256
|
-
});
|
|
257
|
-
} catch (error) {
|
|
258
|
-
console.error('Error serving search:', error);
|
|
259
|
-
return new Response(JSON.stringify({ error: 'Search failed' }), {
|
|
260
|
-
status: 500,
|
|
261
|
-
headers: { 'Content-Type': 'application/json' },
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export function clearContentCache(): void {
|
|
267
|
-
clearCache();
|
|
268
|
-
}
|