This commit is contained in:
Simon Martens
2025-09-17 11:38:18 +02:00
parent aa2b8c9fae
commit 9bfebe5828
3 changed files with 365 additions and 1 deletions

181
CLAUDE.md Normal file
View File

@@ -0,0 +1,181 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Lenz-Web is a Go web application that serves digitized historical letters. It's built with:
- Go 1.24 using Fiber web framework
- Custom templating engine with hot reload for development
- Git-based content management (clones/updates from remote repository)
- XML parsing for letter metadata and content
- Static asset serving with compression and ETags
## Development Commands
### Build and Run
```bash
go build # Build the application
./lenz-web # Run the built binary
go run . # Build and run directly
```
### Docker
```bash
docker-compose up # Run with Docker (exposes port 8085)
docker build . # Build Docker image
```
### Testing
The project does not currently have automated tests. Check for `*_test.go` files before assuming test frameworks.
## Architecture
### Main Application Flow (lenz.go:30-69)
1. Loads configuration from JSON files and environment variables
2. Clones/opens Git repository containing letter data
3. Parses XML models from the repository
4. Sets up templating engine with custom functions
5. Configures Fiber web server with routes and middleware
6. In debug mode, enables file watchers for hot reload
### Core Components
**Configuration (config/)**
- `config.go`: Configuration management with JSON files, environment variables, and defaults
- Uses `KGPZ_` prefix for environment variables
- Default config file: `config.dev.json`
**Templating (templating/)**
- Custom template engine with layout and route registries
- WebSocket-based hot reload in debug mode
- Template functions for XML data processing
**Controllers (controllers/)**
- Route handlers for web endpoints
- `routes.go`: Route registration and URL constants
- Main routes: `/`, `/briefe`, `/brief/:letter`, static pages
**XML Models (xmlmodels/)**
- Parses historical letter data from XML files
- Provides template functions for data access
- Core models: Library, Letter, Traditions, References
**File Watching (watcher.go)**
- Custom file watcher with debouncing (300ms)
- Two watch modes:
- REFRESH_CHANGES: `./views/assets` (clears cache, sends refresh signal)
- RESET_CHANGES: `./views/layouts`, `./views/routes` (clears cache, reloads templates)
### Key Directories
- `views/`: Frontend templates, layouts, and static assets
- `helpers/`: Utility functions and middleware
- `server/`: Fiber server configuration and caching
- `git/`: Git repository management
## Configuration
Configuration is loaded in order: JSON files → environment variables → defaults
Key settings:
- `debug`: Enables hot reload and verbose logging
- `git_url`: Repository URL for letter content
- `base_dir`: Cache directory (default: `_cache`)
- `port`: Server port (default: `8085`)
- `webhook_secret`: GitHub webhook integration
Environment variables use `KGPZ_` prefix (e.g., `KGPZ_DEBUG=true`).
## Views Directory Structure
The `views/` directory contains all frontend templates and assets:
```
views/
├── assets/ # Static files (CSS, JS, fonts, images)
├── layouts/ # Base layout templates
│ ├── components/ # Reusable layout components (_header, _footer, _menu, etc.)
│ └── default/ # Default layout (root.gohtml)
└── routes/ # Page-specific templates
├── brief/ # Individual letter view (head.gohtml, body.gohtml)
├── briefe/ # Letter listing view
├── components/ # Page components (_letterhead, _letterlist, _lettertrad)
├── datenschutz/ # Privacy page
├── edition/ # Edition info pages
└── kontakt/ # Contact page
```
### Template System Architecture
**Build-Time Embedding vs Development**
- Production: Templates embedded via `//go:embed` directives in `views/embed.go`
- Development: Direct filesystem access via `views/embed_dev.go` (when built with `-tags dev`)
- File watchers enable hot reload in debug mode
**Template Structure**
Each route directory contains:
- `head.gohtml`: `<head>` section content for the page
- `body.gohtml`: Main page content
**Layout System (templating/engine.go)**
- Base layout: `views/layouts/default/root.gohtml`
- Uses Go template blocks: `{{ block "head" . }}`, `{{ block "body" . }}`, etc.
- Layout registry loads and caches layouts with template functions
- Template registry loads route-specific templates
**Template Functions Available**
Core functions (templating/engine.go:118-131):
- `Safe`: Render HTML without escaping
- `Today`: Current date
- `GetMonth`: Month name from number
- `Minus`: Subtraction for template math
XML Data Functions (added from xmlmodels/library.go):
- `Person`: Get person data by reference ID
- `Place`: Get location data by reference ID
- `ParseGeneric`: Parse and render letter text with markup
**Template Data Flow**
1. Controllers pass `fiber.Map` data to templates
2. Engine merges with `GlobalData` (debug flags, etc.)
3. Layout loaded and cloned from registry
4. Route templates added to layout clone
5. Combined template executed with merged data
**Hot Reload System**
- WebSocket server on port 9000 for live reload in debug mode
- File watchers trigger different actions:
- `./views/assets` changes: Clear cache + browser refresh signal
- `./views/layouts`, `./views/routes` changes: Clear cache + reload templates
- 300ms debounce to prevent excessive reloads
### Working with Templates
**Template Syntax**
- Standard Go template syntax with custom functions
- Variables: `{{ $model := . }}` for cleaner references
- Conditionals: `{{ if .isDev }}...{{ end }}`
- Loops: `{{ range $item := .items }}...{{ end }}`
- Components: `{{ template "_letterhead" .meta }}`
**Debugging Templates**
- Enable debug mode in config: `"debug": true`
- Templates reload automatically on file changes
- Browser refreshes automatically via WebSocket
- Console shows reload notifications
**Letter Data Templates**
Letter pages have access to rich XML data:
- `.meta`: Letter metadata (dates, persons, places)
- `.text`: Parsed letter content with markup
- `.prev/.next`: Navigation to adjacent letters
- Navigation uses custom functions like `Person` and `Place` to resolve references
## Development Notes
- File watchers only active in debug mode
- Templates are cached; use debug mode for development
- Static assets served with compression and ETags
- Git repository is cloned to `base_dir/git_path` on startup
- WebSocket endpoint available for live reload in debug mode
- Template files use `.gohtml` extension for proper syntax highlighting

View File

@@ -1,6 +1,6 @@
{
"debug": true,
"watch": true,
"git_url": "git@github.com:Theodor-Springmann-Stiftung/lenz-briefe.git",
"git_url": "https://github.com/Theodor-Springmann-Stiftung/lenz-briefe.git",
"webhook_secret": "test_secret"
}

183
views/package-lock.json generated
View File

@@ -8,6 +8,9 @@
"name": "caveman_views",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"pagedjs": "^0.4.3"
},
"devDependencies": {
"@laynezh/vite-plugin-lib-assets": "^1.1.0",
"@tailwindcss/postcss": "^4.1.4",
@@ -31,6 +34,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@babel/polyfill": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz",
"integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==",
"deprecated": "🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.",
"license": "MIT",
"dependencies": {
"core-js": "^2.6.5",
"regenerator-runtime": "^0.13.4"
}
},
"node_modules/@babel/runtime": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.0",
"cpu": [
@@ -254,6 +277,12 @@
"fsevents": "~2.3.2"
}
},
"node_modules/clear-cut": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/clear-cut/-/clear-cut-2.0.2.tgz",
"integrity": "sha512-WVgn/gSejQ+0aoR8ucbKIdo6icduPZW6AbWwyUmAUgxy63rUYjwa5rj/HeoNPhf0/XPrl82X8bO/hwBkSmsFtg==",
"license": "MIT"
},
"node_modules/cliui": {
"version": "8.0.1",
"dev": true,
@@ -283,6 +312,40 @@
"dev": true,
"license": "MIT"
},
"node_modules/core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true,
"license": "MIT"
},
"node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"license": "MIT",
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/d": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
"license": "ISC",
"dependencies": {
"es5-ext": "^0.10.64",
"type": "^2.7.2"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/daisyui": {
"version": "5.0.28",
"dev": true,
@@ -324,6 +387,46 @@
"node": ">=10.13.0"
}
},
"node_modules/es5-ext": {
"version": "0.10.64",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.3",
"esniff": "^2.0.1",
"next-tick": "^1.1.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/es6-iterator": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
"license": "MIT",
"dependencies": {
"d": "1",
"es5-ext": "^0.10.35",
"es6-symbol": "^3.1.1"
}
},
"node_modules/es6-symbol": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
"license": "ISC",
"dependencies": {
"d": "^1.0.2",
"ext": "^1.7.0"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/esbuild": {
"version": "0.25.0",
"dev": true,
@@ -382,6 +485,40 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/esniff": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
"license": "ISC",
"dependencies": {
"d": "^1.0.1",
"es5-ext": "^0.10.62",
"event-emitter": "^0.3.5",
"type": "^2.7.2"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
"license": "MIT",
"dependencies": {
"d": "1",
"es5-ext": "~0.10.14"
}
},
"node_modules/ext": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
"license": "ISC",
"dependencies": {
"type": "^2.7.2"
}
},
"node_modules/fdir": {
"version": "6.4.4",
"dev": true,
@@ -592,6 +729,12 @@
"node": ">= 12.13.0"
}
},
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"license": "CC0-1.0"
},
"node_modules/mrmime": {
"version": "1.0.1",
"dev": true,
@@ -617,6 +760,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/next-tick": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
"license": "ISC"
},
"node_modules/normalize-path": {
"version": "3.0.0",
"dev": true,
@@ -625,6 +774,19 @@
"node": ">=0.10.0"
}
},
"node_modules/pagedjs": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/pagedjs/-/pagedjs-0.4.3.tgz",
"integrity": "sha512-YtAN9JAjsQw1142gxEjEAwXvOF5nYQuDwnQ67RW2HZDkMLI+b4RsBE37lULZa9gAr6kDAOGBOhXI4wGMoY3raw==",
"license": "MIT",
"dependencies": {
"@babel/polyfill": "^7.10.1",
"@babel/runtime": "^7.21.0",
"clear-cut": "^2.0.2",
"css-tree": "^1.1.3",
"event-emitter": "^0.3.5"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"dev": true,
@@ -832,6 +994,12 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT"
},
"node_modules/require-directory": {
"version": "2.1.1",
"dev": true,
@@ -899,6 +1067,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"dev": true,
@@ -975,6 +1152,12 @@
"node": ">=8.0"
}
},
"node_modules/type": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
"license": "ISC"
},
"node_modules/ulid": {
"version": "2.3.0",
"dev": true,