The all-in-one image could not be installed from a GUI at all — the deployment it
exists for. validateEnv treats a missing JWT_SECRET as critical and exits, and the
documented run command supplies it with `openssl rand`, a shell command a Synology
Container Manager or QNAP Container Station form cannot run.
wait-for-db.sh now generates one on first start and persists it next to the database,
extending the existing /run/secrets hydration rather than adding a second mechanism.
Explicit env still wins, then /run/secrets, then the generated file. The write is
load-bearing: JWT_SECRET is exported only when the file actually persisted, because an
unpersisted secret would mint a new one every restart and sign every session out.
Creation writes to a private temp file and hard-links it into place — atomic, fails with
EEXIST when another container won, and the loser adopts the winner's value. Non-regular
paths are rejected before the link, since POSIX ln links INTO a directory rather than
failing, which would make a mistyped -v target unrecoverable.
Also repairs the onboarding paths a new install actually walks: the installer no longer
rotates the secrets of a running install on re-run, deprecates the dead scripts/install.sh
in place, corrects the CONTRIBUTING dev loop, and fixes the vite proxy target that had
been pointing at a stray local port since 0da45e69.
Reviewed over three rounds. Co-authored by @Luca-Timo.
8.3 KiB
Contributing to PicPeak
First off, thank you for considering contributing to PicPeak! It's people like you that make PicPeak such a great tool for photographers worldwide.
🤝 Code of Conduct
This project and everyone participating in it is governed by the PicPeak Code of Conduct. By participating, you are expected to uphold this code.
🎯 How Can I Contribute?
Reporting Bugs
Before creating bug reports, please check the existing issues as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible:
- Use a clear and descriptive title
- Describe the exact steps to reproduce the problem
- Provide specific examples to demonstrate the steps
- Describe the behavior you observed and what you expected
- Include screenshots if possible
- Include your environment details (OS, browser, Docker version, etc.)
Suggesting Enhancements
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include:
- Use a clear and descriptive title
- Provide a detailed description of the suggested enhancement
- Provide specific examples to demonstrate the enhancement
- Describe the current behavior and expected behavior
- Explain why this enhancement would be useful
Your First Code Contribution
Unsure where to begin? You can start by looking through these issues:
- Good first issues - issues which should only require a few lines of code
- Help wanted issues - issues which need extra attention
Pull Requests
- Fork the repo and create your branch from
main(active development) - Install dependencies:
cd backend && npm install cd ../frontend && npm install - Make your changes and ensure:
- Code follows the existing style
- Tests pass:
npm test - Linting passes:
npm run lint
- Write tests if you've added code
- Update documentation if needed
- Attach a screenshot for any UI change (see below)
- Create a Pull Request
📸 Screenshots are required for UI changes. Any PR that changes a user-facing surface — a component, page, layout, style, or in-app copy — must include at least one screenshot of the result in the PR description, showing before/after where it helps reviewers see the difference. PRs that touch the UI without a screenshot will be asked to add one before review. Backend-only or otherwise non-visual changes don't need one.
💻 Development Setup
Prerequisites
- Node.js 18+
- Docker & Docker Compose
- Git
Local Development
# Clone your fork
git clone https://github.com/your-username/picpeak.git
cd picpeak
# Install dependencies
cd backend && npm install
cd ../frontend && npm install
cd ..
# Start Postgres and Redis (the app itself runs on the host, see below)
docker compose up -d postgres redis
# Backend config — note this is backend/.env, not the root one
cp backend/.env.example backend/.env
# JWT_SECRET must be set: the host process validates it and exits without one.
# (The containers generate it themselves; `npm run dev` does not.)
# Backend, with nodemon hot reload — http://localhost:3001
cd backend && npm run dev
# Frontend, with Vite hot reload, in a second shell — http://localhost:5173
cd frontend && npm run dev
Open http://localhost:5173. Vite proxies /api to the backend on 3001, so
you do not need the root .env for this loop at all — that one configures the
compose stack.
Running the two Node processes on the host is the fastest loop: both reload on save, and you get a real debugger and stack traces without rebuilding an image.
Prefer everything in containers? docker compose up -d builds backend, frontend and ml from source using the production Dockerfiles. That works, but there is no hot reload — you rebuild on every change (docker compose up -d --build backend).
docker-compose.dev.ymlis listed in.gitignoreand is not part of the repo. If you keep a local one for live-mounting./backend/srcand./frontend/srcagainstbackend/Dockerfile.dev/frontend/Dockerfile.dev, remember it bakesnode_modulesinto the image: after pulling a change tobackend/package.json, rebuild that image or you will get aMODULE_NOT_FOUNDrestart loop.
Running Tests
# Backend tests
cd backend && npm test
# Frontend tests
cd frontend && npm test
# E2E tests
npm run test:e2e
📝 Styleguides
Git Commit Messages
- Use the present tense ("Add feature" not "Added feature")
- Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
- Limit the first line to 72 characters or less
- Reference issues and pull requests liberally after the first line
- Consider starting the commit message with an applicable emoji:
- 🎨
:art:when improving the format/structure of the code - 🐛
:bug:when fixing a bug - 🔥
:fire:when removing code or files - 📝
:memo:when writing docs - 🚀
:rocket:when improving performance - ✨
:sparkles:when adding a new feature
- 🎨
JavaScript/TypeScript Styleguide
- Use ES6+ features
- Prefer async/await over promises
- Use meaningful variable names
- Add JSDoc comments for functions
- Follow ESLint rules
React Styleguide
- Use functional components with hooks
- Keep components small and focused
- Use TypeScript for type safety
- Follow the existing folder structure
- Write tests for new components
📦 Project Structure
picpeak/
├── backend/
│ ├── src/
│ │ ├── routes/ # API endpoints
│ │ ├── services/ # Business logic
│ │ ├── middleware/ # Express middleware
│ │ └── utils/ # Utilities
│ └── migrations/ # Database migrations
├── frontend/
│ ├── src/
│ │ ├── components/ # Reusable components
│ │ ├── pages/ # Page components
│ │ ├── services/ # API services
│ │ └── hooks/ # Custom hooks
│ └── public/ # Static assets
🌿 Branch model
PicPeak runs on two long-lived branches:
| Branch | Role | What targets it |
|---|---|---|
main |
Active development. The next release is being assembled here. | Feature PRs. Most bugfix PRs. |
stable |
Curated release channel. Production-recommended. | Urgent bugfix backports only — small, surgical PRs that land cleanly without dragging in unrelated changes. |
Which branch should my PR target?
- New feature → target
main. - Bugfix that ONLY affects active dev → target
main. - Bugfix that current stable users need → open a small PR against
main, AND a separate small PR againststablewith the same change. Keep both surgical so each lands cleanly.
Hard rule on PR scope: bugfix PRs against stable must be small enough to backport without conflict. Omnibus PRs (e.g. five unrelated sub-features) are fine for main, but never for stable — they make the next main → stable merge painful and break the "stable is always shippable" invariant.
If you're not sure which branch to target, default to main and a maintainer will retarget during review.
🔄 Release Process
Releases are cut independently from main (pre-release versions for the active channel) and stable (semver releases for the curated channel). release-please handles version bumps, changelog generation, and Docker image publication automatically — contributors don't update package.json or CHANGELOG.md by hand.
Periodic main → stable merges promote a batch of main work to the stable channel. The maintainer chooses when (typically every ~4 weeks, sooner if a hot bug demands it).
See RELEASING.md for the full operational doc (promotion criteria, conflict-resolution checklist for the main → stable merge, hotfix backport path, versioning rules).
📮 Contact
- Create an issue for bugs or features
- Join discussions for questions
- Security issues: Open a security issue on GitHub
Thank you for contributing! 🎉