- 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
- 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
- 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
- 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
- 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)
- 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
- Creates timestamped backup at /opt/receiptnext/backups/expenses-YYYYMMDD-HHMMSS.db
- rsync only targets templates/ and static/ — database is never touched
- 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
- 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
- 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
- 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)
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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- .env.example: placeholder values only
- gemini.go: error on missing API key instead of fallback
- main.go: dynamic from address from SMTP_USER
- Security: old credentials removed from active codebase
- Zero CGO dependencies — builds with CGO_ENABLED=0
- Fully static binaries, no libc required
- Cross-compilation now works without any GCC cross-compilers
- ldd confirms: 'not a dynamic executable'
- Updates go.mod to Go 1.23 (required by modernc.org/sqlite)
- New provider architecture with common interface
- Provider selected via AI_PROVIDER env var (gemini/openai/ollama)
- Gemini (default): existing implementation, uses GEMINI_API_KEY
- OpenAI-compatible: uses OPENAI_API_KEY + AI_MODEL + AI_BASE_URL
- Works with OpenAI, Perplexity, Together AI, Groq, etc.
- Ollama: local LLM, uses AI_BASE_URL + AI_MODEL
- Supports llava, bakllava, and other vision models
- deepseek.go renamed to llm.go (cleanup)
- .env.example updated with all AI provider options
- Two upload buttons: Camera (capture) and Upload (gallery/PDF)
- PDF receipts from Uber/email now accepted and processed by Gemini Vision
- PDF detection via %PDF magic bytes in both handler and AI module
- Descriptions updated to reflect broader file support
- Email now includes both report (CSV/PDF) + ZIP of all receipt images
- ZIP images named {event-name}-{index}.{ext} matching list order
- Uses Go's archive/zip (stdlib, no external deps)
- Sender.SendReport now accepts []*Attachment for multiple files
- Gracefully skips missing image files with warnings
- Input padding increased from 12px to 16px
- Label-to-input gap increased from 4px to 8px
- Form group spacing increased from 16px to 20px
- Buttons padding increased from 8px/16px to 12px/20px
- Login card padding increased for more breathing room
- Added .form-row with responsive flex gap layout
- Added .main-content class with generous padding