feat: I am now using Directus to host my blog!
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Damillora 2024-12-02 00:54:30 +07:00
parent 825dfbbef5
commit e00a73b9c9
15 changed files with 423 additions and 284 deletions

200
package-lock.json generated
View File

@ -8,8 +8,8 @@
"name": "@damillora/shallie",
"version": "1.0.0",
"dependencies": {
"@damillora/plachta": "^6.3.0",
"@tryghost/content-api": "^1.11.16",
"@damillora/plachta": "^6.5.0",
"@directus/sdk": "^18.0.0",
"main": "^1000.0.1"
},
"devDependencies": {
@ -56,13 +56,25 @@
}
},
"node_modules/@damillora/plachta": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.3.0.tgz",
"integrity": "sha512-cRjdAHnlprh9SSTg45QtWinom0+TisuJ4zB+lAWJDHqa1fxMqLgc6OKDcrXFbEn9rxICY8vSMv17Iwol/yocFA==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.5.0.tgz",
"integrity": "sha512-3QJtNCYLlRm/pxqDrUNWX/LtMexxGac+LeIVmsHviLtBjk06XAHQ14TeBLHzE1UMFDXkz4jmzBe+sZkCjUdD1A==",
"dependencies": {
"svelte": "^5.0.0"
}
},
"node_modules/@directus/sdk": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/@directus/sdk/-/sdk-18.0.0.tgz",
"integrity": "sha512-PREPeIKI1/EpvWxZHqWaRgL0HNccaauQm98sABBPMPwH3gCHfSz+sFJyugyx+MYHBiv2K5M8BCxF3MRCbAzljw==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"funding": {
"url": "https://github.com/directus/directus?sponsor=1"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@ -1327,14 +1339,6 @@
"vite": "^5.0.0"
}
},
"node_modules/@tryghost/content-api": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@tryghost/content-api/-/content-api-1.11.21.tgz",
"integrity": "sha512-ozJqEMHDUO7D0SGxPbUnG+RvwBbzC3zmdGOW8cFvkcKzrhe7uOAmVKyq7/J3kRAM2QthTlmiDpqp7NEo9ZLlKg==",
"dependencies": {
"axios": "^1.0.0"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@ -1682,21 +1686,6 @@
"node": ">=8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -1831,17 +1820,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@ -1949,14 +1927,6 @@
"node": ">=0.10.0"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@ -2449,38 +2419,6 @@
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3120,25 +3058,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3522,11 +3441,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -4507,13 +4421,18 @@
}
},
"@damillora/plachta": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.3.0.tgz",
"integrity": "sha512-cRjdAHnlprh9SSTg45QtWinom0+TisuJ4zB+lAWJDHqa1fxMqLgc6OKDcrXFbEn9rxICY8vSMv17Iwol/yocFA==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.5.0.tgz",
"integrity": "sha512-3QJtNCYLlRm/pxqDrUNWX/LtMexxGac+LeIVmsHviLtBjk06XAHQ14TeBLHzE1UMFDXkz4jmzBe+sZkCjUdD1A==",
"requires": {
"svelte": "^5.0.0"
}
},
"@directus/sdk": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/@directus/sdk/-/sdk-18.0.0.tgz",
"integrity": "sha512-PREPeIKI1/EpvWxZHqWaRgL0HNccaauQm98sABBPMPwH3gCHfSz+sFJyugyx+MYHBiv2K5M8BCxF3MRCbAzljw=="
},
"@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@ -5156,14 +5075,6 @@
"debug": "^4.3.7"
}
},
"@tryghost/content-api": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@tryghost/content-api/-/content-api-1.11.21.tgz",
"integrity": "sha512-ozJqEMHDUO7D0SGxPbUnG+RvwBbzC3zmdGOW8cFvkcKzrhe7uOAmVKyq7/J3kRAM2QthTlmiDpqp7NEo9ZLlKg==",
"requires": {
"axios": "^1.0.0"
}
},
"@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@ -5393,21 +5304,6 @@
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"requires": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -5502,14 +5398,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@ -5590,11 +5478,6 @@
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@ -5964,21 +5847,6 @@
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
"dev": true
},
"follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
},
"form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -6457,19 +6325,6 @@
}
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -6710,11 +6565,6 @@
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
"dev": true
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

@ -45,8 +45,8 @@
},
"type": "module",
"dependencies": {
"@damillora/plachta": "^6.3.0",
"@tryghost/content-api": "^1.11.16",
"@damillora/plachta": "^6.5.0",
"@directus/sdk": "^18.0.0",
"main": "^1000.0.1"
}
}

View File

@ -34,13 +34,13 @@
},
"headline": "${post.title}",
"url": "${post.url}",
"datePublished": "${post.published_at}",
"datePublished": "${post.date_published}",
"dateModified": "${post.updated_at}",
"image": {
"@type": "ImageObject",
"url": "${post.feature_image ?? 'https://blog.nanao.moe/images/default-feature.jpg'}"
},
"keywords": "${post.primary_tag.name}",
"keywords": "${post.category.name}",
"description": "${post.excerpt}",
"mainEntityOfPage": {
"@type": "WebPage",
@ -64,7 +64,7 @@
/>
<meta property="article:published_time" content="2022-07-22T12:42:23.000Z" />
<meta property="article:modified_time" content="2022-07-22T12:47:19.000Z" />
<meta property="article:tag" content={post.primary_tag.name} />
<meta property="article:tag" content={post.category.name} />
<meta property="article:publisher" content="https://www.facebook.com/Damillora" />
<meta name="twitter:card" content="summary_large_image" />
@ -78,7 +78,7 @@
<meta name="twitter:label1" content="Written by" />
<meta name="twitter:data1" content={post.primary_author.name} />
<meta name="twitter:label2" content="Filed under" />
<meta name="twitter:data2" content={post.primary_tag.name} />
<meta name="twitter:data2" content={post.category.name} />
<meta name="twitter:site" content="@Damillora" />
<meta name="twitter:creator" content={post.primary_author.twitter} />

View File

@ -1,98 +1,278 @@
import GhostContentAPI from '@tryghost/content-api';
import dayjs from 'dayjs';
import { createDirectus, readItems, rest } from '@directus/sdk';
import { DIRECTUS_URL, mapAuthor, mapCategory, mapIndexPosts, mapPost } from './directusUtils';
export const GHOST_URL = 'https://admin.blog.nanao.moe';
const GHOST_KEY = '8c86e852c31b39b9d4148b2088';
const GHOST_VERSION = 'v5.0';
const POSTS_PER_PAGE = 10;
const api = GhostContentAPI({
url: GHOST_URL,
key: GHOST_KEY,
version: GHOST_VERSION
})
const directus = createDirectus(DIRECTUS_URL).with(rest());
const indexPageFields = [
"title",
"feature_image.*",
"date_published",
"excerpt",
"slug",
"author.*",
"category.*",
"author.profile_image.*",
];
const allPageFields = [
...indexPageFields, "date_updated"
];
const postPageFields = [...indexPageFields, "content"];
const authorPageFields = [
"id",
"name",
"slug",
"bio",
"cover_image.*",
"profile_image.*",
"website",
"twitter",
"facebook",
"bluesky"
];
const categoryPageFields = [
"id",
"name",
"slug",
"description",
"cover_image.*",
"accent_color",
];
const filterPublished = {
"status": {
"_eq": "published",
}
}
export const browsePost = async (page = 1) => {
const posts = await api.posts.browse({ page: page, limit: 10, include: ['tags', 'authors'] });
const posts = await directus.request(readItems("posts", {
limit: POSTS_PER_PAGE,
page: page,
fields: indexPageFields,
filter: filterPublished,
sort: ["-date_published"]
}))
return posts
return posts.map(mapIndexPosts);
}
export const searchPost = async (q: string, page = 1) => {
const posts = await directus.request(readItems("posts", {
limit: POSTS_PER_PAGE,
page: page,
fields: indexPageFields,
search: q,
filter: filterPublished,
}))
return posts.map(mapIndexPosts);
}
export const browseNextPost = async (post: any) => {
const posts = await api.posts.browse({ limit: 1, filter: [`published_at:>'${post.published_at}'`], order: 'published_at ASC' });
const filterPrev = {
"_and": [
{
"_and": [
{
"date_published": {
"_gt": post.date_published,
},
},
{
"id": {
"_neq": post.id,
}
}
]
},
filterPublished,
]
};
const posts = await directus.request(readItems("posts", {
limit: 1,
fields: indexPageFields,
filter: filterPrev,
sort: ["date_published"],
}))
if (posts.length > 0) {
return posts[0];
return posts.map(mapIndexPosts)[0];
}
return null;
}
export const browsePrevPost = async (post: any) => {
const posts = await api.posts.browse({ limit: 1, filter: [`published_at:<'${post.published_at}'`], order: 'published_at DESC' });
const posts = await directus.request(readItems("posts", {
limit: 1,
fields: indexPageFields,
filter: {
"_and": [
{
"_and": [
{
"date_published": {
"_lt": post.date_published,
},
},
{
"id": {
"_neq": post.id,
}
}
]
},
filterPublished,
]
},
sort: ["-date_published"],
}))
if (posts.length > 0) {
return posts[0];
return posts.map(mapIndexPosts)[0];
}
return null;
}
export const browseRelatedPost = async (tag: string | undefined, id: string) => {
const tagCondition = tag ? [`tag:${tag}`] : []
const posts = await api.posts.browse({ limit: 3, filter: [...tagCondition, ...[`id:-${id}`]], order: 'published_at DESC', include: ['tags', 'authors'] });
const filterPost = tag ? {
"_and": [
{
"_and": [
{
"category": {
"slug": {
"_eq": tag,
}
},
},
{
"id": {
"_neq": id,
}
}
]
},
filterPublished,
]
} : filterPublished
const posts = await directus.request(readItems("posts", {
limit: 3,
fields: indexPageFields,
filter: filterPost,
sort: ["-date_published"]
}))
return posts;
return posts.map(mapIndexPosts);
}
export const browseAllPost = async (page = 1) => {
const posts = await api.posts.browse({ limit: 'all' });
const posts = await directus.request(readItems("posts", {
fields: allPageFields,
filter: filterPublished,
sort: ["-date_published"]
}))
return posts
return posts.map(mapIndexPosts);
}
export const browsePostWithTag = async (slug: string, page = 1) => {
const posts = await api.posts.browse({ page: page, limit: 10, include: ['tags', 'authors'], filter: [`tag:${slug}`] });
const filterTag = {
"_and": [
{
"category": {
"slug": {
"_eq": slug,
}
}
},
filterPublished,
]
};
const posts = await directus.request(readItems("posts", {
limit: POSTS_PER_PAGE,
page: page,
fields: indexPageFields,
filter: filterTag,
sort: ["-date_published"]
}))
return posts
return posts.map(mapIndexPosts);
}
export const browsePostWithAuthor = async (slug: string, page = 1) => {
const posts = await api.posts.browse({ page: page, limit: 10, include: ['tags', 'authors'], filter: [`author:${slug}`] });
return posts
const filterAuthor = {
"_and": [
{
"author": {
"slug": {
"_eq": slug,
}
export const readPost = async (slug: string) => {
try {
const post = await api.posts.read({ slug }, { include: ['tags', 'authors'] });
}
},
filterPublished,
]
};
const posts = await directus.request(readItems("posts", {
limit: POSTS_PER_PAGE,
page: page,
fields: indexPageFields,
filter: filterAuthor,
sort: ["-date_published"]
}))
return post;
return posts.map(mapIndexPosts);
}
export const readPost = async (slug: string) => {
const filterPost = {
"slug": {
"_eq": slug,
}
};
try {
const post = await directus.request(readItems("posts", {
limit: 1,
fields: postPageFields,
filter: filterPost,
}))
return post.map(mapPost)[0];
} catch (e) {
return null;
}
}
export const readTag = async (slug: string) => {
const filterTag = {
"slug": {
"_eq": slug,
}
};
try {
const tag = await api.tags.read({ slug });
return tag;
const category = await await directus.request(readItems("categories", {
limit: 1,
fields: categoryPageFields,
filter: filterTag,
}))
return category.map(mapCategory)[0];
} catch (e) {
return null;
}
}
export const readAuthor = async (slug: string) => {
const filterAuthor = {
"slug": {
"_eq": slug,
}
};
try {
const author = await api.authors.read({ slug });
return author;
const author = await directus.request(readItems("authors", {
limit: 1,
fields: authorPageFields,
filter: filterAuthor,
}))
return author.map(mapAuthor)[0];
} catch (e) {
return null;
}
}
export const browseLatestPost = async () => {
const post = await api.posts.browse({ limit: 1 });
return post[0];
}
export const browseLatestTag = async () => {
const tag = await api.tags.browse({ limit: 1, order: 'updated_at DESC' })
}
export const browseLatestAuthor = async () => {
const tag = await api.authors.browse({ limit: 1, order: 'updated_at DESC' })
}
export const browseSettings = async () => {
return {
codeinjection_head: `<script async defer data-domain="blog.nanao.moe" src="https://stats.nanao.moe/js/plausible.js"></script>`,

View File

@ -0,0 +1,105 @@
export const DIRECTUS_URL = 'https://nanakura.nanao.moe';
export const generateAssetUrl = (file: any) => {
if (!file) {
return null;
}
return DIRECTUS_URL + "/assets/" + file.id + "/" + file.filename_download;
};
export const generateCategoryUrl = (slug: string) => {
return "/" + slug;
}
export const generateAuthorUrl = (slug: string) => {
return "/author/" + slug;
}
export const generatePostUrl = (category: string, slug: string) => {
return "/" + category + "/" + slug;
}
export const mapPostAuthor = (author: any) => {
return {
name: author.name,
profile_image: generateAssetUrl(author.profile_image),
url: generateAuthorUrl(author.slug),
};
}
export const mapPostCategory = (category: any) => {
if (category)
{
return {
name: category.name,
accent_color: category.accent_color,
url: generateCategoryUrl(category.slug),
slug: category.slug,
}
}
return null;
}
export const mapIndexPosts = (post: any) => {
const author = mapPostAuthor(post.author);
const category = mapPostCategory(post.category);
return {
title: post.title,
feature_image: generateAssetUrl(post.feature_image),
authors: [author],
category: category,
excerpt: post.excerpt,
date_published: post.date_published,
reading_time: null,
url: generatePostUrl(category?.slug, post.slug),
}
}
export const mapAuthor = (author: any) => {
if (author) {
return {
name: author.name,
profile_image: generateAssetUrl(author.profile_image),
cover_image: generateAssetUrl(author.cover_image),
bio: author.bio,
url: generateAuthorUrl(author.slug),
website: author.website,
facebook: author.facebook,
twitter: author.twitter,
bluesky: author.bluesky,
};
}
return null;
}
export const mapCategory = (category: any) => {
return {
name: category.name,
accent_color: category.accent_color,
url: generateCategoryUrl(category.slug),
slug: category.slug,
description: category.description,
cover_image: generateAssetUrl(category.cover_image),
}
};
export const mapPost = (post: any) => {
const author = mapPostAuthor(post.author);
const category = mapPostCategory(post.category);
return {
id: post.id,
title: post.title,
feature_image: generateAssetUrl(post.feature_image),
authors: [author],
primary_author: author,
category: category,
excerpt: post.excerpt,
date_published: post.date_published,
reading_time: null,
url: generatePostUrl(category?.slug, post.slug),
content: post.content,
}
}

View File

@ -41,10 +41,13 @@ export const postProcessor: Plugin = () => {
// Responsive
if (node.tagName == 'img') {
const src = node.properties.src;
const srcsetString = generateSrcsetString(src);
const sizesString = generateSizesString();
node.properties.srcset = srcsetString;
node.properties.sizes = `${sizesString}`;
// const srcsetString = generateSrcsetString(src);
// const sizesString = generateSizesString();
// node.properties.srcset = srcsetString;
// node.properties.sizes = `${sizesString}`;
node.properties.srcSet = null;
node.properties.sizes = null;
console.log(node.properties);
}
// Embeds
if (node.tagName == 'iframe') {

View File

@ -7,7 +7,7 @@
<title>{$page.status} - Damillora's Virtual Memoir</title>
</svelte:head>
<Hero background="/images/default-feature.jpg" />
<!-- <Hero background="/images/default-feature.jpg" /> -->
<Container>
<Post>
<h1>{$page.status}</h1>

View File

@ -3,16 +3,28 @@
import { goto } from '$app/navigation';
import { browseSettings } from '$lib/content/contentApi';
import { Base, Footer, Header, NavDarkMode, NavMenu, NavSearch, NavigationLoading } from '@damillora/plachta';
import {
Base,
Footer,
Header,
NavDarkMode,
NavMenu,
NavSearch,
NavigationLoading
} from '@damillora/plachta';
import type { Load } from '@sveltejs/kit';
let timer: any = null;
const doSearch = (e: any) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
if (e.detail.query) {
goto('/search?q=' + e.detail.query, { replaceState: true, keepFocus: true });
} else {
goto('/', { replaceState: true, keepFocus: true });
}
}, 500);
};
let { data, children } = $props();
@ -27,16 +39,12 @@
<Base>
<Header>
{#snippet title()}
<a href="/"> <strong>Damillora</strong>'s Virtual Memoir </a>
{/snippet}
{#snippet nav()}
<NavMenu label="nanao.moe" url="https://nanao.moe" />
<NavDarkMode />
<NavSearch on:search={doSearch} />
{/snippet}
</Header>

View File

@ -7,10 +7,9 @@
import { onMount } from 'svelte';
import IndexSeo from '$lib/components/SEO/IndexSEO.svelte';
import { browsePost } from '$lib/content/contentApi';
import type { PostsOrPages } from '@tryghost/content-api';
let { data } = $props();
let posts: PostsOrPages = $state(data.posts);
let posts: any = $state(data.posts);
let newPosts: any[] = $state([]);
let pageNum = 1;
@ -50,9 +49,8 @@
<PostCard
title={post.title}
authors={post.authors}
primary_tag={post.primary_tag}
date={dayjs(post.published_at).format('DD MMM YYYY')}
reading_time={`${post.reading_time} min read`}
primary_tag={post.category}
date={dayjs(post.date_published).format('DD MMM YYYY')}
excerpt={post.excerpt}
feature_image={post.feature_image ?? '/images/default-feature.jpg'}
url={post.url}

View File

@ -7,10 +7,9 @@
import { browser } from '$app/environment';
import dayjs from 'dayjs';
import TagSeo from '$lib/components/SEO/TagSEO.svelte';
import type { PostsOrPages } from '@tryghost/content-api';
let { data } = $props();
let posts: PostsOrPages = $state(data.posts);
let posts: any = $state(data.posts);
let pageNum = 1;
async function loadPage() {
@ -45,7 +44,7 @@
<Container>
<Post>
<TagHeader
background={data.tag.feature_image}
background={data.tag.cover_image}
accent_color={data.tag.accent_color}
name={data.tag.name}
description={data.tag.description}
@ -57,9 +56,8 @@
<PostCard
title={post.title}
authors={post.authors}
primary_tag={post.primary_tag}
date={dayjs(post.published_at).format('DD MMM YYYY')}
reading_time={`${post.reading_time} min read`}
primary_tag={post.category}
date={dayjs(post.date_published).format('DD MMM YYYY')}
excerpt={post.excerpt}
feature_image={post.feature_image ?? '/images/default-feature.jpg'}
url={post.url}

View File

@ -27,30 +27,28 @@
background={data.post.feature_image}
title={data.post.title}
authors={data.post.authors}
primary_tag={data.post.primary_tag}
date={dayjs(data.post.published_at).format('DD MMM YYYY')}
reading_time={`${data.post.reading_time} min read`}
primary_tag={data.post.category}
date={dayjs(data.post.date_published).format('DD MMM YYYY')}
/>
<PostMain>
<GhostStyle>
{@html data.post.html}
{@html data.post.content}
</GhostStyle>
</PostMain>
<PostNavigator prev_post={data.prevPost} next_post={data.nextPost} />
</Post>
<PostRelated
name={data.post.primary_tag?.name}
url={data.post.primary_tag?.url}
accent_color={data.post.primary_tag?.accent_color}
name={data.post.category.name}
url={data.post.category?.url}
accent_color={data.post.category?.accent_color}
>
{#each data.relatedPost as post}
<PostCard
title={post.title}
authors={post.authors}
primary_tag={post.primary_tag}
date={dayjs(post.published_at).format('DD MMM YYYY')}
reading_time={`${post.reading_time} min read`}
primary_tag={post.category}
date={dayjs(post.date_published).format('DD MMM YYYY')}
excerpt={post.excerpt}
feature_image={post.feature_image ?? '/images/default-feature.jpg'}
url={post.url}
@ -58,3 +56,4 @@
{/each}
</PostRelated>
</Container>
categorycategorycategorycategorycategorydate_published

View File

@ -9,11 +9,11 @@ export const load: PageLoad = async ({ params }) => {
if (!post) {
error(404, 'Post not found');
}
const newHtml = await processPostHtml(post.html);
post.html = newHtml;
const newHtml = await processPostHtml(post.content);
post.content = newHtml;
const prevPost = await browsePrevPost(post);
const nextPost = await browseNextPost(post);
const relatedPost = await browseRelatedPost(post.primary_tag?.slug, post.id);
const relatedPost = await browseRelatedPost(post.category?.slug, post.id);
return {
post: post,
prevPost: prevPost,

View File

@ -6,7 +6,6 @@
import { browser } from '$app/environment';
import dayjs from 'dayjs';
import AuthorSeo from '$lib/components/SEO/AuthorSEO.svelte';
import type { PostsOrPages } from '@tryghost/content-api';
import { onMount } from 'svelte';
@ -16,7 +15,7 @@
}
let { data }: Props = $props();
let posts: PostsOrPages = $state(data.posts);
let posts: any = $state(data.posts);
let pageNum = 1;
async function loadPage() {
@ -66,9 +65,8 @@
<PostCard
title={post.title}
authors={post.authors}
primary_tag={post.primary_tag}
date={dayjs(post.published_at).format('DD MMM YYYY')}
reading_time={`${post.reading_time} min read`}
primary_tag={post.category}
date={dayjs(post.date_published).format('DD MMM YYYY')}
excerpt={post.excerpt}
feature_image={post.feature_image ?? '/images/default-feature.jpg'}
url={post.url}

View File

@ -7,6 +7,7 @@
import { onMount } from 'svelte';
import { search } from '$lib/content/searchApi';
import { afterNavigate } from '$app/navigation';
import { searchPost } from '$lib/content/contentApi.js';
let { data } = $props();
let posts: any[] = $state([]);
@ -18,22 +19,22 @@
// let url = $page.url;
let q = url.searchParams.get('q') ?? '';
const loadedPosts = await search(q, pageNum);
if(loadedPosts.result)
const loadedPosts = await searchPost(q, pageNum);
if(loadedPosts)
{
posts = [...loadedPosts.result]
posts = [...loadedPosts]
}
return;
}
// let url = $page.url;
let q = url.searchParams.get('q') ?? '';
const loadedPosts = await search(q, pageNum + 1);
const loadedPosts = await searchPost(q, pageNum + 1);
if(loadedPosts.result)
{
pageNum++;
posts = [...posts, ...loadedPosts.result]
posts = [...posts, ...loadedPosts]
}
}

View File

@ -17,7 +17,7 @@ export async function GET() {
allPosts.map((_post) => {
xml += '<url>'
xml += `<loc>${_post.url}</loc>`
xml += `<lastmod>${_post.updated_at}</lastmod>`
xml += `<lastmod>${_post.date_updated}</lastmod>`
xml += `<changefreq>always</changefreq>`
xml += `<priority>0.5</priority>`
xml += '</url>'
@ -25,7 +25,6 @@ export async function GET() {
xml += '</urlset>'
console.log(`Wrote Sitemap`);
return xml;
}