fix(backend): harden endpoint and startup config
This commit is contained in:
17
README.md
17
README.md
@@ -9,6 +9,16 @@
|
|||||||
- Post management UI should use `/posts` (`@indiekit/endpoint-posts.mountPath`).
|
- Post management UI should use `/posts` (`@indiekit/endpoint-posts.mountPath`).
|
||||||
- Do not set post-management `mountPath` to frontend routes like `/blog`, otherwise backend publishing can be shadowed by the public site.
|
- Do not set post-management `mountPath` to frontend routes like `/blog`, otherwise backend publishing can be shadowed by the public site.
|
||||||
|
|
||||||
|
## Backend endpoints
|
||||||
|
|
||||||
|
- Configured endpoint mount paths:
|
||||||
|
- Posts management: `/posts`
|
||||||
|
- Files: `/files`
|
||||||
|
- Webmentions moderation + API: `/webmentions`
|
||||||
|
- Conversations + API: `/conversations`
|
||||||
|
- GitHub activity + API: `/github`
|
||||||
|
- If IndieKit is reverse-proxied behind `/admin`, these become `/admin/posts`, `/admin/files`, etc.
|
||||||
|
|
||||||
## MongoDB
|
## MongoDB
|
||||||
|
|
||||||
- Preferred: set a full `MONGO_URL` (example: `mongodb://user:pass@host:27017/indiekit?authSource=admin`).
|
- Preferred: set a full `MONGO_URL` (example: `mongodb://user:pass@host:27017/indiekit?authSource=admin`).
|
||||||
@@ -46,4 +56,9 @@
|
|||||||
- `GH_CONTENT_TOKEN`: token for content repo (`blog`), used by `@indiekit/store-github`.
|
- `GH_CONTENT_TOKEN`: token for content repo (`blog`), used by `@indiekit/store-github`.
|
||||||
- `GH_ACTIVITY_TOKEN`: token for GitHub dashboard/activity endpoint, used by `@rmdes/indiekit-endpoint-github`.
|
- `GH_ACTIVITY_TOKEN`: token for GitHub dashboard/activity endpoint, used by `@rmdes/indiekit-endpoint-github`.
|
||||||
- `GITHUB_USERNAME`: GitHub user/owner name.
|
- `GITHUB_USERNAME`: GitHub user/owner name.
|
||||||
- Backward compatibility: if `GH_CONTENT_TOKEN` or `GH_ACTIVITY_TOKEN` are not set, config falls back to `GITHUB_TOKEN`.
|
- Backward compatibility: if `GH_CONTENT_TOKEN` or `GH_ACTIVITY_TOKEN` are not set, config falls back to `GITHUB_TOKEN`.
|
||||||
|
|
||||||
|
## Startup script
|
||||||
|
|
||||||
|
- `start.sh` is intentionally ignored by Git (`.gitignore`) so server secrets are not committed.
|
||||||
|
- Use `start.example.sh` as the tracked template and keep real credentials in environment variables (or `.env` on the server).
|
||||||
@@ -1,2 +1,15 @@
|
|||||||
require('dotenv').config();
|
require("dotenv/config");
|
||||||
require('indiekit').serve();
|
const { spawn } = require("child_process");
|
||||||
|
|
||||||
|
const args = [
|
||||||
|
require.resolve("@indiekit/indiekit/bin/cli.js"),
|
||||||
|
"serve",
|
||||||
|
"--config",
|
||||||
|
"indiekit.config.mjs",
|
||||||
|
];
|
||||||
|
|
||||||
|
const child = spawn(process.execPath, args, { stdio: "inherit" });
|
||||||
|
|
||||||
|
child.on("exit", (code) => {
|
||||||
|
process.exit(code ?? 1);
|
||||||
|
});
|
||||||
|
|||||||
@@ -27,6 +27,18 @@ const githubContentToken =
|
|||||||
process.env.GH_CONTENT_TOKEN || process.env.GITHUB_TOKEN;
|
process.env.GH_CONTENT_TOKEN || process.env.GITHUB_TOKEN;
|
||||||
const githubActivityToken =
|
const githubActivityToken =
|
||||||
process.env.GH_ACTIVITY_TOKEN || process.env.GITHUB_TOKEN;
|
process.env.GH_ACTIVITY_TOKEN || process.env.GITHUB_TOKEN;
|
||||||
|
const publicationBaseUrl = (
|
||||||
|
process.env.PUBLICATION_URL || "https://blog.giersig.eu"
|
||||||
|
).replace(/\/+$/, "");
|
||||||
|
|
||||||
|
let webmentionDomain = process.env.WEBMENTION_IO_DOMAIN;
|
||||||
|
if (!webmentionDomain) {
|
||||||
|
try {
|
||||||
|
webmentionDomain = new URL(publicationBaseUrl).hostname;
|
||||||
|
} catch {
|
||||||
|
webmentionDomain = "blog.giersig.eu";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
debug: "indiekit:*",
|
debug: "indiekit:*",
|
||||||
@@ -35,55 +47,55 @@ export default {
|
|||||||
mongodbUrl: mongoUrl,
|
mongodbUrl: mongoUrl,
|
||||||
},
|
},
|
||||||
publication: {
|
publication: {
|
||||||
me: "https://blog.giersig.eu",
|
me: publicationBaseUrl,
|
||||||
postTypes: {
|
postTypes: {
|
||||||
article: {
|
article: {
|
||||||
name: "Artikel",
|
name: "Artikel",
|
||||||
post: {
|
post: {
|
||||||
path: "content/articles/{slug}.md",
|
path: "content/articles/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/articles/{slug}/",
|
url: `${publicationBaseUrl}/articles/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
note: {
|
note: {
|
||||||
name: "Notiz",
|
name: "Notiz",
|
||||||
post: {
|
post: {
|
||||||
path: "content/notes/{slug}.md",
|
path: "content/notes/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/notes/{slug}/",
|
url: `${publicationBaseUrl}/notes/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
bookmark: {
|
bookmark: {
|
||||||
name: "Lesezeichen",
|
name: "Lesezeichen",
|
||||||
post: {
|
post: {
|
||||||
path: "content/bookmarks/{slug}.md",
|
path: "content/bookmarks/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/bookmarks/{slug}/",
|
url: `${publicationBaseUrl}/bookmarks/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
like: {
|
like: {
|
||||||
name: "Like",
|
name: "Like",
|
||||||
post: {
|
post: {
|
||||||
path: "content/likes/{slug}.md",
|
path: "content/likes/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/likes/{slug}/",
|
url: `${publicationBaseUrl}/likes/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
photo: {
|
photo: {
|
||||||
name: "Foto",
|
name: "Foto",
|
||||||
post: {
|
post: {
|
||||||
path: "content/photos/{slug}.md",
|
path: "content/photos/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/photos/{slug}/",
|
url: `${publicationBaseUrl}/photos/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reply: {
|
reply: {
|
||||||
name: "Antwort",
|
name: "Antwort",
|
||||||
post: {
|
post: {
|
||||||
path: "content/replies/{slug}.md",
|
path: "content/replies/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/replies/{slug}/",
|
url: `${publicationBaseUrl}/replies/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
name: "Seite",
|
name: "Seite",
|
||||||
post: {
|
post: {
|
||||||
path: "content/pages/{slug}.md",
|
path: "content/pages/{slug}.md",
|
||||||
url: "https://blog.giersig.eu/{slug}/",
|
url: `${publicationBaseUrl}/{slug}/`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -113,6 +125,7 @@ export default {
|
|||||||
},
|
},
|
||||||
"@rmdes/indiekit-endpoint-webmention-io": {
|
"@rmdes/indiekit-endpoint-webmention-io": {
|
||||||
token: process.env.WEBMENTION_IO_TOKEN,
|
token: process.env.WEBMENTION_IO_TOKEN,
|
||||||
|
domain: webmentionDomain,
|
||||||
},
|
},
|
||||||
"@rmdes/indiekit-endpoint-conversations": {
|
"@rmdes/indiekit-endpoint-conversations": {
|
||||||
mountPath: "/conversations",
|
mountPath: "/conversations",
|
||||||
|
|||||||
30
start.example.sh
Normal file
30
start.example.sh
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd /usr/local/indiekit
|
||||||
|
|
||||||
|
# Optional: load environment from local .env file.
|
||||||
|
if [ -f .env ]; then
|
||||||
|
set -a
|
||||||
|
. ./.env
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
: "${SECRET:?SECRET is required}"
|
||||||
|
: "${PASSWORD_SECRET:?PASSWORD_SECRET is required}"
|
||||||
|
|
||||||
|
# Allow either full Mongo URL or decomposed credentials.
|
||||||
|
if [ -z "${MONGO_URL:-}" ]; then
|
||||||
|
: "${MONGO_PASSWORD:?MONGO_PASSWORD is required when MONGO_URL is not set}"
|
||||||
|
export MONGO_USERNAME="${MONGO_USERNAME:-indiekit}"
|
||||||
|
export MONGO_AUTH_SOURCE="${MONGO_AUTH_SOURCE:-admin}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${GH_CONTENT_TOKEN:-}" ] && [ -z "${GITHUB_TOKEN:-}" ]; then
|
||||||
|
echo "GH_CONTENT_TOKEN or GITHUB_TOKEN is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export NODE_ENV="${NODE_ENV:-production}"
|
||||||
|
|
||||||
|
exec /usr/local/bin/node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs
|
||||||
Reference in New Issue
Block a user