svc-infra 0.1.594__tar.gz → 0.1.595__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.

Potentially problematic release.


This version of svc-infra might be problematic. Click here for more details.

Files changed (253) hide show
  1. {svc_infra-0.1.594 → svc_infra-0.1.595}/PKG-INFO +1 -1
  2. {svc_infra-0.1.594 → svc_infra-0.1.595}/pyproject.toml +1 -1
  3. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/README.md +26 -0
  4. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/apf_payments/router.py +67 -6
  5. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/routers/oauth_router.py +7 -2
  6. svc_infra-0.1.595/src/svc_infra/api/fastapi/middleware/ratelimit_store.py +78 -0
  7. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/session.py +11 -2
  8. svc_infra-0.1.594/src/svc_infra/api/fastapi/middleware/ratelimit_store.py +0 -30
  9. {svc_infra-0.1.594 → svc_infra-0.1.595}/README.md +0 -0
  10. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/__init__.py +0 -0
  11. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/__init__.py +0 -0
  12. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/alembic.py +0 -0
  13. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/models.py +0 -0
  14. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/provider/__init__.py +0 -0
  15. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/provider/aiydan.py +0 -0
  16. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/provider/base.py +0 -0
  17. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/provider/registry.py +0 -0
  18. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/provider/stripe.py +0 -0
  19. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/schemas.py +0 -0
  20. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/service.py +0 -0
  21. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/apf_payments/settings.py +0 -0
  22. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/__init__.py +0 -0
  23. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/__init__.py +0 -0
  24. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/apf_payments/__init__.py +0 -0
  25. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/apf_payments/setup.py +0 -0
  26. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/__init__.py +0 -0
  27. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/_cookies.py +0 -0
  28. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/add.py +0 -0
  29. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/gaurd.py +0 -0
  30. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/__init__.py +0 -0
  31. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/models.py +0 -0
  32. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/pre_auth.py +0 -0
  33. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/router.py +0 -0
  34. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/security.py +0 -0
  35. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/utils.py +0 -0
  36. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/mfa/verify.py +0 -0
  37. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/policy.py +0 -0
  38. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/providers.py +0 -0
  39. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/routers/__init__.py +0 -0
  40. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/routers/account.py +0 -0
  41. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/routers/apikey_router.py +0 -0
  42. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/routers/session_router.py +0 -0
  43. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/security.py +0 -0
  44. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/sender.py +0 -0
  45. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/settings.py +0 -0
  46. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/auth/state.py +0 -0
  47. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/cache/__init__.py +0 -0
  48. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/cache/add.py +0 -0
  49. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/__init__.py +0 -0
  50. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/http.py +0 -0
  51. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/nosql/__init__.py +0 -0
  52. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/nosql/mongo/__init__.py +0 -0
  53. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/nosql/mongo/add.py +0 -0
  54. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/nosql/mongo/crud_router.py +0 -0
  55. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/nosql/mongo/health.py +0 -0
  56. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/README.md +0 -0
  57. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/__init__.py +0 -0
  58. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/add.py +0 -0
  59. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/crud_router.py +0 -0
  60. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/health.py +0 -0
  61. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/session.py +0 -0
  62. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/db/sql/users.py +0 -0
  63. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dependencies/ratelimit.py +0 -0
  64. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/docs/__init__.py +0 -0
  65. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/docs/landing.py +0 -0
  66. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/docs/scoped.py +0 -0
  67. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dual/__init__.py +0 -0
  68. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dual/dualize.py +0 -0
  69. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dual/protected.py +0 -0
  70. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dual/public.py +0 -0
  71. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dual/router.py +0 -0
  72. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dual/utils.py +0 -0
  73. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/dx.py +0 -0
  74. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/ease.py +0 -0
  75. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/http/__init__.py +0 -0
  76. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/http/concurrency.py +0 -0
  77. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/http/conditional.py +0 -0
  78. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/http/deprecation.py +0 -0
  79. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/__init__.py +0 -0
  80. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/debug.py +0 -0
  81. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/errors/__init__.py +0 -0
  82. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/errors/catchall.py +0 -0
  83. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/errors/exceptions.py +0 -0
  84. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/errors/handlers.py +0 -0
  85. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/idempotency.py +0 -0
  86. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/ratelimit.py +0 -0
  87. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/request_id.py +0 -0
  88. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/middleware/request_size_limit.py +0 -0
  89. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/__init__.py +0 -0
  90. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/apply.py +0 -0
  91. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/conventions.py +0 -0
  92. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/models.py +0 -0
  93. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/mutators.py +0 -0
  94. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/pipeline.py +0 -0
  95. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/responses.py +0 -0
  96. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/openapi/security.py +0 -0
  97. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/pagination.py +0 -0
  98. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/paths/__init__.py +0 -0
  99. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/paths/auth.py +0 -0
  100. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/paths/generic.py +0 -0
  101. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/paths/prefix.py +0 -0
  102. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/paths/user.py +0 -0
  103. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/routers/__init__.py +0 -0
  104. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/routers/ping.py +0 -0
  105. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/api/fastapi/setup.py +0 -0
  106. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/README.md +0 -0
  107. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/__init__.py +0 -0
  108. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/env.py +0 -0
  109. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/logging/__init__.py +0 -0
  110. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/logging/add.py +0 -0
  111. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/logging/filter.py +0 -0
  112. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/logging/formats.py +0 -0
  113. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/app/root.py +0 -0
  114. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/README.md +0 -0
  115. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/__init__.py +0 -0
  116. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/backend.py +0 -0
  117. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/decorators.py +0 -0
  118. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/demo.py +0 -0
  119. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/keys.py +0 -0
  120. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/recache.py +0 -0
  121. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/resources.py +0 -0
  122. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/tags.py +0 -0
  123. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/ttl.py +0 -0
  124. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cache/utils.py +0 -0
  125. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/__init__.py +0 -0
  126. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/__init__.py +0 -0
  127. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/__init__.py +0 -0
  128. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/nosql/__init__.py +0 -0
  129. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/nosql/mongo/README.md +0 -0
  130. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/nosql/mongo/__init__.py +0 -0
  131. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +0 -0
  132. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +0 -0
  133. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/sql/__init__.py +0 -0
  134. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/sql/alembic_cmds.py +0 -0
  135. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +0 -0
  136. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/help.py +0 -0
  137. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/obs/__init__.py +0 -0
  138. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/cmds/obs/obs_cmds.py +0 -0
  139. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/foundation/__init__.py +0 -0
  140. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/foundation/runner.py +0 -0
  141. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/cli/foundation/typer_bootstrap.py +0 -0
  142. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/__init__.py +0 -0
  143. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/crud_schema.py +0 -0
  144. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/__init__.py +0 -0
  145. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/base.py +0 -0
  146. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/constants.py +0 -0
  147. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/core.py +0 -0
  148. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/indexes.py +0 -0
  149. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/management.py +0 -0
  150. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/README.md +0 -0
  151. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/__init__.py +0 -0
  152. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/client.py +0 -0
  153. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/settings.py +0 -0
  154. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/templates/__init__.py +0 -0
  155. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/templates/documents.py.tmpl +0 -0
  156. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/templates/resources.py.tmpl +0 -0
  157. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/mongo/templates/schemas.py.tmpl +0 -0
  158. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/repository.py +0 -0
  159. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/resource.py +0 -0
  160. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/scaffold.py +0 -0
  161. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/service.py +0 -0
  162. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/service_with_hooks.py +0 -0
  163. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/types.py +0 -0
  164. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/nosql/utils.py +0 -0
  165. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/README.md +0 -0
  166. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/__init__.py +0 -0
  167. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/apikey.py +0 -0
  168. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/authref.py +0 -0
  169. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/base.py +0 -0
  170. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/constants.py +0 -0
  171. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/core.py +0 -0
  172. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/management.py +0 -0
  173. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/repository.py +0 -0
  174. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/resource.py +0 -0
  175. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/scaffold.py +0 -0
  176. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/service.py +0 -0
  177. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/service_with_hooks.py +0 -0
  178. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/__init__.py +0 -0
  179. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/models_schemas/__init__.py +0 -0
  180. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/models_schemas/auth/models.py.tmpl +0 -0
  181. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/models_schemas/auth/schemas.py.tmpl +0 -0
  182. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/models_schemas/entity/models.py.tmpl +0 -0
  183. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/models_schemas/entity/schemas.py.tmpl +0 -0
  184. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/setup/__init__.py +0 -0
  185. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/setup/alembic.ini.tmpl +0 -0
  186. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/setup/env_async.py.tmpl +0 -0
  187. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/setup/env_sync.py.tmpl +0 -0
  188. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/templates/setup/script.py.mako.tmpl +0 -0
  189. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/types.py +0 -0
  190. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/uniq.py +0 -0
  191. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/uniq_hooks.py +0 -0
  192. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/sql/utils.py +0 -0
  193. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/db/utils.py +0 -0
  194. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/mcp/__init__.py +0 -0
  195. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/mcp/svc_infra_mcp.py +0 -0
  196. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/README.md +0 -0
  197. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/__init__.py +0 -0
  198. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/add.py +0 -0
  199. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/cloud_dash.py +0 -0
  200. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/metrics/__init__.py +0 -0
  201. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/metrics/asgi.py +0 -0
  202. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/metrics/base.py +0 -0
  203. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/metrics/http.py +0 -0
  204. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/metrics/sqlalchemy.py +0 -0
  205. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/metrics.py +0 -0
  206. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/__init__.py +0 -0
  207. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/compose_cloud/__init__.py +0 -0
  208. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/compose_cloud/templates/__init__.py +0 -0
  209. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/compose_cloud/templates/agent.yaml.tmpl +0 -0
  210. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/compose_cloud/templates/docker-compose.cloud.yml.tmpl +0 -0
  211. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/__init__.py +0 -0
  212. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/dashboards/00_overview.json +0 -0
  213. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/dashboards/10_http.json +0 -0
  214. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/dashboards/20_db.json +0 -0
  215. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/dashboards/30_runtime.json +0 -0
  216. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/dashboards/40_clients.json +0 -0
  217. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/dashboards/__init__.py +0 -0
  218. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/templates/__init__.py +0 -0
  219. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/templates/docker-compose.yml.tmpl +0 -0
  220. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/templates/prometheus.yml.tmpl +0 -0
  221. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/templates/provisioning/__init__.py +0 -0
  222. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/templates/provisioning/dashboards.yml +0 -0
  223. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/providers/grafana/templates/provisioning/datasource.yml +0 -0
  224. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/settings.py +0 -0
  225. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/grafana_dashboard.json +0 -0
  226. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/prometheus_rules.yml +0 -0
  227. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/__init__.py +0 -0
  228. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/compose/__init__.py +0 -0
  229. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/compose/agent.yaml +0 -0
  230. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/compose/docker-compose.yml +0 -0
  231. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/fly/__init__.py +0 -0
  232. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/fly/agent.yaml +0 -0
  233. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/fly/fly.toml.fragment +0 -0
  234. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/k8s/__init__.py +0 -0
  235. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/k8s/configmap.yaml +0 -0
  236. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/k8s/deployment.yaml +0 -0
  237. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/railway/Dockerfile +0 -0
  238. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/railway/README.md +0 -0
  239. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/railway/__init__.py +0 -0
  240. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/obs/templates/sidecars/railway/agent.yaml +0 -0
  241. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/py.typed +0 -0
  242. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/audit.py +0 -0
  243. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/audit_service.py +0 -0
  244. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/headers.py +0 -0
  245. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/hibp.py +0 -0
  246. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/jwt_rotation.py +0 -0
  247. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/lockout.py +0 -0
  248. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/models.py +0 -0
  249. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/org_invites.py +0 -0
  250. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/passwords.py +0 -0
  251. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/permissions.py +0 -0
  252. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/security/signed_cookies.py +0 -0
  253. {svc_infra-0.1.594 → svc_infra-0.1.595}/src/svc_infra/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.594
3
+ Version: 0.1.595
4
4
  Summary: Infrastructure for building and deploying prod-ready services
5
5
  License: MIT
6
6
  Keywords: fastapi,sqlalchemy,alembic,auth,infra,async,pydantic
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "svc-infra"
3
- version = "0.1.594"
3
+ version = "0.1.595"
4
4
  description = "Infrastructure for building and deploying prod-ready services"
5
5
  authors = ["Ali Khatami <aliikhatami94@gmail.com>"]
6
6
  license = "MIT"
@@ -111,6 +111,32 @@ app = setup_service_api(
111
111
  add_payments(app, prefix="/payments")
112
112
  ```
113
113
 
114
+ **Tenant Context**
115
+
116
+ All payments endpoints require a tenant identifier. The FastAPI router now
117
+ derives it automatically from the authenticated principal:
118
+
119
+ - API key principals → ``principal.api_key.tenant_id``
120
+ - User principals → ``principal.user.tenant_id``
121
+ - Fallbacks: ``X-Tenant-Id`` request header or ``request.state.tenant_id``
122
+
123
+ If you need custom mapping logic (for example, translating API keys to an
124
+ external tenant registry), register an override during startup:
125
+
126
+ ```python
127
+ from svc_infra.api.fastapi.apf_payments.router import set_payments_tenant_resolver
128
+
129
+ async def resolve_tenant(request, identity, header):
130
+ # return a string tenant id, or None to fall back to the defaults
131
+ return "tenant-from-custom-logic"
132
+
133
+ set_payments_tenant_resolver(resolve_tenant)
134
+ ```
135
+
136
+ If no tenant can be derived (and the override also returns ``None``), the
137
+ router responds with ``400 tenant_context_missing`` so callers can supply the
138
+ missing context explicitly.
139
+
114
140
  **Environment-Based Configuration**
115
141
 
116
142
  The `easy_service_app` reads these env vars automatically:
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Literal, Optional, cast
3
+ import inspect
4
+ from typing import Annotated, Awaitable, Callable, Literal, Optional, cast
4
5
 
5
- from fastapi import Body, Depends, Header, Request, Response, status
6
+ from fastapi import Body, Depends, Header, HTTPException, Request, Response, status
6
7
  from starlette.responses import JSONResponse
7
8
 
8
9
  from svc_infra.apf_payments.schemas import (
@@ -47,6 +48,7 @@ from svc_infra.apf_payments.schemas import (
47
48
  WebhookReplayOut,
48
49
  )
49
50
  from svc_infra.apf_payments.service import PaymentsService
51
+ from svc_infra.api.fastapi.auth.security import OptionalIdentity, Principal
50
52
  from svc_infra.api.fastapi.db.sql.session import SqlSessionDep
51
53
  from svc_infra.api.fastapi.dual import protected_router, public_router, service_router, user_router
52
54
  from svc_infra.api.fastapi.dual.router import DualAPIRouter
@@ -69,10 +71,69 @@ def _tx_kind(kind: str) -> Literal["payment", "refund", "fee", "payout", "captur
69
71
 
70
72
 
71
73
  # --- deps ---
72
- async def get_service(session: SqlSessionDep) -> PaymentsService:
73
- # TODO: derive tenant_id from auth/session context; placeholder requires explicit value.
74
- # For now, use a fixed test tenant id; production integration must override.
75
- return PaymentsService(session=session, tenant_id="test_tenant")
74
+ TenantOverrideHook = Callable[
75
+ [Request, Optional[Principal], Optional[str]],
76
+ Awaitable[Optional[str]] | Optional[str],
77
+ ]
78
+
79
+ _tenant_override_hook: TenantOverrideHook | None = None
80
+
81
+
82
+ def set_payments_tenant_resolver(resolver: TenantOverrideHook | None) -> None:
83
+ """Override the default tenant resolution used by the payments router.
84
+
85
+ Projects can call this during startup to plug custom logic (e.g. multi-tenant
86
+ mappings). Passing ``None`` resets to the built-in behavior.
87
+ """
88
+
89
+ global _tenant_override_hook
90
+ _tenant_override_hook = resolver
91
+
92
+
93
+ async def resolve_payments_tenant_id(
94
+ request: Request,
95
+ identity: OptionalIdentity = None,
96
+ tenant_header: Annotated[Optional[str], Header(alias="X-Tenant-Id", default=None)] = None,
97
+ ) -> str:
98
+ """Determine the tenant id for the current request.
99
+
100
+ The default strategy prefers authenticated principals (API keys first, then
101
+ user accounts) and falls back to the ``X-Tenant-Id`` header or ``request.state``.
102
+ Applications may override the behavior via
103
+ :func:`set_payments_tenant_resolver`.
104
+ """
105
+
106
+ if _tenant_override_hook is not None:
107
+ maybe = _tenant_override_hook(request, identity, tenant_header)
108
+ if inspect.isawaitable(maybe): # pragma: no cover - depends on override type
109
+ maybe = await maybe
110
+ if maybe is not None:
111
+ return maybe
112
+
113
+ if identity:
114
+ api_key_tenant = getattr(getattr(identity, "api_key", None), "tenant_id", None)
115
+ if api_key_tenant:
116
+ return api_key_tenant
117
+
118
+ user_tenant = getattr(getattr(identity, "user", None), "tenant_id", None)
119
+ if user_tenant:
120
+ return user_tenant
121
+
122
+ if tenant_header:
123
+ return tenant_header
124
+
125
+ state_tenant = getattr(request.state, "tenant_id", None)
126
+ if state_tenant:
127
+ return state_tenant
128
+
129
+ raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="tenant_context_missing")
130
+
131
+
132
+ PaymentsTenantDep = Annotated[str, Depends(resolve_payments_tenant_id)]
133
+
134
+
135
+ async def get_service(session: SqlSessionDep, tenant_id: PaymentsTenantDep) -> PaymentsService:
136
+ return PaymentsService(session=session, tenant_id=tenant_id)
76
137
 
77
138
 
78
139
  # --- routers grouped by auth posture (same prefix is fine; FastAPI merges) ---
@@ -738,12 +738,15 @@ def _create_oauth_router(
738
738
  raise HTTPException(401, "invalid_refresh_token")
739
739
 
740
740
  # Rotate refresh token
741
- new_raw, _new_rt = await rotate_session_refresh(session, current=found)
741
+ try:
742
+ new_raw, _new_rt = await rotate_session_refresh(session, current=found)
743
+ except ValueError:
744
+ # Token expired between validation and rotation; treat as invalid
745
+ raise HTTPException(401, "invalid_refresh_token") from None
742
746
 
743
747
  # Write response (204) with new cookies
744
748
  resp = Response(status_code=status.HTTP_204_NO_CONTENT)
745
749
  await _set_cookie_on_response(resp, auth_backend, user, refresh_raw=new_raw)
746
- return resp
747
750
 
748
751
  # Dead code removed: MFA branch handled earlier in login flow, refresh returns 204 above.
749
752
  if hasattr(policy, "on_token_refresh"):
@@ -752,5 +755,7 @@ def _create_oauth_router(
752
755
  except Exception:
753
756
  pass
754
757
 
758
+ return resp
759
+
755
760
  # Return router at end of factory
756
761
  return router
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Optional, Protocol, Tuple
5
+
6
+
7
+ class RateLimitStore(Protocol):
8
+ def incr(self, key: str, window: int) -> Tuple[int, int, int]:
9
+ """Increment and return (count, limit, resetEpoch).
10
+
11
+ Implementations should manage per-window buckets. The 'limit' is stored configuration.
12
+ """
13
+ ...
14
+
15
+
16
+ class InMemoryRateLimitStore:
17
+ def __init__(self, limit: int = 120):
18
+ self.limit = limit
19
+ self._buckets: dict[tuple[str, int], int] = {}
20
+
21
+ def incr(self, key: str, window: int) -> Tuple[int, int, int]:
22
+ now = int(time.time())
23
+ win = now - (now % window)
24
+ count = self._buckets.get((key, win), 0) + 1
25
+ self._buckets[(key, win)] = count
26
+ reset = win + window
27
+ return count, self.limit, reset
28
+
29
+
30
+ class RedisRateLimitStore:
31
+ """Fixed-window counter store using Redis.
32
+
33
+ Keys are of the form: {prefix}:{key}:{windowStart}
34
+ Values are incremented and expire automatically at window end.
35
+
36
+ This implementation uses atomic INCR and EXPIRE semantics. To avoid race conditions
37
+ on first-set expiry, we set expiry when the counter is created.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ redis_client,
43
+ *,
44
+ limit: int = 120,
45
+ prefix: str = "ratelimit",
46
+ clock: Optional[callable] = None,
47
+ ):
48
+ self.redis = redis_client
49
+ self.limit = limit
50
+ self.prefix = prefix
51
+ self._clock = clock or time.time
52
+
53
+ def _window_key(self, key: str, window: int) -> tuple[str, int, str]:
54
+ now = int(self._clock())
55
+ win = now - (now % window)
56
+ redis_key = f"{self.prefix}:{key}:{win}"
57
+ return redis_key, win, now
58
+
59
+ def incr(self, key: str, window: int) -> Tuple[int, int, int]:
60
+ rkey, win, now = self._window_key(key, window)
61
+ # Increment; if this is the first time we've seen this window key, set expiry to window end
62
+ pipe = self.redis.pipeline()
63
+ pipe.incr(rkey)
64
+ pipe.ttl(rkey)
65
+ count, ttl = pipe.execute()
66
+ if ttl == -1: # key exists without expire or just created; set expire to end of window
67
+ expire_sec = (win + window) - now
68
+ if expire_sec <= 0:
69
+ expire_sec = window
70
+ try:
71
+ self.redis.expire(rkey, expire_sec)
72
+ except Exception:
73
+ pass
74
+ reset = win + window
75
+ return int(count), self.limit, reset
76
+
77
+
78
+ __all__ = ["RateLimitStore", "InMemoryRateLimitStore", "RedisRateLimitStore"]
@@ -64,15 +64,24 @@ async def rotate_session_refresh(
64
64
 
65
65
  Returns: (new_raw_refresh_token, new_refresh_token_model)
66
66
  """
67
+ rotation_ts = datetime.now(timezone.utc)
68
+ if current.revoked_at:
69
+ raise ValueError("refresh token already revoked")
70
+ if current.expires_at and current.expires_at <= rotation_ts:
71
+ raise ValueError("refresh token expired")
67
72
  new_raw, new_hash, expires_at = rotate_refresh_token(
68
73
  current.token_hash, ttl_minutes=ttl_minutes
69
74
  )
70
- current.rotated_at = datetime.now(timezone.utc)
75
+ current.rotated_at = rotation_ts
76
+ current.revoked_at = rotation_ts
77
+ current.revoke_reason = "rotated"
78
+ if current.expires_at is None or current.expires_at > rotation_ts:
79
+ current.expires_at = rotation_ts
71
80
  # create revocation entry for old hash
72
81
  db.add(
73
82
  RefreshTokenRevocation(
74
83
  token_hash=current.token_hash,
75
- revoked_at=current.rotated_at,
84
+ revoked_at=rotation_ts,
76
85
  reason="rotated",
77
86
  )
78
87
  )
@@ -1,30 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import time
4
- from typing import Protocol, Tuple
5
-
6
-
7
- class RateLimitStore(Protocol):
8
- def incr(self, key: str, window: int) -> Tuple[int, int, int]:
9
- """Increment and return (count, limit, resetEpoch).
10
-
11
- Implementations should manage per-window buckets. The 'limit' is stored configuration.
12
- """
13
- ...
14
-
15
-
16
- class InMemoryRateLimitStore:
17
- def __init__(self, limit: int = 120):
18
- self.limit = limit
19
- self._buckets: dict[tuple[str, int], int] = {}
20
-
21
- def incr(self, key: str, window: int) -> Tuple[int, int, int]:
22
- now = int(time.time())
23
- win = now - (now % window)
24
- count = self._buckets.get((key, win), 0) + 1
25
- self._buckets[(key, win)] = count
26
- reset = win + window
27
- return count, self.limit, reset
28
-
29
-
30
- __all__ = ["RateLimitStore", "InMemoryRateLimitStore"]
File without changes