summaryrefslogtreecommitdiff
path: root/issues
diff options
context:
space:
mode:
Diffstat (limited to 'issues')
-rw-r--r--issues/004-integrate-terst-org.md62
-rw-r--r--issues/008-add-google-calendar-support.md85
-rw-r--r--issues/011-add-timeline-view.md86
-rw-r--r--issues/013-quick-add-shopping-list.md81
-rw-r--r--issues/bug_004_trello_cards_missing.md31
-rw-r--r--issues/phase4_step2_efficient_sync.md46
-rw-r--r--issues/phase4_step2_research_sync.md124
-rw-r--r--issues/task_001_remove_obsidian.md37
-rw-r--r--issues/task_002_add_authentication.md36
-rw-r--r--issues/task_003_deployment_prep.md58
10 files changed, 0 insertions, 646 deletions
diff --git a/issues/004-integrate-terst-org.md b/issues/004-integrate-terst-org.md
deleted file mode 100644
index 9ec79bf..0000000
--- a/issues/004-integrate-terst-org.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# [FEATURE] Integrate terst.org start page and styling
-
-## Description
-Integrate terst.org start page widgets and visual styling.
-
-## User Story
-As a user, I want the dashboard to incorporate my terst.org start page widgets and match its visual styling for a unified experience.
-
-## Technical Context
-- Two aspects: visual styling + widget integration
-- Requires understanding terst.org's current design system and widget architecture
-- May need API access or scraping depending on terst.org implementation
-
-## Test Strategy
-
-### E2E Test (Red)
-```
-1. Load dashboard
-2. Assert color scheme matches terst.org (primary colors, fonts)
-3. Assert expected widgets render (TBD based on widget inventory)
-```
-
-## Proposed Approach
-
-### Phase 1: Visual Styling
-1. **Audit terst.org design:**
- - Extract color palette (primary, secondary, accent)
- - Identify fonts and typography scale
- - Note spacing/layout patterns
-2. **Update Tailwind config:**
- - Add terst.org colors to theme
- - Update default styles to match
-3. **Apply to existing components**
-
-### Phase 2: Widget Integration
-1. **Inventory terst.org widgets:**
- - What widgets exist? (clock, weather, links, etc.)
- - How are they implemented? (static HTML, JS, API-backed?)
-2. **Integration options:**
- - Option A: Embed via iframe (quick but limited)
- - Option B: Recreate widgets natively (more work, better integration)
- - Option C: API calls to terst.org backend (if available)
-3. **Implement priority widgets first**
-
-## Discovery Required
-- [ ] Access terst.org to inventory widgets
-- [ ] Extract color palette and design tokens
-- [ ] Determine widget data sources
-
-## Affected Components
-- `tailwind.config.js` (theme colors)
-- `web/static/css/` (global styles)
-- `web/templates/index.html` (widget containers)
-- `web/templates/partials/` (new widget partials)
-- Potentially `internal/handlers/` (widget data endpoints)
-
-## Definition of Done
-- [ ] Color scheme matches terst.org
-- [ ] Typography matches terst.org
-- [ ] Priority widgets integrated
-- [ ] Responsive layout preserved
-- [ ] E2E test verifies styling
diff --git a/issues/008-add-google-calendar-support.md b/issues/008-add-google-calendar-support.md
deleted file mode 100644
index f3a1f3b..0000000
--- a/issues/008-add-google-calendar-support.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# [FEATURE] Add Google Calendar support
-
-## Description
-Add Google Calendar support.
-
-## User Story
-As a user, I want my Google Calendar events displayed so I have a unified daily view.
-
-## Technical Context
-- New integration following existing patterns in `internal/api/`
-- OAuth2 flow required; store refresh token in `sync_tokens` table
-- New partial for calendar events display
-
-## Test Strategy
-
-### Unit Test (Red)
-**File:** `internal/api/gcal_test.go`
-
-```go
-func TestParseCalendarEvents(t *testing.T) {
- // Mock Google Calendar API response
- // Assert events parsed correctly with title, time, all-day flag
-}
-
-func TestGCalClient_FetchEvents(t *testing.T) {
- // Mock HTTP client
- // Assert correct API calls made
- // Assert events returned in expected format
-}
-```
-
-### Integration Test (Red)
-Test cache/store roundtrip for events.
-
-```go
-func TestGCalEventsCache(t *testing.T) {
- // Store events in cache
- // Retrieve from cache
- // Assert data integrity
-}
-```
-
-## Proposed Approach
-
-1. **OAuth Setup:**
- - Add `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` to config
- - Implement OAuth2 flow with PKCE
- - Store refresh token in `sync_tokens` table
-
-2. **API Client:**
- - Create `internal/api/gcal.go` implementing interface pattern from existing clients
- - Use `google.golang.org/api/calendar/v3`
- - Fetch events for configurable date range (default: today + 7 days)
-
-3. **Data Model:**
- - Add `calendar_events` table or reuse existing structure
- - Handle all-day vs timed events
-
-4. **UI:**
- - New partial: `partials/gcal-events.html`
- - Integrate into existing tabs or new calendar tab
-
-## Traps to Avoid
-- **All-day events:** Google returns these differently (date vs dateTime)
-- **Timezone:** Events have their own timezone; convert to user's local
-- **Recurring events:** Decide whether to expand or show as single item
-- **Token refresh:** Handle expired access tokens gracefully
-
-## Affected Components
-- `internal/api/gcal.go` (new)
-- `internal/api/gcal_test.go` (new)
-- `internal/api/interfaces.go` (add interface)
-- `internal/config/config.go`
-- `internal/store/sqlite.go` (new table/queries)
-- `web/templates/partials/gcal-events.html` (new)
-- `cmd/dashboard/main.go` (wire up client)
-
-## Definition of Done
-- [ ] OAuth2 flow authenticates with Google
-- [ ] Events fetched from Google Calendar API
-- [ ] Events cached in SQLite
-- [ ] Events displayed in UI
-- [ ] All-day and timed events handled correctly
-- [ ] Token refresh works
-- [ ] Unit and integration tests pass
diff --git a/issues/011-add-timeline-view.md b/issues/011-add-timeline-view.md
deleted file mode 100644
index 49e744a..0000000
--- a/issues/011-add-timeline-view.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# [FEATURE] Add timeline view
-
-## Description
-Add timeline view.
-
-## User Story
-As a user, I want a timeline/agenda view so I can see my day's schedule visually.
-
-## Technical Context
-- New view aggregating multiple data sources by time
-- Requires merging tasks + meals + (future) calendar events
-- New tab or view mode in existing UI
-
-## Test Strategy
-
-### Unit Test (Red)
-**File:** `internal/handlers/handlers_test.go`
-
-```go
-func TestBuildTimeline(t *testing.T) {
- tasks := []Task{{Name: "Task 1", DueDate: todayAt(10, 0)}}
- meals := []Meal{{Name: "Lunch", Date: today, Type: "lunch"}} // lunch = 12:00
-
- timeline := BuildTimeline(tasks, meals)
-
- assert.Len(t, timeline, 2)
- assert.Equal(t, "Task 1", timeline[0].Title)
- assert.Equal(t, "Lunch", timeline[1].Title)
-}
-
-func TestTimelineItemsSortedByTime(t *testing.T) {
- // Items from different sources
- // Assert sorted by timestamp regardless of source
-}
-```
-
-### E2E Test (Red)
-Timeline renders with correct order and time markers.
-
-## Proposed Approach
-
-1. **Data Structure:**
- ```go
- type TimelineItem struct {
- Time time.Time
- Title string
- Source string // "task", "meal", "calendar"
- Type string // source-specific type
- URL string
- Metadata map[string]any
- }
- ```
-
-2. **Handler:**
- - New endpoint `GET /timeline` or extend tab handler
- - Aggregate items from tasks, meals, (future) calendar
- - Sort by time ascending
- - Group by hour or time block
-
-3. **UI:**
- - Vertical timeline with time gutter on left
- - Color-coded by source
- - Expandable items for details
-
-4. **Meal time mapping:**
- - breakfast: 08:00
- - lunch: 12:00
- - dinner: 18:00
- - snack: configurable or omit from timeline
-
-## Affected Components
-- `internal/handlers/handlers.go` or `internal/handlers/timeline.go` (new)
-- `internal/handlers/handlers_test.go`
-- `internal/models/types.go` (TimelineItem struct)
-- `web/templates/partials/timeline-tab.html` (new)
-- `web/templates/index.html` (add tab)
-
-## Definition of Done
-- [ ] Timeline view accessible from UI
-- [ ] Tasks with times appear at correct position
-- [ ] Meals appear at appropriate meal times
-- [ ] Items sorted chronologically
-- [ ] Visual time markers/gutter
-- [ ] Color coding by source
-- [ ] Unit tests for timeline building
-- [ ] E2E test for rendering
diff --git a/issues/013-quick-add-shopping-list.md b/issues/013-quick-add-shopping-list.md
deleted file mode 100644
index e09ab92..0000000
--- a/issues/013-quick-add-shopping-list.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# [FEATURE] Quick add for shopping list items
-
-## Description
-Add a quick add interface for shopping list items.
-
-## User Story
-As a user, I want to quickly add items to my shopping list without navigating away.
-
-## Technical Context
-- Requires decision on backend: PlanToEat API vs local SQLite
-- May need new table `shopping_items` if local
-- Could integrate with modal menu from issue #002
-
-## Test Strategy
-
-### Unit Test (Red)
-**File:** `internal/store/sqlite_test.go`
-
-```go
-func TestAddShoppingItem(t *testing.T) {
- store := setupTestStore(t)
-
- err := store.AddShoppingItem(ShoppingItem{
- Name: "Milk",
- Quantity: "1 gallon",
- })
-
- assert.NoError(t, err)
-
- items, _ := store.GetShoppingItems()
- assert.Len(t, items, 1)
- assert.Equal(t, "Milk", items[0].Name)
-}
-```
-
-### E2E Test (Red)
-Quick-add form submits, item appears in list.
-
-## Proposed Approach
-
-1. **Decide backend:**
- - Option A: PlanToEat API (if it supports shopping lists)
- - Option B: Local SQLite table (simpler, offline-capable)
-
-2. **Database (if local):**
- ```sql
- CREATE TABLE shopping_items (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL,
- quantity TEXT,
- checked BOOLEAN DEFAULT 0,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
- ```
-
-3. **Handler:**
- - `POST /api/shopping/add` — add item
- - `GET /api/shopping` — list items
- - `PUT /api/shopping/{id}/check` — toggle checked
- - `DELETE /api/shopping/{id}` — remove item
-
-4. **UI:**
- - Quick-add form in modal menu (integrate with #002) or meals tab
- - Simple input: item name, optional quantity
- - List view with checkboxes
-
-## Affected Components
-- `internal/store/sqlite.go` (new table/methods)
-- `internal/store/sqlite_test.go`
-- `internal/handlers/handlers.go` (new endpoints)
-- `web/templates/partials/shopping-list.html` (new)
-- Potentially `web/templates/partials/modal-menu.html` (from #002)
-
-## Definition of Done
-- [ ] Can add items via quick-add form
-- [ ] Items persisted (API or local)
-- [ ] Items displayed in list view
-- [ ] Can check/uncheck items
-- [ ] Can delete items
-- [ ] Unit tests for store operations
-- [ ] E2E test for add flow
diff --git a/issues/bug_004_trello_cards_missing.md b/issues/bug_004_trello_cards_missing.md
deleted file mode 100644
index c882424..0000000
--- a/issues/bug_004_trello_cards_missing.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Bug: Trello cards not showing on all boards
-
-## Status
-Open
-
-## Symptoms
-- Only one board shows cards
-- Other boards with cards appear empty
-- Refresh works (no 403 error) but doesn't fix the issue
-- No errors in server logs
-
-## Investigation Notes
-- CSRF fix applied - refresh now works
-- API client code looks correct (fetches all boards, then cards per board)
-- Cache logic appears correct (SaveBoards clears and replaces all data)
-- Need to investigate:
- - Is the Trello API returning all cards?
- - Is there a filter issue (`visible` vs `all`)?
- - Board permissions issue on Trello side?
- - Race condition in concurrent card fetching?
-
-## Reproduction
-1. Login to dashboard
-2. Go to Planning tab
-3. Observe: only one board has cards, others are empty
-4. Compare with actual Trello - should have cards on multiple boards
-
-## Next Steps
-- Add logging to trace Trello API responses
-- Check if specific boards are returning empty card arrays
-- Verify Trello API credentials have access to all boards
diff --git a/issues/phase4_step2_efficient_sync.md b/issues/phase4_step2_efficient_sync.md
deleted file mode 100644
index 92fa03c..0000000
--- a/issues/phase4_step2_efficient_sync.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Phase 4 Step 2: Efficient Sync Research & Implementation
-
-## Status: COMPLETE
-
-## Goal
-Improve the efficiency of data synchronization to reduce API calls and latency. Previously, the dashboard fetched all data on every refresh.
-
-## Implementation
-
-### Todoist Sync API v9
-- **Endpoint**: `https://api.todoist.com/sync/v9/sync`
-- **Method**: POST with `sync_token` and `resource_types`
-- **Full sync**: When token is `*` or empty, returns all items
-- **Incremental sync**: With valid token, returns only changes since last sync
-- **Response**: Contains `sync_token` (for next request), `full_sync` flag, `items`, `projects`
-
-### Storage
-- New `sync_tokens` table in SQLite for persisting tokens across restarts
-- Store methods: `GetSyncToken()`, `SetSyncToken()`, `ClearSyncToken()`
-- Incremental update methods: `UpsertTask()`, `DeleteTasksByIDs()`
-
-### Handler Logic
-- `fetchTasks()` uses Sync API with stored token
-- If `FullSync=true`: Replace all cached tasks
-- If `FullSync=false`: Merge changes (upsert active, delete completed/deleted)
-- Falls back to cached data on API errors
-
-### Trello Optimization
-- Added `fields` parameter to API calls to reduce response payload:
- - `GetBoards()`: `fields=id,name`
- - `GetCards()`: `fields=id,name,idList,due,url,idBoard`
- - `getLists()`: `fields=id,name`
-
-## Files Modified
-- `migrations/003_add_sync_tokens.sql` (new)
-- `internal/store/sqlite.go` - Added sync token and incremental update methods
-- `internal/api/todoist.go` - Added Sync API support
-- `internal/api/interfaces.go` - Added Sync method to interface
-- `internal/api/trello.go` - Added field filtering
-- `internal/handlers/handlers.go` - Updated fetchTasks to use Sync API
-- `internal/handlers/handlers_test.go` - Updated mock to implement Sync
-
-## Benefits
-- **Todoist**: Reduced data transfer on subsequent syncs (only changes returned)
-- **Trello**: Smaller API responses (only required fields)
-- **Persistence**: Sync token survives restarts, enabling incremental sync on startup
diff --git a/issues/phase4_step2_research_sync.md b/issues/phase4_step2_research_sync.md
deleted file mode 100644
index d690e6f..0000000
--- a/issues/phase4_step2_research_sync.md
+++ /dev/null
@@ -1,124 +0,0 @@
-# Phase 4 Step 2: Research Efficient Sync
-
-## Research Findings
-
-### Todoist Sync API
-
-**Test Results (from `research_test.go`):**
-- Full sync response: **17,502 bytes**
-- Incremental sync response: **179 bytes** (99% reduction!)
-- `sync_token` successfully enables incremental updates
-
-**Response Structure:**
-```json
-{
- "sync_token": "...",
- "full_sync": true/false,
- "items": [...] // tasks
-}
-```
-
-**Item (Task) Fields Available:**
-- `id`, `content`, `description`, `project_id`
-- `checked` (completion status)
-- `is_deleted` (for handling deletions)
-- `updated_at` (for tracking changes)
-- `due` (with `date`, `is_recurring`, `string`)
-
-**Implementation Approach:**
-1. Store `sync_token` in database (new table or cache_metadata)
-2. On refresh, call Sync API with stored token
-3. Apply incremental changes to local database:
- - Update modified tasks
- - Delete tasks where `is_deleted=true`
- - Mark tasks where `checked=true` as completed
-4. If `full_sync=true` in response, clear and rebuild local state
-
-### Trello API Optimization
-
-**Test Results:**
-- Full boards response: **48,228 bytes** (all fields)
-- Limited fields (id,name): **462 bytes** (99% reduction!)
-- Single board with embedded cards/lists: **10,452 bytes**
-
-**Optimization Strategies:**
-
-1. **Field Filtering** (immediate win):
- ```
- /boards?fields=id,name,dateLastActivity
- /boards/{id}/cards?fields=id,name,idList,due,url
- ```
-
-2. **Batch Requests** (reduce API calls):
- ```
- /boards/{id}?cards=visible&card_fields=id,name,idList,due,url&lists=open&list_fields=id,name
- ```
- - Fetches board + cards + lists in single request
-
-3. **`dateLastActivity` Caching**:
- - Store `dateLastActivity` for each board
- - On refresh, fetch boards with minimal fields
- - Only fetch cards for boards where `dateLastActivity` changed
- - Note: `since` parameter exists but returns full data (not truly incremental)
-
-### Architecture Implications
-
-**Current Architecture:**
-- Cache-aside pattern: Check cache -> Miss -> Fetch API -> Store in cache
-- Problem: Full data fetch on every cache miss
-
-**Proposed Architecture:**
-- **Sync Engine Pattern**:
- - SQLite is the source of truth
- - Sync process updates SQLite incrementally
- - Read operations always hit SQLite (fast)
-
-**Migration Path:**
-1. Phase A: Add field filtering to Trello (immediate benefit, no architecture change)
-2. Phase B: Implement Todoist Sync API with sync_token storage
-3. Phase C: Add dateLastActivity tracking for Trello boards
-
-## Implementation Plan
-
-### Phase A: Trello Field Optimization (Low effort, High impact)
-
-1. Update `GetBoards` to use `fields=id,name,dateLastActivity`
-2. Update `GetCards` to use `fields=id,name,idList,due,url,desc`
-3. Consider batch endpoint for single-board-with-cards
-
-### Phase B: Todoist Incremental Sync (Medium effort, High impact)
-
-1. Add `sync_tokens` table:
- ```sql
- CREATE TABLE sync_tokens (
- service TEXT PRIMARY KEY,
- token TEXT NOT NULL,
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
- ```
-
-2. Create `SyncTasks` method in `TodoistClient`:
- - Accept previous sync_token
- - Return new sync_token + changed items
-
-3. Create `ApplyTaskChanges` method in `Store`:
- - Handle updates, deletions, completions
- - Update sync_token after successful apply
-
-4. Update `fetchTasks` to use sync when token exists
-
-### Phase C: Trello Smart Refresh (Medium effort, Medium impact)
-
-1. Store `dateLastActivity` per board in database
-2. On refresh:
- - Fetch boards with minimal fields
- - Compare `dateLastActivity` with stored values
- - Only fetch cards for changed boards
-
-## Recommendation
-
-**Start with Phase A** - simple field filtering provides immediate 99% reduction in Trello response size with minimal code changes.
-
-**Then Phase B** - Todoist Sync API provides the best efficiency gains (99% reduction) and is well-documented.
-
-**Phase C is optional** - Trello doesn't have true incremental sync, so the benefit is limited to skipping unchanged boards.
diff --git a/issues/task_001_remove_obsidian.md b/issues/task_001_remove_obsidian.md
deleted file mode 100644
index c02a785..0000000
--- a/issues/task_001_remove_obsidian.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Remove Obsidian Functionality (COMPLETED)
-
-**Description:**
-Remove all code related to Obsidian integration to prepare for public server deployment. Obsidian relies on local filesystem access, which is not suitable for a public web server environment.
-
-**Status:**
-✅ COMPLETED
-
-**Changes Made:**
-1. **Core Logic:**
- * Deleted `internal/api/obsidian.go` and `internal/api/obsidian_test.go`.
- * Removed `ObsidianAPI` interface from `internal/api/interfaces.go`.
- * Removed `Note` struct from `internal/models/types.go`.
- * Removed `SourceObsidian` and `NoteToAtom` from `internal/models/atom.go`.
-
-2. **Configuration:**
- * Removed `ObsidianVaultPath` and `HasObsidian()` from `internal/config/config.go`.
- * Removed `OBSIDIAN_VAULT_PATH` from `.env.example`.
-
-3. **Handlers:**
- * Removed `obsidianClient` field from `Handler` struct in `internal/handlers/handlers.go`.
- * Updated `New` function signature.
- * Removed `HandleNotesTab` and `fetchNotes` methods.
- * Removed `HandleNotes` from `internal/handlers/tabs.go`.
- * Removed `obsidian` logic from `aggregateData`.
-
-4. **UI:**
- * Removed `web/templates/partials/obsidian-notes.html`.
- * Removed `web/templates/partials/notes-tab.html`.
- * Removed "Notes" tab button from `web/templates/index.html`.
- * Removed `obsidian` color from `tailwind.config.js`.
-
-5. **Main Entry Point:**
- * Updated `cmd/dashboard/main.go` (Handled by Implementor).
-
-6. **Tests:**
- * Updated `test/acceptance_test.go` and `internal/handlers/handlers_test.go`.
diff --git a/issues/task_002_add_authentication.md b/issues/task_002_add_authentication.md
deleted file mode 100644
index 018fff0..0000000
--- a/issues/task_002_add_authentication.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Task: Add Authentication
-
-## Goal
-Implement session-based authentication to secure the application for public deployment.
-
-## Plan
-
-1. **Dependencies:**
- * Add `github.com/alexedwards/scs/v2` (Session management).
- * Add `github.com/alexedwards/scs/sqlite3store` (SQLite store for sessions).
- * Add `golang.org/x/crypto/bcrypt` (Password hashing).
-
-2. **Database Schema:**
- * Create migration `migrations/003_add_auth.sql`.
- * Create `users` table (`id`, `username`, `password_hash`).
- * Create `sessions` table (required by `scs` SQLite store).
-
-3. **Core Logic (`internal/auth`):**
- * Create `AuthService` to handle login, logout, and password verification.
- * Implement `User` model.
-
-4. **Configuration:**
- * Update `Config` to include `SessionSecret` (for cookie encryption, if needed, though `scs` handles this well).
-
-5. **Handlers & Middleware:**
- * Initialize `SessionManager` in `main.go`.
- * Create `LoginHandler` (GET/POST).
- * Create `LogoutHandler` (POST).
- * Create `AuthMiddleware` to protect routes.
-
-6. **UI:**
- * Create `web/templates/login.html`.
- * Update `web/templates/base.html` (or similar) to show Logout button when logged in.
-
-7. **Seed Data:**
- * Create a CLI command or startup check to ensure a default admin user exists (or provide instructions to create one).
diff --git a/issues/task_003_deployment_prep.md b/issues/task_003_deployment_prep.md
deleted file mode 100644
index a05c2eb..0000000
--- a/issues/task_003_deployment_prep.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Task 003: VPS Deployment Preparation (Apache + Systemd)
-
-Prepare the application for deployment on a VPS using Apache2 as a reverse proxy and Systemd for process management.
-
-## Target Environment
-- **OS:** Linux (VPS)
-- **Web Server:** Apache2
-- **Process Manager:** Systemd
-- **Directory Structure:**
- - Root: `/site/{fqdn}/`
- - Binary: `/site/{fqdn}/app`
- - Data: `/site/{fqdn}/data/` (Database location)
- - Webroot: `/site/{fqdn}/public/` (Static assets)
-
-## Plan
-
-### 1. Configuration Updates
-- Ensure the application can accept configuration via Environment Variables for:
- - `PORT` (Default: 8080)
- - `DB_PATH` (Default: `./task.db`, Target: `/site/{fqdn}/data/dashboard.db`)
- - `SESSION_KEY` (Secret)
- - `STATIC_DIR` (Optional: if we want to point to `public` explicitly)
-
-### 2. Create Deployment Artifacts
-Create a `deployment/` directory containing:
-
-#### A. Systemd Unit File (`deployment/task-dashboard.service`)
-- **Description:** Manages the Go application process.
-- **Key Settings:**
- - `ExecStart=/site/{fqdn}/app`
- - `WorkingDirectory=/site/{fqdn}/`
- - `User=www-data` (or specific user)
- - `Restart=always`
- - `EnvironmentFile=/site/{fqdn}/.env` (or inline env vars)
-
-#### B. Apache VHost Configuration (`deployment/apache.conf`)
-- **Description:** Reverse proxy configuration.
-- **Key Settings:**
- - Based on provided template.
- - `DocumentRoot /site/{fqdn}/public`
- - `ProxyPass / http://localhost:8080/`
- - `ProxyPassReverse / http://localhost:8080/`
- - Serve static assets directly via Apache for performance (optional but recommended).
-
-### 3. Documentation
-- Create `docs/deployment.md` with instructions:
- 1. **Build:** `go build -o app cmd/dashboard/main.go`
- 2. **Setup:**
- - `mkdir -p /site/{fqdn}/{data,public}`
- - Copy `app` to `/site/{fqdn}/`
- - Copy `web/static` content to `/site/{fqdn}/public/`
- - Copy `web/templates` to `/site/{fqdn}/templates` (or ensure app can find them)
- 3. **Service:** Install and enable systemd service.
- 4. **Apache:** Enable site and modules (`proxy`, `proxy_http`).
-
-## Considerations
-- **Static Files:** We should decide if Apache serves `/static` directly from `/site/{fqdn}/public` or if the Go app handles it. Serving via Apache is preferred for performance.
-- **Templates:** The Go app needs access to HTML templates. We need to ensure the `WorkingDirectory` allows relative path resolution to `web/templates` or configure an absolute path.