Skip to content

Commit 7db1798

Browse files
authored
Merge pull request #210 from Sagargupta16/feat/flowai-ui-rework
feat: rework ledger workspace UI
2 parents 53780cb + c07e98e commit 7db1798

108 files changed

Lines changed: 5559 additions & 8701 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ AWS_SESSION_TOKEN=your-aws-session-token
5454
AWS_PROFILE=default
5555

5656
# Frontend
57-
VITE_API_BASE_URL=http://localhost:8000
57+
# Local development uses the Vite /api proxy. Set this only for split production hosting.
58+
# VITE_API_BASE_URL=https://ledger-sync-api.vercel.app
5859

5960
# Deployment helpers
6061
GITHUB_PAGES=false

CHANGELOG.md

Lines changed: 42 additions & 19 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 19 additions & 19 deletions
Large diffs are not rendered by default.

CONTRIBUTING.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Contributing
22

3-
Thanks for improving Ledger Sync. Keep changes focused and verify both stacks before opening a PR.
3+
Thanks for improving Ledger Sync. Keep changes focused and verify the affected behavior plus both quality gates before opening a PR.
44

55
## Setup
66

77
```bash
8+
corepack enable
89
pnpm install
910
pnpm run setup
1011
pnpm run dev
@@ -16,7 +17,7 @@ Local services:
1617
- Backend API: http://localhost:8000
1718
- API docs: http://localhost:8000/docs
1819

19-
Use `.env.example` as the template. Put backend values in `backend/.env` and frontend values in `frontend/.env.local`. Never commit real secrets or `.env` files.
20+
Use `.env.example` as the template. Put backend values in `backend/.env`. Local frontend development needs no environment file because Vite proxies `/api`; use `frontend/.env.local` only when testing a separate API origin. Never commit real secrets or `.env` files.
2021

2122
## Checks
2223

@@ -51,4 +52,5 @@ pnpm run build
5152
- Keep changes surgical and tied to one problem.
5253
- Use conventional commits such as `fix: update onboarding docs`.
5354
- Include what changed, why, and the checks you ran.
55+
- Update API, page, calculation, database, deployment, or changelog documentation when a change affects those contracts.
5456
- Do not include generated caches, local databases, `node_modules`, or secrets.

README.md

Lines changed: 119 additions & 197 deletions
Large diffs are not rendered by default.

backend/README.md

Lines changed: 137 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,167 @@
1-
# Ledger Sync -- Backend
2-
3-
FastAPI backend powering the Ledger Sync personal finance dashboard. Handles Excel import, transaction reconciliation, financial analytics, exchange rate proxying, user preferences, AI assistant configuration with encrypted key storage, and the Bedrock Converse proxy.
4-
5-
## Features
6-
7-
- OAuth authentication (Google, GitHub) with JWT tokens
8-
- Excel file ingestion with duplicate detection
9-
- SHA-256 based transaction reconciliation
10-
- Financial analytics and calculations
11-
- SQLite (dev) / PostgreSQL (prod) with SQLAlchemy ORM
12-
- Alembic database migrations
13-
- AI assistant config with AES-256-GCM encrypted API keys (PBKDF2 + per-ciphertext random salt)
14-
- Bedrock Converse proxy via boto3 (SigV4 or Bedrock API-key auth, JSON response)
15-
16-
## Tech Stack
17-
18-
| Component | Technology |
19-
| ---------- | -------------- |
20-
| Language | Python 3.13+ |
21-
| Framework | FastAPI |
22-
| ORM | SQLAlchemy 2.0 |
23-
| Database | SQLite (dev) / PostgreSQL (prod) |
24-
| Migrations | Alembic |
25-
| Testing | pytest |
26-
| Linting | Ruff |
27-
| Type Check | mypy |
28-
| Packaging | uv |
1+
# Ledger Sync Backend
2+
3+
FastAPI backend for OAuth authentication, JSON transaction ingestion, reconciliation, financial analytics, preferences, planning data, external rate proxies, and the AI assistant.
4+
5+
## Stack
6+
7+
| Area | Technology |
8+
| --- | --- |
9+
| Runtime | Python 3.13+ |
10+
| API | FastAPI |
11+
| ORM | SQLAlchemy 2 |
12+
| Validation | Pydantic 2 |
13+
| Migrations | Alembic |
14+
| Local database | SQLite |
15+
| Production database | Neon PostgreSQL 17 |
16+
| Serverless adapter | Mangum on Vercel |
17+
| Tests | pytest |
18+
| Quality | Ruff and mypy |
19+
| Packaging | uv |
2920

3021
## Quick Start
3122

3223
```bash
33-
# Install dependencies (including dev tools)
3424
uv sync --group dev
35-
36-
# Initialize database
3725
uv run alembic upgrade head
38-
39-
# Start development server
4026
uv run uvicorn ledger_sync.api.main:app --reload --port 8000
4127
```
4228

43-
Backend available at http://localhost:8000
44-
45-
## Project Structure
46-
29+
Local endpoints:
30+
31+
- API: `http://localhost:8000`
32+
- Swagger UI: `http://localhost:8000/docs`
33+
- ReDoc: `http://localhost:8000/redoc`
34+
- Health: `http://localhost:8000/health`
35+
- Database health: `http://localhost:8000/health/db`
36+
37+
## Responsibilities
38+
39+
- Google and GitHub OAuth with 10-minute HMAC-signed state tokens.
40+
- JWT access and refresh tokens with server-side `token_version` invalidation.
41+
- Authenticated JSON ingestion at `/api/upload`.
42+
- User-scoped transaction reconciliation, tags, saved views, and categorization rules.
43+
- On-demand calculations plus precomputed analytics rollups.
44+
- Preferences, account classifications, budgets, goals, recurring items, and anomaly review.
45+
- Exchange-rate, instrument-rate, and stock-price proxies.
46+
- Fifteen read-only AI tools and AI usage accounting.
47+
- Bedrock Converse proxy for server-side Bedrock authentication.
48+
49+
## Web Import Contract
50+
51+
The browser parses Excel and CSV files. The backend receives:
52+
53+
```json
54+
{
55+
"file_name": "statement.xlsx",
56+
"file_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
57+
"force": false,
58+
"rows": [
59+
{
60+
"date": "2026-07-01",
61+
"amount": 85000,
62+
"currency": "INR",
63+
"type": "Income",
64+
"account": "HDFC Bank",
65+
"category": "Salary",
66+
"subcategory": "Monthly",
67+
"note": "July salary"
68+
}
69+
]
70+
}
4771
```
48-
backend/
49-
├── src/ledger_sync/
50-
│ ├── api/ # FastAPI routers (one file per resource)
51-
│ │ ├── main.py # Application entry point
52-
│ │ ├── auth.py # Token refresh, logout, profile
53-
│ │ ├── oauth.py # Google/GitHub OAuth login
54-
│ │ ├── analytics.py # On-the-fly analytics
55-
│ │ ├── analytics_v2.py # Pre-aggregated analytics
56-
│ │ ├── calculations.py # Financial calculation endpoints
57-
│ │ ├── preferences.py # User preferences (incl. AI config)
58-
│ │ ├── ai_chat.py # Bedrock Converse proxy
59-
│ │ ├── account_classifications.py
60-
│ │ ├── exchange_rates.py, stock_price.py
61-
│ │ └── meta.py, reports.py, transactions.py, upload.py
62-
│ ├── core/ # Business logic
63-
│ │ ├── reconciler.py # Transaction reconciliation
64-
│ │ ├── calculator.py # Financial calculations
65-
│ │ ├── analytics_engine.py # Heavy analytics computation
66-
│ │ ├── _analytics_helpers.py # Module-level helpers for analytics_engine
67-
│ │ ├── encryption.py # AES-256-GCM for API keys
68-
│ │ ├── sync_engine.py # Upload orchestration
69-
│ │ ├── query_helpers.py # Shared SQL aggregation helpers
70-
│ │ ├── time_filter.py, insights.py, report_generator.py
71-
│ │ └── auth/ # JWT token creation/verification
72-
│ ├── db/ # Database layer
73-
│ │ ├── models.py # 21-line facade that re-exports from _models/
74-
│ │ ├── _models/ # Split by bounded context
75-
│ │ │ ├── __init__.py, _constants.py, enums.py
76-
│ │ │ ├── user.py, transactions.py
77-
│ │ │ └── investments.py, analytics.py, planning.py
78-
│ │ ├── session.py # Database session
79-
│ │ ├── base.py
80-
│ │ └── migrations/versions/ # Alembic migrations
81-
│ ├── schemas/ # Pydantic request/response models
82-
│ ├── services/ # Cross-cutting services
83-
│ ├── ingest/ # Data ingestion (CLI path only)
84-
│ │ ├── excel_loader.py, csv_loader.py
85-
│ │ ├── normalizer.py, validator.py, hash_id.py
86-
│ ├── config/ # Configuration
87-
│ │ └── settings.py
88-
│ └── utils/ # Utilities
89-
├── tests/ # Test suite
90-
│ ├── unit/ # Unit tests
91-
│ └── integration/ # Integration tests
92-
└── pyproject.toml # Dependencies (uv)
93-
```
94-
95-
## API Endpoints
96-
97-
### Upload
9872

99-
| Method | Endpoint | Description |
100-
| ------ | ------------- | ----------------- |
101-
| POST | `/api/upload` | Upload Excel file |
102-
103-
**Response includes:**
104-
105-
- `processed` - Total rows processed
106-
- `inserted` - New transactions
107-
- `updated` - Modified transactions
108-
- `deleted` - Soft-deleted transactions
109-
- `unchanged` - Skipped (no changes)
110-
111-
### Analytics
73+
Requirements:
74+
75+
- JWT bearer authentication.
76+
- Exactly 64 hexadecimal characters in `file_hash`.
77+
- Between 1 and 100,000 rows.
78+
- Non-negative amounts and non-empty account and category values.
79+
- `force=true` only when intentionally reprocessing an already imported file.
80+
81+
The CLI still supports direct Excel and CSV loading through `SyncEngine.import_file()`.
82+
83+
## Source Layout
84+
85+
```text
86+
src/ledger_sync/
87+
api/ FastAPI routers and dependencies
88+
analytics_v2_impl/ Split analytics v2 routers
89+
ai_tools_impl/ AI tool registry and implementations
90+
core/ Business rules and calculations
91+
analytics/ Analytics engine mixins and rollup builders
92+
auth/ JWT helpers
93+
db/
94+
_models/ SQLAlchemy models by domain
95+
migrations/ Alembic environment and revisions
96+
models.py Public model facade
97+
session.py Engine and session configuration
98+
ingest/ CLI loaders, normalization, validation, hashing
99+
schemas/ Pydantic request and response models
100+
services/ Cross-cutting services
101+
config/ Runtime settings
102+
utils/ Logging and helpers
103+
```
112104

113-
| Method | Endpoint | Description |
114-
| ------ | ------------------------- | -------------------------- |
115-
| GET | `/api/analytics/overview` | Financial overview |
116-
| GET | `/api/analytics/kpis` | Key performance indicators |
117-
| GET | `/api/analytics/trends` | Financial trends |
105+
`core/analytics_engine.py` is a compatibility facade. The active analytics implementation lives under `core/analytics/`.
118106

119-
### Calculations
107+
## Security
120108

121-
- `GET /api/calculations/totals` - Income/expense totals
122-
- `GET /api/calculations/monthly-aggregation` - Monthly data
123-
- `GET /api/calculations/category-breakdown` - Category analysis
124-
- `GET /api/calculations/insights` - Financial insights
109+
- All financial queries and mutations are scoped by `user_id`.
110+
- OAuth callbacks require a valid, unexpired signed state.
111+
- Logout increments `token_version`, invalidating outstanding access and refresh tokens.
112+
- API responses include security headers and authenticated data uses `Cache-Control: no-store`.
113+
- CORS uses an explicit allowlist.
114+
- Upload is limited to 10 requests per user per minute and 50 per IP per minute.
115+
- Bedrock chat is limited to 30 requests per user per minute and 60 per IP per minute.
116+
- Current BYOK ciphertexts use AES-256-GCM with HKDF-SHA256 and `LEDGER_SYNC_ENCRYPTION_KEY`.
117+
- Legacy PBKDF2 ciphertexts are read-only compatibility data and are upgraded when revealed.
125118

126-
## Development
119+
## Configuration
127120

128-
### Running Tests
121+
Environment variables use their full `LEDGER_SYNC_` names.
122+
123+
| Variable | Default | Purpose |
124+
| --- | --- | --- |
125+
| `LEDGER_SYNC_ENVIRONMENT` | `development` | Runtime mode |
126+
| `LEDGER_SYNC_DATABASE_URL` | `sqlite:///./ledger_sync.db` | SQLAlchemy database URL |
127+
| `LEDGER_SYNC_DATABASE_ECHO` | `false` | SQL logging |
128+
| `LEDGER_SYNC_LOG_LEVEL` | `INFO` | Application log level |
129+
| `LEDGER_SYNC_FRONTEND_URL` | `http://localhost:5173` | OAuth callback base |
130+
| `LEDGER_SYNC_CORS_ORIGINS` | Local origins | JSON allowlist |
131+
| `LEDGER_SYNC_JWT_SECRET_KEY` | Generated in development | JWT and OAuth state signing |
132+
| `LEDGER_SYNC_ENCRYPTION_KEY` | JWT key fallback | Dedicated BYOK encryption key |
133+
| `LEDGER_SYNC_GOOGLE_CLIENT_ID` | Empty | Enable Google OAuth |
134+
| `LEDGER_SYNC_GOOGLE_CLIENT_SECRET` | Empty | Google OAuth secret |
135+
| `LEDGER_SYNC_GITHUB_CLIENT_ID` | Empty | Enable GitHub OAuth |
136+
| `LEDGER_SYNC_GITHUB_CLIENT_SECRET` | Empty | GitHub OAuth secret |
137+
| `LEDGER_SYNC_BEDROCK_API_KEY` | Empty | App-mode Bedrock credential |
138+
| `LEDGER_SYNC_DB_POOL_SIZE` | `5` | PostgreSQL pool size |
139+
| `LEDGER_SYNC_DB_MAX_OVERFLOW` | `3` | PostgreSQL overflow connections |
140+
141+
See [.env.example](../.env.example) for the complete template.
142+
143+
## Quality Checks
129144

130145
```bash
131-
# Run all tests
146+
uv run ruff check src/ tests/
147+
uv run ruff format --check src/ tests/
148+
uv run mypy src/
132149
uv run pytest tests/ -v
133-
134-
# Run with coverage
135-
uv run pytest --cov=ledger_sync tests/
136-
137-
# Run specific test file
138-
uv run pytest tests/unit/test_hash_id.py -v
139150
```
140151

141-
### Linting & Type Checking
142-
143-
```bash
144-
# Lint
145-
uv run ruff check .
146-
147-
# Auto-fix lint issues
148-
uv run ruff check --fix .
149-
150-
# Type check
151-
uv run mypy src/
152-
```
152+
The current backend suite contains 328 tests.
153153

154-
### Database Migrations
154+
## Migrations
155155

156156
```bash
157-
# Apply migrations
157+
uv run alembic revision --autogenerate -m "describe change"
158158
uv run alembic upgrade head
159-
160-
# Create new migration
161-
uv run alembic revision --autogenerate -m "description"
162-
163-
# Rollback one migration
164-
uv run alembic downgrade -1
165159
```
166160

167-
## API Documentation
168-
169-
- Interactive docs: http://localhost:8000/docs
170-
- ReDoc: http://localhost:8000/redoc
171-
172-
## Configuration
173-
174-
Environment variables (prefix: `LEDGER_SYNC_`):
161+
Read [MIGRATION_NOTES.md](src/ledger_sync/db/migrations/MIGRATION_NOTES.md) before attempting a downgrade. Several recent revisions intentionally have no schema-reversing downgrade.
175162

176-
| Variable | Default | Description |
177-
|----------|---------|-------------|
178-
| `DATABASE_URL` | `sqlite:///./ledger_sync.db` | Database connection string |
179-
| `LOG_LEVEL` | `INFO` | Python log level |
180-
| `GOOGLE_CLIENT_ID` | - | Google OAuth client ID |
181-
| `GOOGLE_CLIENT_SECRET` | - | Google OAuth client secret |
182-
| `GITHUB_CLIENT_ID` | - | GitHub OAuth client ID |
183-
| `GITHUB_CLIENT_SECRET` | - | GitHub OAuth client secret |
184-
| `FRONTEND_URL` | `http://localhost:5173` | Frontend URL for OAuth redirects |
185-
| `JWT_SECRET_KEY` | dev default | JWT signing secret (required in production) |
186-
| `ENVIRONMENT` | `development` | `development` or `production` |
163+
## Deployment
187164

188-
## License
165+
Production runs on Vercel through `api/index.py` and `vercel.json`, with Neon PostgreSQL behind the PgBouncer pooler. Database migrations are applied by `.github/workflows/migrate.yml`.
189166

190-
MIT
167+
See [docs/DEPLOYMENT.md](../docs/DEPLOYMENT.md) and [docs/API.md](../docs/API.md).

0 commit comments

Comments
 (0)