- 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>
74 lines
4.5 KiB
Markdown
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
|