Files
jeanlucmakiola.de/README.md
T
makiolaj 8a1eb54a45 Render the app privacy policies from the app repos
Each policy now has exactly one copy: docs/PRIVACY.md in the app's own
repository. The pages keep their URLs and chrome and render that file
through a content collection, so the published page and the app's own
documentation cannot drift.

scripts/sync-external.mjs shallow-clones both repos into external/ from
prebuild and predev — not from CI: Coolify builds the site from the repo,
so a checkout that only ran in a Gitea job would never reach the deploy.
It falls back to the raw file if git is unavailable, and takes <APP>_REF
or <APP>_LOCAL for work against a branch or an unpushed working copy.

A missing, empty or malformed policy fails the build, verified against the
real image: the deploy stops rather than publishing an empty privacy page.
2026-09-09 16:33:11 +02:00

108 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# jeanlucmakiola.de
Personal website + blog. Built with [Astro](https://astro.build), deployed on
self-hosted [Coolify](https://coolify.io), analytics via self-hosted
[Umami](https://umami.is).
See [PLANNING.md](./PLANNING.md) for the decisions and roadmap.
## Develop
```sh
npm install
npm run dev # http://localhost:4321
npm run build # output -> dist/
npm run preview # serve the built site locally
```
## App privacy policies (rendered from the app repos)
`/calendula/privacy` and `/agendula/privacy` hold **no copy** of their prose.
Each policy lives in its own app repository, as `docs/PRIVACY.md` with
`title` / `description` / `updated` frontmatter, and is the single copy of that
policy anywhere:
| Page | Source |
|------|--------|
| `/calendula/privacy` | [`jlmakiola/calendula`](https://codeberg.org/jlmakiola/calendula) → `docs/PRIVACY.md` |
| `/agendula/privacy` | [`jlmakiola/agendula`](https://codeberg.org/jlmakiola/agendula) → `docs/PRIVACY.md` |
`scripts/sync-external.mjs` clones them (shallow, single-branch, no
credentials) into `external/` before every build and dev server — it is wired
to `prebuild` and `predev`, **not** to CI, because Coolify builds the site from
the repo and a checkout that only ran in a Gitea job would never reach the
deployed page. `src/content.config.ts` exposes each file as a content
collection; the pages render it and supply the `<h1>` from `title`.
**To change a policy, edit it in the app repo, in a PR.** It reaches the live
page on the site's next build.
```sh
npm run sync:external # refresh the checkouts by hand
CALENDULA_REF=some/branch npm run build # build against a branch
AGENDULA_LOCAL=../agendula npm run dev # render a working copy, no network
```
The build **fails** if either policy is missing, empty, or malformed. That is
deliberate: a privacy page silently rendering nothing is the one failure this
arrangement exists to prevent, and it is worth a red deploy.
### Getting a policy edit onto the site
A build is what publishes it, so:
- **Floor:** the daily `scheduled-deploy` cron rebuilds every morning, so any
edit is live within a day without anyone doing anything.
- **Immediate:** add a webhook in the app repo (Codeberg → Settings → Webhooks)
pointing at the same Coolify deploy URL the cron uses, so a merge to the app's
`main` triggers a site rebuild at once. One-time setup per app repo; the
secret lives in Coolify, not here.
## Writing a post
Create a Markdown file in `src/content/blog/`, e.g. `my-post.md`:
```md
---
title: My post
description: One-line summary (used for SEO + RSS).
pubDate: 2026-06-28
tags: [notes]
draft: false # set true to hide from listings/feeds
---
Body in Markdown.
```
The filename (minus extension) becomes the URL slug: `/blog/my-post/`.
Commit + push → Coolify rebuilds → live.
## Analytics (Umami)
Analytics is **off** unless these env vars are set (so it stays off in dev).
Set them as **build-time** variables in Coolify:
| Variable | Value |
|-------------------------|-----------------------------------------|
| `PUBLIC_UMAMI_SRC` | `https://<your-umami>/script.js` |
| `PUBLIC_UMAMI_WEBSITE` | the website id (UUID) from Umami |
## Deploy on Coolify
This repo ships a multi-stage `Dockerfile` (build with Node → serve `dist/`
with Caddy on port 80).
1. New Resource → **Dockerfile** (or auto-detect), point at this repo.
2. Set the two `PUBLIC_UMAMI_*` build variables.
3. Set the domain to `jeanlucmakiola.de`; Coolify provisions HTTPS.
4. Deploy Umami separately (Coolify has a one-click Umami template), then copy
its script URL + website id into the build variables above.
## SEO
- Sitemap at `/sitemap-index.xml` (via `@astrojs/sitemap`)
- RSS at `/rss.xml`
- Canonical URLs + Open Graph/Twitter tags in `src/components/BaseHead.astro`
- `robots.txt` in `public/`
- Add an `og-default.png` (1200×630) to `public/` for link previews.