Commit graph

75 commits

Author SHA1 Message Date
afd4b6a1cf fix: use sudo_if rm -f app — delete root-owned binary as app user 2026-06-03 08:18:17 +00:00
5abe8c49d9 fix: stop service before build so old binary can be removed
- Prevents 'text file busy' error when rebuilding running binary
- Service is started again after the build completes
2026-06-03 08:17:05 +00:00
f8d3cb6b33 fix: remove old binary before rebuild — prevents permission denied when overwriting root-owned file
- go build can't overwrite a root-owned binary as app user
- Added rm -f app before go build in both update and fresh install paths
- Also chown the app binary alongside .go cache
2026-06-03 08:15:58 +00:00
433b5a4ed8 fix: chown .go build cache before build to prevent permission errors
- Build cache may be root-owned from previous builds
- Added sudo_if chown before both update and fresh build steps
- Prevents 'permission denied' on go mod cache writes
2026-06-03 08:14:18 +00:00
67f1a6c845 fix: add -buildvcs=false to go build to prevent VCS errors
- .git directory may have ownership issues after sudo operations
- -buildvcs=false disables VCS stamping without affecting the binary
2026-06-03 08:11:53 +00:00
6e61740f0a fix: update mode chowns .go build cache to app user
- .go/ directory was root-owned from earlier builds
- Prevents 'permission denied' errors during go build
2026-06-03 08:10:57 +00:00
0d42a35cb8 refactor: remove duplicate edit from event page, add delete to edit form
- Event page now shows read-only metadata only (no edit/delete buttons)
- Edit works from dashboard (Edit button on event cards)
- Delete added to the edit form as a button alongside Save/Cancel
- htmx.trigger #delete div in the edit form fragment
2026-06-03 08:09:31 +00:00
b9721a9bab fix: event edit and delete now use htmx.trigger() instead of .submit() / hx.trigger()
- form.submit() triggered standard GET submission with query params
- hx.trigger() is not a valid function — htmx.trigger() is the correct API
- Both edit save and delete now properly fire HTMX requests
2026-06-03 08:03:47 +00:00
c815e022b5 fix: OTP email subject 'Your ExpenseFlow OTP' → 'Your ReceiptNext OTP' 2026-06-03 08:01:00 +00:00
e547eee57f fix: install.sh backup preserves database owner using stat
- Uses stat to get original owner:group before backup
- chown restores backup file to original owner
- Prevents database becoming readonly after update
2026-06-03 07:59:02 +00:00
91534ed8bd fix: renderOTPForm in auth.go now uses cached single-field template
- Was using its own inline template with 6 digit_0..digit_5 fields
- Now calls getTemplate('otp_form') which has the single 6-digit input
- templates.go already had the correct single-field version
2026-06-03 07:57:56 +00:00
cb42efa395 fix: database ownership preserved during backup — app user must be able to write
- Backup now uses chown --reference to preserve original owner
- Found: rsync changed database ownership to root, blocking writes
2026-06-03 07:52:23 +00:00
bb34528bf5 feat: single OTP field, event metadata on page, delete event
- OTP: 6 separate inputs → single 6-digit field with copy/paste support
- Event page: shows name, currency, exchange rate at top
- Edit toggle: unlock to edit fields, lock to save (via PUT)
- Delete button with confirmation popup
- DeleteEvent handler + DB function + route
- Backward compatible: collectOTP strips non-digits
2026-06-03 07:46:12 +00:00
0748af7c26 fix: EditEvent returns standalone form fragment, not full page
- Edit button now shows inline form with pre-filled values
- Cancel button hides the form again
- Computes sample claim from existing exchange rate
- No full-page rendering issues
2026-06-03 07:41:29 +00:00
0f39f3dc24 fix: Edit button shows hidden form — added onclick to remove hidden class
- Edit button now reveals the form before loading content
- Without this, the form was loaded into a hidden div and invisible
2026-06-03 07:38:52 +00:00
5f5f2be75b feat: event editing — change exchange rate and currency
- Added Edit button on open event cards in dashboard
- EditEvent handler returns dashboard.html with pre-filled form
- UpdateEvent handler (PUT /events/{id}) saves new currency + rate
- UpdateEvent in db.go updates base_currency and exchange_rate
- Form auto-switches between create (POST) and edit (PUT) mode
- Reuses the same conversion sample pattern as event creation
2026-06-03 07:33:37 +00:00
4ea3d63b29 feat: OTP inputs auto-advance to next field when digit is typed
- oninput moves focus to next field when digit entered
- onkeydown Backspace moves to previous field when current is empty
- Last field (digit_5) only handles backspace
2026-06-02 00:00:57 +00:00
7ab94a14d0 feat: add × close button to image lightbox
- Added visible close button (×) top-right of image overlay
- Image clicks no longer propagate (prevents accidental close)
- Overlay background click still closes
2026-06-01 23:57:17 +00:00
2d259a2b7c fix: add 'unsafe-inline' to script-src CSP so onclick handlers work 2026-06-01 23:51:39 +00:00
814cba1f53 fix: image lightbox overlay hidden not working — display:flex overrode hidden class
- Removed display:flex from inline style so the hidden class can apply display:none
- All image modals were visible and stacked on top of the page content
- This also blocked event creation (the transparent overlay covered the form)
2026-06-01 23:35:35 +00:00
0b55ac1fac fix: normalize ImagePath for old database entries with storage/ prefix
- Added normalizeImagePath() helper that strips legacy storage/ prefix
- Applied in ViewEventExpenses, SaveExpense, UpdateExpense, EditExpense
- Prevents double storage/storage/ in image URLs for old expenses
2026-06-01 23:30:56 +00:00
bb2c06fa3a fix: validate sudo access upfront in install.sh and update mode
- Added sudo -v at start of both fresh install and update mode
- Caches sudo credentials so subsequent sudo_if calls don't prompt
- If sudo is unavailable, shows clear error message
- Prevents 'Access denied' on systemctl restart
2026-06-01 23:27:04 +00:00
9c239e565b docs: update README — binary name, update process, config table, structure
- Binary name: receiptnext → app
- Update: no sudo, adds backups, templates/static sync
- Config table: added AI_MODEL, AI_BASE_URL, OPENAI_API_KEY
- Structure: added backups/ directory
- Security: .env is optional, not root-owned
2026-06-01 23:21:21 +00:00
5aca6c06a5 fix: update mode backs up database before making changes
- Creates timestamped backup at /opt/receiptnext/backups/expenses-YYYYMMDD-HHMMSS.db
- rsync only targets templates/ and static/ — database is never touched
2026-06-01 23:20:14 +00:00
c386b150c4 fix: update mode now copies templates, static assets, install.sh
- update mode previously only rebuilt the binary
- Now also rsyncs templates/ and static/ to install dir
- Copies updated install.sh to install dir
- Fixes ownership of new files
- Uses sudo_if for systemctl restart
2026-06-01 23:18:40 +00:00
8ff1a4175a feat: view receipt image from expense list
- Added 🖼️ button on each expense — opens full-screen lightbox
- Click anywhere on the overlay to close
- Images served via /storage/{filename} (auth-protected)
- Fixed ImagePath to store only filename (was storage/storage/...)
- Both expense_list.html fragment and event_expenses.html page updated
2026-06-01 23:16:35 +00:00
4593dd1f66 refactor: replace currency dropdowns with simple text inputs
- Currency ISO3 dropdowns replaced with <input type="text" maxlength="3">
- Users can type any currency code (KES, USD, EUR, etc.)
- No need to maintain a long list of currencies
- Exchange rate is user-defined so validation is unnecessary
- All currency inputs have text-transform: uppercase for consistency
2026-06-01 23:14:16 +00:00
e49b184be8 fix: install.sh installs git, curl, python3 if missing
- Checks for git before clone/pull — installs via apt/dnf/apk
- Checks for curl before downloading release binary
- Checks for python3 before parsing release JSON
- Multi-distro support (apt-get, dnf, apk)
2026-05-31 03:02:01 +00:00
92f070440f refactor: implement best-practice recommendations from code review
MUST FIX:
- M1: Fixed ignored errors in AI providers (json.Marshal, http.NewRequest, json.Unmarshal)
- M2: Template cache — pre-parse all templates once at startup, reuse via getTemplate()
- M3: Fixed silent ParseFloat error fallbacks — now returns HTTP 400 on invalid amounts
- M4: Wrapped readFile errors with context (fmt.Errorf with %w)
- M5: Deleted stale llm.go placeholder file
- M6: Renamed utils.New() to utils.NewUUID() for clarity
- M7: Validate current_event_id cookie UUID format, prevent tampering

SHOULD FIX:
- S4: Added utils.Timestamp() helper to replace repeated time.Now().Format() calls
- S6: Added request ID middleware for concurrent request log tracing
- S7: Increased DB pool from 1 to 4 connections (HTMX concurrency)
- S8: Graceful shutdown via http.Server.Shutdown() on SIGINT/SIGTERM
- S9: Storage served behind auth middleware with path traversal check

COULD FIX:
- C2: renderOTPForm uses cached template (not per-request Must)
- C3: CSP pinned to unpkg.com/htmx.org@1.9.10
- C4: Added ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout
- C7: PDF generation auto-adds page breaks when content overflows

ADDITIONAL:
- Pass config to AI provider constructors (newGeminiProvider, newOpenAIProvider)
- Value receivers on geminiProvider/openaiProvider (empty structs)
- Added envOrDefault() helper in ai/receipt.go
- Session cleanup goroutine started in main.go
- Removed duplicate imports and unused html/template from handlers
2026-05-31 02:13:07 +00:00
6a902f0b85 chore: remove last DeepSeek reference from main.go header 2026-05-31 02:02:02 +00:00
a7381bde0c fix: final vulnerability sweep — storage auth, security headers, body limits, cookie flags
- Storage route moved behind auth middleware (was publicly accessible)
- Security headers: X-Content-Type-Options, X-Frame-Options, CSP, Referrer-Policy
- Request body size limit: 10 MB on all endpoints via MaxBytesReader
- Session cookie now sets Secure flag when BASE_URL uses HTTPS
- readFile() returns proper errors for dirs & oversized files (was nil,nil)
- Removed dead DEEPSEEK_API_KEY code from main.go
- Added fmt import to ai/receipt.go for error formatting
2026-05-31 02:01:12 +00:00
e831fcf617 fix: resolve 7 critical security findings from code review
CR-1: Path traversal in createReceiptZip — validate image_path is within storage/
CR-2: Missing authz on EditExpense/UpdateExpense — verify event ownership
CR-3: OTP timing side-channel — use crypto/subtle.ConstantTimeCompare
CR-4: Logout doesn't invalidate session — moved to AuthHandler with Sessions.Delete()
CR-5: OTP reuse race condition — mutex lock around validate+delete
CR-6: Live credentials on disk — removed .env from disk entirely
CR-7: No TLS — documented as expected behind-proxy deployment

Additional:
- Removed stale github.com/expenseflow import path from auth.go
- Made EnvironmentFile optional (prefix with -) so .env is not required
- App runs and starts clean without any .env file
2026-05-31 01:50:08 +00:00
2f26abfb2f docs: update README — remove Ollama references, update config table 2026-05-30 17:44:13 +00:00
9f52198647 fix: remove orphaned fallthrough after ollama removal 2026-05-30 17:40:40 +00:00
3327fd4fac feat: use glm-ocr as default Ollama model (specialized OCR, not a general LLM)
- glm-ocr is a 1.1B parameter model built specifically for OCR
- No reasoning overhead, no thinking field issues
- Faster inference than qwen3.5 on CPU
- Removed old qwen3.5 models (2B + 0.8B) to free ~4GB disk
- Updated install.sh, ollama.go, .env.example defaults
2026-05-30 17:17:06 +00:00
1eca4ae777 fix: qwen3.5 reasoning model outputs to thinking field not content
- ollamaResponse now reads both content and thinking fields
- Falls back to thinking if content is empty
- Install script adds model warm-up prompt to preload into memory
- Warm-up uses OLLAMA_HOST for correct user context
- Makes first real receipt analysis faster
2026-05-30 16:51:43 +00:00
59f73830fb fix: chown /usr/share/ollama after install so ollama user can write to it
- The Ollama installer run via sudo leaves /usr/share/ollama owned by root
- The ollama service runs as the ollama user and needs write access
- Added chown after installation to fix permissions
2026-05-30 16:45:00 +00:00
51c5d9a23e fix: set GOCACHE alongside GOMODCACHE for users without home write access
- Go build cache was trying to write to /app/.cache/go-build (unwritable)
- Now uses /opt/receiptnext/.go/build instead
2026-05-30 16:13:26 +00:00
de29841341 docs: remove sudo from local clone install command 2026-05-30 16:11:18 +00:00
0ab9f1e1be fix: go build module cache permission + README sudo
- Set GOMODCACHE + GOPATH to /opt/receiptnext/.go (writable location)
- Removes sudo from README one-liner — script self-elevates
- Build now works for users whose home directory isn't writable
2026-05-30 16:10:42 +00:00
b7fddf0069 fix: set OLLAMA_HOST explicitly for model pull
- ollama pull failed because the CLI didn't know where the server was
- Uses OLLAMA_HOST=http://127.0.0.1:11434 to connect regardless of user
2026-05-30 16:06:03 +00:00
bc5a5172dc fix: git safe.directory + ownership after clone/pull
- Adds safe.directory exception so git doesn't reject the repo
- chown directory to invoking user after clone/pull so build works
2026-05-30 16:02:44 +00:00
bb11a7f32c fix: wait for Ollama API before pulling model
- Wait loop now checks HTTP endpoint (127.0.0.1:11434/api/tags)
  instead of just the CLI version
- Increased retries from 10 to 15 (30s → 30s with 2s intervals)
- Uses sudo -u ollama for pull so models go to correct user directory
2026-05-30 16:00:08 +00:00
378c8e5b98 docs: update help text — no global sudo needed 2026-05-30 15:56:41 +00:00
e977907da3 fix: install.sh syntax errors from sed replacements
- Fixed stray dots and missing quotes in download URLs
- Fixed stale receiptnext binary references (now app)
- Fixed missing closing quote on info message
2026-05-30 15:55:28 +00:00
e20a484ebc chore: rename binary from receiptnext to app
- Binary output changed from 'receiptnext' to 'app'
- Release assets renamed to app-linux-amd64 / app-linux-arm64
- install.sh, Makefile, README.md updated to reference 'app'
- contrib/receiptnext.service uses /opt/receiptnext/app
- Systemd service updated to ExecStart=/opt/receiptnext/app
2026-05-30 15:53:24 +00:00
f9a7df1580 feat: greenfield deployment — no Go required
- install.sh detects if Go is installed
- If Go available: builds from source (current behavior)
- If Go missing: downloads pre-built binary from latest release
- Detects amd64/arm64 architecture automatically
- Same logic applies to -update mode
- Removed hard dependency on ollama.service in systemd unit
2026-05-30 15:49:05 +00:00
59788ba0c8 fix: app runs as dedicated user, not root
- install.sh detects the real user (SUDO_USER or whoami)
- If root, creates receiptnext system user
- If regular user, uses that user for the service
- User is added to ollama group for CLI access
- Systemd service uses User=receiptnext (or detected user)
- All file ownership set to the app user
2026-05-30 15:44:10 +00:00
b6c2c3f99d fix: Ollama timeout + image compression + selective sudo in install.sh 2026-05-30 15:41:47 +00:00
357483cfd2 fix: install zstd dependency before Ollama installer 2026-05-30 15:22:03 +00:00