Integrate ghost cms

Signed-off-by: baalajimaestro <me@baalajimaestro.me>
This commit is contained in:
baalajimaestro 2024-07-13 00:36:15 +05:30
parent d59a8e91a6
commit 7d4dbb00a0
Signed by: baalajimaestro
GPG key ID: F93C394FE9BBAFD5
9 changed files with 164 additions and 93 deletions

View file

@ -16,6 +16,7 @@
"@astrojs/rss": "^4.0.7", "@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6", "@astrojs/sitemap": "^3.1.6",
"@astrojs/vue": "^4.5.0", "@astrojs/vue": "^4.5.0",
"@tryghost/content-api": "^1.11.21",
"@unocss/reset": "^0.61.0", "@unocss/reset": "^0.61.0",
"astro": "^4.11.3", "astro": "^4.11.3",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -27,6 +28,7 @@
"@iconify/json": "^2.2.204", "@iconify/json": "^2.2.204",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/tryghost__content-api": "^1.3.16",
"@vueuse/core": "^10.11.0", "@vueuse/core": "^10.11.0",
"bumpp": "^9.4.1", "bumpp": "^9.4.1",
"eslint": "^8.57.0", "eslint": "^8.57.0",

View file

@ -20,6 +20,9 @@ importers:
'@astrojs/vue': '@astrojs/vue':
specifier: ^4.5.0 specifier: ^4.5.0
version: 4.5.0(astro@4.11.5(typescript@5.5.3))(rollup@4.18.1)(vite@5.3.3)(vue@3.4.31(typescript@5.5.3)) version: 4.5.0(astro@4.11.5(typescript@5.5.3))(rollup@4.18.1)(vite@5.3.3)(vue@3.4.31(typescript@5.5.3))
'@tryghost/content-api':
specifier: ^1.11.21
version: 1.11.21
'@unocss/reset': '@unocss/reset':
specifier: ^0.61.0 specifier: ^0.61.0
version: 0.61.3 version: 0.61.3
@ -48,6 +51,9 @@ importers:
'@types/nprogress': '@types/nprogress':
specifier: ^0.2.3 specifier: ^0.2.3
version: 0.2.3 version: 0.2.3
'@types/tryghost__content-api':
specifier: ^1.3.16
version: 1.3.16
'@vueuse/core': '@vueuse/core':
specifier: ^10.11.0 specifier: ^10.11.0
version: 10.11.0(vue@3.4.31(typescript@5.5.3)) version: 10.11.0(vue@3.4.31(typescript@5.5.3))
@ -846,6 +852,9 @@ packages:
peerDependencies: peerDependencies:
eslint: '>=8.40.0' eslint: '>=8.40.0'
'@tryghost/content-api@1.11.21':
resolution: {integrity: sha512-ozJqEMHDUO7D0SGxPbUnG+RvwBbzC3zmdGOW8cFvkcKzrhe7uOAmVKyq7/J3kRAM2QthTlmiDpqp7NEo9ZLlKg==}
'@types/acorn@4.0.6': '@types/acorn@4.0.6':
resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
@ -915,6 +924,9 @@ packages:
'@types/sax@1.2.7': '@types/sax@1.2.7':
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
'@types/tryghost__content-api@1.3.16':
resolution: {integrity: sha512-i3TryXx8ZoW5LC9dMDWLkCg6vFMeCh4tAvr0ijAdlEzIhoSC8q5RPMANNmkiDxDcNMRCByVMoT4+RfB5qeURZQ==}
'@types/unist@2.0.10': '@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@ -1301,6 +1313,12 @@ packages:
peerDependencies: peerDependencies:
'@astrojs/compiler': '>=0.27.0' '@astrojs/compiler': '>=0.27.0'
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios@1.7.2:
resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
axobject-query@4.0.0: axobject-query@4.0.0:
resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==}
@ -1490,6 +1508,10 @@ packages:
colorette@2.0.20: colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
comma-separated-tokens@2.0.3: comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
@ -1582,6 +1604,10 @@ packages:
defu@6.1.4: defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dequal@2.0.3: dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2006,6 +2032,19 @@ packages:
resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
follow-redirects@1.15.6:
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
fs-extra@11.2.0: fs-extra@11.2.0:
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
engines: {node: '>=14.14'} engines: {node: '>=14.14'}
@ -2657,6 +2696,14 @@ packages:
resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mimic-fn@2.1.0: mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2962,6 +3009,9 @@ packages:
property-information@6.5.0: property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
punycode@2.3.1: punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -4490,6 +4540,12 @@ snapshots:
- supports-color - supports-color
- typescript - typescript
'@tryghost/content-api@1.11.21':
dependencies:
axios: 1.7.2
transitivePeerDependencies:
- debug
'@types/acorn@4.0.6': '@types/acorn@4.0.6':
dependencies: dependencies:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
@ -4570,6 +4626,8 @@ snapshots:
dependencies: dependencies:
'@types/node': 17.0.45 '@types/node': 17.0.45
'@types/tryghost__content-api@1.3.16': {}
'@types/unist@2.0.10': {} '@types/unist@2.0.10': {}
'@types/unist@3.0.2': {} '@types/unist@3.0.2': {}
@ -5197,6 +5255,16 @@ snapshots:
'@astrojs/compiler': 2.8.2 '@astrojs/compiler': 2.8.2
synckit: 0.9.0 synckit: 0.9.0
asynckit@0.4.0: {}
axios@1.7.2:
dependencies:
follow-redirects: 1.15.6
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
axobject-query@4.0.0: axobject-query@4.0.0:
dependencies: dependencies:
dequal: 2.0.3 dequal: 2.0.3
@ -5392,6 +5460,10 @@ snapshots:
colorette@2.0.20: {} colorette@2.0.20: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
comma-separated-tokens@2.0.3: {} comma-separated-tokens@2.0.3: {}
commander@12.1.0: {} commander@12.1.0: {}
@ -5458,6 +5530,8 @@ snapshots:
defu@6.1.4: {} defu@6.1.4: {}
delayed-stream@1.0.0: {}
dequal@2.0.3: {} dequal@2.0.3: {}
destr@2.0.3: {} destr@2.0.3: {}
@ -6010,6 +6084,14 @@ snapshots:
flattie@1.1.1: {} flattie@1.1.1: {}
follow-redirects@1.15.6: {}
form-data@4.0.0:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
fs-extra@11.2.0: fs-extra@11.2.0:
dependencies: dependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -6980,6 +7062,12 @@ snapshots:
braces: 3.0.3 braces: 3.0.3
picomatch: 2.3.1 picomatch: 2.3.1
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
mimic-fn@2.1.0: {} mimic-fn@2.1.0: {}
mimic-fn@4.0.0: {} mimic-fn@4.0.0: {}
@ -7293,6 +7381,8 @@ snapshots:
property-information@6.5.0: {} property-information@6.5.0: {}
proxy-from-env@1.1.0: {}
punycode@2.3.1: {} punycode@2.3.1: {}
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}

View file

@ -2,10 +2,14 @@
interface Post { interface Post {
id: string id: string
slug: string slug: string
body: string title: string
data: Record<string, any> html: string
collection: string excerpt: string
render: any feature_image: string
published_at: string
reading_time: number
tags: Array<{ name: string, slug: string }>
primary_author: { name: string, profile_image: string }
} }
withDefaults(defineProps<{ withDefaults(defineProps<{
@ -19,17 +23,9 @@ function getDate(date: string) {
} }
function getHref(post: Post) { function getHref(post: Post) {
if (post.data.redirect)
return post.data.redirect
return `/posts/${post.slug}` return `/posts/${post.slug}`
} }
function getTarget(post: Post) {
if (post.data.redirect)
return '_blank'
return '_self'
}
function isSameYear(a: Date | string | number, b: Date | string | number) { function isSameYear(a: Date | string | number, b: Date | string | number) {
return a && b && getYear(a) === getYear(b) return a && b && getYear(a) === getYear(b)
} }
@ -46,30 +42,26 @@ function getYear(date: Date | string | number) {
nothing here yet. nothing here yet.
</div> </div>
</template> </template>
<li v-for="(post, index) in list " :key="post.data.title" mb-8> <li v-for="(post, index) in list " :key="post.id" mb-8>
<div v-if="!isSameYear(post.data.date, list[index - 1]?.data.date)" select-none relative h18 pointer-events-none> <div v-if="!isSameYear(post.published_at, list[index - 1]?.published_at)" select-none relative h18 pointer-events-none>
<span text-7em color-transparent font-bold text-stroke-2 text-stroke-hex-aaa op14 absolute top--0.2em> <span text-7em color-transparent font-bold text-stroke-2 text-stroke-hex-aaa op14 absolute top--0.2em>
{{ getYear(post.data.date) }} {{ getYear(post.published_at) }}
</span> </span>
</div> </div>
<a text-lg lh-tight nav-link flex="~ col gap-2" :aria-label="post.data.title" :target="getTarget(post)" :href="getHref(post)"> <a text-lg lh-tight nav-link flex="~ col gap-2" :aria-label="post.title" :href="getHref(post)">
<div flex="~ col md:row gap-2 md:items-center"> <div flex="~ col md:row gap-2 md:items-center">
<div flex="~ gap-2 items-center text-wrap"> <div flex="~ gap-2 items-center text-wrap">
<span lh-normal> <span lh-normal>
<i v-if="post.data.draft" text-base vertical-mid i-ri-draft-line /> {{ post.title }}
{{ post.data.title }}
</span> </span>
</div> </div>
<div opacity-50 text-sm ws-nowrap flex="~ gap-2 items-center"> <div opacity-50 text-sm ws-nowrap flex="~ gap-2 items-center">
<i v-if="post.data.redirect" text-base i-ri-external-link-line /> <time v-if="post.published_at" :datetime="getDate(post.published_at)">{{ new Date(post.published_at).toLocaleDateString() }}</time>
<i v-if="post.data.recording || post.data.video" text-base i-ri:film-line /> <span v-if="post.reading_time">· {{ post.reading_time }} min read</span>
<time v-if="post.data.date" :datetime="getDate(post.data.date)">{{ post.data.date.split(',')[0] }}</time> <span v-if="post.tags && post.tags.length">· {{ post.tags[0].name }}</span>
<span v-if="post.data.duration">· {{ post.data.duration }}</span>
<span v-if="post.data.tag">· {{ post.data.tag }}</span>
<span v-if="post.data.lang && post.data.lang.includes('zh')">· 中文</span>
</div> </div>
</div> </div>
<div opacity-50 text-sm>{{ post.data.description }}</div> <div opacity-50 text-sm>{{ post.excerpt }}</div>
</a> </a>
</li> </li>
</ul> </ul>

View file

@ -12,6 +12,7 @@ const { ...head } = Astro.props
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta name="darkreader-lock">
<BaseHead {...head} /> <BaseHead {...head} />
<ViewTransitions /> <ViewTransitions />
</head> </head>

View file

@ -2,23 +2,16 @@
import BaseLayout from '@/layouts/BaseLayout.astro' import BaseLayout from '@/layouts/BaseLayout.astro'
import ListPosts from '@/components/ListPosts.vue' import ListPosts from '@/components/ListPosts.vue'
import siteConfig from '@/site-config' import siteConfig from '@/site-config'
import { getPosts } from '@/utils/posts' import { posts } from '@/utils/ghost'
export async function getStaticPaths() {
const paths = siteConfig.page.blogLinks.map((nav) => {
const href = nav.href.replace('/blog', '')
return {
params: {
path: href === '' ? undefined : href.replace(/^\/+|\/+$/g, ''),
},
}
})
return paths
}
const { path } = Astro.params const { path } = Astro.params
const posts = await getPosts(path) const ghostPosts = await posts
.browse({
limit: 'all',
include: ['tags', 'authors'],
filter: path ? `tag:${path}` : undefined
})
function activeLink(pathname: string) { function activeLink(pathname: string) {
return Astro.url.pathname.replace(/\/+/g, '/').replace(/\/$/, '') === pathname return Astro.url.pathname.replace(/\/+/g, '/').replace(/\/$/, '') === pathname
@ -44,5 +37,5 @@ function activeLink(pathname: string) {
)) ))
} }
</div> </div>
<ListPosts list={posts} /> <ListPosts list={ghostPosts} />
</BaseLayout> </BaseLayout>

View file

@ -2,7 +2,6 @@
import BaseLayout from '@/layouts/BaseLayout.astro' import BaseLayout from '@/layouts/BaseLayout.astro'
import siteConfig from '@/site-config' import siteConfig from '@/site-config'
--- ---
<BaseLayout description={siteConfig.description}> <BaseLayout description={siteConfig.description}>
<article class="prose"> <article class="prose">
<h1 class="text-title">Vitesse theme</h1> <h1 class="text-title">Vitesse theme</h1>

View file

@ -1,47 +1,44 @@
--- ---
import BaseLayout from '@/layouts/BaseLayout.astro' import BaseLayout from '@/layouts/BaseLayout.astro'
import { type CollectionPosts } from '@/types' import { api } from '@/utils/ghost'
import { getPosts } from '@/utils/posts'
export async function getStaticPaths() { export async function getStaticPaths() {
const posts = await getPosts() const posts = await api.posts
return posts.map((post) => { .browse({
return { limit: 'all',
params: { slug: post.slug }, fields: ['slug', 'title']
props: { })
post, .catch(err => {
}, console.error('Error fetching posts:', err);
} return [];
}) });
return posts.map((post) => ({
params: { slug: post.slug },
props: { slug: post.slug },
}));
} }
type Props = { post: CollectionPosts } const { slug } = Astro.props;
const { post } = Astro.props let post;
const { title, image, description, date, duration, tag } = post.data post = await api.posts.read({ slug }, { include: ['tags', 'authors'] });
if (!post) {
const { Content } = await post.render() return Astro.redirect('/404');
function getDate(date: string) {
return new Date(date).toISOString()
} }
--- ---
<BaseLayout title={post.title} description={post.excerpt} pageType="article">
<BaseLayout title={title} description={description} pageType="article">
<article class="prose"> <article class="prose">
<h1>{title}</h1> <h1>{post.title}</h1>
<p op-50> <p op-50>
{date && <time datetime={getDate(date)}>{date.split(',')}</time>} {post.published_at && <time datetime={post.published_at}>{new Date(post.published_at).toLocaleDateString()}</time>}
{duration && <span>· {duration}</span>} {post.reading_time && <span>· {post.reading_time} min read</span>}
{tag && <span>· {tag}</span>} {post.primary_tag && <span>· {post.primary_tag.name}</span>}
</p> </p>
{ {post.feature_image && (
image && ( <img width="640" height="360" src={post.feature_image} alt={post.feature_image_alt || ''} />
<p> )}
<img width="640" height="360" src={image.src} alt={image.alt || ''} />
</p> <div set:html={post.html}></div>
)
}
<Content />
</article> </article>
</BaseLayout> </BaseLayout>

View file

@ -47,14 +47,6 @@ export const siteConfig = {
text: 'Blog', text: 'Blog',
href: '/blog', href: '/blog',
}, },
{
text: 'Notes',
href: '/blog/notes',
},
{
text: 'Talks',
href: '/blog/talks',
},
{ {
text: 'Projects', text: 'Projects',
href: '/projects', href: '/projects',
@ -66,15 +58,7 @@ export const siteConfig = {
{ {
text: 'Blog', text: 'Blog',
href: '/blog', href: '/blog',
}, }
{
text: 'Notes',
href: '/blog/notes',
},
{
text: 'Talks',
href: '/blog/talks',
},
], ],
}, },
footer: { footer: {

13
src/utils/ghost.ts Normal file
View file

@ -0,0 +1,13 @@
import GhostContentAPI from '@tryghost/content-api';
export const api = new GhostContentAPI({
url: import.meta.env.CONTENT_API_URL,
key: import.meta.env.CONTENT_API_KEY,
version: 'v5.0',
});
export const posts = api.posts
export const tags = api.tags
export const authors = api.authors
export const pages = api.pages
export const settings = api.settings