feat(auth): enable local user signup without SMTP, add admin user creation
- Remove SMTP hard-requirement from /api/auth/signup: when SMTP is not
configured accounts are activated immediately (no email verification).
When SMTP is configured the existing email-verification flow is kept.
- Inject allow_signup into global template context via app/views/base.py
- Add data-allow-signup attribute to base.html body tag
- Update common.js _renderLoggedOutAuth to show Sign Up (→ /signup) when
signup is enabled, otherwise Get Started (→ /pricing)
- Add admin API endpoints before the /{user_id:path} catch-all:
GET /api/admin/users/local – list all local accounts
POST /api/admin/users/local – admin-create local account (active immediately)
DELETE /api/admin/users/local/{id} – delete local account + profile
- Add LocalUserCreate / LocalUserResponse Pydantic schemas
- Update admin_users.html with Local User Accounts section and modals
- Update .env.demo to document ALLOW_LOCAL_SIGNUP
- Update docs/BillingSetup.md: SMTP is optional, document both flows
- Update tests: test_signup_smtp_not_configured now asserts 201 + immediate
activation; add 7 new integration tests for admin local user endpoints
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -304,11 +304,14 @@ function _makeMenuLink(href, iconClass, label, extraClasses = '') {
|
||||
|
||||
/**
|
||||
* Render the login / get-started buttons for unauthenticated visitors.
|
||||
* Reads the data-multi-user attribute that the server injects on <body> to
|
||||
* decide whether to show a prominent "Get Started" CTA alongside the login link.
|
||||
* Reads the data-multi-user and data-allow-signup attributes that the server
|
||||
* injects on <body> to decide whether to show a prominent "Get Started" CTA
|
||||
* alongside the login link, and whether it should link to /signup or /pricing.
|
||||
*/
|
||||
function _renderLoggedOutAuth(authSection, mobileAuthSection) {
|
||||
const multiUser = document.body.getAttribute('data-multi-user') === 'true';
|
||||
const allowSignup = document.body.getAttribute('data-allow-signup') === 'true';
|
||||
const startHref = allowSignup ? '/signup' : '/pricing';
|
||||
|
||||
if (authSection) {
|
||||
authSection.textContent = '';
|
||||
@@ -324,10 +327,10 @@ function _renderLoggedOutAuth(authSection, mobileAuthSection) {
|
||||
|
||||
if (multiUser) {
|
||||
const startLink = document.createElement('a');
|
||||
startLink.href = '/pricing';
|
||||
startLink.href = startHref;
|
||||
startLink.className =
|
||||
'px-3 py-1.5 rounded-md text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500';
|
||||
startLink.textContent = 'Get Started';
|
||||
startLink.textContent = allowSignup ? 'Sign Up' : 'Get Started';
|
||||
row.appendChild(startLink);
|
||||
}
|
||||
|
||||
@@ -350,14 +353,14 @@ function _renderLoggedOutAuth(authSection, mobileAuthSection) {
|
||||
|
||||
if (multiUser) {
|
||||
const startLink = document.createElement('a');
|
||||
startLink.href = '/pricing';
|
||||
startLink.href = startHref;
|
||||
startLink.className =
|
||||
'block px-3 py-3 rounded-md text-base font-medium text-white bg-blue-600 hover:text-white hover:bg-blue-700 mt-1';
|
||||
const startIcon = document.createElement('i');
|
||||
startIcon.className = 'fas fa-arrow-right mr-2';
|
||||
startIcon.setAttribute('aria-hidden', 'true');
|
||||
startLink.appendChild(startIcon);
|
||||
startLink.appendChild(document.createTextNode('Get Started'));
|
||||
startLink.appendChild(document.createTextNode(allowSignup ? 'Sign Up' : 'Get Started'));
|
||||
mobileAuthSection.appendChild(startLink);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user