Files
beanflows/web/CHANGELOG.md
Deeman 4b7d4d5a74 Update from Copier template v0.4.0
- Accept RBAC system: user_roles table, role_required decorator, grant_role/revoke_role/ensure_admin_role functions
- Accept improved billing architecture: billing_customers table separation, provider-agnostic naming
- Accept enhanced user loading with subscription/roles eager loading in app.py
- Accept improved email templates with branded styling
- Accept new infrastructure: migration tracking, transaction logging, A/B testing
- Accept template improvements: Resend SDK, Tailwind build stage, UMAMI analytics config
- Keep beanflows-specific configs: BASE_URL 5001, coffee PLAN_FEATURES/PLAN_LIMITS
- Keep beanflows analytics integration and DuckDB health check
- Add new test files and utility scripts from template

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
2026-02-19 22:22:13 +01:00

74 lines
4.5 KiB
Markdown

# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Changed
- **Role-based access control**: `user_roles` table with `role_required()` decorator replaces password-based admin auth
- **Admin is a real user**: admins authenticate via magic links; `ADMIN_EMAILS` env var auto-grants admin role on login
- **Separated billing entities**: `billing_customers` table holds payment provider identity; `subscriptions` table holds only subscription state
- **Multiple subscriptions per user**: dropped UNIQUE constraint on `subscriptions.user_id`; `upsert_subscription` finds by `provider_subscription_id`
### Added
- Simple A/B testing with `@ab_test` decorator and optional Umami `data-tag` integration (`UMAMI_SCRIPT_URL` / `UMAMI_WEBSITE_ID` env vars)
- `user_roles` table and `grant_role()` / `revoke_role()` / `ensure_admin_role()` functions
- `billing_customers` table and `upsert_billing_customer()` / `get_billing_customer()` functions
- `role_required(*roles)` decorator in auth
- `is_admin` template context variable
- Migration `0001_roles_and_billing_customers.py` for existing databases
### Removed
- `ADMIN_PASSWORD` env var and password-based admin login
- `provider_customer_id` column from `subscriptions` table
- `admin/templates/admin/login.html`
### Changed
- **Provider-agnostic schema**: generic `provider_customer_id` / `provider_subscription_id` columns replace provider-prefixed names (`stripe_customer_id`, `paddle_customer_id`, `lemonsqueezy_customer_id`) — eliminates all Jinja conditionals from schema, SQL helpers, and route code
- **Consolidated `subscription_required` decorator**: single implementation in `auth/routes.py` supporting both plan and status checks, reads from eager-loaded `g.subscription` (zero extra queries)
- **Eager-loaded `g.subscription`**: `load_user` in `app.py` now fetches user + subscription in a single JOIN; available in all routes and templates via `g.subscription`
### Added
- `transactions` table for recording payment/refund events with idempotent `record_transaction()` helper
- Billing event hook system: `on_billing_event()` decorator and `_fire_hooks()` for domain code to react to subscription changes; errors are logged and never cause webhook 500s
### Removed
- Duplicate `subscription_required` decorator from `billing/routes.py` (consolidated in `auth/routes.py`)
- `get_user_with_subscription()` from `auth/routes.py` (replaced by eager-loaded `g.subscription`)
### Changed
- **Email SDK migration**: replaced raw httpx calls with official `resend` SDK in `core.py`
- Added `from_addr` parameter to `send_email()` for multi-address support
- Added `EMAIL_ADDRESSES` dict for named sender addresses (transactional, etc.)
- **Paddle SDK migration**: replaced raw httpx calls with official `paddle-python-sdk` in `billing/routes.py`
- Checkout, manage, cancel routes now use typed SDK methods (`PaddleClient`, `CreateTransaction`)
- Webhook verification uses SDK's `Verifier` instead of hand-rolled HMAC
- Added `PADDLE_ENVIRONMENT` config for sandbox/production toggling
- Added `_paddle_client()` helper factory
- **Dependencies**: `resend` replaces `httpx` for email; `paddle-python-sdk` replaces `httpx` for Paddle billing; `httpx` now only included for LemonSqueezy projects
- Worker `send_email` task handler now passes through `from_addr`
### Added
- `scripts/setup_paddle.py` — CLI script to create Paddle products/prices programmatically (Paddle projects only)
### Changed
- **Pico CSS → Tailwind CSS v4** — full design system migration across all templates
- Standalone Tailwind CLI binary (no Node.js) with `make css-build` / `make css-watch`
- Brand theme with component classes (`.btn`, `.card`, `.form-input`, `.table`, `.badge`, `.flash`, etc.)
- Self-hosted Commit Mono font for monospace data display
- Docker multi-stage build: CSS compiled in dedicated stage before Python build
### Removed
- Pico CSS CDN dependency
- `custom.css` (replaced by Tailwind `input.css` with `@layer components`)
- JetBrains Mono font (replaced by self-hosted Commit Mono)
### Fixed
- Admin template collision: namespaced admin templates under `admin/` subdirectory to prevent Quart's template loader from resolving auth's `login.html` or dashboard's `index.html` instead of admin's
- Admin user detail: `stripe_customer_id` hardcoded regardless of payment provider — now uses provider-aware Copier conditional (Stripe/Paddle/LemonSqueezy)
### Added
- Initial project scaffolded from quart_saas_boilerplate