regstack 0.4.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {regstack-0.4.0 → regstack-0.5.0}/CLAUDE.md +14 -0
- {regstack-0.4.0 → regstack-0.5.0}/PKG-INFO +5 -5
- {regstack-0.4.0 → regstack-0.5.0}/README.md +4 -4
- {regstack-0.4.0 → regstack-0.5.0}/docs/changelog.md +27 -0
- regstack-0.5.0/docs/cli.md +187 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/configuration.md +8 -7
- {regstack-0.4.0 → regstack-0.5.0}/docs/embedding.md +5 -5
- {regstack-0.4.0 → regstack-0.5.0}/docs/index.md +53 -2
- {regstack-0.4.0 → regstack-0.5.0}/docs/quickstart.md +1 -1
- {regstack-0.4.0 → regstack-0.5.0}/docs/theming.md +46 -1
- {regstack-0.4.0 → regstack-0.5.0}/pyproject.toml +1 -1
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/__main__.py +15 -0
- regstack-0.5.0/src/regstack/version.py +1 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/__init__.py +8 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/cli.py +181 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/routes.py +278 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/server.py +87 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/static/designer.css +348 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/static/designer.js +272 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/templates/designer.html +89 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/validators.py +145 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/window.py +63 -0
- regstack-0.5.0/src/regstack/wizard/theme_designer/writer.py +196 -0
- regstack-0.5.0/tests/e2e/conftest.py +121 -0
- regstack-0.5.0/tests/e2e/test_theme_designer.py +87 -0
- regstack-0.5.0/tests/unit/test_cli_migrate.py +151 -0
- regstack-0.5.0/tests/unit/test_theme_designer_cli.py +190 -0
- regstack-0.5.0/tests/unit/test_theme_designer_routes.py +179 -0
- regstack-0.5.0/tests/unit/test_theme_designer_validators.py +154 -0
- regstack-0.5.0/tests/unit/test_theme_designer_writer.py +156 -0
- regstack-0.5.0/tests/unit/test_wizard_oauth_cli.py +230 -0
- {regstack-0.4.0 → regstack-0.5.0}/uv.lock +1 -1
- regstack-0.4.0/docs/cli.md +0 -87
- regstack-0.4.0/src/regstack/version.py +0 -1
- regstack-0.4.0/tests/e2e/conftest.py +0 -82
- regstack-0.4.0/tests/unit/test_wizard_oauth_cli.py +0 -93
- {regstack-0.4.0 → regstack-0.5.0}/.github/workflows/publish.yml +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/.github/workflows/test.yml +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/.gitignore +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/.python-version +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/.readthedocs.yaml +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/CHANGELOG.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/LICENSE +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/NOTICE +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/SECURITY.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/_static/.gitkeep +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/_templates/.gitkeep +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/api.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/architecture.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/conf.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/oauth.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/security-reports/README.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/docs/security.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/_common/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/_common/app.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/README.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/branding/theme.css +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/main.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/mongo/regstack.toml +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/postgres/README.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/postgres/main.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/postgres/regstack.toml +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/sqlite/README.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/sqlite/main.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/examples/sqlite/regstack.toml +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/regstack.toml.example +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/scripts/security-review-prompt.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/app.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/clock.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/dependencies.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/jwt.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/lockout.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/mfa.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/password.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/auth/tokens.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/base.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/factory.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/backend.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/client.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/indexes.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/blacklist_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/login_attempt_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/mfa_code_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/oauth_identity_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/oauth_state_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/pending_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/mongo/repositories/user_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/protocols.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/backend.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/env.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/script.py.mako +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/versions/0001_initial_schema.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/migrations/versions/0002_oauth.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/blacklist_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/login_attempt_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/mfa_code_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/oauth_identity_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/oauth_state_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/pending_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/repositories/user_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/schema.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/backends/sql/types.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/_runtime.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/admin.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/doctor.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/init.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/cli/migrate.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/loader.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/schema.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/config/secrets.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/base.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/composer.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/console.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/factory.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/ses.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/smtp.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/email_change.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/email_change.subject.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/email_change.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/password_reset.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/password_reset.subject.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/password_reset.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/sms_login_mfa.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/sms_phone_setup.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/verification.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/verification.subject.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/email/templates/verification.txt +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/hooks/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/hooks/events.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/_objectid.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/login_attempt.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/mfa_code.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/oauth_identity.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/oauth_state.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/pending_registration.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/models/user.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/base.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/errors.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/providers/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/providers/google.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/oauth/registry.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/_schemas.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/account.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/admin.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/login.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/logout.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/oauth.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/password.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/phone.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/register.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/routers/verify.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/base.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/factory.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/null.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/sns.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/sms/twilio.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/pages.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/static/css/core.css +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/static/css/theme.css +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/static/js/regstack.js +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/email_change_confirm.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/forgot.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/login.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/me.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/mfa_confirm.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/oauth_complete.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/register.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/reset.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/auth/verify.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/ui/templates/base.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/cli.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/routes.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/server.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/static/wizard.css +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/static/wizard.js +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/templates/wizard.html +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/validators.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/window.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/src/regstack/wizard/oauth_google/writer.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tasks/oauth-design.md +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tasks.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/_fake_google/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/_fake_google/provider.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/conftest.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/e2e/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/e2e/test_wizard_oauth_flow.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_account_management.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_admin_router.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_happy_path.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_indexes.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_login_lockout.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_mfa.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_oauth_google_router.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_oauth_repos.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_oauth_ui.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_password_reset.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_sql_migrations.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_ui_router.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/integration/test_verification.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/__init__.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_base_install_imports.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_cli.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_cli_doctor.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_cli_init.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_config_loader.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_jwt.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_lockout.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_mail_composer.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_mfa_code_repo.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_oauth_google.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_password.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_ses_backend.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_sms.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_smtp_backend.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_ui_env.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_wizard_oauth_routes.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_wizard_oauth_validators.py +0 -0
- {regstack-0.4.0 → regstack-0.5.0}/tests/unit/test_wizard_oauth_writer.py +0 -0
|
@@ -77,6 +77,20 @@ The full plan, including milestone scope and deferred items, lives at
|
|
|
77
77
|
four layers: validators (unit), writer (golden-file), routes
|
|
78
78
|
(TestClient), full SPA flow (Playwright e2e). Run e2e with
|
|
79
79
|
`inv test-e2e`; `inv test-all` chains it after the backend matrix.
|
|
80
|
+
- **Theme designer — done.** `regstack theme design` opens a sibling
|
|
81
|
+
pywebview window with controls for every `--rs-*` variable and a
|
|
82
|
+
real-time preview of the bundled SSR widgets (renders the same
|
|
83
|
+
`.rs-*` classes the SSR pages render, so what you see is what
|
|
84
|
+
you'll ship). Save writes `regstack-theme.css`; the designer
|
|
85
|
+
round-trips values back into the form on next launch so iteration
|
|
86
|
+
is non-destructive. Lives in `src/regstack/wizard/theme_designer/`,
|
|
87
|
+
registered via a `_LazyThemeGroup` mirroring the OAuth pattern.
|
|
88
|
+
Same four-layer test stack (validators / writer / routes /
|
|
89
|
+
Playwright e2e). The shared scaffold (FastAPI app, uvicorn launcher,
|
|
90
|
+
pywebview shim, e2e fixture pattern) is duplicated rather than
|
|
91
|
+
abstracted into a base class — the two tools have meaningfully
|
|
92
|
+
different inputs and an early shared base would calcify the wrong
|
|
93
|
+
decisions. Refactor only when a third pywebview tool lands.
|
|
80
94
|
|
|
81
95
|
## Three kinds of single-use proof
|
|
82
96
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: regstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Embeddable user registration, login, and account management for FastAPI apps. SQLite / Postgres / MongoDB.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jdrumgoole/regstack
|
|
6
6
|
Project-URL: Repository, https://github.com/jdrumgoole/regstack
|
|
@@ -143,7 +143,7 @@ result everywhere is what regstack is for.
|
|
|
143
143
|
✔ Server-rendered HTML pages, theme with one CSS file
|
|
144
144
|
✔ Pluggable email (console / SMTP / Amazon SES) and SMS (Amazon SNS / Twilio)
|
|
145
145
|
✔ Argon2 password hashing, CSP-friendly templates
|
|
146
|
-
✔ Setup
|
|
146
|
+
✔ Setup wizards (live in their own pywebview windows): `regstack init` (project bootstrap), `regstack oauth setup` (guided Google OAuth client config), `regstack theme design` (live theme designer with preview), and `regstack doctor` (config validator)
|
|
147
147
|
✔ Three storage backends: SQLite, PostgreSQL, MongoDB — chosen by URL
|
|
148
148
|
```
|
|
149
149
|
|
|
@@ -186,7 +186,7 @@ register from the command line:
|
|
|
186
186
|
```bash
|
|
187
187
|
curl -X POST http://localhost:8000/api/auth/register \
|
|
188
188
|
-H 'content-type: application/json' \
|
|
189
|
-
-d '{"email":"alice@example.com","password":"
|
|
189
|
+
-d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
The bundled example serves themed SSR pages at `/account/*`, prints
|
|
@@ -251,8 +251,8 @@ The same docs are also browsable as Markdown in [`docs/`](https://github.com/jdr
|
|
|
251
251
|
Alpha. Single-file SQLite is the default and runs with no infrastructure;
|
|
252
252
|
PostgreSQL and MongoDB backends pass the same parametrized integration
|
|
253
253
|
suite. OAuth (Google) shipped in `v0.3.0`; the `regstack oauth setup`
|
|
254
|
-
guided wizard
|
|
255
|
-
See the
|
|
254
|
+
guided wizard in `v0.4.0`; the live `regstack theme design` tool in
|
|
255
|
+
`v0.5.0`. Latest tagged release: `v0.5.0`. See the
|
|
256
256
|
[changelog](https://regstack.readthedocs.io/en/latest/changelog.html)
|
|
257
257
|
for the per-release breakdown.
|
|
258
258
|
|
|
@@ -72,7 +72,7 @@ result everywhere is what regstack is for.
|
|
|
72
72
|
✔ Server-rendered HTML pages, theme with one CSS file
|
|
73
73
|
✔ Pluggable email (console / SMTP / Amazon SES) and SMS (Amazon SNS / Twilio)
|
|
74
74
|
✔ Argon2 password hashing, CSP-friendly templates
|
|
75
|
-
✔ Setup
|
|
75
|
+
✔ Setup wizards (live in their own pywebview windows): `regstack init` (project bootstrap), `regstack oauth setup` (guided Google OAuth client config), `regstack theme design` (live theme designer with preview), and `regstack doctor` (config validator)
|
|
76
76
|
✔ Three storage backends: SQLite, PostgreSQL, MongoDB — chosen by URL
|
|
77
77
|
```
|
|
78
78
|
|
|
@@ -115,7 +115,7 @@ register from the command line:
|
|
|
115
115
|
```bash
|
|
116
116
|
curl -X POST http://localhost:8000/api/auth/register \
|
|
117
117
|
-H 'content-type: application/json' \
|
|
118
|
-
-d '{"email":"alice@example.com","password":"
|
|
118
|
+
-d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
The bundled example serves themed SSR pages at `/account/*`, prints
|
|
@@ -180,8 +180,8 @@ The same docs are also browsable as Markdown in [`docs/`](https://github.com/jdr
|
|
|
180
180
|
Alpha. Single-file SQLite is the default and runs with no infrastructure;
|
|
181
181
|
PostgreSQL and MongoDB backends pass the same parametrized integration
|
|
182
182
|
suite. OAuth (Google) shipped in `v0.3.0`; the `regstack oauth setup`
|
|
183
|
-
guided wizard
|
|
184
|
-
See the
|
|
183
|
+
guided wizard in `v0.4.0`; the live `regstack theme design` tool in
|
|
184
|
+
`v0.5.0`. Latest tagged release: `v0.5.0`. See the
|
|
185
185
|
[changelog](https://regstack.readthedocs.io/en/latest/changelog.html)
|
|
186
186
|
for the per-release breakdown.
|
|
187
187
|
|
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Versions follow
|
|
4
4
|
[Semantic Versioning](https://semver.org/) once `1.0.0` ships.
|
|
5
5
|
|
|
6
|
+
## 0.5.0 — 2026-05-02
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Theme designer.** `regstack theme design` opens a native pywebview
|
|
11
|
+
window with controls for every `--rs-*` CSS custom property and a
|
|
12
|
+
real-time preview of the bundled SSR widgets (sign-in form, success
|
|
13
|
+
/ error banners, danger-zone button). Saving writes
|
|
14
|
+
`regstack-theme.css`; the designer round-trips values back into the
|
|
15
|
+
form on next launch so iteration is non-destructive. `--print-only`
|
|
16
|
+
mode takes repeatable `--var NAME=VALUE` pairs (with a `dark:`
|
|
17
|
+
prefix for dark-scheme overrides) and writes the file headlessly.
|
|
18
|
+
Lives in `regstack.wizard.theme_designer`; registered as a lazy
|
|
19
|
+
Click subgroup so `regstack init` / `doctor` don't pay the
|
|
20
|
+
pywebview/uvicorn import cost.
|
|
21
|
+
- "Why use regstack" pitch in `docs/index.md` updated to surface the
|
|
22
|
+
two pywebview tools (`oauth setup` + `theme design`) as a
|
|
23
|
+
distinguishing feature vs. fastapi-users / Auth0 / Keycloak.
|
|
24
|
+
|
|
25
|
+
### Docs
|
|
26
|
+
|
|
27
|
+
- New "About the examples" convention block at the top of
|
|
28
|
+
`docs/index.md`. Every URL, email, smtp host, and admin command
|
|
29
|
+
across the docs now extrapolates from the same fictional app at
|
|
30
|
+
`app.example.com` with `<username>` / `<password>` placeholders —
|
|
31
|
+
no more `user:pw@host/dbname` / `db.internal/myapp` mishmash.
|
|
32
|
+
|
|
6
33
|
## 0.4.0 — 2026-05-02
|
|
7
34
|
|
|
8
35
|
### Added
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# CLI reference
|
|
2
|
+
|
|
3
|
+
`regstack` is the entry point installed by the package. All sub-commands
|
|
4
|
+
share a config-loading model: programmatic kwargs > env vars >
|
|
5
|
+
`regstack.secrets.env` > `regstack.toml` > defaults. The `--config <path>`
|
|
6
|
+
flag overrides where the TOML file is found.
|
|
7
|
+
|
|
8
|
+
## `regstack init`
|
|
9
|
+
|
|
10
|
+
Interactive wizard that writes `regstack.toml` and `regstack.secrets.env`
|
|
11
|
+
in the current directory. Asks which backend to use (SQLite default →
|
|
12
|
+
Postgres → MongoDB), generates a 64-byte JWT secret, runs DNS sanity
|
|
13
|
+
checks if asked, never provisions infrastructure.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv run regstack init
|
|
17
|
+
uv run regstack init --target /etc/app --force
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
|
|
22
|
+
- `--target DIR` — directory to write the config files (default cwd).
|
|
23
|
+
- `--force` — overwrite without confirming.
|
|
24
|
+
|
|
25
|
+
Re-running the wizard prompts before overwriting unless `--force` is
|
|
26
|
+
passed; pre-existing answers aren't kept (the wizard is intentionally
|
|
27
|
+
stateless).
|
|
28
|
+
|
|
29
|
+
## `regstack oauth setup`
|
|
30
|
+
|
|
31
|
+
Opens a guided 12-step wizard in a native webview window that walks you
|
|
32
|
+
through registering a Google OAuth 2.0 client (project selection,
|
|
33
|
+
consent screen, redirect URI, credentials) and merges the result into
|
|
34
|
+
your existing `regstack.toml` and `regstack.secrets.env`. The merge is
|
|
35
|
+
**non-clobbering** — comments, unrelated tables (`[email]`, `[sms]`,
|
|
36
|
+
…), and unrelated top-level keys are preserved. Re-run any time to
|
|
37
|
+
rotate credentials or change the linking policy.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv run regstack oauth setup
|
|
41
|
+
uv run regstack oauth setup --target /etc/app
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
|
|
46
|
+
- `--target DIR` — directory containing (or to receive) `regstack.toml`
|
|
47
|
+
(default cwd).
|
|
48
|
+
- `--api-prefix PREFIX` — router prefix the host mounts regstack under
|
|
49
|
+
(default `/api/auth`). Used to compute the suggested redirect URI.
|
|
50
|
+
- `--port N` — pin the wizard server's TCP port (default: random free
|
|
51
|
+
port on `127.0.0.1`).
|
|
52
|
+
- `--print-only` — skip the GUI; print the TOML + secrets diff that
|
|
53
|
+
*would* be written to stdout, then exit. Useful for headless hosts
|
|
54
|
+
(CI, servers without a webview backend) and dry-run smoke tests.
|
|
55
|
+
Pair with `--client-id`, `--client-secret`, `--base-url`,
|
|
56
|
+
`--auto-link/--no-auto-link`, `--mfa/--no-mfa`.
|
|
57
|
+
|
|
58
|
+
The interactive mode requires a desktop environment with a webview
|
|
59
|
+
backend (WebKit on macOS, GTK / QtWebEngine on Linux, Edge WebView2 on
|
|
60
|
+
Windows). On a headless host it exits with a clear error pointing at
|
|
61
|
+
`--print-only`.
|
|
62
|
+
|
|
63
|
+
The wizard binds to `127.0.0.1` only and authenticates every API call
|
|
64
|
+
with a per-launch random token, so a hostile process on the same host
|
|
65
|
+
can't drive the write endpoint.
|
|
66
|
+
|
|
67
|
+
## `regstack theme design`
|
|
68
|
+
|
|
69
|
+
Opens a live designer for `regstack-theme.css` in a native pywebview
|
|
70
|
+
window. The left pane has controls for every `--rs-*` CSS custom
|
|
71
|
+
property; the right pane renders the bundled SSR widgets (sign-in
|
|
72
|
+
form, success / error messages, danger-zone button) with your changes
|
|
73
|
+
applied in real time. Click **Save** to write the file; **Reset to
|
|
74
|
+
defaults** to start over; **Copy CSS** to put the generated
|
|
75
|
+
stylesheet on the clipboard without writing.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv run regstack theme design
|
|
79
|
+
uv run regstack theme design --target /var/www/static
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
|
|
84
|
+
- `--target DIR` — directory to write `regstack-theme.css` into
|
|
85
|
+
(default cwd).
|
|
86
|
+
- `--filename NAME` — output filename
|
|
87
|
+
(default `regstack-theme.css`).
|
|
88
|
+
- `--port N` — pin the designer's TCP port (default: random free
|
|
89
|
+
port on `127.0.0.1`).
|
|
90
|
+
- `--print-only` — skip the GUI; write the file from `--var` pairs
|
|
91
|
+
and emit a JSON summary. For headless / CI use.
|
|
92
|
+
- `--var NAME=VALUE` — repeatable. Used with `--print-only`. Prefix
|
|
93
|
+
with `dark:` to set the dark-scheme value, e.g.
|
|
94
|
+
`--var dark:--rs-accent=#2dd4bf`.
|
|
95
|
+
|
|
96
|
+
Re-running the designer reloads the previous values out of the file,
|
|
97
|
+
so iterating on a theme is non-destructive. Only the `:root` and
|
|
98
|
+
`@media (prefers-color-scheme: dark)` blocks are managed by the
|
|
99
|
+
designer — anything else in the file is left alone.
|
|
100
|
+
|
|
101
|
+
Same security shape as `regstack oauth setup`: binds `127.0.0.1`
|
|
102
|
+
only, every API call requires a per-launch random token.
|
|
103
|
+
|
|
104
|
+
## `regstack create-admin`
|
|
105
|
+
|
|
106
|
+
Create or promote a superuser. Idempotent.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
uv run regstack create-admin --email admin@app.example.com
|
|
110
|
+
uv run regstack create-admin --email admin@app.example.com --password 'long-strong-password'
|
|
111
|
+
uv run regstack create-admin --email admin@app.example.com --config /etc/app/regstack.toml
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Options:
|
|
115
|
+
|
|
116
|
+
- `--email EMAIL` *(required)*.
|
|
117
|
+
- `--password PW` — if omitted, prompts (with confirmation).
|
|
118
|
+
- `--config PATH` — TOML file to load (default: env or cwd).
|
|
119
|
+
|
|
120
|
+
If the user already exists, the command sets `is_superuser=True` and
|
|
121
|
+
keeps the existing password. If they don't exist, it creates the user
|
|
122
|
+
with `is_active=True`, `is_verified=True`, `is_superuser=True` and the
|
|
123
|
+
provided password.
|
|
124
|
+
|
|
125
|
+
## `regstack migrate`
|
|
126
|
+
|
|
127
|
+
Runs the bundled Alembic migrations against the configured
|
|
128
|
+
`database_url`. Idempotent — re-running on a DB already at the target
|
|
129
|
+
revision is a no-op. Use this on SQL backends (SQLite / PostgreSQL)
|
|
130
|
+
to roll the schema forward to a new regstack release.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
uv run regstack migrate
|
|
134
|
+
uv run regstack migrate --target head
|
|
135
|
+
uv run regstack migrate --config /etc/app/regstack.toml --target 0001
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Options:
|
|
139
|
+
|
|
140
|
+
- `--config PATH` — TOML file to load (default: cwd / `$REGSTACK_CONFIG`).
|
|
141
|
+
- `--target REV` — revision to upgrade to (default `head`). Accepts
|
|
142
|
+
any Alembic revision spec: a revision id (`0001`), a relative step
|
|
143
|
+
(`+1`), or `head`.
|
|
144
|
+
|
|
145
|
+
Mongo backends are silently skipped: TTL indexes are installed by
|
|
146
|
+
`RegStack.install_schema()` on every app start, so there's no separate
|
|
147
|
+
migration story to run. Output prints the before / after revision and
|
|
148
|
+
exits non-zero if Alembic raises.
|
|
149
|
+
|
|
150
|
+
## `regstack doctor`
|
|
151
|
+
|
|
152
|
+
Runs read-only validation against the loaded config and reports each
|
|
153
|
+
check on a green/red line. Exit code is the number of failed checks —
|
|
154
|
+
suitable for use in container health checks.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
uv run regstack doctor
|
|
158
|
+
uv run regstack doctor --config /etc/app/regstack.toml
|
|
159
|
+
uv run regstack doctor --check-dns
|
|
160
|
+
uv run regstack doctor --send-test-email alice@app.example.com
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Options:
|
|
164
|
+
|
|
165
|
+
- `--config PATH` — TOML file to load.
|
|
166
|
+
- `--check-dns` — run SPF / DMARC / MX `dig`s on the sender domain.
|
|
167
|
+
Internet-dependent; off by default.
|
|
168
|
+
- `--send-test-email TO` — actually send a probe email through the
|
|
169
|
+
configured backend. Costs real money on SES; off by default.
|
|
170
|
+
|
|
171
|
+
Default checks:
|
|
172
|
+
|
|
173
|
+
| Check | Pass criterion |
|
|
174
|
+
|-------|----------------|
|
|
175
|
+
| `jwt secret` | Present, ≥ 32 chars |
|
|
176
|
+
| `backend` | `Backend.ping()` succeeds (works for any backend) |
|
|
177
|
+
| `schema` | Mongo: required indexes present. SQL: `users` table responds to `count(*)` |
|
|
178
|
+
| `email backend` | `build_email_service(config.email)` instantiates |
|
|
179
|
+
|
|
180
|
+
Optional checks:
|
|
181
|
+
|
|
182
|
+
| Check | Pass criterion |
|
|
183
|
+
|-------|----------------|
|
|
184
|
+
| `dns mx` | At least one MX record on the sender domain |
|
|
185
|
+
| `dns spf` | A TXT record containing `v=spf1` |
|
|
186
|
+
| `dns dmarc` | A TXT record at `_dmarc.<domain>` containing `v=DMARC1` |
|
|
187
|
+
| `email send` | The configured backend's `send()` returned without raising |
|
|
@@ -85,15 +85,16 @@ regstack picks a backend at construction time from the URL scheme of
|
|
|
85
85
|
- Notes
|
|
86
86
|
|
|
87
87
|
* - SQLite
|
|
88
|
-
- `sqlite+aiosqlite:///./
|
|
88
|
+
- `sqlite+aiosqlite:///./dbname.db`
|
|
89
89
|
- Default. Bundled in the base install — no extras needed.
|
|
90
90
|
`:memory:` works too (per-test).
|
|
91
91
|
* - Postgres
|
|
92
|
-
- `postgresql+asyncpg
|
|
92
|
+
- `postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname`
|
|
93
93
|
- Requires the `postgres` extra (pulls in `asyncpg`). The driver is
|
|
94
94
|
pinned to `+asyncpg` — sync drivers won't work.
|
|
95
95
|
* - MongoDB
|
|
96
|
-
- `mongodb
|
|
96
|
+
- `mongodb://<username>:<password>@dbhost.example.com:27017/dbname`
|
|
97
|
+
(or `mongodb+srv://<username>:<password>@app.abc123.mongodb.net/dbname`)
|
|
97
98
|
- Requires the `mongo` extra (pulls in `pymongo`). Database is taken
|
|
98
99
|
from the URL path; falls back to ``mongodb_database`` if absent.
|
|
99
100
|
```
|
|
@@ -234,14 +235,14 @@ The active backend exposes the same five repository protocols on
|
|
|
234
235
|
```toml
|
|
235
236
|
[email]
|
|
236
237
|
backend = "console" # console | smtp | ses
|
|
237
|
-
from_address = "noreply
|
|
238
|
-
from_name = "
|
|
238
|
+
from_address = "noreply@app.example.com"
|
|
239
|
+
from_name = "Example App"
|
|
239
240
|
|
|
240
241
|
# smtp
|
|
241
|
-
smtp_host = "smtp.example.com"
|
|
242
|
+
smtp_host = "smtp.app.example.com"
|
|
242
243
|
smtp_port = 587
|
|
243
244
|
smtp_starttls = true
|
|
244
|
-
smtp_username = "
|
|
245
|
+
smtp_username = "<username>"
|
|
245
246
|
# smtp_password is a SecretStr — set via REGSTACK_EMAIL__SMTP_PASSWORD
|
|
246
247
|
|
|
247
248
|
# ses
|
|
@@ -16,13 +16,13 @@ a working backend with no infrastructure. To switch:
|
|
|
16
16
|
# regstack.toml
|
|
17
17
|
|
|
18
18
|
# SQLite (default — file lives wherever the path points)
|
|
19
|
-
database_url = "sqlite+aiosqlite:///./
|
|
19
|
+
database_url = "sqlite+aiosqlite:///./dbname.db"
|
|
20
20
|
|
|
21
21
|
# Postgres (needs the `postgres` extra → asyncpg)
|
|
22
|
-
database_url = "postgresql+asyncpg
|
|
22
|
+
database_url = "postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname"
|
|
23
23
|
|
|
24
24
|
# MongoDB (needs the `mongo` extra → pymongo)
|
|
25
|
-
database_url = "mongodb
|
|
25
|
+
database_url = "mongodb://<username>:<password>@dbhost.example.com:27017/dbname"
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Hosts that already manage their own connection pool — for example, an
|
|
@@ -210,7 +210,7 @@ one does not validate against the other. The
|
|
|
210
210
|
## Bootstrapping the first admin
|
|
211
211
|
|
|
212
212
|
```bash
|
|
213
|
-
uv run regstack create-admin --email admin@example.com
|
|
213
|
+
uv run regstack create-admin --email admin@app.example.com
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
The CLI prompts for a password (with confirmation). Re-running with
|
|
@@ -220,7 +220,7 @@ their password.
|
|
|
220
220
|
In code:
|
|
221
221
|
|
|
222
222
|
```python
|
|
223
|
-
await regstack.bootstrap_admin("admin@example.com", "long-strong-password")
|
|
223
|
+
await regstack.bootstrap_admin("admin@app.example.com", "long-strong-password")
|
|
224
224
|
```
|
|
225
225
|
|
|
226
226
|
This is idempotent — promotes an existing user, creates one if not
|
|
@@ -63,6 +63,52 @@ and keep the user table in your own database** — not "stand up a
|
|
|
63
63
|
separate auth product" and not "write the boring 80% from scratch
|
|
64
64
|
each time".
|
|
65
65
|
|
|
66
|
+
### What's distinctive about regstack
|
|
67
|
+
|
|
68
|
+
- **Native pywebview setup tools, not just Click prompts.**
|
|
69
|
+
- `regstack oauth setup` opens a real desktop window that walks an
|
|
70
|
+
operator through registering a Google OAuth 2.0 client and
|
|
71
|
+
merging the credentials into `regstack.toml` non-clobberingly —
|
|
72
|
+
no copy-pasting the redirect URI four times.
|
|
73
|
+
- `regstack theme design` opens a live theme designer with a
|
|
74
|
+
real-time preview of the SSR auth pages: tweak a colour, see
|
|
75
|
+
the Sign-in card update on the right; click Save to write
|
|
76
|
+
`regstack-theme.css`. CSS-only re-skin in minutes, no template
|
|
77
|
+
editing required.
|
|
78
|
+
Both tools are local-only (`127.0.0.1` + per-launch token), have
|
|
79
|
+
a `--print-only` mode for headless CI, and ship under the same
|
|
80
|
+
test-from-the-outside Playwright suite the SSR pages do.
|
|
81
|
+
- **Zero vendor lock-in.** Your user table is in your database, not
|
|
82
|
+
someone else's. Switch storage backends by changing one URL.
|
|
83
|
+
- **Opt-in everything.** No SMS extra → no `twilio` / `aioboto3`
|
|
84
|
+
install. No SES → no AWS SDK. No SSR → no Jinja templates loaded.
|
|
85
|
+
The base install is small.
|
|
86
|
+
|
|
87
|
+
## About the examples
|
|
88
|
+
|
|
89
|
+
To keep URLs and config values consistent across the docs, every
|
|
90
|
+
example pretends to embed regstack into a fictional app hosted at
|
|
91
|
+
`app.example.com`. Throughout the docs:
|
|
92
|
+
|
|
93
|
+
| What | Value |
|
|
94
|
+
|---|---|
|
|
95
|
+
| Public host | `app.example.com` |
|
|
96
|
+
| `base_url` | `https://app.example.com` |
|
|
97
|
+
| Database host (prod) | `dbhost.example.com` |
|
|
98
|
+
| Database user | `<username>` |
|
|
99
|
+
| Database password | `<password>` |
|
|
100
|
+
| Database name | `dbname` |
|
|
101
|
+
| Email sender | `noreply@app.example.com` |
|
|
102
|
+
| Local dev port | `localhost:8000` |
|
|
103
|
+
|
|
104
|
+
So a Postgres URL looks like
|
|
105
|
+
`postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname`,
|
|
106
|
+
a MongoDB URL like
|
|
107
|
+
`mongodb://<username>:<password>@dbhost.example.com:27017/dbname`,
|
|
108
|
+
and the local SQLite path `sqlite+aiosqlite:///./dbname.db`. Substitute
|
|
109
|
+
your own values when copying — the shape is the only thing that
|
|
110
|
+
matters.
|
|
111
|
+
|
|
66
112
|
## What's in the box
|
|
67
113
|
|
|
68
114
|
- **Three storage backends, one API.** SQLite (the default — single
|
|
@@ -76,7 +122,12 @@ each time".
|
|
|
76
122
|
forgot, reset, MFA confirm, account dashboard. Themed via CSS
|
|
77
123
|
custom properties — no template editing required for a re-skin.
|
|
78
124
|
Full template overrides are still possible per host.
|
|
79
|
-
- **CLIs
|
|
125
|
+
- **CLIs + native pywebview tools.**
|
|
126
|
+
`regstack init` (interactive project bootstrap),
|
|
127
|
+
`regstack oauth setup` (guided Google OAuth client configuration in
|
|
128
|
+
a native webview — see [OAuth guide](oauth.md)),
|
|
129
|
+
`regstack theme design` (live theme designer with a real-time preview
|
|
130
|
+
of the SSR widgets — see [theming guide](theming.md)),
|
|
80
131
|
`regstack create-admin`, `regstack doctor`.
|
|
81
132
|
- **OAuth — Sign in with Google** (opt-in, since 0.3.0). Authorization
|
|
82
133
|
Code with PKCE, ID-token verification, identity-linking with a
|
|
@@ -139,7 +190,7 @@ changelog
|
|
|
139
190
|
|
|
140
191
|
## Project status
|
|
141
192
|
|
|
142
|
-
Alpha. Latest tagged release: `v0.
|
|
193
|
+
Alpha. Latest tagged release: `v0.5.0`. SQLite is the default and
|
|
143
194
|
runs with no infrastructure; PostgreSQL and MongoDB pass the same
|
|
144
195
|
parametrized integration suite. The full backend matrix runs in
|
|
145
196
|
parallel against every test, so a green CI on `main` is a strong
|
|
@@ -144,7 +144,7 @@ uv run uvicorn examples.sqlite.main:app --reload
|
|
|
144
144
|
# In another terminal
|
|
145
145
|
curl -X POST http://localhost:8000/api/auth/register \
|
|
146
146
|
-H 'content-type: application/json' \
|
|
147
|
-
-d '{"email":"
|
|
147
|
+
-d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
|
|
148
148
|
```
|
|
149
149
|
|
|
150
150
|
The SQLite demo enables verification by default. Look at the demo's
|
|
@@ -4,6 +4,30 @@ The bundled UI ships a structural stylesheet (`core.css`) and a default
|
|
|
4
4
|
theme stylesheet (`theme.css`). Hosts pick one of three integration
|
|
5
5
|
levels depending on how much they want to override.
|
|
6
6
|
|
|
7
|
+
## Quickest path: the live designer
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv run regstack theme design
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Opens a native pywebview window with controls for every `--rs-*`
|
|
14
|
+
variable on the left and a real-time preview of the bundled SSR
|
|
15
|
+
widgets on the right. Tweak a colour, watch the Sign-in card update;
|
|
16
|
+
click Save to write `regstack-theme.css`. There's a `--print-only`
|
|
17
|
+
mode for headless / CI use:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run regstack theme design --print-only \
|
|
21
|
+
--var --rs-accent=#0d9488 \
|
|
22
|
+
--var --rs-radius=10 \
|
|
23
|
+
--var dark:--rs-accent=#2dd4bf
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Re-running the designer reloads your previous values back into the
|
|
27
|
+
form, so you can iterate without losing state. The rest of this guide
|
|
28
|
+
explains the underlying override mechanisms — useful when you want
|
|
29
|
+
to go beyond what the designer exposes.
|
|
30
|
+
|
|
7
31
|
## Level 1 — swap one stylesheet
|
|
8
32
|
|
|
9
33
|
Set `config.theme_css_url` to a URL where your host serves a custom
|
|
@@ -34,9 +58,30 @@ Your `static/my-theme.css`:
|
|
|
34
58
|
```
|
|
35
59
|
|
|
36
60
|
That's it — every regstack page picks up the new palette. See
|
|
37
|
-
`examples/
|
|
61
|
+
`examples/mongo/branding/theme.css` for a working wine-themed
|
|
38
62
|
example.
|
|
39
63
|
|
|
64
|
+
To support both light and dark mode, add a `prefers-color-scheme`
|
|
65
|
+
block re-declaring whichever variables need to differ:
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
:root {
|
|
69
|
+
--rs-accent: #0d9488;
|
|
70
|
+
--rs-accent-bg: rgba(13, 148, 136, 0.08);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@media (prefers-color-scheme: dark) {
|
|
74
|
+
:root {
|
|
75
|
+
--rs-accent: #2dd4bf;
|
|
76
|
+
--rs-accent-bg: rgba(45, 212, 191, 0.12);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The bundled `theme.css` already supplies dark-mode defaults for every
|
|
82
|
+
variable, so you only need to override the ones whose dark variant
|
|
83
|
+
differs from the auto-derived contrast.
|
|
84
|
+
|
|
40
85
|
## Variables
|
|
41
86
|
|
|
42
87
|
```{list-table}
|
|
@@ -28,6 +28,20 @@ class _LazyOauthGroup(click.Group):
|
|
|
28
28
|
return setup_cmd
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class _LazyThemeGroup(click.Group):
|
|
32
|
+
"""Same lazy-import pattern for the theme designer subtree."""
|
|
33
|
+
|
|
34
|
+
def list_commands(self, ctx: click.Context) -> list[str]:
|
|
35
|
+
return ["design"]
|
|
36
|
+
|
|
37
|
+
def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
|
|
38
|
+
if name != "design":
|
|
39
|
+
return None
|
|
40
|
+
from regstack.wizard.theme_designer.cli import design as design_cmd
|
|
41
|
+
|
|
42
|
+
return design_cmd
|
|
43
|
+
|
|
44
|
+
|
|
31
45
|
@click.group(help="regstack — embeddable account registration for FastAPI apps.")
|
|
32
46
|
@click.version_option(__version__, prog_name="regstack")
|
|
33
47
|
def cli() -> None:
|
|
@@ -39,6 +53,7 @@ cli.add_command(create_admin_cmd)
|
|
|
39
53
|
cli.add_command(doctor_cmd)
|
|
40
54
|
cli.add_command(migrate_cmd)
|
|
41
55
|
cli.add_command(_LazyOauthGroup(name="oauth", help="OAuth provider setup wizards."))
|
|
56
|
+
cli.add_command(_LazyThemeGroup(name="theme", help="Theme designer for the SSR pages."))
|
|
42
57
|
|
|
43
58
|
|
|
44
59
|
def main() -> None:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.0"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Interactive theme designer for regstack's SSR pages.
|
|
2
|
+
|
|
3
|
+
``regstack theme design`` opens a native pywebview window with a live
|
|
4
|
+
preview of the bundled SSR widgets and a panel of controls for the
|
|
5
|
+
``--rs-*`` CSS custom properties. Saving writes a single
|
|
6
|
+
``regstack-theme.css`` the host can serve and reference via
|
|
7
|
+
``config.theme_css_url``.
|
|
8
|
+
"""
|