From 42a4e32daca13b518e64e5821080ff3d6adf0e39 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Mon, 26 Jan 2026 16:49:44 -1000 Subject: Use configured timezone throughout codebase - Add config/timezone.go with timezone utilities: - SetDisplayTimezone(), GetDisplayTimezone() - Now(), Today() - current time/date in display TZ - ParseDateInDisplayTZ(), ToDisplayTZ() - parsing helpers - Initialize timezone at startup in main.go - Update all datetime logic to use configured timezone: - handlers/handlers.go - all time.Now() calls - handlers/timeline.go - date parsing - handlers/timeline_logic.go - now calculation - models/atom.go - ComputeUIFields() - models/timeline.go - ComputeDaySection() - api/plantoeat.go - meal date parsing - api/todoist.go - due date parsing - api/trello.go - due date parsing This ensures all dates/times display correctly regardless of server timezone setting. Co-Authored-By: Claude Opus 4.5 --- issues/004-integrate-terst-org.md | 62 --------------- issues/008-add-google-calendar-support.md | 85 -------------------- issues/011-add-timeline-view.md | 86 --------------------- issues/013-quick-add-shopping-list.md | 81 ------------------- issues/bug_004_trello_cards_missing.md | 31 -------- issues/phase4_step2_efficient_sync.md | 46 ----------- issues/phase4_step2_research_sync.md | 124 ------------------------------ issues/task_001_remove_obsidian.md | 37 --------- issues/task_002_add_authentication.md | 36 --------- issues/task_003_deployment_prep.md | 58 -------------- 10 files changed, 646 deletions(-) delete mode 100644 issues/004-integrate-terst-org.md delete mode 100644 issues/008-add-google-calendar-support.md delete mode 100644 issues/011-add-timeline-view.md delete mode 100644 issues/013-quick-add-shopping-list.md delete mode 100644 issues/bug_004_trello_cards_missing.md delete mode 100644 issues/phase4_step2_efficient_sync.md delete mode 100644 issues/phase4_step2_research_sync.md delete mode 100644 issues/task_001_remove_obsidian.md delete mode 100644 issues/task_002_add_authentication.md delete mode 100644 issues/task_003_deployment_prep.md (limited to 'issues') 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. -- cgit v1.2.3