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
89 changes: 12 additions & 77 deletions .env
Original file line number Diff line number Diff line change
@@ -1,88 +1,23 @@
# ------------------------------------------------------------------------------
# UniFi Time-Machine Environment Variables
# UniFi Time-Machine — Bootstrap Configuration
#
# This file configures the application when using Docker Compose.
# Copy this file to .env and fill in the values for your environment.
# These are the only values the app reads from the environment.
# All operational settings (intervals, quality, retention, formats, etc.)
# are managed at runtime via Admin → Settings and stored in the database.
# ------------------------------------------------------------------------------

# --- Docker Container Settings ---
# The external port to access the web UI.
# --- Docker port ---
HTTP_PORT=8000

# --- UniFi Protect Integration Settings ---
# The IP address or hostname of your UniFi Protect controller.
# --- UniFi Protect (required) ---
UFP_HOST=192.168.1.1
# The API key from your UniFi Protect user account.
# Go to your UniFi OS Console -> Integrations -> Create API Key
# This should be set as an environment variable in your shell:
# export UFP_API_KEY="your_key_here"
# UFP_API_KEY=
# The ID of the camera you want to capture snapshots from.
# You can find this in the URL when viewing the camera in the Protect web UI.
TARGET_CAMERA_ID=
UFP_API_KEY= # API key from Protect → Integrations
TARGET_CAMERA_ID= # Camera ID from the Protect URL

# --- Authentication Settings ---
# A mandatory, base64-encoded secret key for application security.
# The application will not start without it.
# This should be set as an environment variable in your shell:
# export APP_KEY=$(head -c 32 /dev/urandom | base64)
# APP_KEY=
# --- Authentication (required) ---
APP_KEY= # generate: head -c 32 /dev/urandom | base64
ADMIN_PASSWORD= # initial admin password (can be changed in UI)


# The password for the initial 'admin' user, created on first launch.
# This is required to be set on the first run. It can be changed or removed later.
ADMIN_PASSWORD=

# --- Timelapse and Video Generation Settings ---
# The interval, in seconds, between each snapshot.
# Default is 3600 (1 hour). A value of 60 is every minute.
TIMELAPSE_INTERVAL=60
# The interval, in seconds, at which to generate a new timelapse video.
# Default is 300 (5 minutes).
VIDEO_CRON_INTERVAL=600
# The quality of the generated video.
# Options: low, medium, high, ultra ( ultra is recommended )
VIDEO_QUALITY=ultra

# --- Share Link Expiry ---
# The number of hours a shared link is valid for. Set to 0 for unlimited.
SHARE_LINK_EXPIRY_HOURS=4

# --- Formatting Settings ---
# The format for displaying dates (e.g. DD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD)
# DATE_FORMAT=DD/MM/YYYY
# The format for displaying times (e.g. 12h, 24h)
# TIME_FORMAT=12h

# --- Daylight Filtering Settings ---
# Only images taken within this hour range are used in weekly, monthly, and yearly timelapses.
# 24-hour daily timelapses are unaffected and always include all hours.
# Default is 7–19 (7am to 7pm). Set both to 0 and 24 to disable filtering.
# DAYLIGHT_START_HOUR=7
# DAYLIGHT_END_HOUR=19
# The target hour (0–23) used to pick the best image for each day in monthly timelapses.
# The image whose capture time is closest to this hour is selected. Default is 12 (noon).
# DAYLIGHT_TARGET_HOUR=12

# --- Timelapse Retention Settings ---
# Number of calendar-week timelapses to keep (one per Monday). Default: 4
# WEEKLY_LAPSES_TO_KEEP=4
# Number of calendar-month timelapses to keep. Default: 3
# MONTHLY_LAPSES_TO_KEEP=3

# --- Data Retention and Cleanup Settings ---
# The directory inside the container for storing snapshots.
SNAPSHOTS_DIR=snapshots
# The directory inside the container for storing gallery images.
GALLERY_DIR=gallery
# The path to the ffmpeg log file.
FFMPEG_LOG_PATH=ffmpeglogs

# --- Gin Web Framework Settings ---
# Set the Gin mode. Use 'release' for production, 'debug' for development.
# --- Runtime ---
GIN_MODE=release

# --- Timezone ---
# The timezone to use for the container.
# e.g., "Australia/Sydney", "America/New_York"
TZ=Australia/Sydney
120 changes: 120 additions & 0 deletions DEVGUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Developer Guide

Notes for building, running locally, and contributing to UniFi Time-Machine.

---

## Tech stack

- **Go 1.26+** — single binary, CGO enabled for SQLite
- **SQLite** — embedded database via `modernc.org/sqlite` (no system dependency)
- **Gin** — HTTP framework
- **HTMX + Bootstrap** — frontend, vanilla JS, no build step
- **FFmpeg** — video encoding (injected at runtime via the Docker image)

The app follows 12-factor principles: config at the boundary (env vars for bootstrap, DB for everything else), stateless process, data in a mounted volume.

---

## Project structure

```
cmd/server/ main entrypoint
pkg/config/ bootstrap config (env vars only)
pkg/services/ business logic — snapshots, timelapse, settings, auth
pkg/handlers/ HTTP handlers
pkg/database/ SQLite helpers and migrations
web/ HTML templates, static assets
```

---

## Running locally

You'll need Go 1.26+ and FFmpeg installed.

```bash
go mod tidy
export UFP_API_KEY="..."
export TARGET_CAMERA_ID="..."
export APP_KEY="$(head -c 32 /dev/urandom | base64)"
export ADMIN_PASSWORD="dev"
export GIN_MODE=debug
go run ./cmd/server
```

The web UI will be at `http://localhost:8080`.

> The app expects a `web/` directory relative to the working directory. Run from the repo root or set `DATA_DIR` explicitly.

---

## Building the Docker image

`build.sh` produces a multi-arch image for `linux/amd64` and `linux/arm64` and pushes it to Docker Hub.

```bash
bash build.sh [tag]
```

Tag defaults to `latest`. The script also tags with the current date (`YYYYMMDD`).

Requires `docker buildx` and a configured builder instance. The script creates one named `mybuilder` if it doesn't exist.

### Dockerfiles

| File | Base | Notes |
|---|---|---|
| `Dockerfile` | `debian:bookworm-slim` | Standard image, recommended |
| `Dockerfile_chainguard` | Chainguard | Minimal/hardened alternative |

The build pipeline runs tests (`go test -v ./...`) in a separate stage before compiling, so a failing test will abort the image build.

---

## Releasing

1. Merge to `main`
2. Tag the commit: `git tag v1.2.3 && git push origin v1.2.3`
3. Run `bash build.sh v1.2.3` to build and push the versioned + dated tags

---

## Settings architecture

Bootstrap config (things the app needs before the DB is open) lives in env vars — see `pkg/config/config.go`.

All operational settings (snapshot interval, video quality, retention counts, daylight hours, etc.) are seeded into SQLite on first launch and managed at runtime via the Admin → Settings UI. The seed table is in `pkg/services/settings/settings.go` (`KnownSettings`). Env vars listed there only take effect on the very first run; after that the DB value wins.

---

## Tests

```bash
go test ./...
```

All packages should have a corresponding `_test.go`. New features should ship with tests.

---

## Timelapse file naming

Current naming convention (calendar-based):

| Type | Filename pattern |
|---|---|
| Daily (24 h) | `timelapse_24_hour_YYYY-MM-DD.webm` |
| Weekly | `timelapse_week_YYYY-MM-DD.webm` (Monday date) |
| Monthly | `timelapse_month_YYYY-MM.webm` |
| Yearly | `timelapse_year_YYYY.webm` |

Older installs may have rolling-window files named `timelapse_1_week.webm`, `timelapse_1_month.webm`, `timelapse_1_year.webm`. These are no longer generated and can be safely deleted.

---

## Style notes

- UK/Australian English spelling
- No third-party packages without discussion — prefer stdlib
- Dates/times in UTC internally; display timezone applied in the UI layer
Loading