mirror of
https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
synced 2025-10-28 08:45:32 +00:00
CLAUDE
This commit is contained in:
181
CLAUDE.md
Normal file
181
CLAUDE.md
Normal 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
|
||||
@@ -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
183
views/package-lock.json
generated
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user