Update content licenses and footer information; enhance contact and privacy policy pages

This commit is contained in:
Adrian Altner 2026-03-30 16:04:48 +02:00
parent 8241f5b0b3
commit ad7fa6f966
6 changed files with 61 additions and 41 deletions

View file

@ -2,36 +2,46 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Prerequisites
Node.js >= 22. Package manager is pnpm (enforced via `preinstall` script). Husky pre-commit hook runs `pnpm check`.
## Commands
```bash
npm run dev # Start dev server (localhost:4321)
npm run build # astro check + build + copy-sw (use this to verify changes)
npm run check # Type check + Biome lint
npm run check:fix # Type check + Biome lint with auto-fix
npm run stylelint # Lint CSS/Astro styles
npm run stylelint:fix # Fix style issues
pnpm dev # Start dev server (localhost:4321)
pnpm build # astro check + build + copy-sw (use this to verify changes)
pnpm build:production # Production build (skips astro check, uses --mode production)
pnpm check # Type check + Biome lint
pnpm check:fix # Type check + Biome lint with auto-fix
pnpm stylelint # Lint CSS/Astro styles
pnpm stylelint:fix # Fix style issues
pnpm generate:icons # Regenerate PWA icon assets
```
There are no automated tests. Verification is done via `npm run build` (0 errors required) and the preview MCP tools.
There are no automated tests. Verification is done via `pnpm build` (0 errors required) and the preview MCP tools.
## Architecture
Astro 6 site running in SSR mode (Node.js standalone adapter) with static output for most routes. TypeScript strict mode. Path alias `@/*``src/*`.
Astro 6 site running in SSR mode (Node.js standalone adapter) with static output for most routes. Pure Astro components — no React/Vue/Svelte. TypeScript strict mode (`astro/tsconfigs/strictest`). Path alias `@/*``src/*`.
**Formatter/linter:** Biome (not ESLint/Prettier). Run `check:fix` after larger edits.
**Formatter/linter:** Biome (not ESLint/Prettier). Run `check:fix` after larger edits. Biome config disables some rules for `.astro` files (`useConst`, `useImportType`, `noUnusedVariables`, `noUnusedImports`).
**Markdown plugins:** `remarkObsidianLinks` (custom, `src/lib/remark-obsidian-links.mjs`) for `[[wiki-link]]` syntax, `rehypeExternalLinks` for `target="_blank"` on external links.
### Content Collections (`src/content.config.ts`)
Five collections defined with Zod schemas:
Seven collections defined with Zod schemas:
| Collection | Path | Notes |
|---|---|---|
| `blog` | `src/content/blog/` | Posts with series support (`seriesParent`, `seriesOrder`), tags, category ref, syndication URLs |
| `categories` | `src/content/categories/` | Referenced by blog posts |
| `notes` | `src/content/notes/` | Short-form with optional cover image |
| `links` | `src/content/links/` | Curated external links |
| `collections_photos` | `src/content/photos/collections/` | Photo collections; photos stored as JPG + JSON sidecar files in `img/` subdirs |
| Collection | Loader | Path | Notes |
|---|---|---|---|
| `blog` | glob | `src/content/blog/posts/` | Series support (`seriesParent`, `seriesOrder`), tags, category ref, syndication URLs |
| `categories` | glob | `src/content/blog/categories/` | Referenced by blog posts |
| `notes` | glob | `src/content/notes/` | Short-form with optional cover image |
| `links_json` | file | `src/content/links/links.json` | JSON file with auto-generated IDs (`json/date/slug`) |
| `projects` | glob | `src/content/projects/project/` | Portfolio items with optional URL/GitHub links |
| `projects_categories` | glob | `src/content/projects/categories/` | Referenced by projects |
| `collections_photos` | glob | `src/content/photos/collections/` | Matches `**/index.{md,mdx}`; photos as JPG + JSON sidecar files in `img/` subdirs |
### Key Routing Patterns
@ -39,22 +49,35 @@ Five collections defined with Zod schemas:
- `/og/blog/[...slug].png` — OG images generated server-side via Satori (`src/lib/og.ts`)
- `/rss/blog.xml`, `/rss/notes.xml`, `/rss/links.xml`, `/rss/photos.xml` — Separate RSS feeds per content type
- `/photos/collections/[...slug]` — Nested photo collections with breadcrumb navigation
- `/projects/[...slug]`, `/projects/category/[...slug]` — Project portfolio
- `/tags/[slug]` — Cross-content-type tag pages
- `/archives/` — Timeline of all content
- `/api/webmentions.json` — Webmention data endpoint
### Lib Utilities (`src/lib/`)
- `collections.ts` — Photo collection helpers: `collectionSlug()`, `buildCollectionPhotos()`, `buildBreadcrumbs()`, `getChildCollections()`
- `og.ts` — OG image generation using Satori (`buildArticleVNode`, `renderOgImage`)
- `webmentions.ts` — Fetch and filter webmentions from webmention.io
- `webmentions.ts` — Fetch and filter webmentions from webmention.io (includes Mastodon/Bluesky validation)
- `photo-albums.ts` — Photo album organisation utilities
- `links.ts` — Link collection helpers
- `remark-obsidian-links.mjs` — Custom remark plugin for Obsidian-style `[[links]]`
### Scripts (`scripts/`)
- `mastodon-syndicate.js` — Scans `src/content/blog` and `src/content/notes` for posts without a `syndication` field, posts to Mastodon, writes the status URL back to frontmatter. Env vars: `MASTODON_BASE_URL`, `MASTODON_ACCESS_TOKEN`, `MASTODON_VISIBILITY`, `MASTODON_DRY_RUN`, `MASTODON_LIMIT`
- `publish-posts.sh` — Full deploy orchestration: rsync content to VPS → rebuild container → send webmentions → run mastodon-syndicate
- `mastodon-syndicate.mjs` — POSSE: scans blog/notes for posts without `syndication` field, posts to Mastodon, writes status URL back to frontmatter. Env vars: `MASTODON_BASE_URL`, `MASTODON_ACCESS_TOKEN`, `MASTODON_VISIBILITY`, `MASTODON_DRY_RUN`, `MASTODON_LIMIT`
- `vision.ts` — Uses Claude Vision API to auto-generate EXIF sidecars + metadata for photo collections. Requires `ANTHROPIC_API_KEY`
- `publish-all.sh` — Full deploy orchestration: rsync content to VPS → rebuild container → send webmentions → run mastodon-syndicate. Per-collection variants: `publish-blog.sh`, `publish-notes.sh`, `publish-links.sh`, `publish-photos.sh`, `publish-projects.sh`
- `new-post.sh`, `new-note.sh` — Content scaffolding templates
- `copy-sw.js` — Post-build service worker copy
### IndieWeb / Syndication
Blog posts support POSSE via `mastodon-syndicate.js`. After posting, the Mastodon status URL is written to frontmatter as `syndication: ["https://..."]`. The `SyndicationLinks` component reads this and renders `u-syndication` microformat links. Webmentions are fetched at build time from webmention.io and displayed via `WebMentions.astro`.
Blog posts support POSSE via `mastodon-syndicate.mjs`. After posting, the Mastodon status URL is written to frontmatter as `syndication: ["https://..."]`. The `SyndicationLinks` component renders `u-syndication` microformat links. Webmentions are fetched at build time from webmention.io and displayed via `WebMentions.astro`.
### Deployment
Multi-stage container build (`Containerfile`): Node 22 with pnpm for build, Node 22 slim for runtime. Runs `node dist/server/entry.mjs` on port 4321. Orchestrated via `compose.yml`. Deploy scripts rsync content to VPS and rebuild the container.
---
@ -78,7 +101,7 @@ Blog posts support POSSE via `mastodon-syndicate.js`. After posting, the Mastodo
### 4. Verification Before Done
- Never mark a task complete without proving it works
- Run `npm run build` — must complete with 0 errors
- Run `pnpm build` — must complete with 0 errors
- Use preview MCP tools to visually verify UI changes
### 5. Demand Elegance (Balanced)

View file

@ -26,7 +26,8 @@ SOFTWARE.
All content in `src/content/` and `public/` — including but not limited to
articles, notes, and photographs — is licensed under
[Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/).
[Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/).
You are free to share and adapt the content, as long as you give appropriate
credit and distribute your contributions under the same license.
You are free to share and adapt the content for non-commercial purposes, as long
as you give appropriate credit and distribute your contributions under the same
license.

View file

@ -8,7 +8,7 @@ const { dark } = Astro.props;
<footer class={`footer${dark ? " footer--dark" : ""}`}>
<div class="footer__inner">
<p class="footer__license">
© 2026 Adrian Altner · <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener noreferrer">CC BY-SA 4.0</a> · <a href="/imprint">Imprint</a> · <a href="/privacy-policy">Privacy Policy</a> · <a href="/contact">Contact</a>
© 2026 Adrian Altner · Content: <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener noreferrer">CC BY-NC-SA 4.0</a> · Code: <a href="https://mit-license.org/" target="_blank" rel="noopener noreferrer">MIT</a> · <a href="/imprint">Imprint</a> · <a href="/privacy-policy">Privacy Policy</a> · <a href="/contact">Contact</a>
</p>
<div class="footer__icons">
<a href="https://mastodon.social/@altner" aria-label="Mastodon" target="_blank" rel="me noopener noreferrer">

View file

@ -13,6 +13,9 @@ import BaseLayout from "@/layouts/BaseLayout.astro";
<p>Feedback and corrections are always welcome. I read everything, but rarely reply — please don't be discouraged if you don't hear back.</p>
<hr />
<div class="note">
<strong>Please note:</strong> Unsolicited press releases, advertising, and SEO pitches will be deleted without a response.
</div>
<h2>How to reach me</h2>
@ -26,7 +29,7 @@ import BaseLayout from "@/layouts/BaseLayout.astro";
</div>
<div class="card__body">
<p class="card__label">Email</p>
<a class="card__value r" data-obf="bW9je3RvZH1yZW50bGEtbmFpcmRhe3RhfXllaA==" data-obf-href="bWFpbHRvOmhleUBhZHJpYW4tYWx0bmVyLmNvbQ=="></a>
<span class="card__value r" data-obf="bW9je3RvZH1yZW50bGEtbmFpcmRhe3RhfXllaA=="></span>
</div>
</div>
@ -42,12 +45,6 @@ import BaseLayout from "@/layouts/BaseLayout.astro";
</div>
</div>
</div>
<hr />
<div class="note">
<strong>Please note:</strong> Unsolicited press releases, advertising, and SEO pitches will be deleted without a response.
</div>
</div>
</main>
</div>
@ -179,8 +176,7 @@ import BaseLayout from "@/layouts/BaseLayout.astro";
.card__value {
font-size: clamp(0.8rem, 3.5vw, 1rem);
color: var(--accent);
text-underline-offset: 3px;
color: #333;
margin: 0;
white-space: nowrap;
}

View file

@ -13,8 +13,8 @@ description: Legal notice for adrian-altner.com.
<span class="r" data-obf="bmVkc2VyRCA3OTAxMA=="></span><br>
<span class="r" data-obf="eW5hbXJlRw=="></span>
**Phone:** <a class="r" data-obf="MDI0MDM1ODcgNjUxIDk0Kw==" data-obf-href="dGVsOis0OTE1Njc4NTMwNDIw"></a>
**Email:** <a class="r" data-obf="bW9je3RvZH1yZW50bGEtbmFpcmRhe3RhfXllaA==" data-obf-href="bWFpbHRvOmhleUBhZHJpYW4tYWx0bmVyLmNvbQ=="></a>
**Phone:** <span class="r" data-obf="MDI0MDM1ODcgNjUxIDk0Kw=="></span><br>
**Email:** <span class="r" data-obf="bW9je3RvZH1yZW50bGEtbmFpcmRhe3RhfXllaA=="></span>
---
@ -27,9 +27,9 @@ description: Legal notice for adrian-altner.com.
---
## Content License
## License
Unless otherwise noted, content published on this website is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/) license.
Unless otherwise noted, content published on this website is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/) license. The source code is licensed under the [MIT License](https://mit-license.org/).
---

View file

@ -15,7 +15,7 @@ description: Privacy policy and data protection information for adrian-altner.co
The controller responsible for data processing on this website within the meaning of the General Data Protection Regulation (GDPR) is:
<strong><span class="r" data-obf="cmVudGxBIG5haXJkQQ=="></span></strong><br>
**Email:** <a class="r" data-obf="bW9je3RvZH1yZW50bGEtbmFpcmRhe3RhfXllaA==" data-obf-href="bWFpbHRvOmhleUBhZHJpYW4tYWx0bmVyLmNvbQ=="></a>
**Email:** <span class="r" data-obf="bW9je3RvZH1yZW50bGEtbmFpcmRhe3RhfXllaA=="></span>
---