Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ pnpm preview
pnpm generate
```

## Setting up Ghost ECM local instance

```bash
docker compose up -d
```

This will shoot up a Ghost instance on port 2368. You can access the ghost admin dashboard through `http://localhost:2368/ghost`.
You'll be prompted to make an admin account. After that:

1. Go to settings -> integrations.
2. Make a custom integration to generate a Content API Key and URL.
3. Update `.env` file with the API URL and Content API key.

```bash
NUXT_PUBLIC_GHOST_URL= <API URL>
NUXT_PUBLIC_GHOST_KEY= <Content API key>
```

## Code style and linting
- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
- [ESLint v9](https://eslint.org/)
Expand Down
6 changes: 0 additions & 6 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,6 @@ export default defineAppConfig({
href: 'https://github.com/storacha',
icon: 'i-simple-icons:github',
},
{
name: 'Medium',
description: 'Read our blog',
href: 'https://medium.com/@storacha',
icon: 'i-simple-icons:medium',
},
{
name: 'YouTube',
description: 'Watch our demos',
Expand Down
21 changes: 15 additions & 6 deletions components/Blog/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,43 @@ defineProps<{
item: Item
showSnippet?: boolean
}>()

// Change the path as required
const fallbackImage = '/img/fallback.webp'

function handleImageError(event: Event) {
const target = event.target as HTMLImageElement
target.src = fallbackImage
}
</script>

<template>
<Card class="blog-card overflow-clip">
<template #header>
<AppLink :href="item.link" class="aspect-ratio-video overflow-hidden">
<NuxtLink :to="item.link" class="aspect-ratio-video overflow-hidden">
<img
:src="item.images?.[0]"
:src="item.images?.[0] || fallbackImage"
loading="lazy"
:alt="item.title"
class="h-full w-full object-cover object-left"
@error="handleImageError"
>
</AppLink>
</NuxtLink>
</template>
<article>
<AppLink :href="item.link" style="display:block">
<NuxtLink :to="item.link" style="display:block">
<div class="flex flex-col gap-2">
<Heading type="h5" class="font-medium">
{{ item.title }}
</Heading>
<time class="h5 text-sm" :datetime="item.pubDate">
{{ useAppDateFormat(item.pubDate) }}
</time>
<p v-if="showSnippet" class="text-base p1 break-words">
<p v-if="showSnippet" class="break-words text-base p1">
{{ item.snippet }}
</p>
</div>
</AppLink>
</NuxtLink>
</article>
</Card>
</template>
15 changes: 15 additions & 0 deletions composables/useGhostClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import GhostContentAPI from '@tryghost/content-api'

export function useGhostClient() {
const config = useRuntimeConfig()
const { url, key, version } = config.public.ghost

// Create API instance with site credentials
const api = new GhostContentAPI({
url,
key,
version,
})

return { api }
}
30 changes: 30 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
services:

ghost:
image: ghost:5-alpine
restart: always
ports:
- 2368:2368
environment:
# Configuration: https://ghost.org/docs/config
database__client: mysql
database__connection__host: db
database__connection__user: root
database__connection__password: example
database__connection__database: ghost
security__staffDeviceVerification: false
url: http://localhost:2368
volumes:
- ghost:/var/lib/ghost/content

db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
volumes:
- db:/var/lib/mysql

volumes:
ghost:
db:
8 changes: 6 additions & 2 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,13 @@ export default defineNuxtConfig({
runtimeConfig: {
// public runtime config
public: {
// feed URL used for /api/blog
blogFeedUrl: 'https://medium.com/feed/@storacha',
consoleUrl: import.meta.env.NUXT_PUBLIC_CONSOLE_URL || 'https://console.storacha.network',
// Ghost CMS settings used for /blog
ghost: {
url: import.meta.env.NUXT_PUBLIC_GHOST_URL || 'http://localhost:2368', // TODO: update this to your Ghost CMS URL
key: import.meta.env.NUXT_PUBLIC_GHOST_KEY,
version: 'v5.0',
},
},
},

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@nuxtjs/fontaine": "^0.4.3",
"@nuxtjs/plausible": "^1.0.3",
"@nuxtjs/seo": "^2.0.0-rc.23",
"@tryghost/content-api": "^1.11.26",
"@unocss/nuxt": "^0.63.6",
"@unocss/preset-icons": "^0.63.6",
"@vueuse/core": "^11.2.0",
Expand All @@ -38,6 +39,7 @@
"devDependencies": {
"@antfu/eslint-config": "^3.8.0",
"@nuxt/eslint": "^0.6.1",
"@types/tryghost__content-api": "^1.3.17",
"@unocss/eslint-config": "^0.63.6",
"eslint": "^9.13.0",
"nuxt": "^3.13.2",
Expand Down
57 changes: 0 additions & 57 deletions pages/blog.vue

This file was deleted.

61 changes: 61 additions & 0 deletions pages/blog/[slug].vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script lang="ts" setup>
definePageMeta({
layout: 'default',
})

const route = useRoute()
const slug = computed(() => route.params.slug as string)

// Fetch the post
const { data: post, error } = await useFetch(`/api/blog/${slug.value}`)

// Handle error or post not found
if (error.value) {
throw createError({
statusCode: 404,
statusMessage: 'Post not found',
fatal: true,
})
}

// Set page meta
useSeoMeta({
title: post.value?.title || 'Blog Post',
description: post.value?.snippet || '',
ogImage: post.value?.images?.[0],
ogType: 'article',
})
</script>

<template>
<Section v-if="post" class="bg-white" padding>
<article class="blog-post lg:prose-xl mt-24 max-w-none prose">
<Heading type="h1">
{{ post.title }}
</Heading>

<div class="mb-8 mt-4 flex items-center gap-4">
<time class="text-sm text-gray-500" :datetime="post.pubDate">
{{ useAppDateFormat(post.pubDate) }}
</time>
</div>

<img
v-if="post.images?.[0]"
:src="post.images[0]"
:alt="post.title"
class="mb-8 max-h-96 w-full rounded-lg object-cover"
>

<div class="post-content" v-html="post.content" />
</article>
</Section>
</template>

<style>
.post-content img {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
}
</style>
33 changes: 33 additions & 0 deletions pages/blog/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts" setup>
const { data: blog } = await useFetch('/api/blog')

// Set page metadata
useSeoMeta({
title: 'Blog - Storacha Network',
description: 'The latest and greatest from the Storacha team.',
})
</script>

<template>
<Section class="bg-white" padding>
<div class="mt-20 flex flex-col py-4 md:flex-row">
<div class="mb-4 flex-none md:mb-0">
<Heading type="h4" class="color-brand-3 uppercase">
Blazing Hot News
</Heading>
<p class="max-w-50ch text-pretty color-brand-3 prose p1">
The latest and greatest from the Storacha team.
</p>
</div>
</div>
<div class="blog-cell grid gap-4 lg:cols-3 md:cols-2">
<BlogCard
v-for="item in blog?.items"
:key="item.title"
:item="item"
class="grid-rows-subgrid"
show-snippet
/>
</div>
</Section>
</template>
Loading