Files
where_woof/where_woof.md
2026-08-05 10:21:02 +10:00

9.7 KiB

WhereWoof — Local Pet Location Tracker

Architecture — Two Separate Systems

1. Front-Facing Website (GOAT stack)

Quick win — lightweight, public-facing, pet owner experience.

Detail Value
Stack Go + HTMX + Alpine.js + Tailwind CSS + SQLite (GOAT stack)
Audience Pet owners (family members) Lost pets
Purpose View pet info, update location, quick CRUD
Auth Minimal — HTTP basic auth or Tailscale-only (household, not public)
Deploy .13:3020 behind Caddy on .35
DNS where-woof.com

URL format. https://where-woof.com/x=5&productid=TXQ3NHR4OGNuZGVwdFdkcFBTbm5BZz094 Will be redirected from live website to local .13 where caddy will handle local DNS.

IMPORTANT INFO

  • Users scan QR code on tag or use NFC. This opens webiste at URL example. On load website asks to ALLOW USERS LOCATION to send info to owners. If request is granted location is sent back with productid and the server admin with send alerts via SMS, email of location of dog. Info of dog is displayed with phone number - press for automatic call hook into phone. Also a button if user declines location then they can press button to initiate again. Also input box for the person who has found the dog to input their mobile. Finder of dog is advised that number is not kept only sent to owner so they can contact you.

2. Admin Dashboard (Laravel)

Separate system — full business management. Needs proper auth, billing, multi-user.

Detail Value
Stack Laravel + MySQL/PostgreSQL + Blade (or Filament/Nova)
Audience Staff / admins
Purpose User management, subscriptions, payments, device provisioning
Auth Laravel built-in (roles, permissions, password reset, 2FA)
Deploy .13:3030 (or Docker container) behind Caddy on .35
DNS admin.where-woof.com

Why separate:

  • The GOAT frontend is an HTMX experiment — fast UI, minimal JS, quick to build
  • The admin needs forms-heavy workflows, role-based access, payment processing — Laravel excels here with Filament (admin panel generator), Cashier (Stripe/Paddle billing), and built-in auth
  • Two codebases, one shared SQLite/MySQL for pet/location data (read by both)

Features

Frontend (GOAT — Quick Win)

  • Add a pet — form with name, type (dog/cat/other), photo URL, Address, Two phones
  • Update location
  • View last known — table showing each pet's last reported location + time ago
  • Delete pet — soft delete

Admin (Laravel)

  • User management — CRUD, roles (admin/staff/owner), password reset, 2FA
  • Subscription & payments — Stripe/Paddle via Laravel Cashier, invoices, overdue tracking
  • Device provisioning — register new trackers, assign to user accounts, activation keys
  • Dashboard — overdue accounts, expiring subscriptions, new signups, active devices
  • Audit log — who changed what, login history

Future / Shared

  • Location history log per pet (timeline view)
  • "Notify me" → NTFY ping when a pet's location updates
  • Map view (leaflet.js) of all current locations
  • Multi-user family group linking

Tech Details

Stack Rationale

The GOAT stackGo + Alpine.js + Tailwind + HTMX + SQLite:

  • Go net/http — routing, form handling, template rendering. No framework.
  • HTMXhx-post, hx-get, hx-delete for inline form submission, live table updates, delete confirmations — no page reloads.
  • Alpine.js — form validation, dropdown bindings, timestamp formatting (x-data, x-model, x-init).
  • Tailwind CSS — utility-first styling via CDN, rapid UI without CSS files.
  • SQLite — single file DB (data.db), zero config, survives restarts. Migrations via schema.sql.

Database Schema

CREATE TABLE pets (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    name        TEXT NOT NULL,
    type        TEXT NOT NULL CHECK(type IN ('dog', 'cat', 'other')),
    chip_id     TEXT UNIQUE,
    photo_url   TEXT,
    created_at  TEXT DEFAULT (datetime('now')),
    deleted     INTEGER DEFAULT 0
);

CREATE TABLE locations (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    pet_id      INTEGER NOT NULL REFERENCES pets(id),
    location    TEXT NOT NULL,         -- human-readable: "Backyard", "Vet, Main St"
    notes       TEXT,
    reported_at TEXT DEFAULT (datetime('now'))
);

CREATE INDEX idx_locations_pet_time ON locations(pet_id, reported_at DESC);

Endpoints

Method Path Handler UI Behavior
GET / ServeIndex Full page: pet list table + "New Location" dropdown + "Add Pet" button
GET /pets/table ServePetTable HTMX: re-renders the pet+location table
POST /pets CreatePet HTMX: form submit → table update, form reset
DELETE /pets/:id DeletePet HTMX: confirm → row fade-out
POST /pets/:id/locations AddLocation HTMX: dropdown form submit → table cell updates
GET /pets/:id/history ServeHistory HTMX: expand row to show location timeline

UI Sketch

┌──────────────────────────────────────────────────────────────┐
│  WhereWoof                                           [+ Add Pet] │
├──────────────────────────────────────────────────────────────┤
│                                                                │
│  Quick Update: [Buddy ▼] is at [_______________] [Update]     │
│                                                                │
├──────────┬────────┬──────────────────┬──────────┬─────────────┤
│  Pet     │ Type   │ Last Seen        │ Location │ Actions     │
├──────────┼────────┼──────────────────┼──────────┼─────────────┤
│  🐕 Buddy│  dog   │ 10 min ago       │ Backyard │ [History][✕]│
│  🐈 Mittens│ cat  │ 2 hours ago      │ Couch    │ [History][✕]│
│  🐕 Rocky │  dog   │ 3 days ago       │ Vet      │ [History][✕]│
└──────────┴────────┴──────────────────┴──────────┴─────────────┘

Directory Structure

/home/sam/home_network/web_sites/where_woof
├── frontend/                 # GOAT stack — public pet tracker
│   ├── main.go
│   ├── db.go
│   ├── handlers.go
│   ├── models.go
│   ├── go.mod
│   ├── templates/
│   │   ├── base.html
│   │   ├── index.html
│   │   ├── pet-table.html
│   │   └── location-form.html
│   ├── static/
│   │   └── style.css
│   └── data/
│       └── .gitkeep
├── admin/                    # Laravel — staff dashboard
│   ├── (standard Laravel structure)
│   ├── app/Models/
│   │   ├── User.php
│   │   ├── Pet.php
│   │   ├── Subscription.php
│   │   └── Device.php
│   ├── app/Filament/         # Filament admin panel resources
│   ├── database/migrations/
│   └── routes/web.php
└── shared/                   # Shared between frontend and admin
    └── schema.sql            # Core pet/location tables (used by both)

Deployment

Dev (local on .27)

# Frontend (GOAT)
cd /home/sam/home_network/web_sites/where_woof/frontend
go run . --port 3020

# Admin (Laravel)
cd /home/sam/home_network/web_sites/where_woof/admin
php artisan serve --port 3030

Build + Push to .13

# Frontend — Go binary
cd  /home/sam/home_network/web_sites/where_woof/frontend
GOOS=linux GOARCH=amd64 go build -o where-woof .
rsync -avz where-woof templates/ static/ sam@192.168.20.13:/var/www/where-woof

# Admin — Laravel
rsync -avz --exclude vendor --exclude node_modules \
  /home/sam/home_network/web_sites/where_woof/admin/ \
  sam@192.168.20.13:/var/www/where-woof
# On .13: composer install, php artisan migrate

Run on .13

ssh sam@192.168.20.13

# Frontend
cd /var/www/where-woof && ./where-woof --port 3020

# Admin (needs PHP-FPM + nginx, or Docker)
# Option A: php artisan serve --port 3030 (dev only)
# Option B: Docker with php:8.3-fpm + nginx

Caddy (.35)

# Frontend — public pet tracker
where-woof.com {
    reverse_proxy 192.168.20.13:3020
}
where-woof.home.lab {
    reverse_proxy 192.168.20.13:3020
}

# Admin — staff dashboard
admin.where-woof.com {
    reverse_proxy 192.168.20.13:3030
}

Pi-hole DNS

  • where-woof.home.lab192.168.20.35
  • where-woof.com → public DNS → .35
  • admin.where-woof.com → public DNS → .35

Roadmap

Phase What Status
1 Go scaffold, schema, pet CRUD, table view (frontend)
2 Quick location update, history timeline (frontend)
3 HTMX polish — inline edits, fade transitions (frontend)
4 Laravel admin scaffold — Filament, user CRUD
5 Laravel billing — Cashier, subscriptions, overdue tracking
6 NTFY integration for location alerts (frontend)
7 Deploy frontend to .13, Caddy DNS, live
8 Deploy admin to .13, Caddy DNS, live