docs: add proper README and MIT license

This commit is contained in:
2026-05-31 05:32:54 +02:00
parent c2ebca0da0
commit 4e2fc99065
2 changed files with 86 additions and 113 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Gabriel Kaszewski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

178
README.md
View File

@@ -1,137 +1,89 @@
# k-template
# K-Photos
A cargo-generate template for personal Rust web services. Gives you auth, persistence, logging, CORS, and API docs out of the box so you can start writing domain code immediately.
Self-hosted media orchestrator and gallery. Alternative to Apple Photos, Google Photos, and Immich.
Follows the same hexagonal/ports-and-adapters architecture used in [thoughts](https://git.gabrielkaszewski.dev/GKaszewski/thoughts) and [movies-diary](https://git.gabrielkaszewski.dev/GKaszewski/movies-diary).
## Philosophy
## What you get
- **Files are sacred** — original file bytes and embedded metadata are never modified. Ever.
- **No lock-in** — all metadata exportable to standard formats (XMP, JSON). Stop using K-Photos and your tagged, organized file system remains intact.
- **Virtual layer** — all edits (dates, tags, albums, face regions) live in a separate layer. DB at runtime, sidecar exports for portability. Corrupt the layer? Rebuild from originals.
- **Modular** — core works without AI/ML. Face detection, classification, smart search are optional plugins.
- **BYOS** — bring your own storage. Local NAS, S3, GCS — the domain doesn't care.
- **Full hexagonal architecture** — `domain``application``adapters``presentation``bootstrap`, each as a separate crate with clear boundaries
- **JWT auth wired end-to-end** — register, login, and `GET /auth/me` working from day one
- **SQLite or PostgreSQL** — chosen at generation time, migrations included
- **CORS + structured logging** — tower-http middleware configured in bootstrap
- **Scalar API docs** at `/scalar`, OpenAPI JSON at `/api-docs/openapi.json`
- **Optional worker binary** — tokio-based background job runner with an example job
- **Optional OIDC stub** — placeholder adapter for OAuth2/OpenID Connect flows
- **Docker-ready** — multi-stage Dockerfile with dependency layer caching, no live DB needed at build time
## Architecture
## Generate a new project
Hexagonal / DDD with CQRS. Dependencies point inward:
```bash
cargo generate --git https://git.gabrielkaszewski.dev/GKaszewski/k-template.git
```
Infrastructure (Axum, Postgres, NATS, S3)
-> Adapters (Controllers, Repos, Storage Providers)
-> Application (Commands / Queries)
-> Domain (Entities, Value Objects, Ports, Services)
```
You'll be prompted for:
### Bounded Contexts
| Option | Choices | Default |
|--------|---------|---------|
| `project_name` | any snake_case string | — |
| `database` | `sqlite`, `postgres` | `sqlite` |
| `worker` | bool | false |
| `auth_oidc` | bool | false |
| Context | Purpose |
|---|---|
| **Identity** | Users, roles, RBAC permissions, groups |
| **Storage** | Volumes, library paths, ingestion, quotas, BYOS |
| **Catalog** | Assets, metadata layers, stacks, derivatives, duplicates |
| **Organization** | Albums, tags, collections (smart albums) |
| **Sharing** | Share scopes, targets, links, invite codes, visibility filters |
| **Sidecar** | XMP/JSON export, sync state, conflict resolution |
| **Processing** | Jobs, batches, plugins, pipelines |
## Generated project structure
### Project Structure
```
crates/
domain/ pure Rust — entities, value objects, port traits, errors
application/ use cases (RegisterUser, LoginUser, GetProfile) + test fakes
api-types/ shared request/response DTOs with OpenAPI derives
adapters/
sqlite/ sqlx SQLite UserRepository + migrations
postgres/ sqlx PostgreSQL UserRepository + migrations
auth/ BcryptPasswordHasher, JwtTokenIssuer, OidcAdapter stub
presentation/ axum handlers, JwtClaims extractor, routes, Scalar mount
bootstrap/ Config from env, factory wiring, main entry point
worker/ (optional) Job trait, JobRunner, ExampleJob, WorkerConfig
domain/ pure Rust — entities, value objects, ports, services
common/ errors, events, value objects (SystemId, Checksum, etc.)
identity/ user, role, permission, group
storage/ volumes, library paths, ingestion, quotas
catalog/ assets, metadata, stacks, derivatives, duplicates
organization/ albums, tags, collections
sharing/ share scopes, targets, links, invites
sidecar/ sidecar records, sync config
processing/ jobs, batches, plugins, pipelines
application/ CQRS commands + queries with Arc<dyn Port> injection
identity/commands/ RegisterUser, LoginUser
identity/queries/ GetProfile
storage/commands/ RegisterVolume, RegisterLibraryPath, IngestAsset
storage/queries/ CheckQuota
catalog/commands/ RegisterAsset, UpdateMetadata
catalog/queries/ GetTimeline, GetAsset
organization/ CreateAlbum, ManageAlbumEntries, TagAsset, GetAlbum
sharing/ ShareResource, GenerateShareLink, RevokeShare, AccessSharedResource
sidecar/ ExportSidecar, DetectChanges, Import, ResolveConflict, FullExport/Import
processing/ EnqueueJob, StartJob, CompleteJob, FailJob, ManagePlugin, ConfigurePipeline
testing/ in-memory repo fakes + stub ports
api-types/ HTTP request/response DTOs with OpenAPI derives
adapters/ postgres, auth (bcrypt, JWT), object storage
presentation/ axum handlers, routes, extractors
bootstrap/ config, DI wiring, entry point
worker/ background job runner
```
## Running locally
## Development
```bash
cp .env.example .env
cargo run -p bootstrap
# run tests (no DB required)
cargo test -p domain -p application
# format + lint
cargo fmt --all
cargo clippy -p domain -p application
```
The server starts at `http://localhost:3000`.
148 tests cover all domain entities, services, and application use cases.
## Endpoints (out of the box)
## Status
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/v1/auth/register` | — | Create account → `AuthResponse` |
| `POST` | `/api/v1/auth/login` | — | Login → `AuthResponse` |
| `GET` | `/api/v1/auth/me` | Bearer | Current user profile |
| `GET` | `/health` | — | `{"status":"ok"}` |
| `GET` | `/scalar` | — | Interactive API docs |
| `GET` | `/api-docs/openapi.json` | — | OpenAPI spec |
```bash
# Register
curl -s -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"me@example.com","password":"password123"}' | jq
# Login and get token
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"me@example.com","password":"password123"}' | jq -r '.token')
# Profile
curl -s http://localhost:3000/api/v1/auth/me \
-H "Authorization: Bearer $TOKEN" | jq
```
## Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `sqlite://data.db` | Database connection string |
| `JWT_SECRET` | *(required)* | Signing secret — min 32 chars in production |
| `HOST` | `0.0.0.0` | Bind address |
| `PORT` | `3000` | Listen port |
| `CORS_ALLOWED_ORIGINS` | `http://localhost:3000` | Comma-separated allowed origins |
## Tests
```bash
# Unit tests (no DB required)
cargo test -p domain -p application -p adapters-auth
```
13 unit tests cover email validation, use case logic (register/login/get_profile), bcrypt roundtrip, and JWT encode/verify.
## Docker
```bash
# Build
docker build -t my-app .
# Run
docker run -p 3000:3000 \
-e DATABASE_URL=sqlite:///data/app.db \
-e JWT_SECRET=change-me-32-chars-minimum-here \
my-app
```
Or with compose:
```bash
docker compose up
```
The Dockerfile uses dependency layer caching (manifests copied and fetched before source) so rebuilds after source-only changes are fast. No live database is needed at compile time — the `.sqlx` offline cache is committed.
## What to do after generating
1. Add your domain entities and value objects to `crates/domain/`
2. Write use cases in `crates/application/`
3. Add DB columns/tables via new migration files in `crates/adapters/sqlite/migrations/`
4. Add handlers in `crates/presentation/src/handlers/`
5. Wire new use cases in `crates/bootstrap/src/factory.rs`
Auth, CORS, logging, and docs are already done — focus on what makes your project unique.
Domain and application layers complete. Next: adapters (Postgres, NATS, filesystem) and presentation layer.
## License
MIT
[MIT](LICENSE)