V1.0.0 Released! See the release notes.
nielsshootsfilm.com is my personal photography portfolio website, a experiment in a novel hybrid static/dynamic website architecture, and, a playground for agentic AI supported development.
As a "product", this repository provides a modern photography portfolio with a unique hybrid static/dynamic architecture, designed for speed, simplicity, and ease of content management.
Hybrid Static/Dynamic Architecture:
- Public-facing site: Pure static files (HTML, CSS, JS, JSON) served blazingly fast by any web server
- Admin interface: Dynamic Go backend for easy content management
- No traditional database: JSON files (
albums.json,site_config.json) act as the data store - Result: Fast visitor experience + simple content management + minimal hosting requirements
Why This Works:
- Visitors get instant page loads from static files
- Admin gets a user-friendly interface for managing albums and photos
- No database setup or maintenance needed
- Easy to back up (just copy JSON files)
- Can be hosted anywhere (GitHub Pages, Netlify, any web server)
- Frontend: TypeScript + Lit web components (~5KB), Vite for dev server
- Package Manager: pnpm (fast, disk-efficient)
- Backend: Go admin server for JSON file manipulation
- Build: Simple shell scripts (frontend/scripts/, backend/scripts/)
- Data: JSON files as database
- Testing: Pre-commit hooks + manual E2E checklist (MVP)
Single Developer + Agentic AI: This project is developed by a solo developer working with AI agents (GitHub Copilot).
Quality Standards:
- Automated code quality checks via pre-commit hooks
- Type-safe TypeScript and Go
Run the provisioning script to install all dependencies:
# Clone repository
git clone <repo-url>
cd nielsshootsfilm
# Run provisioning script (installs everything)
./provision.shThe script installs:
- ✅ Node.js 20.x (frontend)
- ✅ pnpm (fast package manager)
- ✅ Go 1.22+ (backend)
- ✅ Frontend packages via pnpm
- ✅ Pre-commit hooks
- ✅ Optional dev tools (golangci-lint, jq)
Note: Go dependencies are vendored in backend/vendor/ and committed to the repository. No go mod download is needed - the backend builds hermetically from the repository contents alone.
Note: Frontend dependencies are vendored in frontend/.pnpm-store/ and committed to the repository. No npm registry access is needed - the frontend builds hermetically with pnpm install --offline.
If you prefer manual installation:
# Install system dependencies (macOS)
brew install node@20 go@1.22 pre-commit golangci-lint pnpm
# Install frontend dependencies (uses vendored packages)
cd frontend && pnpm install --offline
# Install pre-commit hooks
pre-commit install
# Bootstrap project (create data files, set admin password)
./bootstrap.shNote: Both Go and frontend dependencies are already vendored - no network downloads needed.
The project uses direnv to automatically load environment variables and add scripts to your PATH when you enter the project directory.
Setup direnv:
# Install direnv
brew install direnv # macOS
# or: sudo apt-get install direnv # Linux
# Add hook to your shell (choose one):
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc # for zsh
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc # for bash
# Restart shell or reload config
source ~/.zshrc # or ~/.bashrcEnable for this project:
# In the project directory, allow direnv
direnv allowBenefits:
- Scripts available from anywhere:
dev.sh,build.sh,format.sh, etc. - Automatic
envfile loading - Project-specific environment variables
Without direnv, you'll need to run scripts with their full path (e.g., ./dev.sh or ./scripts/test-api.sh).
Run the development servers in two separate terminals:
# Terminal 1 - Frontend dev server with hot reload
./frontend/scripts/dev.sh
# Terminal 2 - Backend admin server with auto-reload
./backend/scripts/dev.shOr use the convenience script (runs sequentially):
./dev.shAccess:
- Frontend: http://localhost:5173
- Backend API: http://localhost:6180
- Admin Interface: http://localhost:5173/admin
Run tests using the unified test script:
# Run all unit tests (backend and frontend)
./test.sh
# Run specific test suites
./test.sh backend # Backend unit tests only (with race detector)
./test.sh frontend # Frontend unit tests only
./test.sh api # API integration tests + schema validation
# Run specific backend tests
./test.sh -- backend/internal/handlers
./test.sh -- backend/...
# Run specific frontend tests
./test.sh -- storage-stats.test.ts
./test.sh -- frontend/src/components/storage-stats.test.ts
# Or run tests directly in each directory
cd backend && go test -race ./... # Always use -race for concurrency safety
cd frontend && pnpm testNote: Backend tests always run with Go's race detector (-race flag) to catch concurrency bugs early.
The test script automatically:
- Detects whether to run Go or npm tests based on the path
- Exits immediately after tests complete (no waiting for input)
- Provides colored output for easy scanning
┌─────────────────────────────────────┐
│ Public Website (Static Files) │
│ - Portfolio page │
│ - Album galleries │
│ - Password-protected albums │
└─────────────────────────────────────┘
▲
│ Served by any web server
│
┌─────────────────────────────────────┐
│ Admin Backend (Go Server) │
│ - Album management (CRUD) │
│ - Photo upload & processing │
│ - JSON file manipulation │
└─────────────────────────────────────┘
▲
│ Reads/Writes
▼
┌─────────────────────────────────────┐
│ Data Layer (JSON Files) │
│ - albums.json │
│ - site_config.json │
└─────────────────────────────────────┘
provision.sh- First-time setup script (run this first!)docs/plan/PLAN_MVP.md- Complete implementation plandocs/DEVELOPMENT_SETUP.md- Tool configurationsscripts/README.md- Available utility scriptsfrontend/scripts/- Frontend development scriptsbackend/scripts/- Backend development scripts
This is a personal project, but suggestions are welcome! Open an issue to discuss ideas.
MIT License - See LICENSE file for details.

