update obsidian

This commit is contained in:
2025-11-03 23:47:58 +00:00
parent 2b7ec96d37
commit f1dc375c5f
2 changed files with 125 additions and 125 deletions
+8 -12
View File
@@ -4,21 +4,17 @@
"type": "split", "type": "split",
"children": [ "children": [
{ {
"id": "83169ae72c9bc357", "id": "af94f814e06296f9",
"type": "tabs", "type": "tabs",
"children": [ "children": [
{ {
"id": "6f192f9477d55538", "id": "7ac1561c78f61154",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "markdown", "type": "empty",
"state": { "state": {},
"file": "Crussell/Crussell Nails.md",
"mode": "source",
"source": false
},
"icon": "lucide-file", "icon": "lucide-file",
"title": "Crussell Nails" "title": "New tab"
} }
} }
] ]
@@ -169,9 +165,9 @@
"bases:Create new base": false "bases:Create new base": false
} }
}, },
"active": "6f192f9477d55538", "active": "7ac1561c78f61154",
"lastOpenFiles": [ "lastOpenFiles": [
"Crussell/Backend/bookings.md", "Crussell/Crussell Nails.md",
"Crussell/Crussell Nails.md" "Crussell/Backend/bookings.md"
] ]
} }
+117 -113
View File
@@ -1,86 +1,86 @@
## ✅ / ⏳ Project Checklist ## ✅ / ⏳ Project Checklist
### Backend (Go / Chi / Postgres) ### Backend (Go / Chi / Postgres)
- [x] JWT authentication (login, refresh, role verification) - [x] JWT authentication (login, refresh, role verification)
- [x] User registration with input validation - [x] User registration with input validation
- [x] Password hashing with bcrypt - [x] Password hashing with bcrypt
- [x] Middleware for auth/roles - [x] Middleware for auth/roles
- [x] DB connection pooling - [x] DB connection pooling
- [ ] Booking endpoints - [ ] Booking endpoints
-`/api/bookings` (CRUD for users)
-`/api/admin/bookings` (list, search, userbyuser, progress, confirm)
- [ ] Admin endpoints - [ ] Admin endpoints
-`/api/admin/services` (create, delete, list, toggle)
-`/api/admin/bookings` (list, search, user bookings, progress, confirm)
-`/api/admin/users` (list, view) *handlers still to be implemented*
- [ ] Unit tests - [ ] Unit tests
- [ ] CI/CD - [ ] CI/CD
### Frontend (SvelteKit / Tailwind / shadcn) ### Frontend (SvelteKit / Tailwind / shadcn)
- [x] Core pages (Home, Prices, Contact, Book) - [x] Core pages (Home, Prices, Contact, Book)
- [x] Booking wizard prototype - [x] Booking wizard prototype
- [x] Calendar/time slot selector - [x] Calendar/time slot selector
- [ ] API integration for booking flow - [ ] API integration for booking flow
- [ ] Payments (Stripe/Square) *The wizard currently only shows a prototype alert. Wire it to the backend `/api/bookings` POST endpoint in the next iteration.*
- [ ] Payments (Stripe/Square) *pending integration*
- [ ] Auth screens - [ ] Auth screens
- [ ] Admin dashboard - [ ] Admin dashboard
### Infrastructure ### Infrastructure
- [x] `.env` config - [x] `.env` config
- [x] Docker Compose full stack - [x] Docker Compose full stack
- [ ] nginx reverse proxy - [ ] nginx reverse proxy *still under review*
- [ ] Monitoring/logging - [ ] Monitoring/logging *no stack configured*
- [ ] CI/CD pipeline - [ ] CI/CD pipeline *not yet defined*
### Integrations ### Integrations
- [x] CardDAV sync for contacts - [x] CardDAV sync for contacts
- [ ] Email/SMS reminders - [ ] Email/SMS reminders *not yet implemented*
- [ ] Payment provider - [ ] Payment provider *placeholder only*
- [ ] Loyalty tracking frontend - [ ] Loyalty tracking frontend *no component yet*
--- ---
## 📐 Current Architecture ## 📐 Current Architecture
### Overview Diagram ### Overview Diagram
```mermaid ```mermaid
flowchart TD flowchart TD
User([Customer]) User([Customer])
Admin([Admin]) Admin([Admin])
CardReader[Square Card Reader<br/>Physical Terminal] CardReader[Square Card Reader<br/>Physical Terminal]
subgraph CF[Cloudflare Protection] subgraph CF[Cloudflare Protection]
CDN[CDN / DDoS Protection] CDN[CDN / DDoS Protection]
end end
subgraph Lightsail[AWS Lightsail Instance] subgraph Lightsail[AWS Lightsail Instance]
subgraph NGINX[Nginx Reverse Proxy] subgraph NGINX[Nginx Reverse Proxy]
Proxy[Port 443/80] Proxy[Port 443/80]
end end
subgraph Frontend[SvelteKit Frontend] subgraph Frontend[SvelteKit Frontend]
UI[Booking UI / Prices / Contact] UI[Booking UI / Prices / Contact]
AdminUI[Admin Dashboard] AdminUI[Admin Dashboard]
APIProxy[API Proxy Routes] APIProxy[API Proxy Routes]
end end
subgraph Backend[Go + Chi Backend] subgraph Backend[Go + Chi Backend]
Router[chi Router] Router[chi Router]
Auth[JWT Middleware] Auth[JWT Middleware]
Handlers[API Handlers] Handlers[API Handlers]
end end
subgraph Database[PostgreSQL] subgraph Database[PostgreSQL]
DB[(Users / Bookings<br/>Transactions)] DB[(Users / Bookings<br/>Transactions)]
end end
subgraph DAV[SabreDAV Server] subgraph DAV[SabreDAV Server]
CardDAV[(vCard Contacts)] CardDAV[(vCard Contacts)]
CalDAV[(Calendar Events)] CalDAV[(Calendar Events)]
end end
end end
subgraph External[External Services] subgraph External[External Services]
Gmail[Gmail SMTP<br/>smtp.gmail.com:587] Gmail[Gmail SMTP<br/>smtp.gmail.com:587]
SquareAPI[Square API<br/>Online Payments] SquareAPI[Square API<br/>Online Payments]
@@ -90,94 +90,46 @@ flowchart TD
User -->|HTTPS| CF User -->|HTTPS| CF
Admin -->|HTTPS| CF Admin -->|HTTPS| CF
CF --> Proxy CF --> Proxy
Proxy --> UI Proxy --> UI
Proxy --> AdminUI Proxy --> AdminUI
Proxy --> APIProxy Proxy --> APIProxy
UI --> APIProxy UI --> APIProxy
AdminUI --> APIProxy AdminUI --> APIProxy
APIProxy --> Router APIProxy --> Router
Router --> Auth Router --> Auth
Auth --> Handlers Auth --> Handlers
Handlers --> DB Handlers --> DB
Handlers --> CardDAV Handlers --> CardDAV
Handlers --> CalDAV Handlers --> CalDAV
Handlers -->|Send Emails| Gmail Handlers -->|Send Emails| Gmail
Handlers -->|Process Payments| SquareAPI Handlers -->|Process Payments| SquareAPI
Admin -.->|Sync Contacts/Calendar| DAV Admin -.->|Sync Contacts/Calendar| DAV
CardReader -->|Transaction Data| SquareAPI CardReader -->|Transaction Data| SquareAPI
Handlers -.->|Poll Transactions| SquareAPI Handlers -.->|Poll Transactions| SquareAPI
%% Force External Services to appear below %% Force External Services to appear below
Lightsail --> External Lightsail --> External
``` ```
--- ---
## 🔧 Backend Implementation ## 🔧 Backend Implementation
### JWT Auth ### JWT Auth
JWT uses **HS256** algorithm with 30day expiry. Implemented via `go-chi/jwtauth`.
JWT uses **HS256** algorithm with 30-day expiry. Implemented via `go-chi/jwtauth`.
**Code:**
```go
// auth/jwt.go
var TokenAuth *jwtauth.JWTAuth
func InitJWT(secret string) {
TokenAuth = jwtauth.New("HS256", []byte(secret), nil)
}
func GenerateToken(userID string, role string) (string, error) {
_, token, err := TokenAuth.Encode(map[string]interface{}{
"user_id": userID,
"role": role,
"exp": time.Now().Add(30 * 24 * time.Hour).Unix(),
})
return token, err
}
```
### Middleware ### Middleware
Validates JWT and attaches user context. Supports role restrictions. Validates JWT and attaches user context. Supports role restrictions.
```go
// mw/auth.go
func RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") {
http.Error(w, "missing token", http.StatusUnauthorized)
return
}
token := strings.TrimPrefix(authHeader, "Bearer ")
userID, role, err := auth.VerifyToken(token, r.Context())
if err != nil {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), UserIDKey, userID)
ctx = context.WithValue(ctx, UserRoleKey, role)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
```
### Registration Flow ### Registration Flow
The registration process: The registration process:
- Validates names, email, phone, DOB - Validates names, email, phone, DOB
- Rejects users under 16 years old - Rejects users under 16 years old
- Hashes password with bcrypt - Hashes password with bcrypt
@@ -191,30 +143,38 @@ defer tx.Rollback(r.Context())
_, err := tx.Exec(r.Context(), _, err := tx.Exec(r.Context(),
`INSERT INTO users (first_name, last_name, email, phone, dob, password_hash) `INSERT INTO users (first_name, last_name, email, phone, dob, password_hash)
VALUES ($1,$2,$3,$4,$5,$6)`, VALUES ($1,$2,$3,$4,$5,$6)`,
req.FirstName, req.LastName, req.Email, req.Phone, dob, string(hash)) req.FirstName, req.LastName, req.Email, req.Phone, dob, string(hash))
``` ```
### CardDAV Integration ### CardDAV Integration
Each registered user generates a `.vcf` file in SabreDAV, ensuring external calendar and contact apps stay in sync. The code now uses the internal `dav.BaseService.CreateContact` helper.
Each registered user generates a `.vcf` file in SabreDAV, ensuring external calendar and contact apps stay in sync.
```go ```go
func saveToCardDAV(userID, firstName, lastName, email, phone, dob string) error { func CreateCardDAVContact(service *dav.BaseService, addressBookID int, userID, firstName, lastName, email, phone, dob string) error {
vcard := createVCard(userID, firstName, lastName, email, phone, dob) input := dav.ContactInput{
url := fmt.Sprintf("http://nginx/dav/addressbooks/principals/default/default/%s.vcf", userID) UserID: userID,
req, _ := http.NewRequest("PUT", url, bytes.NewBufferString(vcard)) FirstName: firstName,
req.SetBasicAuth("admin", "admin") LastName: lastName,
_, err := http.DefaultClient.Do(req) Email: email,
return err Phone: phone,
DOB: dob,
}
return service.CreateContact(addressBookID, userID, input)
} }
``` ```
The addressbook ID is currently hardcoded in the database (`dav_cards` table). A migration will expose the ID via a dedicated endpoint in the next sprint.
### Admin Endpoints
*Handlers for `/api/admin/services`, `/api/admin/bookings`, and `/api/admin/users` are defined in the router, but the concrete implementation files are still empty. These will be fleshed out in the next sprint.*
--- ---
## 🎨 Frontend Implementation ## 🎨 Frontend Implementation
### API Proxy ### API Proxy
Prevents CORS issues by proxying all API calls through SvelteKit. Prevents CORS issues by proxying all API calls through SvelteKit.
```ts ```ts
@@ -223,22 +183,23 @@ const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:8080';
async function proxyRequest(request: Request, path: string) { async function proxyRequest(request: Request, path: string) {
const url = `${BACKEND_URL}/api/${path}`; const url = `${BACKEND_URL}/api/${path}`;
const backendRes = await fetch(url, { const backendRes = await fetch(url, {
method: request.method, method: request.method,
headers: request.headers headers: request.headers
}); });
return new Response(backendRes.body, {
status: backendRes.status, // Forward everything transparently
headers: backendRes.headers return new Response(backendRes.body, {
status: backendRes.status,
headers: new Headers(backendRes.headers)
}); });
} }
``` ```
### Booking Flow ### Booking Flow
**Step 1**: Select services
**Step 1**: Select services **Step 2**: Choose date/time
**Step 2**: Choose date/time **Step 3**: Enter details
**Step 3**: Enter details
**Step 4**: Payment (TODO) **Step 4**: Payment (TODO)
**Calendar Component:** **Calendar Component:**
@@ -268,28 +229,71 @@ function generateTimeSlots(duration: number) {
--- ---
## 🎯 Next Steps ## 🎯 Next Steps
- **Booking Integration** wire the wizard to the backend `/api/bookings` POST endpoint.
- **Payments** integrate Stripe or Square SDK; add a payment screen.
- **Admin Dashboard** implement admin routes and UI.
- **Monitoring/Logging** add a lightweight Prometheus/Grafana stack or use CloudWatch.
- **CI/CD Pipeline** create GitHub Actions workflow for linting, testing, and container publishing.
- **Admin Handlers** flesh out `/api/admin/services`, `/api/admin/bookings`, and `/api/admin/users` endpoints.
- **Email/SMS Reminders** add scheduled job to send reminders.
- **Loyalty Tracking** add a frontend component to display loyalty stamps.
### Bookings ---
- Add `bookings` table: user_id, service, date, time, status ## 📦 Environment & Runtime
- Backend: `/api/bookings` (create, list, cancel)
- Frontend: wire booking wizard → backend
### Payments | Variable | Purpose | Default / Example |
|----------|---------|-------------------|
| `JWT_SECRET_KEY` | Secret used to sign JWTs | **REQUIRED** set in `.env` or Docker secrets |
| `DATABASE_URL` | PostgreSQL connection string | `postgres://user:pass@localhost:5432/crussell?sslmode=disable` |
| `VITE_BACKEND_URL` | Frontend proxy target | `http://localhost:8080` (dev) |
| `VITE_SQUARE_ENV` | Square environment | `sandbox` or `production` |
| `VITE_STRIPE_KEY` | Stripe publishable key | `pk_test_...` |
- Integrate Stripe or Square SDK > **Tip**: The `.env.example` file contains all required keys copy it to `.env` and edit.
- Secure checkout at Step 4
### Admin Dashboard ---
- View/manage bookings ## 🐳 Docker Compose
- User management
- Loyalty overview
### Security ```yaml
# docker-compose.yml
services:
backend:
build: ./backend
environment:
- JWT_SECRET_KEY=supersecret
- DATABASE_URL=postgres://crussell:crussell@db:5432/crussell
depends_on: [db, dav]
dav:
image: sabredav/sabredav
environment:
- DAV_URL=http://dav:8000
volumes:
- dav-data:/data
db:
image: postgres:15
environment:
- POSTGRES_USER=crussell
- POSTGRES_PASSWORD=crussell
- POSTGRES_DB=crussell
nginx:
image: nginx:alpine
depends_on: [backend, dav]
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
frontend:
build: ./frontend
environment:
- VITE_BACKEND_URL=http://backend:8080
```
- Move JWT secret → env > Run `docker compose up -d` to bring the stack up.
- HTTPS everywhere
- Harden API roles
--- ---
## 📜 GDPR & Data Retention
The database schema includes a `anonymize_user()` function (see `init-script.sql`) that removes PII after a users account is closed. The code also contains a `export_all_user_data()` helper to support subjectaccess requests.
---