more blog stuff
This commit is contained in:
parent
2a48d4b6da
commit
44543b5992
@ -222,4 +222,53 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-archives {
|
||||||
|
margin: 50px 0;
|
||||||
|
&__year {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
font-size: 0.85em;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
margin-left: 5px;
|
||||||
|
line-height: 1em;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #999;
|
||||||
|
text-shadow: 0 1px #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__posts {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__time {
|
||||||
|
color: #999 !important;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__post {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
81
components/blog-archive.vue
Normal file
81
components/blog-archive.vue
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div class="blog-archives" v-for="entry in yearGroup">
|
||||||
|
<div class="blog-archives__year">
|
||||||
|
<NuxtLink :to="'/blog/archive/' + entry.year">{{ entry.year }}</NuxtLink>
|
||||||
|
</div>
|
||||||
|
<div class="blog-archives__posts">
|
||||||
|
<article class="blog-archives__post" v-for="post in entry.posts">
|
||||||
|
<header>
|
||||||
|
<NuxtLink
|
||||||
|
:to="'/blog/' + post.fullSlug"
|
||||||
|
class="blog-archives__time"
|
||||||
|
>{{ getStamp(post) }}</NuxtLink
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
<NuxtLink :to="'/blog/' + post.fullSlug">{{ post.title }}</NuxtLink>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
|
interface Archive {
|
||||||
|
year: number;
|
||||||
|
posts: BlogPost[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{ posts: BlogPost[] }>();
|
||||||
|
|
||||||
|
const monthNames = [
|
||||||
|
'Jan',
|
||||||
|
'Feb',
|
||||||
|
'Mar',
|
||||||
|
'Apr',
|
||||||
|
'May',
|
||||||
|
'Jun',
|
||||||
|
'Jul',
|
||||||
|
'Aug',
|
||||||
|
'Sep',
|
||||||
|
'Oct',
|
||||||
|
'Nov',
|
||||||
|
'Dec',
|
||||||
|
];
|
||||||
|
|
||||||
|
const yearGroup = computed<Archive[]>(() => {
|
||||||
|
const groups: Archive[] = [];
|
||||||
|
|
||||||
|
props.posts
|
||||||
|
.sort((a, b) =>
|
||||||
|
new Date(b.date)
|
||||||
|
.toISOString()
|
||||||
|
.localeCompare(new Date(a.date).toISOString(), 'en', { numeric: true })
|
||||||
|
)
|
||||||
|
.forEach((post) => {
|
||||||
|
const date = new Date(post.date);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
|
||||||
|
const contains = groups.find((item) => item.year === year);
|
||||||
|
|
||||||
|
if (contains) {
|
||||||
|
contains.posts.push(post);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups.push({
|
||||||
|
year,
|
||||||
|
posts: [post],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getStamp = (post: BlogPost) => {
|
||||||
|
const date = new Date(post.date);
|
||||||
|
return `${monthNames[date.getMonth()]} ${date.getDate()}`;
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<article class="blog-post">
|
<article class="blog-post">
|
||||||
<div class="blog-post__meta">
|
<div class="blog-post__meta">
|
||||||
<a :href="'/blog/' + post.fullSlug">
|
<NuxtLink :to="'/blog/' + post.fullSlug">
|
||||||
<time :datetime="new Date(post.date).toISOString()">
|
<time :datetime="new Date(post.date).toISOString()">
|
||||||
{{ post.date }}
|
{{ post.date }}
|
||||||
</time>
|
</time>
|
||||||
</a>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="blog-post__inner">
|
<div class="blog-post__inner">
|
||||||
<header class="blog-post__title">
|
<header class="blog-post__title">
|
||||||
@ -14,18 +14,18 @@
|
|||||||
{{ post.title }}
|
{{ post.title }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a :href="'/blog/' + post.fullSlug">{{ post.title }}</a>
|
<NuxtLink :to="'/blog/' + post.fullSlug">{{ post.title }}</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
</h1>
|
</h1>
|
||||||
</header>
|
</header>
|
||||||
<div class="blog-post__content" v-html="post.html"></div>
|
<div class="blog-post__content" v-html="post.html"></div>
|
||||||
<div class="blog-post__footer">
|
<div class="blog-post__footer">
|
||||||
<div class="blog-post__tags">
|
<div class="blog-post__tags">
|
||||||
<a
|
<NuxtLink
|
||||||
v-for="tag of post.tags"
|
v-for="tag of post.tags"
|
||||||
:href="'/blog/tags/' + tag"
|
:to="'/blog/tags/' + tag"
|
||||||
class="blog-post__tag"
|
class="blog-post__tag"
|
||||||
>#{{ tag }}</a
|
>#{{ tag }}</NuxtLink
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -34,5 +34,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps<{ post: any; detail?: boolean }>();
|
import { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
|
defineProps<{ post: BlogPost; detail?: boolean }>();
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
<h2>{{ title }}</h2>
|
<h2>{{ title }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="item in list">
|
<li v-for="item in list">
|
||||||
<a :href="item.href" v-bind:target="item.blank ? '_blank' : undefined"
|
<NuxtLink
|
||||||
><span :class="item.icon"></span>{{ item.name }}</a
|
:to="item.href"
|
||||||
|
v-bind:target="item.blank ? '_blank' : undefined"
|
||||||
|
><span :class="item.icon"></span>{{ item.name }}</NuxtLink
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="blog">
|
<div class="blog">
|
||||||
|
<Head>
|
||||||
|
<Meta property="og:type" content="website" />
|
||||||
|
<Meta property="og:title" content="Evert's Blog" />
|
||||||
|
<Meta property="og:url" content="https://lunasqu.ee/blog/index.html" />
|
||||||
|
<Meta property="og:site_name" content="Evert's Blog" />
|
||||||
|
<Meta property="og:locale" content="en_US" />
|
||||||
|
<Meta property="article:author" content="Evert Prants" />
|
||||||
|
<Meta name="twitter:card" content="summary" />
|
||||||
|
</Head>
|
||||||
<header class="blog__header">
|
<header class="blog__header">
|
||||||
<h1><a href="/blog">Blog</a></h1>
|
<h1><NuxtLink to="/blog">Blog</NuxtLink></h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section class="blog__content">
|
<section class="blog__content">
|
||||||
@ -13,18 +22,18 @@
|
|||||||
<BlogSidebar title="Tags">
|
<BlogSidebar title="Tags">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="tag of tags">
|
<li v-for="tag of tags">
|
||||||
<a :href="'/tags/' + tag.name">{{ tag.name }}</a>
|
<NuxtLink :to="'/blog/tags/' + tag.name">{{ tag.name }}</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</BlogSidebar>
|
</BlogSidebar>
|
||||||
|
|
||||||
<BlogSidebar title="Tag cloud">
|
<BlogSidebar title="Tag cloud">
|
||||||
<div class="tag-cloud">
|
<div class="tag-cloud">
|
||||||
<a
|
<NuxtLink
|
||||||
v-for="tag of tags"
|
v-for="tag of tags"
|
||||||
:href="'/blog/tags/' + tag.name"
|
:to="'/blog/tags/' + tag.name"
|
||||||
:style="{ fontSize: getFontSize(tag) }"
|
:style="{ fontSize: getFontSize(tag) }"
|
||||||
>{{ tag.name }}</a
|
>{{ tag.name }}</NuxtLink
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</BlogSidebar>
|
</BlogSidebar>
|
||||||
@ -32,7 +41,7 @@
|
|||||||
<BlogSidebar title="Archive">
|
<BlogSidebar title="Archive">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="archive of monthList">
|
<li v-for="archive of monthList">
|
||||||
<a :href="archive.href">{{ archive.name }}</a>
|
<NuxtLink :href="archive.href">{{ archive.name }}</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</BlogSidebar>
|
</BlogSidebar>
|
||||||
@ -42,6 +51,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { BlogPostTag } from '~~/lib/types/post';
|
||||||
|
|
||||||
const { data: tags } = await useFetch('/api/blog/tags');
|
const { data: tags } = await useFetch('/api/blog/tags');
|
||||||
const { data: archive } = await useFetch('/api/blog/archive');
|
const { data: archive } = await useFetch('/api/blog/archive');
|
||||||
|
|
||||||
@ -83,7 +94,7 @@ const monthList = computed(() => {
|
|||||||
const minTag = computed(() =>
|
const minTag = computed(() =>
|
||||||
tags.value.reduce<number>(
|
tags.value.reduce<number>(
|
||||||
(min, current) => (min > current.count ? current.count : min),
|
(min, current) => (min > current.count ? current.count : min),
|
||||||
100
|
1000
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -98,7 +109,7 @@ function convertRange(value: number, r1: number[], r2: number[]) {
|
|||||||
return ((value - r1[0]) * (r2[1] - r2[0])) / (r1[1] - r1[0]) + r2[0];
|
return ((value - r1[0]) * (r2[1] - r2[0])) / (r1[1] - r1[0]) + r2[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFontSize = (tag: any): string => {
|
const getFontSize = (tag: BlogPostTag): string => {
|
||||||
return convertRange(tag.count, [minTag.value, maxTag.value], [10, 20]) + 'px';
|
return convertRange(tag.count, [minTag.value, maxTag.value], [10, 20]) + 'px';
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,6 +4,7 @@ import yaml from 'yaml';
|
|||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import hljs from 'highlight.js';
|
import hljs from 'highlight.js';
|
||||||
import { getDateObject } from '../utils/date-object';
|
import { getDateObject } from '../utils/date-object';
|
||||||
|
import { BlogPost, BlogPostTag } from '../types/post';
|
||||||
|
|
||||||
const dir = join('content', 'blog');
|
const dir = join('content', 'blog');
|
||||||
|
|
||||||
@ -14,11 +15,16 @@ marked.use({
|
|||||||
const created = hljs.highlight(code, { language }).value;
|
const created = hljs.highlight(code, { language }).value;
|
||||||
return `<div class="codeblock"><pre><code class="hljs language-${language}">${created}</code></pre></div>`;
|
return `<div class="codeblock"><pre><code class="hljs language-${language}">${created}</code></pre></div>`;
|
||||||
},
|
},
|
||||||
|
link: (href: string, title: string, text: string): string => {
|
||||||
|
return `<a href="${href}" rel="nofollow" target="_blank"${
|
||||||
|
title ? ` title="${title}"` : ''
|
||||||
|
}>${text}</a>`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function readBlogPosts(
|
export async function readBlogPosts(
|
||||||
condition?: (body: any) => boolean,
|
condition?: (body: BlogPost) => boolean,
|
||||||
render = true,
|
render = true,
|
||||||
includeContent = true
|
includeContent = true
|
||||||
) {
|
) {
|
||||||
@ -45,10 +51,10 @@ export async function readBlogPosts(
|
|||||||
|
|
||||||
export async function readBlogPost(
|
export async function readBlogPost(
|
||||||
slug: string,
|
slug: string,
|
||||||
condition?: (body: any) => boolean,
|
condition?: (body: BlogPost) => boolean,
|
||||||
render = true,
|
render = true,
|
||||||
includeContent = true
|
includeContent = true
|
||||||
): Promise<any> {
|
): Promise<BlogPost[]> {
|
||||||
const decoded = decodeURIComponent(slug);
|
const decoded = decodeURIComponent(slug);
|
||||||
if (!slug || decoded.includes('/') || decoded.includes('.'))
|
if (!slug || decoded.includes('/') || decoded.includes('.'))
|
||||||
throw new Error('Invalid post slug');
|
throw new Error('Invalid post slug');
|
||||||
@ -80,9 +86,9 @@ export async function readBlogPost(
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTags() {
|
export async function getTags(): Promise<BlogPostTag[]> {
|
||||||
const posts = await readBlogPosts(undefined, false, false);
|
const posts = await readBlogPosts(undefined, false, false);
|
||||||
const obj = [];
|
const obj: BlogPostTag[] = [];
|
||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
for (const tag of post.tags || []) {
|
for (const tag of post.tags || []) {
|
||||||
@ -104,8 +110,8 @@ export async function getTags() {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getArchiveTree() {
|
export async function getArchiveTree(condition?: (body: BlogPost) => boolean) {
|
||||||
const posts = await readBlogPosts(undefined, false, false);
|
const posts = await readBlogPosts(condition, false, false);
|
||||||
const obj = {};
|
const obj = {};
|
||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
|
16
lib/types/post.ts
Normal file
16
lib/types/post.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export interface BlogPost {
|
||||||
|
date: string;
|
||||||
|
title: string;
|
||||||
|
tags: string[];
|
||||||
|
file: string;
|
||||||
|
slug: string;
|
||||||
|
fullSlug: string;
|
||||||
|
markdown: string;
|
||||||
|
html: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlogPostTag {
|
||||||
|
name: string;
|
||||||
|
count: number;
|
||||||
|
posts: string[];
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
export function getDateObject(post) {
|
import { BlogPost } from '../types/post';
|
||||||
|
|
||||||
|
export function getDateObject(post: BlogPost) {
|
||||||
const date = new Date(post.date);
|
const date = new Date(post.date);
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
@ -1,10 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<NuxtLayout name="blog">
|
<NuxtLayout name="blog">
|
||||||
|
<Head>
|
||||||
|
<Meta name="description" :content="preview" />
|
||||||
|
<Meta property="og:type" content="article" />
|
||||||
|
<Meta property="og:title" content="Self-hosting, Part 1" />
|
||||||
|
<Meta
|
||||||
|
property="og:url"
|
||||||
|
:content="'https://lunasqu.ee/blog/' + post.fullSlug"
|
||||||
|
/>
|
||||||
|
<Meta property="og:site_name" content="Evert's Blog" />
|
||||||
|
<Meta property="og:description" :content="preview" />
|
||||||
|
<Meta property="og:locale" content="en_US" />
|
||||||
|
<Meta property="article:published_time" :content="isostamp" />
|
||||||
|
<Meta property="article:modified_time" :content="isostamp" />
|
||||||
|
<Meta property="article:author" content="Evert Prants" />
|
||||||
|
<Meta v-for="tag in post.tags" property="article:tag" :content="tag" />
|
||||||
|
<Meta name="twitter:card" content="summary" />
|
||||||
|
</Head>
|
||||||
|
|
||||||
<BlogPost :post="post" :detail="true" />
|
<BlogPost :post="post" :detail="true" />
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { data: post } = await useFetch(`/api/blog/${route.params.slug}`);
|
const { data: post, refresh } = await useFetch<BlogPost>(
|
||||||
|
`/api/blog/${route.params.slug}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const isostamp = computed(() => new Date(post.value.date).toISOString());
|
||||||
|
const preview = computed(() =>
|
||||||
|
post.value.html
|
||||||
|
.replace(/<[^>]*>?/gm, '')
|
||||||
|
.replace('\n', ' ')
|
||||||
|
.substring(0, 120)
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<NuxtLayout name="blog">
|
<NuxtLayout name="blog">
|
||||||
<template v-for="post of posts">
|
<BlogArchive :posts="posts" />
|
||||||
<BlogPost :post="post" :detail="false" />
|
|
||||||
</template>
|
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { data: posts } = await useFetch(
|
const { data: posts, refresh } = await useFetch<BlogPost[]>(`/api/blog`, {
|
||||||
`/api/blog?year=${route.params.year}&month=${route.params.month}&day=${route.params.day}`
|
params: {
|
||||||
);
|
year: route.params.year,
|
||||||
|
month: route.params.month,
|
||||||
|
day: route.params.day,
|
||||||
|
body: false,
|
||||||
|
render: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<NuxtLayout name="blog">
|
<NuxtLayout name="blog">
|
||||||
<template v-for="post of posts">
|
<BlogArchive :posts="posts" />
|
||||||
<BlogPost :post="post" :detail="false" />
|
|
||||||
</template>
|
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { data: posts } = await useFetch(
|
const { data: posts, refresh } = await useFetch<BlogPost[]>(`/api/blog`, {
|
||||||
`/api/blog?year=${route.params.year}&month=${route.params.month}`
|
params: {
|
||||||
);
|
year: route.params.year,
|
||||||
|
month: route.params.month,
|
||||||
|
body: false,
|
||||||
|
render: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<NuxtLayout name="blog">
|
<NuxtLayout name="blog">
|
||||||
<template v-for="post of posts">
|
<BlogArchive :posts="posts" />
|
||||||
<BlogPost :post="post" :detail="false" />
|
|
||||||
</template>
|
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { data: posts } = await useFetch(`/api/blog?year=${route.params.year}`);
|
const { data: posts, refresh } = await useFetch<BlogPost[]>(`/api/blog`, {
|
||||||
|
params: {
|
||||||
|
year: route.params.year,
|
||||||
|
body: false,
|
||||||
|
render: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
16
pages/blog/archive/index.vue
Normal file
16
pages/blog/archive/index.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<NuxtLayout name="blog">
|
||||||
|
<BlogArchive :posts="posts" />
|
||||||
|
</NuxtLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
|
const { data: posts } = await useFetch<BlogPost[]>(`/api/blog`, {
|
||||||
|
params: {
|
||||||
|
body: false,
|
||||||
|
render: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
22
pages/blog/tags/[tag].vue
Normal file
22
pages/blog/tags/[tag].vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<NuxtLayout name="blog">
|
||||||
|
<BlogArchive :posts="posts" />
|
||||||
|
</NuxtLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { BlogPost } from '~~/lib/types/post';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { data: posts, refresh } = await useFetch<BlogPost[]>(`/api/blog`, {
|
||||||
|
params: {
|
||||||
|
tag: route.params.tag,
|
||||||
|
body: false,
|
||||||
|
render: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
</script>
|
@ -140,7 +140,7 @@ const linksList = [
|
|||||||
{
|
{
|
||||||
name: 'Web apps',
|
name: 'Web apps',
|
||||||
icon: 'icon-controller-classic',
|
icon: 'icon-controller-classic',
|
||||||
href: '/apps',
|
href: 'https://lunasqu.ee/apps',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'GnuPG Public Key',
|
name: 'GnuPG Public Key',
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
import { getArchiveTree } from '~~/lib/blog/read-posts';
|
import { getArchiveTree } from '~~/lib/blog/read-posts';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
return getArchiveTree();
|
const query = getQuery(event);
|
||||||
|
const include = (content) => {
|
||||||
|
if (query.tag) {
|
||||||
|
if (!content.tags?.length || !content.tags.includes(query.tag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return getArchiveTree(include);
|
||||||
});
|
});
|
||||||
|
@ -23,8 +23,18 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (query.tag) {
|
||||||
|
if (!content.tags?.length || !content.tags.includes(query.tag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
return readBlogPosts(include);
|
return readBlogPosts(
|
||||||
|
include,
|
||||||
|
query.render !== 'false',
|
||||||
|
query.body !== 'false'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user