175 lines
8.5 KiB
Markdown
175 lines
8.5 KiB
Markdown
# WhereWoof — Return-Tag Platform
|
||
|
||
> **Branding (2026-08):** "WhereWoof" → **"Where Woof"** (play on *werewolf* — dog-themed wolf). Page title: **"Where Woof !"**. Future logo concept follows the werewolf wordplay (dog/wolf). Phase 2 change includes the rename task (headings, title, templates).
|
||
|
||
WhereWoof is a **return-item tag platform** (PetHub / ReturnMe style). Physical tags are pre-coded with a unique QR code and NFC chip, each pointing to its own URL. A finder scans the tag on a lost item (dog, baggage, skis…) and immediately sees how to return it.
|
||
|
||
> **This is NOT a location/GPS tracker.** The tag has no electronics beyond the printed code and NFC. The system carries the *return details*, and — only when a finder permits — sends the *finder's* location to the owner so they can collect their item.
|
||
|
||
## How it works — customer journey
|
||
|
||
1. **Buy** — user buys a tag (pre-coded, unique `tag_code` printed on QR + NFC).
|
||
2. **First scan / first visit** — `https://where-woof.com/t/<tag_code>` shows "This tag is not set up yet."
|
||
3. **Set up** — the user creates an account or logs in → **adds the tag to their account** → fills in the item details.
|
||
4. **Details form** — item type (dog / cat / baggage / skis / other), description, photo, phone number, address, notes.
|
||
5. **Done** — scanning the tag from now on shows the return details.
|
||
6. **Edit** — the tag page shows an edit icon for a logged-in owner → account CRUD page. The account page is also reachable from the main site (`where-woof.com`).
|
||
|
||
## Scan flow — finder experience (front-end)
|
||
|
||
When someone finds a lost item and scans the tag:
|
||
|
||
1. Page loads the return details: item type, description, photo, owner contact.
|
||
2. **Geolocation prompt** (browser permission). If granted, the location is sent immediately to the server → **SMS alert to the owner** with the coordinates. If rejected, the page still works fully.
|
||
3. **Ring owner** — `tel:` link that dials the owner's number directly.
|
||
4. **Re-check location** — button to re-request the finder's location (in case they changed their mind).
|
||
5. **Send SMS** — `sms:` link with a prefilled message asking the finder for their phone number (opens their SMS app).
|
||
6. **Finder contact input** — the finder can type their mobile number; it is forwarded to the owner (not stored long-term).
|
||
7. The location button is hidden once location has been shared.
|
||
|
||
> ⚠️ **Auto-reading the finder's phone number is NOT possible on the web** — there is no browser API for it (privacy restriction; only native apps can). Manual input only.
|
||
|
||
## Architecture — two systems, one shared Postgres
|
||
|
||
| System | Stack | Port / DNS | Audience | Purpose |
|
||
|--------|-------|-----------|----------|---------|
|
||
| **Front-end** | **GOAT**: Go + HTMX + Alpine.js + Tailwind + **Postgres** | `.13:3020` → `where-woof.com` | Finders + owners | Public tag pages, scan flow, account setup/CRUD |
|
||
| **Admin** | Laravel + Filament/Cashier + Postgres | `.13:3030` → `admin.where-woof.com` | Staff (Sam) | Users, tags, plans, billing, system management |
|
||
|
||
**One shared Postgres database.** Both apps are peers over it — the Go app reads/writes directly (via `pgx` / `sqlc`), Laravel manages the same schema, and **Laravel owns migrations** as the long-term convention. (Until Laravel exists, a canonical `db/schema.sql` is the source of truth.)
|
||
|
||
**Why this design:**
|
||
- The GOAT stack is a **skills-update exercise** — the point is Go + HTMX + Alpine + Tailwind as the front-end stack. The "S" (SQLite) from the original AI-written plan is swappable and is **dropped in favour of Postgres**: a multi-tenant product needs one real database, not hundreds of per-user SQLite files (no cross-user queries, migration/backup nightmare, Laravel can't query them).
|
||
- Dev Postgres already available locally: Supabase Postgres on `.27:5434`.
|
||
- Both apps sharing one DB means no sync layer and one backup.
|
||
|
||
**Domains:**
|
||
- Live site at InMotion Hosting redirects → `where-woof.com` → **.35 (Caddy)** → **.13** (apps).
|
||
- `admin.where-woof.com` → .35 → .13:3030.
|
||
|
||
## Database schema (v2)
|
||
|
||
```sql
|
||
CREATE TABLE users (
|
||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||
email TEXT NOT NULL UNIQUE,
|
||
password_hash TEXT NOT NULL,
|
||
name TEXT,
|
||
phone TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
|
||
CREATE TABLE tags (
|
||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||
tag_code TEXT NOT NULL UNIQUE, -- printed on QR + NFC, public identifier
|
||
owner_id BIGINT REFERENCES users(id),
|
||
status TEXT NOT NULL DEFAULT 'unset'
|
||
CHECK (status IN ('unset', 'active', 'suspended')),
|
||
item_type TEXT CHECK (item_type IN ('dog', 'cat', 'baggage', 'skis', 'other')),
|
||
description TEXT,
|
||
photo_url TEXT,
|
||
phone TEXT, -- owner contact phone (shown via tel:)
|
||
address TEXT,
|
||
notes TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
|
||
CREATE TABLE scans (
|
||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||
tag_id BIGINT NOT NULL REFERENCES tags(id),
|
||
scanned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
lat DOUBLE PRECISION,
|
||
lng DOUBLE PRECISION,
|
||
location_shared BOOLEAN NOT NULL DEFAULT FALSE,
|
||
scanner_phone TEXT,
|
||
alert_sent BOOLEAN NOT NULL DEFAULT FALSE
|
||
);
|
||
```
|
||
|
||
*Plans / subscriptions tables come later with billing (Phase 5).*
|
||
|
||
## Accounts, plans & anti-abuse
|
||
|
||
- One account can hold **10–20 tags** (limit enforced).
|
||
- Business model: **annual fee + per-tag cost + max scans per plan**.
|
||
- **Scan limits are the anti-scam / cost protection** — every scan can trigger an SMS (which costs us money), so caps prevent SMS-bombing and tag re-selling abuse.
|
||
- Tag status: `unset` → `active` → `suspended` (admin can suspend).
|
||
- Billing: **Stripe (Australia)** — **NOT in the front-end phase**; phased later.
|
||
|
||
## Endpoints — GOAT front-end
|
||
|
||
| Method | Path | Purpose |
|
||
|--------|------|---------|
|
||
| `GET` | `/` | Main site home |
|
||
| `GET` | `/t/:tag_code` | Public tag page — setup prompt or details + scan flow |
|
||
| `POST` | `/t/:tag_code/scan` | Record scan + optional location → alert owner |
|
||
| `POST` | `/t/:tag_code/contact` | Finder leaves their phone number for the owner |
|
||
| `GET/POST` | `/register`, `/login`, `/logout` | Account auth (email + password, session cookie) |
|
||
| `GET` | `/account` | Account CRUD: my tags |
|
||
| `POST` | `/account/tags` | Add a tag to the account (enter tag code) |
|
||
| `GET/POST` | `/account/tags/:id/edit` | Edit tag details |
|
||
| `POST` | `/account/tags/:id/delete` | Remove tag |
|
||
|
||
## Admin — Laravel (later phases)
|
||
|
||
User management, tag management (suspend/transfer), plans & billing (Stripe/Cashier), scan history & alerts dashboard, audit log. Not built in Phase 1.
|
||
|
||
## Deployment
|
||
|
||
### Dev (local on .27)
|
||
```bash
|
||
cd frontend && go run . --port 3020 # GOAT front-end
|
||
cd admin && php artisan serve --port 3030 # Laravel (later)
|
||
```
|
||
|
||
### Build + push to .13
|
||
```bash
|
||
cd 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 (later): rsync + `composer install` + `php artisan migrate` on .13.
|
||
|
||
### Run on .13
|
||
```bash
|
||
cd /var/www/where-woof && ./where-woof --port 3020
|
||
```
|
||
|
||
### Caddy (.35)
|
||
```caddy
|
||
where-woof.com {
|
||
reverse_proxy 192.168.20.13:3020
|
||
}
|
||
where-woof.home.lab {
|
||
reverse_proxy 192.168.20.13:3020
|
||
}
|
||
admin.where-woof.com {
|
||
reverse_proxy 192.168.20.13:3030
|
||
}
|
||
```
|
||
|
||
### DNS
|
||
- Live site (InMotion) → redirect / DNS update → `where-woof.com` → public DNS → .35 (Caddy) → .13.
|
||
- Pi-hole: `where-woof.home.lab` → 192.168.20.35.
|
||
|
||
## Roadmap
|
||
|
||
| Phase | What | Status |
|
||
|-------|------|--------|
|
||
| 1 | **Front-end foundation**: Go scaffold, Postgres schema, auth, tag setup, public tag page, account CRUD (GOAT) | ⬜ |
|
||
| 2 | **Scan flow**: geolocation → SMS alert, `tel:`/`sms:` links, finder contact, re-check button | ⬜ |
|
||
| 3 | HTMX / Alpine polish — inline edit, animations | ⬜ |
|
||
| 4 | Laravel admin: users, tags, dashboard (same Postgres) | ⬜ |
|
||
| 5 | Billing: Stripe (AU), plans, scan-limit enforcement, invoices | ⬜ |
|
||
| 6 | Photo uploads + object storage | ⬜ |
|
||
| 7 | Deploy front-end to .13, Caddy, DNS, InMotion redirect | ⬜ |
|
||
| 8 | Deploy admin to .13 | ⬜ |
|
||
| 9 | Anti-abuse hardening: relay numbers, rate limits | ⬜ |
|
||
|
||
## Future ideas
|
||
|
||
- NTFY ping to owner when a scan happens (alongside SMS).
|
||
- Tag transfer / reset for resale (admin feature).
|
||
- Relay number so the owner's real mobile isn't exposed to scanners.
|