- 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>
4.5 KiB
4.5 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[Unreleased]
Changed
- Role-based access control:
user_rolestable withrole_required()decorator replaces password-based admin auth - Admin is a real user: admins authenticate via magic links;
ADMIN_EMAILSenv var auto-grants admin role on login - Separated billing entities:
billing_customerstable holds payment provider identity;subscriptionstable holds only subscription state - Multiple subscriptions per user: dropped UNIQUE constraint on
subscriptions.user_id;upsert_subscriptionfinds byprovider_subscription_id
Added
- Simple A/B testing with
@ab_testdecorator and optional Umamidata-tagintegration (UMAMI_SCRIPT_URL/UMAMI_WEBSITE_IDenv vars) user_rolestable andgrant_role()/revoke_role()/ensure_admin_role()functionsbilling_customerstable andupsert_billing_customer()/get_billing_customer()functionsrole_required(*roles)decorator in authis_admintemplate context variable- Migration
0001_roles_and_billing_customers.pyfor existing databases
Removed
ADMIN_PASSWORDenv var and password-based admin loginprovider_customer_idcolumn fromsubscriptionstableadmin/templates/admin/login.html
Changed
- Provider-agnostic schema: generic
provider_customer_id/provider_subscription_idcolumns 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_requireddecorator: single implementation inauth/routes.pysupporting both plan and status checks, reads from eager-loadedg.subscription(zero extra queries) - Eager-loaded
g.subscription:load_userinapp.pynow fetches user + subscription in a single JOIN; available in all routes and templates viag.subscription
Added
transactionstable for recording payment/refund events with idempotentrecord_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_requireddecorator frombilling/routes.py(consolidated inauth/routes.py) get_user_with_subscription()fromauth/routes.py(replaced by eager-loadedg.subscription)
Changed
- Email SDK migration: replaced raw httpx calls with official
resendSDK incore.py- Added
from_addrparameter tosend_email()for multi-address support - Added
EMAIL_ADDRESSESdict for named sender addresses (transactional, etc.)
- Added
- Paddle SDK migration: replaced raw httpx calls with official
paddle-python-sdkinbilling/routes.py- Checkout, manage, cancel routes now use typed SDK methods (
PaddleClient,CreateTransaction) - Webhook verification uses SDK's
Verifierinstead of hand-rolled HMAC - Added
PADDLE_ENVIRONMENTconfig for sandbox/production toggling - Added
_paddle_client()helper factory
- Checkout, manage, cancel routes now use typed SDK methods (
- Dependencies:
resendreplaceshttpxfor email;paddle-python-sdkreplaceshttpxfor Paddle billing;httpxnow only included for LemonSqueezy projects - Worker
send_emailtask handler now passes throughfrom_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
- Standalone Tailwind CLI binary (no Node.js) with
Removed
- Pico CSS CDN dependency
custom.css(replaced by Tailwindinput.csswith@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'slogin.htmlor dashboard'sindex.htmlinstead of admin's - Admin user detail:
stripe_customer_idhardcoded regardless of payment provider — now uses provider-aware Copier conditional (Stripe/Paddle/LemonSqueezy)
Added
- Initial project scaffolded from quart_saas_boilerplate