Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Exclude local env files from Docker build context
.env
.env.local
.env.development
.env.test
.env.docker

# Rust build artifacts
target

# Git
.git
.gitignore

# Node/npm caches if any future UI gets added
node_modules
npm-debug.log
yarn.lock
pnpm-lock.yaml
30 changes: 30 additions & 0 deletions .env.docker.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SleepTracker Docker Compose environment file (example)
# Copy this file to ".env.docker" and fill in the values before running:
# docker compose up --build
#
# Notes:
# - COOKIE_SECURE is omitted here; the application defaults to secure cookies (true) when unset.
# You may explicitly set COOKIE_SECURE=1 if you prefer, but do NOT set it to 0 in Docker.
# - Do not commit your real .env.docker with secrets to version control.

# Use a file-based SQLite database inside the container volume
DATABASE_URL=sqlite:///data/sleep.db

# Single admin user credentials used by the application
# - Generate an Argon2id password hash with:
# cargo run -p sleep-api --bin pw-hash
# - Paste the full $argon2id$... string below
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD_HASH='$argon2id$v=19$REPLACE_WITH_HASH'

# Session secret (base64-encoded random bytes, 32+ bytes recommended)
# Examples to generate:
# Linux/macOS: head -c 32 /dev/urandom | base64
# PowerShell: [Convert]::ToBase64String((1..32 | ForEach-Object {[byte](Get-Random -Max 256)}))
SESSION_SECRET=REPLACE_WITH_BASE64_SECRET

# Optional: enable HSTS when served over HTTPS/behind TLS-terminating proxy
# ENABLE_HSTS=1

# Optional: explicitly enforce secure cookies in Docker (default is secure=true when unset)
# COOKIE_SECURE=1
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,10 @@ ADMIN_PASSWORD_HASH=$argon2id$v=19$REPLACE_WITH_HASH
# Note: CSRF uses a random per-login cookie (double-submit) and does not use a CSRF secret.
SESSION_SECRET=REPLACE_WITH_BASE64_SECRET

# Local development over HTTP (do NOT use in production or Docker)
# Set to 0 to allow non-Secure cookies and dev-friendly cookie names ("session"/"csrf").
# In Docker/production, omit this variable (defaults to secure=true) or set COOKIE_SECURE=1.
COOKIE_SECURE=0

# Optional: enable HSTS header (only when served over HTTPS/behind TLS)
# ENABLE_HSTS=1
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ target
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.env
.env.docker
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ WORKDIR /app
ENV DATABASE_URL=sqlite:///data/sleep.db
COPY --from=builder /app/target/release/sleep-api /app/sleep-api
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Normalize line endings in case the script was checked out with CRLF on Windows
RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh && chmod +x /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
EXPOSE 8080
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ SleepTracker is a small, single-user web API for tracking sleep sessions, built
- Set ADMIN_EMAIL and ADMIN_PASSWORD_HASH
- Generate a password hash:
cargo run -p sleep-api --bin pw-hash
- Paste the $argon2id$... string into ADMIN_PASSWORD_HASH
- Paste the $argon2id$... string into ADMIN_PASSWORD_HASH (IMPORTANT: use single quotes in .env/.env.docker to prevent $-expansion by dotenv)
Example:
ADMIN_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...$...'
- Set SESSION_SECRET to a base64-encoded random value (32+ bytes recommended)
- Optional: Set ENABLE_HSTS=1 when serving over HTTPS
- Optional: See COOKIE_SECURE below for local HTTP development
Expand All @@ -21,6 +23,30 @@ SleepTracker is a small, single-user web API for tracking sleep sessions, built

Server will listen on 0.0.0.0:8080.

## Run with Docker

Use Docker Compose to build and run the app.

- Docker Compose (recommended)
- Copy .env.docker.example to .env.docker and fill values:
- ADMIN_EMAIL, ADMIN_PASSWORD_HASH (quote the $argon2id$... string with single quotes)
- SESSION_SECRET: base64-encoded random value (32+ bytes)
- For local HTTP development, set COOKIE_SECURE=0. For HTTPS/prod, use COOKIE_SECURE=1.
- Build and start:
docker compose up --build
- Add -d to run in the background.
- Access the API at http://localhost:8080
- Follow logs:
docker compose logs -f api
- Stop:
docker compose down
- Stop and delete the persistent data volume (DESTROYS DB):
docker compose down -v
- Notes:
- Data is stored at /data inside the container and persists in a named volume across restarts.
- Migrations run automatically on startup.


## Authentication and sessions

- Single-user login based on ADMIN_EMAIL and ADMIN_PASSWORD_HASH.
Expand Down Expand Up @@ -92,3 +118,17 @@ OpenAPI specification is in openapi.yaml and includes:

- The cookie encryption Key is derived from SESSION_SECRET if present; otherwise a random key is generated (sessions will break on restart in that case).
- Default database is sqlite::memory: for ephemeral dev/testing. For a persistent DB use DATABASE_URL=sqlite://./data/sleep.db and create the directory.

## Environments

- Local (cargo run):
- Use .env for local settings (e.g., COOKIE_SECURE=0 for http://).
- Start: `cargo run -p sleep-api`.

- Docker Compose:
- Copy `.env.docker.example` to `.env.docker` and fill values (ADMIN_EMAIL, ADMIN_PASSWORD_HASH, SESSION_SECRET; optionally COOKIE_SECURE=1).
- Compose injects only `.env.docker` into the container; your local `.env` is not used inside the container.
- Start: `docker compose up --build`.

- Paths in Docker:
- DATABASE_URL should point to the named volume path: `sqlite:///data/sleep.db`.
4 changes: 2 additions & 2 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ services:
api:
build: .
working_dir: /data
environment:
- DATABASE_URL=sqlite://sleep.db
env_file:
- .env.docker
ports:
- "8080:8080"
volumes:
Expand Down