supython 0.1.9__tar.gz → 0.1.11__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.
- {supython-0.1.9 → supython-0.1.11}/.gitignore +9 -1
- {supython-0.1.9 → supython-0.1.11}/CHANGELOG.md +68 -2
- {supython-0.1.9 → supython-0.1.11}/PKG-INFO +46 -152
- {supython-0.1.9 → supython-0.1.11}/README.md +44 -151
- {supython-0.1.9 → supython-0.1.11}/pyproject.toml +2 -1
- {supython-0.1.9 → supython-0.1.11}/src/supython/app.py +2 -1
- {supython-0.1.9 → supython-0.1.11}/src/supython/cli.py +33 -13
- supython-0.1.11/src/supython/extensions.py +68 -0
- supython-0.1.11/src/supython/scaffold/init_project.py +217 -0
- supython-0.1.11/src/supython/scaffold/templates/README.md.tmpl +32 -0
- supython-0.1.9/src/supython/scaffold/templates/apps_hooks.py.tmpl → supython-0.1.11/src/supython/scaffold/templates/apps_hooks_welcome.py.tmpl +1 -1
- supython-0.1.9/src/supython/scaffold/templates/apps_jobs.py.tmpl → supython-0.1.11/src/supython/scaffold/templates/apps_jobs_example.py.tmpl +1 -1
- supython-0.1.11/src/supython/scaffold/templates/package_hooks_init.py.tmpl +8 -0
- supython-0.1.11/src/supython/scaffold/templates/package_jobs_init.py.tmpl +8 -0
- supython-0.1.11/src/supython/scaffold/templates/pyproject.toml.tmpl +15 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/settings.py.tmpl +5 -1
- supython-0.1.9/src/supython/migrations/0003_demo_todos.sql → supython-0.1.11/src/supython/scaffold/templates/static/0001_create_todos.sql +10 -3
- supython-0.1.11/src/supython/scaffold/templates/static/functions_hello.py +13 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/settings.py +31 -0
- supython-0.1.9/src/supython/extensions.py +0 -36
- supython-0.1.9/src/supython/scaffold/init_project.py +0 -144
- supython-0.1.9/src/supython/scaffold/templates/README.md.tmpl +0 -22
- supython-0.1.9/src/supython/scaffold/templates/migrations/.gitkeep +0 -0
- {supython-0.1.9 → supython-0.1.11}/LICENSE +0 -0
- {supython-0.1.9 → supython-0.1.11}/SECURITY.md +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/auth.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/auth_templates.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/auth_users.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/db.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/functions.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/jobs.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/ops.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/realtime.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_auth.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_auth_templates.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_auth_users.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_db.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_functions.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_jobs.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_ops.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_realtime.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/service_storage.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/storage.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/api/system.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/audit.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/deps.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/errors.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/session.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/spa.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Alert-dluGVkos.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Alert-dluGVkos.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Audit-Njung3HI.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Audit-Njung3HI.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Backups-DzPlFgrm.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Backups-DzPlFgrm.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Buckets-ByacGkU1.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Buckets-ByacGkU1.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Channels-BoIuTtam.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Channels-BoIuTtam.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/ChevronRight-CtQH1EQ1.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/ChevronRight-CtQH1EQ1.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/CodeViewer-Bqy7-wvH.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/CodeViewer-Bqy7-wvH.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Crons-B67vc39F.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Crons-B67vc39F.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DashboardView-CUTFVL6k.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DashboardView-CUTFVL6k.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DataTable-COAAWEft.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DataTable-COAAWEft.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DescriptionsItem-P8JUDaBs.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DescriptionsItem-P8JUDaBs.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DrawerContent-TpYTFgF1.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/DrawerContent-TpYTFgF1.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Empty-cr2r7e2u.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Empty-cr2r7e2u.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/EmptyState-DeDck-OL.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/EmptyState-DeDck-OL.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Grid-hFkp9F4P.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Grid-hFkp9F4P.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Input-DppYTq9C.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Input-DppYTq9C.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Invoke-DW3Nveeh.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Invoke-DW3Nveeh.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/JsonField-DibyJgun.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/JsonField-DibyJgun.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/LoginView-BjLyE3Ds.css +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/LoginView-CoOjECT_.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/LoginView-CoOjECT_.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Logs-D9WYrnIT.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Logs-D9WYrnIT.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Logs-DS1XPa0h.css +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Migrations-DOSC2ddQ.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Migrations-DOSC2ddQ.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/ObjectBrowser-_5w8vOX8.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/ObjectBrowser-_5w8vOX8.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Queue-CywZs6vI.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Queue-CywZs6vI.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/RefreshTokens-Ccjr53jg.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/RefreshTokens-Ccjr53jg.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/RlsEditor-BSlH9vSc.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/RlsEditor-BSlH9vSc.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Routes-BiLXE49D.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Routes-BiLXE49D.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Routes-C-ianIGD.css +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SchemaBrowser-DKy2_KQi.css +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SchemaBrowser-XFvFbtDB.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SchemaBrowser-XFvFbtDB.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Select-DIzZyRZb.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Select-DIzZyRZb.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Space-n5-XcguU.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Space-n5-XcguU.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SqlEditor-b8pTsILY.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SqlEditor-b8pTsILY.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SqlWorkspace-BUS7IntH.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/SqlWorkspace-BUS7IntH.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/TableData-CQIagLKn.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/TableData-CQIagLKn.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Tag-D1fOKpTH.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Tag-D1fOKpTH.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Templates-BS-ugkdq.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Templates-BS-ugkdq.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Thing-CEAniuMg.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Thing-CEAniuMg.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Users-wzwajhlh.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/Users-wzwajhlh.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/_plugin-vue_export-helper-DGA9ry_j.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/dist-VXIJLCYq.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/dist-VXIJLCYq.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/format-length-CGCY1rMh.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/format-length-CGCY1rMh.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/get-Ca6unauB.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/get-Ca6unauB.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/index-CeE6v959.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/index-CeE6v959.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/pinia-COXwfrOX.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/pinia-COXwfrOX.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/resources-Bt6thQCD.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/resources-Bt6thQCD.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/use-locale-mtgM0a3a.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/use-locale-mtgM0a3a.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/use-merged-state-BvhkaHNX.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/use-merged-state-BvhkaHNX.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useConfirm-tMjvBFXR.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useConfirm-tMjvBFXR.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useResource-C_rJCY8C.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useResource-C_rJCY8C.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useTable-CnZc5zhi.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useTable-CnZc5zhi.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useTable-Dg0XlRlq.css +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useToast-DsZKx0IX.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/useToast-DsZKx0IX.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/utils-sbXoq7Ir.js +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/assets/utils-sbXoq7Ir.js.map +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/favicon.svg +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/icons.svg +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/admin/static/index.html +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/_email_job.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/claims.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/deps.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/providers/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/providers/github.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/providers/google.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/providers/oauth.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/providers/registry.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/ratelimit.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/router.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/auth/service.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/backups/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/backups/_backup_job.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/backups/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/backups/service.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/body_size.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/_auth.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/_client.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/_config.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/_functions.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/_storage.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/client/py.typed +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/db.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/db_admin.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/functions/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/functions/context.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/functions/loader.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/functions/router.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/functions/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/gen/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/gen/_introspect.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/gen/types_py.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/gen/types_ts.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/health.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/hooks.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/backends.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/context.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/cron.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/cron_inproc.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/decorators.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/registry.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/router.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/service.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jobs/worker.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/jwks.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/keyset.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/logging_config.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/mail.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/mailer.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrate.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0001_extensions_and_roles.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0002_auth_schema.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0004_auth_v0_2.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0005_storage_schema.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0006_realtime_schema.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0007_jobs_schema.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0008_jobs_last_error.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0009_auth_rate_limits.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0010_worker_heartbeat.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0011_admin_schema.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0012_auth_banned_until.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0013_email_templates.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0014_realtime_payload_warning.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/migrations/0015_backups_schema.sql +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/passwords.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/broker.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/protocol.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/router.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/service.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/topics.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/realtime/websocket.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/Caddyfile.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/asgi.py.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/docker-compose.prod.yml.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/docker-compose.yml.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/docker_postgres_Dockerfile.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/docker_postgres_postgresql.conf.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/env.example.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/functions_README.md.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/gitignore.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/manage.py.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/scaffold/templates/package_init.py.tmpl +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/secretset.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/security_headers.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/settings_module.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/storage/__init__.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/storage/backends.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/storage/router.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/storage/schemas.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/storage/service.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/storage/signing.py +0 -0
- {supython-0.1.9 → supython-0.1.11}/src/supython/tokens.py +0 -0
|
@@ -29,7 +29,8 @@ dev-app/backups/
|
|
|
29
29
|
dev-app/__pycache__/
|
|
30
30
|
dev-app/functions/__pycache__/
|
|
31
31
|
dev-app/.tmp/
|
|
32
|
-
|
|
32
|
+
dev-app/
|
|
33
|
+
examples/
|
|
33
34
|
.references/
|
|
34
35
|
.references/*.md
|
|
35
36
|
.claude/
|
|
@@ -43,3 +44,10 @@ ts-sdk/*.log
|
|
|
43
44
|
.tmp/
|
|
44
45
|
|
|
45
46
|
admin-ui/node_modules/
|
|
47
|
+
|
|
48
|
+
/.supython/secrets/
|
|
49
|
+
/.supython/secrets.json
|
|
50
|
+
/.supython/jwt_private.pem
|
|
51
|
+
/.supython/keys/
|
|
52
|
+
/.supython/keyset.json
|
|
53
|
+
/.supython/jwks.json
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to supython are recorded here. The format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
|
-
project uses **ZeroVer** (`0.x.y`)
|
|
6
|
-
what counts as a breaking change. There is no scheduled `1.0.0`; treat
|
|
5
|
+
project uses **ZeroVer** (`0.x.y`). There is no scheduled `1.0.0`; treat
|
|
7
6
|
every `MINOR` as a potential breaking release.
|
|
8
7
|
|
|
9
8
|
Categories used per release:
|
|
@@ -31,6 +30,71 @@ Each entry links the relevant `PROJECT.md` section and decision-log row
|
|
|
31
30
|
|
|
32
31
|
---
|
|
33
32
|
|
|
33
|
+
## [0.1.11] — 2026-06-13
|
|
34
|
+
|
|
35
|
+
Project-initialization ergonomics. `supython init` now produces a project that
|
|
36
|
+
boots with no manual setup.
|
|
37
|
+
|
|
38
|
+
### Breaking
|
|
39
|
+
|
|
40
|
+
- `supython init` arguments changed. The first argument is now the **importable
|
|
41
|
+
package name** (still required); the optional second argument is the **target
|
|
42
|
+
directory** (Django-style — `.` for the current directory), defaulting to
|
|
43
|
+
`./<name>`. The `--here` flag is **removed**: `supython init myapp --here`
|
|
44
|
+
becomes `supython init myapp .`.
|
|
45
|
+
- The scaffolded `jobs.py` and `hooks.py` single-file seeds are replaced by
|
|
46
|
+
auto-discovered **packages** `<name>/jobs/` and `<name>/hooks/`. Every module
|
|
47
|
+
inside each package is imported at boot, so jobs and hooks can be split across
|
|
48
|
+
files. `EXTENSIONS` in `settings.py` now points at the packages (`<name>.jobs`,
|
|
49
|
+
`<name>.hooks`) rather than the old modules.
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- `supython init` generates a ready-to-run, gitignored `.env` (from the example
|
|
54
|
+
template) so the stack boots without a manual `cp .env.example .env` step.
|
|
55
|
+
- `supython init` generates a `pyproject.toml` that declares the project and
|
|
56
|
+
pins the installed supython version; install the scaffold with `pip install -e .`.
|
|
57
|
+
- The scaffold seeds an example edge function (`functions/hello.py`) and an
|
|
58
|
+
example application migration (`migrations/0001_create_todos.sql`).
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- Re-running `supython init` is now a safe top-up: existing files are skipped,
|
|
63
|
+
not overwritten, unless `--force` is passed.
|
|
64
|
+
- The generated JWT keypair and signing secrets under `.supython/` are **never**
|
|
65
|
+
overwritten — not even with `--force` — to avoid silently rotating keys and
|
|
66
|
+
invalidating live tokens, sessions, and signed URLs. Rotation has dedicated
|
|
67
|
+
homes: `supython keygen` and `supython secret rotate`.
|
|
68
|
+
- The scaffolded `todos` table moved out of supython's framework migrations into
|
|
69
|
+
the project's own `migrations/`. `supython migrate` applies only supython's
|
|
70
|
+
framework schemas (auth, storage, realtime, jobs); apply your application
|
|
71
|
+
migrations with a tool of your choice (dbmate recommended).
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## [0.1.10] — 2026-06-12
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- `python-dotenv` is now a direct dependency (previously only transitive via
|
|
80
|
+
`pydantic-settings`), backing the new boot-time `.env` export.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- `.env` is now exported into `os.environ` at the start of every boot path
|
|
85
|
+
(`create_app`, `supython worker run`, CLI subcommands) via the shared
|
|
86
|
+
`settings.export_env_file()` helper, **before** extensions load. Previously
|
|
87
|
+
pydantic-settings loaded `.env` into the typed `Settings` model only, so
|
|
88
|
+
dynamically-named secrets read via `os.environ.get(<name>)` (the `secret_ref`
|
|
89
|
+
convention) resolved to `None` under `supython dev` / `worker run` / CLI —
|
|
90
|
+
working in containers only because docker-compose's `env_file:` injected
|
|
91
|
+
`.env` first. The export uses `override=False`, so real environment variables
|
|
92
|
+
set by an orchestrator always win, and the path is sourced from the same
|
|
93
|
+
`SettingsConfigDict.env_file`. Downstream apps can delete their ad-hoc
|
|
94
|
+
`load_dotenv()` boot shims. (#1)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
34
98
|
## [0.1.9] — 2026-05-29
|
|
35
99
|
|
|
36
100
|
### Fixed
|
|
@@ -234,5 +298,7 @@ v0.1–v0.7 plus a v1.1.x admin track; see §19 decision log
|
|
|
234
298
|
---
|
|
235
299
|
|
|
236
300
|
|
|
301
|
+
[0.1.11]: https://github.com/Tkeby/supython/releases/tag/v0.1.11
|
|
302
|
+
[0.1.10]: https://github.com/Tkeby/supython/releases/tag/v0.1.10
|
|
237
303
|
[0.1.9]: https://github.com/Tkeby/supython/releases/tag/v0.1.9
|
|
238
304
|
[0.1.0]: https://github.com/Tkeby/supython/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: supython
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: A lightweight Postgres-first BaaS framework for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/Tkeby/supython
|
|
6
6
|
Project-URL: Repository, https://github.com/Tkeby/supython
|
|
@@ -35,6 +35,7 @@ Requires-Dist: itsdangerous>=2.2
|
|
|
35
35
|
Requires-Dist: pydantic-settings>=2.6
|
|
36
36
|
Requires-Dist: pydantic>=2.9
|
|
37
37
|
Requires-Dist: pyjwt[crypto]>=2.10
|
|
38
|
+
Requires-Dist: python-dotenv>=1.0
|
|
38
39
|
Requires-Dist: python-multipart>=0.0.20
|
|
39
40
|
Requires-Dist: typer>=0.15
|
|
40
41
|
Requires-Dist: uvicorn[standard]>=0.32
|
|
@@ -152,20 +153,25 @@ python -m venv .venv && source .venv/bin/activate
|
|
|
152
153
|
pip install supython
|
|
153
154
|
|
|
154
155
|
# 2. scaffold a new project
|
|
156
|
+
# `myapp` is the importable package name. An optional second argument
|
|
157
|
+
# sets the target dir (`.` = current dir); it defaults to ./myapp.
|
|
155
158
|
supython init myapp
|
|
156
159
|
cd myapp
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
# the value PostgREST will use (docker-compose.yml injects it via env).
|
|
160
|
+
# install the project (and supython, pinned in the generated pyproject.toml)
|
|
161
|
+
pip install -e .
|
|
160
162
|
#
|
|
161
|
-
# The scaffold
|
|
162
|
-
#
|
|
163
|
+
# The scaffold generates a ready-to-run, gitignored .env plus a JWT keypair
|
|
164
|
+
# and signing secrets under .supython/ — no manual edits needed to boot.
|
|
165
|
+
# It also creates:
|
|
166
|
+
# pyproject.toml — declares this project, pins the supython version
|
|
163
167
|
# myapp/settings.py — declare EXTENSIONS, EXTRA_ROUTERS, EXTRA_MIDDLEWARE
|
|
164
|
-
# myapp/jobs
|
|
165
|
-
# myapp/hooks
|
|
168
|
+
# myapp/jobs/ — package of @job / @cron modules (all auto-imported)
|
|
169
|
+
# myapp/hooks/ — package of @on(...) lifecycle hooks (all auto-imported)
|
|
166
170
|
# myapp/asgi.py — optional entrypoint for uvicorn/gunicorn
|
|
171
|
+
# functions/hello.py — example edge function (GET /functions/hello)
|
|
172
|
+
# migrations/0001_create_todos.sql — example app migration (apply with dbmate)
|
|
167
173
|
|
|
168
|
-
# 3. boot Postgres + PostgREST and
|
|
174
|
+
# 3. boot Postgres + PostgREST and apply supython's framework migrations
|
|
169
175
|
supython up
|
|
170
176
|
|
|
171
177
|
# 4. run the auth/API service (separate terminal)
|
|
@@ -190,6 +196,10 @@ You should now have:
|
|
|
190
196
|
|
|
191
197
|
## End-to-end smoke test
|
|
192
198
|
|
|
199
|
+
This uses the scaffolded `todos` table, so first apply the example app migration
|
|
200
|
+
(`migrations/0001_create_todos.sql`) with [dbmate](https://github.com/amacneil/dbmate)
|
|
201
|
+
or your migration tool of choice — `supython migrate` only handles framework schemas.
|
|
202
|
+
|
|
193
203
|
```bash
|
|
194
204
|
# sign up
|
|
195
205
|
curl -sS -X POST http://localhost:8000/auth/v1/signup \
|
|
@@ -531,128 +541,31 @@ need to edit SQL.
|
|
|
531
541
|
|
|
532
542
|
```
|
|
533
543
|
supython/
|
|
534
|
-
├──
|
|
535
|
-
├──
|
|
536
|
-
├──
|
|
537
|
-
├──
|
|
538
|
-
├──
|
|
539
|
-
├──
|
|
540
|
-
│ ├──
|
|
541
|
-
│ ├──
|
|
542
|
-
│ ├──
|
|
543
|
-
│ ├──
|
|
544
|
-
│
|
|
545
|
-
├──
|
|
546
|
-
│ ├──
|
|
547
|
-
│ ├──
|
|
548
|
-
│ ├──
|
|
549
|
-
│ ├──
|
|
550
|
-
│ ├──
|
|
551
|
-
│
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
│ ├── 0014_realtime_payload_warning.sql # >8KB payload warning counter
|
|
560
|
-
│ └── 0015_backups_schema.sql # backups metadata
|
|
561
|
-
├── examples/
|
|
562
|
-
│ ├── todos.http # HTTP smoke tests (auth + PostgREST)
|
|
563
|
-
│ ├── storage.http # storage upload / signed URL examples
|
|
564
|
-
│ ├── functions.http # edge-function call examples
|
|
565
|
-
│ └── chat.html # two-browser realtime demo (vanilla JS, zero deps)
|
|
566
|
-
├── docs/
|
|
567
|
-
│ ├── PROJECT.md # architecture + roadmap (single source of truth)
|
|
568
|
-
│ ├── Installation.md # full install guide (dev, prod, managed Postgres)
|
|
569
|
-
│ └── admin-ui/
|
|
570
|
-
│ ├── admin-surface-plan.md # admin implementation plan + phase status
|
|
571
|
-
│ └── admin-surface.md # admin architecture + contracts
|
|
572
|
-
├── tests/
|
|
573
|
-
│ ├── conftest.py # cross-tree fixtures (keys, capturing mailer)
|
|
574
|
-
│ ├── _keys.py # JWT-forging helpers
|
|
575
|
-
│ ├── fixtures/ # test function modules, etc.
|
|
576
|
-
│ ├── unit/ # pure-Python tests (~6s, no Docker)
|
|
577
|
-
│ │ ├── test_admin_session.py
|
|
578
|
-
│ │ ├── test_admin_service_db.py
|
|
579
|
-
│ │ └── ...
|
|
580
|
-
│ └── integration/ # full ASGI + Postgres on port 54323
|
|
581
|
-
│ ├── conftest.py # pool, app, client, autouse DB cleaners
|
|
582
|
-
│ ├── test_auth_signup_login.py
|
|
583
|
-
│ ├── test_admin_auth.py
|
|
584
|
-
│ ├── test_admin_auth_users.py
|
|
585
|
-
│ ├── test_admin_db_rows.py
|
|
586
|
-
│ ├── test_admin_jobs.py
|
|
587
|
-
│ ├── test_admin_ops_backups.py
|
|
588
|
-
│ ├── test_admin_storage.py
|
|
589
|
-
│ ├── test_postgrest_rls.py
|
|
590
|
-
│ ├── test_realtime_ws.py
|
|
591
|
-
│ └── ...
|
|
592
|
-
├── admin-ui/ # Vue 3 + Vite SPA (built → src/supython/admin/static/)
|
|
593
|
-
│ └── src/
|
|
594
|
-
│ ├── api/ # single fetch seam
|
|
595
|
-
│ ├── components/ # shell, data, editors, feedback
|
|
596
|
-
│ ├── composables/ # useResource, useTable, useConfirm, useImpersonate, …
|
|
597
|
-
│ ├── stores/ # auth, ui
|
|
598
|
-
│ ├── views/ # Dashboard, db/, auth/, storage/, functions/, …
|
|
599
|
-
│ └── router/
|
|
600
|
-
└── src/supython/
|
|
601
|
-
├── __init__.py # single version string
|
|
602
|
-
├── settings.py # pydantic-settings, .env-driven
|
|
603
|
-
├── db.py # asyncpg pool + lifespan + as_role() / as_service_role()
|
|
604
|
-
├── mailer.py # ConsoleBackend / SmtpBackend
|
|
605
|
-
├── tokens.py # RS256/ES256 JWT + JWKS
|
|
606
|
-
├── passwords.py # argon2id
|
|
607
|
-
├── migrate.py # ~50-line SQL migration runner
|
|
608
|
-
├── app.py # FastAPI factory
|
|
609
|
-
├── cli.py # typer: up, dev, keygen, admin, worker, test, …
|
|
610
|
-
├── extensions.py # eager-import dotted module paths at boot
|
|
611
|
-
├── settings_module.py # user settings (EXTENSIONS, EXTRA_ROUTERS, …)
|
|
612
|
-
├── health.py # /livez, /readyz, /health endpoints
|
|
613
|
-
├── logging_config.py # structured JSON log setup
|
|
614
|
-
├── security_headers.py # HSTS, CSP, etc.
|
|
615
|
-
├── body_size.py # request body size guards
|
|
616
|
-
├── jwks.py # JWKS generation + rotation helpers
|
|
617
|
-
├── keyset.py # asymmetric key rotation manifest
|
|
618
|
-
├── secretset.py # symmetric secret rotation manifest
|
|
619
|
-
├── hooks.py # generic hook system: on() / fire()
|
|
620
|
-
├── mail.py # email send with job-retry fallback
|
|
621
|
-
├── auth/
|
|
622
|
-
│ ├── schemas.py
|
|
623
|
-
│ ├── service.py # full auth layer: signup / OAuth / OTP / recover …
|
|
624
|
-
│ ├── router.py # all /auth/v1/* routes
|
|
625
|
-
│ └── providers/ # Google, GitHub, OAuth2 helpers
|
|
626
|
-
├── storage/
|
|
627
|
-
│ ├── backends.py # LocalBackend, S3Backend
|
|
628
|
-
│ ├── service.py
|
|
629
|
-
│ └── router.py # /storage/v1/*
|
|
630
|
-
├── functions/
|
|
631
|
-
│ ├── loader.py # filesystem discovery + hot reload
|
|
632
|
-
│ └── router.py # /functions/v1/*
|
|
633
|
-
├── realtime/
|
|
634
|
-
│ ├── protocol.py # Phoenix Channels encode/decode
|
|
635
|
-
│ ├── broker.py # fan-out engine with RLS filtering
|
|
636
|
-
│ ├── websocket.py # WS route with JWT auth
|
|
637
|
-
│ └── router.py # /realtime/v1/*
|
|
638
|
-
├── jobs/
|
|
639
|
-
│ ├── registry.py # @job / @cron decorator store
|
|
640
|
-
│ ├── service.py # enqueue, claim_next, mark_*
|
|
641
|
-
│ ├── worker.py # long-running poll/dispatch/drain loop
|
|
642
|
-
│ ├── cron.py # pg_cron sync + InProcScheduler
|
|
643
|
-
│ └── router.py # /jobs/v1/*
|
|
644
|
-
├── admin/
|
|
645
|
-
│ ├── session.py # admin cookie session (SHA-256 hashed, 8h TTL)
|
|
646
|
-
│ ├── deps.py # require_admin dependency
|
|
647
|
-
│ ├── spa.py # static SPA mount + index.html fallback
|
|
648
|
-
│ ├── schemas.py # shared Pydantic models
|
|
649
|
-
│ ├── audit.py # admin audit log writer
|
|
650
|
-
│ ├── static/ # pre-built Vue 3 SPA bundle (committed)
|
|
651
|
-
│ └── api/ # /admin/api/v1/* route handlers
|
|
652
|
-
├── backups/ # pg_dump wrapper + restore
|
|
653
|
-
├── gen/ # supython gen types --lang py|ts
|
|
654
|
-
├── scaffold/ # supython init templates
|
|
655
|
-
└── client/ # Python SDK (optional [client] extra)
|
|
544
|
+
├── src/supython/ # the FastAPI service + CLI (the published package)
|
|
545
|
+
│ ├── app.py # FastAPI factory
|
|
546
|
+
│ ├── cli.py # typer CLI: init, up, dev, worker, migrate, gen, …
|
|
547
|
+
│ ├── db.py # asyncpg pool + as_role() / as_service_role()
|
|
548
|
+
│ ├── settings.py # pydantic-settings, .env-driven
|
|
549
|
+
│ ├── tokens.py # RS256/ES256 JWT + JWKS
|
|
550
|
+
│ ├── migrate.py # ~50-line SQL migration runner
|
|
551
|
+
│ ├── migrations/ # framework schema (auth, storage, realtime, jobs, …)
|
|
552
|
+
│ ├── auth/ # /auth/v1/* — signup, OAuth, OTP, recovery
|
|
553
|
+
│ ├── storage/ # /storage/v1/* — local + S3 backends
|
|
554
|
+
│ ├── functions/ # /functions/v1/* — filesystem-discovered handlers
|
|
555
|
+
│ ├── realtime/ # /realtime/v1/* — Phoenix Channels over LISTEN/NOTIFY
|
|
556
|
+
│ ├── jobs/ # /jobs/v1/* — queue, worker, pg_cron scheduling
|
|
557
|
+
│ ├── admin/ # /admin/api/v1/* + the built Vue SPA (static/)
|
|
558
|
+
│ ├── backups/ # pg_dump wrapper + restore
|
|
559
|
+
│ ├── gen/ # `supython gen types --lang py|ts`
|
|
560
|
+
│ ├── scaffold/ # `supython init` templates
|
|
561
|
+
│ └── client/ # optional Python SDK ([client] extra)
|
|
562
|
+
├── admin-ui/ # Vue 3 + Vite SPA, built → src/supython/admin/static/
|
|
563
|
+
├── ts-sdk/ # @supython/sdk — TypeScript SDK
|
|
564
|
+
├── dev-app/ # scaffolded sample project for local development
|
|
565
|
+
├── examples/ # .http smoke tests + chat.html realtime demo
|
|
566
|
+
├── tests/ # unit/ (no Docker) + integration/ (Postgres :54323)
|
|
567
|
+
├── Dockerfile
|
|
568
|
+
└── pyproject.toml
|
|
656
569
|
```
|
|
657
570
|
|
|
658
571
|
## Plugins & extensions
|
|
@@ -692,10 +605,10 @@ supython down # stop the stack (keeps data)
|
|
|
692
605
|
supython down --prod # stop the prod stack
|
|
693
606
|
supython reset # stop the stack and DELETE the volume (destructive)
|
|
694
607
|
supython reset --prod # stop prod stack and DELETE volumes
|
|
695
|
-
supython migrate # apply
|
|
608
|
+
supython migrate # apply supython's framework migrations (auth, storage, …)
|
|
696
609
|
supython info # print resolved settings
|
|
697
610
|
supython doctor # diagnose roles, extensions, JWKS, grants, migration drift
|
|
698
|
-
supython init <
|
|
611
|
+
supython init <pkg> [dir] # scaffold a project (pkg = package name; dir defaults to ./<pkg>)
|
|
699
612
|
supython gen types --lang py --out db_schema.py # emit typed dataclasses + TypedDicts
|
|
700
613
|
supython gen types --lang ts --out types.ts # emit TypeScript Database interface
|
|
701
614
|
|
|
@@ -818,25 +731,6 @@ unit tests always run in isolation.
|
|
|
818
731
|
**CI:** runners with Docker run `supython test up && supython test run`;
|
|
819
732
|
runners without Docker run `pytest tests/unit` for a meaningful subset.
|
|
820
733
|
|
|
821
|
-
## Roadmap
|
|
822
|
-
|
|
823
|
-
- ✅ Email/password auth, PostgREST contract, RLS demo
|
|
824
|
-
- ✅ OAuth, password reset, magic link, OTP, reuse detection, email backend, test suite
|
|
825
|
-
- ✅ Storage (S3/local) with RLS-on-metadata, edge-style functions from a `functions/` directory; `db.as_role(role, claims)` helper; `supython init` scaffold; `supython gen types --lang py`
|
|
826
|
-
- ✅ Realtime over `LISTEN/NOTIFY` with RLS-aware fan-out; Phoenix Channels wire format; broadcast + presence; `examples/chat.html` demo
|
|
827
|
-
- ✅ Job queue worker + `pg_cron` scheduling + hooks + CLI management commands
|
|
828
|
-
- ✅ Grooming + security foundation: unified versioning, CORS closed by default, RS256 JWT, rate limiting, `supython doctor`, pool sizing, statement timeout
|
|
829
|
-
- ✅ Production observable: structured JSON logs, `/livez`/`/readyz`/`/health`, security headers, input size guards, audit log completeness, OAuth PKCE, secret rotation runbooks
|
|
830
|
-
- ✅ (partial) Multi-arch Docker images, admin control plane (Vue 3 SPA — database, auth, storage, functions, realtime, jobs, backups, log tail), CI buildx workflow; benchmarks + security audit pass + dependency budget CI remaining
|
|
831
|
-
- *(deferred)* — Realtime v2 over logical replication
|
|
832
|
-
- ✅ **TypeScript SDK** — `@supython/sdk` wrapping `@supabase/postgrest-js` + `@supabase/realtime-js`
|
|
833
|
-
|
|
834
|
-
### Future
|
|
835
|
-
|
|
836
|
-
- **Admin control plane** polish (tests + remaining DoD items)
|
|
837
|
-
- **Realtime v2** — logical replication (demand-driven; swap when trigger overhead or >8KB payload data warrants it)
|
|
838
|
-
- **Prometheus `/metrics`** + **OpenTelemetry** — optional extras
|
|
839
|
-
|
|
840
734
|
## License
|
|
841
735
|
|
|
842
736
|
MIT
|
|
@@ -94,20 +94,25 @@ python -m venv .venv && source .venv/bin/activate
|
|
|
94
94
|
pip install supython
|
|
95
95
|
|
|
96
96
|
# 2. scaffold a new project
|
|
97
|
+
# `myapp` is the importable package name. An optional second argument
|
|
98
|
+
# sets the target dir (`.` = current dir); it defaults to ./myapp.
|
|
97
99
|
supython init myapp
|
|
98
100
|
cd myapp
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# the value PostgREST will use (docker-compose.yml injects it via env).
|
|
101
|
+
# install the project (and supython, pinned in the generated pyproject.toml)
|
|
102
|
+
pip install -e .
|
|
102
103
|
#
|
|
103
|
-
# The scaffold
|
|
104
|
-
#
|
|
104
|
+
# The scaffold generates a ready-to-run, gitignored .env plus a JWT keypair
|
|
105
|
+
# and signing secrets under .supython/ — no manual edits needed to boot.
|
|
106
|
+
# It also creates:
|
|
107
|
+
# pyproject.toml — declares this project, pins the supython version
|
|
105
108
|
# myapp/settings.py — declare EXTENSIONS, EXTRA_ROUTERS, EXTRA_MIDDLEWARE
|
|
106
|
-
# myapp/jobs
|
|
107
|
-
# myapp/hooks
|
|
109
|
+
# myapp/jobs/ — package of @job / @cron modules (all auto-imported)
|
|
110
|
+
# myapp/hooks/ — package of @on(...) lifecycle hooks (all auto-imported)
|
|
108
111
|
# myapp/asgi.py — optional entrypoint for uvicorn/gunicorn
|
|
112
|
+
# functions/hello.py — example edge function (GET /functions/hello)
|
|
113
|
+
# migrations/0001_create_todos.sql — example app migration (apply with dbmate)
|
|
109
114
|
|
|
110
|
-
# 3. boot Postgres + PostgREST and
|
|
115
|
+
# 3. boot Postgres + PostgREST and apply supython's framework migrations
|
|
111
116
|
supython up
|
|
112
117
|
|
|
113
118
|
# 4. run the auth/API service (separate terminal)
|
|
@@ -132,6 +137,10 @@ You should now have:
|
|
|
132
137
|
|
|
133
138
|
## End-to-end smoke test
|
|
134
139
|
|
|
140
|
+
This uses the scaffolded `todos` table, so first apply the example app migration
|
|
141
|
+
(`migrations/0001_create_todos.sql`) with [dbmate](https://github.com/amacneil/dbmate)
|
|
142
|
+
or your migration tool of choice — `supython migrate` only handles framework schemas.
|
|
143
|
+
|
|
135
144
|
```bash
|
|
136
145
|
# sign up
|
|
137
146
|
curl -sS -X POST http://localhost:8000/auth/v1/signup \
|
|
@@ -473,128 +482,31 @@ need to edit SQL.
|
|
|
473
482
|
|
|
474
483
|
```
|
|
475
484
|
supython/
|
|
476
|
-
├──
|
|
477
|
-
├──
|
|
478
|
-
├──
|
|
479
|
-
├──
|
|
480
|
-
├──
|
|
481
|
-
├──
|
|
482
|
-
│ ├──
|
|
483
|
-
│ ├──
|
|
484
|
-
│ ├──
|
|
485
|
-
│ ├──
|
|
486
|
-
│
|
|
487
|
-
├──
|
|
488
|
-
│ ├──
|
|
489
|
-
│ ├──
|
|
490
|
-
│ ├──
|
|
491
|
-
│ ├──
|
|
492
|
-
│ ├──
|
|
493
|
-
│
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
│ ├── 0014_realtime_payload_warning.sql # >8KB payload warning counter
|
|
502
|
-
│ └── 0015_backups_schema.sql # backups metadata
|
|
503
|
-
├── examples/
|
|
504
|
-
│ ├── todos.http # HTTP smoke tests (auth + PostgREST)
|
|
505
|
-
│ ├── storage.http # storage upload / signed URL examples
|
|
506
|
-
│ ├── functions.http # edge-function call examples
|
|
507
|
-
│ └── chat.html # two-browser realtime demo (vanilla JS, zero deps)
|
|
508
|
-
├── docs/
|
|
509
|
-
│ ├── PROJECT.md # architecture + roadmap (single source of truth)
|
|
510
|
-
│ ├── Installation.md # full install guide (dev, prod, managed Postgres)
|
|
511
|
-
│ └── admin-ui/
|
|
512
|
-
│ ├── admin-surface-plan.md # admin implementation plan + phase status
|
|
513
|
-
│ └── admin-surface.md # admin architecture + contracts
|
|
514
|
-
├── tests/
|
|
515
|
-
│ ├── conftest.py # cross-tree fixtures (keys, capturing mailer)
|
|
516
|
-
│ ├── _keys.py # JWT-forging helpers
|
|
517
|
-
│ ├── fixtures/ # test function modules, etc.
|
|
518
|
-
│ ├── unit/ # pure-Python tests (~6s, no Docker)
|
|
519
|
-
│ │ ├── test_admin_session.py
|
|
520
|
-
│ │ ├── test_admin_service_db.py
|
|
521
|
-
│ │ └── ...
|
|
522
|
-
│ └── integration/ # full ASGI + Postgres on port 54323
|
|
523
|
-
│ ├── conftest.py # pool, app, client, autouse DB cleaners
|
|
524
|
-
│ ├── test_auth_signup_login.py
|
|
525
|
-
│ ├── test_admin_auth.py
|
|
526
|
-
│ ├── test_admin_auth_users.py
|
|
527
|
-
│ ├── test_admin_db_rows.py
|
|
528
|
-
│ ├── test_admin_jobs.py
|
|
529
|
-
│ ├── test_admin_ops_backups.py
|
|
530
|
-
│ ├── test_admin_storage.py
|
|
531
|
-
│ ├── test_postgrest_rls.py
|
|
532
|
-
│ ├── test_realtime_ws.py
|
|
533
|
-
│ └── ...
|
|
534
|
-
├── admin-ui/ # Vue 3 + Vite SPA (built → src/supython/admin/static/)
|
|
535
|
-
│ └── src/
|
|
536
|
-
│ ├── api/ # single fetch seam
|
|
537
|
-
│ ├── components/ # shell, data, editors, feedback
|
|
538
|
-
│ ├── composables/ # useResource, useTable, useConfirm, useImpersonate, …
|
|
539
|
-
│ ├── stores/ # auth, ui
|
|
540
|
-
│ ├── views/ # Dashboard, db/, auth/, storage/, functions/, …
|
|
541
|
-
│ └── router/
|
|
542
|
-
└── src/supython/
|
|
543
|
-
├── __init__.py # single version string
|
|
544
|
-
├── settings.py # pydantic-settings, .env-driven
|
|
545
|
-
├── db.py # asyncpg pool + lifespan + as_role() / as_service_role()
|
|
546
|
-
├── mailer.py # ConsoleBackend / SmtpBackend
|
|
547
|
-
├── tokens.py # RS256/ES256 JWT + JWKS
|
|
548
|
-
├── passwords.py # argon2id
|
|
549
|
-
├── migrate.py # ~50-line SQL migration runner
|
|
550
|
-
├── app.py # FastAPI factory
|
|
551
|
-
├── cli.py # typer: up, dev, keygen, admin, worker, test, …
|
|
552
|
-
├── extensions.py # eager-import dotted module paths at boot
|
|
553
|
-
├── settings_module.py # user settings (EXTENSIONS, EXTRA_ROUTERS, …)
|
|
554
|
-
├── health.py # /livez, /readyz, /health endpoints
|
|
555
|
-
├── logging_config.py # structured JSON log setup
|
|
556
|
-
├── security_headers.py # HSTS, CSP, etc.
|
|
557
|
-
├── body_size.py # request body size guards
|
|
558
|
-
├── jwks.py # JWKS generation + rotation helpers
|
|
559
|
-
├── keyset.py # asymmetric key rotation manifest
|
|
560
|
-
├── secretset.py # symmetric secret rotation manifest
|
|
561
|
-
├── hooks.py # generic hook system: on() / fire()
|
|
562
|
-
├── mail.py # email send with job-retry fallback
|
|
563
|
-
├── auth/
|
|
564
|
-
│ ├── schemas.py
|
|
565
|
-
│ ├── service.py # full auth layer: signup / OAuth / OTP / recover …
|
|
566
|
-
│ ├── router.py # all /auth/v1/* routes
|
|
567
|
-
│ └── providers/ # Google, GitHub, OAuth2 helpers
|
|
568
|
-
├── storage/
|
|
569
|
-
│ ├── backends.py # LocalBackend, S3Backend
|
|
570
|
-
│ ├── service.py
|
|
571
|
-
│ └── router.py # /storage/v1/*
|
|
572
|
-
├── functions/
|
|
573
|
-
│ ├── loader.py # filesystem discovery + hot reload
|
|
574
|
-
│ └── router.py # /functions/v1/*
|
|
575
|
-
├── realtime/
|
|
576
|
-
│ ├── protocol.py # Phoenix Channels encode/decode
|
|
577
|
-
│ ├── broker.py # fan-out engine with RLS filtering
|
|
578
|
-
│ ├── websocket.py # WS route with JWT auth
|
|
579
|
-
│ └── router.py # /realtime/v1/*
|
|
580
|
-
├── jobs/
|
|
581
|
-
│ ├── registry.py # @job / @cron decorator store
|
|
582
|
-
│ ├── service.py # enqueue, claim_next, mark_*
|
|
583
|
-
│ ├── worker.py # long-running poll/dispatch/drain loop
|
|
584
|
-
│ ├── cron.py # pg_cron sync + InProcScheduler
|
|
585
|
-
│ └── router.py # /jobs/v1/*
|
|
586
|
-
├── admin/
|
|
587
|
-
│ ├── session.py # admin cookie session (SHA-256 hashed, 8h TTL)
|
|
588
|
-
│ ├── deps.py # require_admin dependency
|
|
589
|
-
│ ├── spa.py # static SPA mount + index.html fallback
|
|
590
|
-
│ ├── schemas.py # shared Pydantic models
|
|
591
|
-
│ ├── audit.py # admin audit log writer
|
|
592
|
-
│ ├── static/ # pre-built Vue 3 SPA bundle (committed)
|
|
593
|
-
│ └── api/ # /admin/api/v1/* route handlers
|
|
594
|
-
├── backups/ # pg_dump wrapper + restore
|
|
595
|
-
├── gen/ # supython gen types --lang py|ts
|
|
596
|
-
├── scaffold/ # supython init templates
|
|
597
|
-
└── client/ # Python SDK (optional [client] extra)
|
|
485
|
+
├── src/supython/ # the FastAPI service + CLI (the published package)
|
|
486
|
+
│ ├── app.py # FastAPI factory
|
|
487
|
+
│ ├── cli.py # typer CLI: init, up, dev, worker, migrate, gen, …
|
|
488
|
+
│ ├── db.py # asyncpg pool + as_role() / as_service_role()
|
|
489
|
+
│ ├── settings.py # pydantic-settings, .env-driven
|
|
490
|
+
│ ├── tokens.py # RS256/ES256 JWT + JWKS
|
|
491
|
+
│ ├── migrate.py # ~50-line SQL migration runner
|
|
492
|
+
│ ├── migrations/ # framework schema (auth, storage, realtime, jobs, …)
|
|
493
|
+
│ ├── auth/ # /auth/v1/* — signup, OAuth, OTP, recovery
|
|
494
|
+
│ ├── storage/ # /storage/v1/* — local + S3 backends
|
|
495
|
+
│ ├── functions/ # /functions/v1/* — filesystem-discovered handlers
|
|
496
|
+
│ ├── realtime/ # /realtime/v1/* — Phoenix Channels over LISTEN/NOTIFY
|
|
497
|
+
│ ├── jobs/ # /jobs/v1/* — queue, worker, pg_cron scheduling
|
|
498
|
+
│ ├── admin/ # /admin/api/v1/* + the built Vue SPA (static/)
|
|
499
|
+
│ ├── backups/ # pg_dump wrapper + restore
|
|
500
|
+
│ ├── gen/ # `supython gen types --lang py|ts`
|
|
501
|
+
│ ├── scaffold/ # `supython init` templates
|
|
502
|
+
│ └── client/ # optional Python SDK ([client] extra)
|
|
503
|
+
├── admin-ui/ # Vue 3 + Vite SPA, built → src/supython/admin/static/
|
|
504
|
+
├── ts-sdk/ # @supython/sdk — TypeScript SDK
|
|
505
|
+
├── dev-app/ # scaffolded sample project for local development
|
|
506
|
+
├── examples/ # .http smoke tests + chat.html realtime demo
|
|
507
|
+
├── tests/ # unit/ (no Docker) + integration/ (Postgres :54323)
|
|
508
|
+
├── Dockerfile
|
|
509
|
+
└── pyproject.toml
|
|
598
510
|
```
|
|
599
511
|
|
|
600
512
|
## Plugins & extensions
|
|
@@ -634,10 +546,10 @@ supython down # stop the stack (keeps data)
|
|
|
634
546
|
supython down --prod # stop the prod stack
|
|
635
547
|
supython reset # stop the stack and DELETE the volume (destructive)
|
|
636
548
|
supython reset --prod # stop prod stack and DELETE volumes
|
|
637
|
-
supython migrate # apply
|
|
549
|
+
supython migrate # apply supython's framework migrations (auth, storage, …)
|
|
638
550
|
supython info # print resolved settings
|
|
639
551
|
supython doctor # diagnose roles, extensions, JWKS, grants, migration drift
|
|
640
|
-
supython init <
|
|
552
|
+
supython init <pkg> [dir] # scaffold a project (pkg = package name; dir defaults to ./<pkg>)
|
|
641
553
|
supython gen types --lang py --out db_schema.py # emit typed dataclasses + TypedDicts
|
|
642
554
|
supython gen types --lang ts --out types.ts # emit TypeScript Database interface
|
|
643
555
|
|
|
@@ -760,25 +672,6 @@ unit tests always run in isolation.
|
|
|
760
672
|
**CI:** runners with Docker run `supython test up && supython test run`;
|
|
761
673
|
runners without Docker run `pytest tests/unit` for a meaningful subset.
|
|
762
674
|
|
|
763
|
-
## Roadmap
|
|
764
|
-
|
|
765
|
-
- ✅ Email/password auth, PostgREST contract, RLS demo
|
|
766
|
-
- ✅ OAuth, password reset, magic link, OTP, reuse detection, email backend, test suite
|
|
767
|
-
- ✅ Storage (S3/local) with RLS-on-metadata, edge-style functions from a `functions/` directory; `db.as_role(role, claims)` helper; `supython init` scaffold; `supython gen types --lang py`
|
|
768
|
-
- ✅ Realtime over `LISTEN/NOTIFY` with RLS-aware fan-out; Phoenix Channels wire format; broadcast + presence; `examples/chat.html` demo
|
|
769
|
-
- ✅ Job queue worker + `pg_cron` scheduling + hooks + CLI management commands
|
|
770
|
-
- ✅ Grooming + security foundation: unified versioning, CORS closed by default, RS256 JWT, rate limiting, `supython doctor`, pool sizing, statement timeout
|
|
771
|
-
- ✅ Production observable: structured JSON logs, `/livez`/`/readyz`/`/health`, security headers, input size guards, audit log completeness, OAuth PKCE, secret rotation runbooks
|
|
772
|
-
- ✅ (partial) Multi-arch Docker images, admin control plane (Vue 3 SPA — database, auth, storage, functions, realtime, jobs, backups, log tail), CI buildx workflow; benchmarks + security audit pass + dependency budget CI remaining
|
|
773
|
-
- *(deferred)* — Realtime v2 over logical replication
|
|
774
|
-
- ✅ **TypeScript SDK** — `@supython/sdk` wrapping `@supabase/postgrest-js` + `@supabase/realtime-js`
|
|
775
|
-
|
|
776
|
-
### Future
|
|
777
|
-
|
|
778
|
-
- **Admin control plane** polish (tests + remaining DoD items)
|
|
779
|
-
- **Realtime v2** — logical replication (demand-driven; swap when trigger overhead or >8KB payload data warrants it)
|
|
780
|
-
- **Prometheus `/metrics`** + **OpenTelemetry** — optional extras
|
|
781
|
-
|
|
782
675
|
## License
|
|
783
676
|
|
|
784
677
|
MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "supython"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.11"
|
|
8
8
|
description = "A lightweight Postgres-first BaaS framework for Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -32,6 +32,7 @@ dependencies = [
|
|
|
32
32
|
"asyncpg>=0.30",
|
|
33
33
|
"pydantic>=2.9",
|
|
34
34
|
"pydantic-settings>=2.6",
|
|
35
|
+
"python-dotenv>=1.0",
|
|
35
36
|
"email-validator>=2.2",
|
|
36
37
|
"argon2-cffi>=23.1",
|
|
37
38
|
"pyjwt[crypto]>=2.10",
|
|
@@ -27,7 +27,7 @@ from .body_size import BodySizeLimitMiddleware
|
|
|
27
27
|
from .security_headers import SecurityHeadersMiddleware
|
|
28
28
|
from .realtime import get_broker
|
|
29
29
|
from .realtime.router import router as realtime_router
|
|
30
|
-
from .settings import get_settings
|
|
30
|
+
from .settings import export_env_file, get_settings
|
|
31
31
|
from .storage.router import router as storage_router
|
|
32
32
|
|
|
33
33
|
logger = logging.getLogger(__name__)
|
|
@@ -96,6 +96,7 @@ async def _lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def create_app() -> FastAPI:
|
|
99
|
+
export_env_file()
|
|
99
100
|
settings = get_settings()
|
|
100
101
|
configure_logging(settings.log_level, json_format=settings.log_json)
|
|
101
102
|
|