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", "name": "@damillora/shallie",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@damillora/plachta": "^6.3.0", "@damillora/plachta": "^6.5.0",
"@tryghost/content-api": "^1.11.16", "@directus/sdk": "^18.0.0",
"main": "^1000.0.1" "main": "^1000.0.1"
}, },
"devDependencies": { "devDependencies": {
@ -56,13 +56,25 @@
} }
}, },
"node_modules/@damillora/plachta": { "node_modules/@damillora/plachta": {
"version": "6.3.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.3.0.tgz", "resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.5.0.tgz",
"integrity": "sha512-cRjdAHnlprh9SSTg45QtWinom0+TisuJ4zB+lAWJDHqa1fxMqLgc6OKDcrXFbEn9rxICY8vSMv17Iwol/yocFA==", "integrity": "sha512-3QJtNCYLlRm/pxqDrUNWX/LtMexxGac+LeIVmsHviLtBjk06XAHQ14TeBLHzE1UMFDXkz4jmzBe+sZkCjUdD1A==",
"dependencies": { "dependencies": {
"svelte": "^5.0.0" "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": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@ -1327,14 +1339,6 @@
"vite": "^5.0.0" "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": { "node_modules/@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@ -1682,21 +1686,6 @@
"node": ">=8" "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": { "node_modules/axobject-query": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -1831,17 +1820,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "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": { "node_modules/comma-separated-tokens": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@ -1949,14 +1927,6 @@
"node": ">=0.10.0" "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": { "node_modules/detect-libc": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@ -2449,38 +2419,6 @@
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
"dev": true "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": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3120,25 +3058,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "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": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3522,11 +3441,6 @@
"url": "https://github.com/sponsors/wooorm" "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": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -4507,13 +4421,18 @@
} }
}, },
"@damillora/plachta": { "@damillora/plachta": {
"version": "6.3.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.3.0.tgz", "resolved": "https://registry.npmjs.org/@damillora/plachta/-/plachta-6.5.0.tgz",
"integrity": "sha512-cRjdAHnlprh9SSTg45QtWinom0+TisuJ4zB+lAWJDHqa1fxMqLgc6OKDcrXFbEn9rxICY8vSMv17Iwol/yocFA==", "integrity": "sha512-3QJtNCYLlRm/pxqDrUNWX/LtMexxGac+LeIVmsHviLtBjk06XAHQ14TeBLHzE1UMFDXkz4jmzBe+sZkCjUdD1A==",
"requires": { "requires": {
"svelte": "^5.0.0" "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": { "@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@ -5156,14 +5075,6 @@
"debug": "^4.3.7" "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": { "@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@ -5393,21 +5304,6 @@
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true "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": { "axobject-query": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -5502,14 +5398,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "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": { "comma-separated-tokens": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "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==", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true "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": { "detect-libc": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@ -5964,21 +5847,6 @@
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
"dev": true "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": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "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": { "minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -6710,11 +6565,6 @@
"integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
"dev": true "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": { "punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

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

View File

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

View File

@ -1,98 +1,278 @@
import GhostContentAPI from '@tryghost/content-api'; import { createDirectus, readItems, rest } from '@directus/sdk';
import dayjs from 'dayjs'; import { DIRECTUS_URL, mapAuthor, mapCategory, mapIndexPosts, mapPost } from './directusUtils';
export const GHOST_URL = 'https://admin.blog.nanao.moe'; const POSTS_PER_PAGE = 10;
const GHOST_KEY = '8c86e852c31b39b9d4148b2088';
const GHOST_VERSION = 'v5.0';
const api = GhostContentAPI({ const directus = createDirectus(DIRECTUS_URL).with(rest());
url: GHOST_URL,
key: GHOST_KEY, const indexPageFields = [
version: GHOST_VERSION "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) => { 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) => { 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) { if (posts.length > 0) {
return posts[0]; return posts.map(mapIndexPosts)[0];
} }
return null; return null;
} }
export const browsePrevPost = async (post: any) => { 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) { if (posts.length > 0) {
return posts[0]; return posts.map(mapIndexPosts)[0];
} }
return null; return null;
} }
export const browseRelatedPost = async (tag: string | undefined, id: string) => { export const browseRelatedPost = async (tag: string | undefined, id: string) => {
const tagCondition = tag ? [`tag:${tag}`] : [] const filterPost = tag ? {
const posts = await api.posts.browse({ limit: 3, filter: [...tagCondition, ...[`id:-${id}`]], order: 'published_at DESC', include: ['tags', 'authors'] }); "_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) => { 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) => { 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) => { export const browsePostWithAuthor = async (slug: string, page = 1) => {
const posts = await api.posts.browse({ page: page, limit: 10, include: ['tags', 'authors'], filter: [`author:${slug}`] }); const filterAuthor = {
"_and": [
{
"author": {
"slug": {
"_eq": slug,
}
}
},
filterPublished,
]
};
const posts = await directus.request(readItems("posts", {
limit: POSTS_PER_PAGE,
page: page,
fields: indexPageFields,
filter: filterAuthor,
sort: ["-date_published"]
}))
return posts return posts.map(mapIndexPosts);
} }
export const readPost = async (slug: string) => {
try {
const post = await api.posts.read({ slug }, { include: ['tags', 'authors'] });
return post; 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) { } catch (e) {
return null; return null;
} }
} }
export const readTag = async (slug: string) => { export const readTag = async (slug: string) => {
const filterTag = {
"slug": {
"_eq": slug,
}
};
try { try {
const tag = await api.tags.read({ slug }); const category = await await directus.request(readItems("categories", {
limit: 1,
return tag; fields: categoryPageFields,
filter: filterTag,
}))
return category.map(mapCategory)[0];
} catch (e) { } catch (e) {
return null; return null;
} }
} }
export const readAuthor = async (slug: string) => { export const readAuthor = async (slug: string) => {
const filterAuthor = {
"slug": {
"_eq": slug,
}
};
try { try {
const author = await api.authors.read({ slug }); const author = await directus.request(readItems("authors", {
return author; limit: 1,
fields: authorPageFields,
filter: filterAuthor,
}))
return author.map(mapAuthor)[0];
} catch (e) { } catch (e) {
return null; 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 () => { export const browseSettings = async () => {
return { return {
codeinjection_head: `<script async defer data-domain="blog.nanao.moe" src="https://stats.nanao.moe/js/plausible.js"></script>`, 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 // Responsive
if (node.tagName == 'img') { if (node.tagName == 'img') {
const src = node.properties.src; const src = node.properties.src;
const srcsetString = generateSrcsetString(src); // const srcsetString = generateSrcsetString(src);
const sizesString = generateSizesString(); // const sizesString = generateSizesString();
node.properties.srcset = srcsetString; // node.properties.srcset = srcsetString;
node.properties.sizes = `${sizesString}`; // node.properties.sizes = `${sizesString}`;
node.properties.srcSet = null;
node.properties.sizes = null;
console.log(node.properties);
} }
// Embeds // Embeds
if (node.tagName == 'iframe') { if (node.tagName == 'iframe') {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,11 +9,11 @@ export const load: PageLoad = async ({ params }) => {
if (!post) { if (!post) {
error(404, 'Post not found'); error(404, 'Post not found');
} }
const newHtml = await processPostHtml(post.html); const newHtml = await processPostHtml(post.content);
post.html = newHtml; post.content = newHtml;
const prevPost = await browsePrevPost(post); const prevPost = await browsePrevPost(post);
const nextPost = await browseNextPost(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 { return {
post: post, post: post,
prevPost: prevPost, prevPost: prevPost,

View File

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

View File

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

View File

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