fastapi-startkit 0.67.0__tar.gz → 0.69.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.
Files changed (425) hide show
  1. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/PKG-INFO +1 -1
  2. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/pyproject.toml +1 -1
  3. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/application.py +1 -2
  4. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/config.py +1 -1
  5. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/provider.py +59 -62
  6. fastapi_startkit-0.69.0/src/fastapi_startkit/broadcasting/reverb/channel_manager.py +21 -0
  7. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/reverb/connection.py +10 -3
  8. fastapi_startkit-0.69.0/src/fastapi_startkit/broadcasting/reverb/server.py +119 -0
  9. fastapi_startkit-0.69.0/src/fastapi_startkit/broadcasting/reverb/signature.py +43 -0
  10. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/__init__.py +30 -0
  11. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/array_store.py +62 -0
  12. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/config/__init__.py +45 -0
  13. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/database_store.py +94 -0
  14. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/file_store.py +135 -0
  15. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/lock.py +65 -0
  16. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/manager.py +49 -0
  17. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/memo_store.py +48 -0
  18. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/migrations/2026_10_21_000000_create_caches_table.py +17 -0
  19. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/provider.py +48 -0
  20. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/redis_store.py +85 -0
  21. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/repository.py +124 -0
  22. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/store.py +29 -0
  23. fastapi_startkit-0.69.0/src/fastapi_startkit/cache/tagged_store.py +57 -0
  24. fastapi_startkit-0.69.0/src/fastapi_startkit/console/db_install_command.py +65 -0
  25. fastapi_startkit-0.69.0/src/fastapi_startkit/console/install_command.py +250 -0
  26. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/exceptions/__init__.py +2 -0
  27. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/exceptions/exceptions.py +16 -9
  28. fastapi_startkit-0.69.0/src/fastapi_startkit/facades/Cache.py +29 -0
  29. fastapi_startkit-0.69.0/src/fastapi_startkit/facades/Cache.pyi +25 -0
  30. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Vite.pyi +3 -0
  31. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/config/fastapi.py +1 -0
  32. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/exceptions.py +33 -0
  33. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/providers/fastapi_provider.py +5 -2
  34. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/foundation/app_provider.py +2 -1
  35. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/inertia.py +11 -2
  36. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/plan.py +3 -1
  37. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/DBSeedCommand.py +22 -25
  38. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MakeModelCommand.py +8 -11
  39. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MakeObserverCommand.py +1 -1
  40. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MigrateFreshCommand.py +7 -9
  41. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MigrateRefreshCommand.py +7 -9
  42. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/stubs/create_seed.stub +1 -4
  43. fastapi_startkit-0.69.0/src/fastapi_startkit/masoniteorm/commands/stubs/model.stub +5 -0
  44. fastapi_startkit-0.69.0/src/fastapi_startkit/masoniteorm/commands/stubs/observer.stub +27 -0
  45. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/exceptions.py +3 -2
  46. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/providers/DatabaseProvider.py +4 -0
  47. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/grammars/BaseGrammar.py +1 -6
  48. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/BelongsToMany.py +16 -22
  49. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/platforms/MSSQLPlatform.py +44 -6
  50. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/platforms/PostgresPlatform.py +38 -29
  51. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/drivers/local.py +8 -6
  52. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/drivers/s3.py +9 -7
  53. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/helper.py +7 -0
  54. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/storage.py +7 -0
  55. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/__init__.py +2 -0
  56. fastapi_startkit-0.69.0/src/fastapi_startkit/vite/middleware.py +25 -0
  57. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/providers/provider.py +7 -0
  58. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/vite.py +58 -22
  59. fastapi_startkit-0.67.0/src/fastapi_startkit/broadcasting/reverb/channel_manager.py +0 -17
  60. fastapi_startkit-0.67.0/src/fastapi_startkit/broadcasting/reverb/server.py +0 -59
  61. fastapi_startkit-0.67.0/src/fastapi_startkit/facades/Cache.py +0 -5
  62. fastapi_startkit-0.67.0/src/fastapi_startkit/masoniteorm/commands/stubs/model.stub +0 -9
  63. fastapi_startkit-0.67.0/src/fastapi_startkit/masoniteorm/commands/stubs/observer.stub +0 -101
  64. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/README.md +0 -0
  65. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/.DS_Store +0 -0
  66. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/__init__.py +0 -0
  67. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/__init__.py +0 -0
  68. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/agent.py +0 -0
  69. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/ai.py +0 -0
  70. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/audio.py +0 -0
  71. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/audio_factory.py +0 -0
  72. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/config/ai.py +0 -0
  73. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/config/config.py +0 -0
  74. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/decorators.py +0 -0
  75. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/document.py +0 -0
  76. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/image.py +0 -0
  77. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/image_factory.py +0 -0
  78. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/judge.py +0 -0
  79. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/lab.py +0 -0
  80. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/pipeline.py +0 -0
  81. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/providers/__init__.py +0 -0
  82. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/providers/ai_provider.py +0 -0
  83. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/recording.py +0 -0
  84. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/runner.py +0 -0
  85. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/state.py +0 -0
  86. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/testing.py +0 -0
  87. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/tinker.py +0 -0
  88. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/ai/types.py +0 -0
  89. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/__init__.py +0 -0
  90. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/auth.py +0 -0
  91. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/channels.py +0 -0
  92. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/drivers/__init__.py +0 -0
  93. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/drivers/log_driver.py +0 -0
  94. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/drivers/reverb_driver.py +0 -0
  95. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/event.py +0 -0
  96. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/helpers.py +0 -0
  97. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/manager.py +0 -0
  98. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/reverb/__init__.py +0 -0
  99. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/stubs/__init__.py +0 -0
  100. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/broadcasting/stubs/channels.py +0 -0
  101. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/carbon/__init__.py +0 -0
  102. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/carbon/carbon.py +0 -0
  103. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/config/__init__.py +0 -0
  104. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/config/app.py +0 -0
  105. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/configuration/Configuration.py +0 -0
  106. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/configuration/__init__.py +0 -0
  107. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/configuration/config.py +0 -0
  108. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/configuration/helpers.py +0 -0
  109. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/configuration/providers/ConfigurationProvider.py +0 -0
  110. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/configuration/providers/__init__.py +0 -0
  111. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/console/__init__.py +0 -0
  112. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/console/application.py +0 -0
  113. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/console/can_override_config.py +0 -0
  114. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/console/can_override_default_options.py +0 -0
  115. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/console/command.py +0 -0
  116. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/console/publish_command.py +0 -0
  117. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/container/__init__.py +0 -0
  118. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/container/container.py +0 -0
  119. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/environment/__init__.py +0 -0
  120. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/environment/environment.py +0 -0
  121. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/events/__init__.py +0 -0
  122. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/events/dispatcher.py +0 -0
  123. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/events/fake.py +0 -0
  124. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/events/helpers.py +0 -0
  125. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/events/listener.py +0 -0
  126. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/events/provider.py +0 -0
  127. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/exceptions/handler.py +0 -0
  128. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/AI.py +0 -0
  129. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/AI.pyi +0 -0
  130. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Config.py +0 -0
  131. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Config.pyi +0 -0
  132. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Dump.py +0 -0
  133. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Dump.pyi +0 -0
  134. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Event.py +0 -0
  135. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Event.pyi +0 -0
  136. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Facade.py +0 -0
  137. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Gate.py +0 -0
  138. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Gate.pyi +0 -0
  139. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Hash.py +0 -0
  140. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Hash.pyi +0 -0
  141. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Loader.py +0 -0
  142. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Loader.pyi +0 -0
  143. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Mail.py +0 -0
  144. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Mail.pyi +0 -0
  145. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Notification.py +0 -0
  146. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Notification.pyi +0 -0
  147. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Queue.py +0 -0
  148. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Queue.pyi +0 -0
  149. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/RateLimiter.py +0 -0
  150. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/RateLimiter.pyi +0 -0
  151. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Redis.py +0 -0
  152. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Redis.pyi +0 -0
  153. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Request.py +0 -0
  154. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Request.pyi +0 -0
  155. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Response.py +0 -0
  156. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Response.pyi +0 -0
  157. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Session.py +0 -0
  158. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Session.pyi +0 -0
  159. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Url.py +0 -0
  160. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Url.pyi +0 -0
  161. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/View.py +0 -0
  162. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/View.pyi +0 -0
  163. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/Vite.py +0 -0
  164. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/facades/__init__.py +0 -0
  165. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/__init__.py +0 -0
  166. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/commands/__init__.py +0 -0
  167. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/commands/serve_command.py +0 -0
  168. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/config/__init__.py +0 -0
  169. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/events.py +0 -0
  170. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/middleware.py +0 -0
  171. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/referer.py +0 -0
  172. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/requests/model.py +0 -0
  173. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/routers/router.py +0 -0
  174. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/testing/__init__.py +0 -0
  175. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/testing/assertable_json.py +0 -0
  176. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/testing/test_case.py +0 -0
  177. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/fastapi/testing/test_response.py +0 -0
  178. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/__init__.py +0 -0
  179. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/constant.py +0 -0
  180. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/context.py +0 -0
  181. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/encoding.py +0 -0
  182. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/errors.py +0 -0
  183. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/middleware.py +0 -0
  184. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/props/props.py +0 -0
  185. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/props/resolver.py +0 -0
  186. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/props/scroll.py +0 -0
  187. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/provider.py +0 -0
  188. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/redirect.py +0 -0
  189. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/session.py +0 -0
  190. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/ssr.py +0 -0
  191. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/inertia/testing.py +0 -0
  192. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/__init__.py +0 -0
  193. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/cli.py +0 -0
  194. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/init_command.py +0 -0
  195. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/prompts.py +0 -0
  196. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/README.md.stub +0 -0
  197. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/app.py.stub +0 -0
  198. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/artisan.stub +0 -0
  199. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/bootstrap.py.stub +0 -0
  200. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/controller.py.stub +0 -0
  201. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/fastapi.py.stub +0 -0
  202. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/gitignore.stub +0 -0
  203. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/logging.py.stub +0 -0
  204. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/provider.py.stub +0 -0
  205. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/routes.py.stub +0 -0
  206. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/base/test_application.py.stub +0 -0
  207. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/database/config.py.stub +0 -0
  208. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/database/seeder.py.stub +0 -0
  209. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/frontend/app.css.stub +0 -0
  210. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/frontend/config.py.stub +0 -0
  211. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/frontend/controller.py.stub +0 -0
  212. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/frontend/index.html.stub +0 -0
  213. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/README.md.stub +0 -0
  214. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/command.py.stub +0 -0
  215. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/config.py.stub +0 -0
  216. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/provider.py.stub +0 -0
  217. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/task.py.stub +0 -0
  218. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/test_application.py.stub +0 -0
  219. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/queue/worker.py.stub +0 -0
  220. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/react/Home.tsx.stub +0 -0
  221. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/react/app.tsx.stub +0 -0
  222. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/react/package.json.stub +0 -0
  223. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/react/vite.config.ts.stub +0 -0
  224. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/vue/Home.vue.stub +0 -0
  225. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/vue/app.ts.stub +0 -0
  226. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/vue/package.json.stub +0 -0
  227. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/stubs/vue/vite.config.ts.stub +0 -0
  228. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/installer/writer.py +0 -0
  229. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/jsonapi/__init__.py +0 -0
  230. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/jsonapi/response.py +0 -0
  231. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/loader/Loader.py +0 -0
  232. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/loader/__init__.py +0 -0
  233. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/ChannelFactory.py +0 -0
  234. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/__init__.py +0 -0
  235. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/BaseChannel.py +0 -0
  236. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/DailyChannel.py +0 -0
  237. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/MultiBaseChannel.py +0 -0
  238. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/SingleChannel.py +0 -0
  239. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/SlackChannel.py +0 -0
  240. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/StackChannel.py +0 -0
  241. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/SyslogChannel.py +0 -0
  242. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/TerminalChannel.py +0 -0
  243. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/channels/__init__.py +0 -0
  244. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/config/__init__.py +0 -0
  245. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/config/channels.py +0 -0
  246. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/config/logging.py +0 -0
  247. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/drivers/BaseDriver.py +0 -0
  248. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/drivers/LogSingleDriver.py +0 -0
  249. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/drivers/LogSlackDriver.py +0 -0
  250. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/drivers/LogSyslogDriver.py +0 -0
  251. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/drivers/LogTerminalDriver.py +0 -0
  252. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/drivers/__init__.py +0 -0
  253. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/factory.py +0 -0
  254. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/file.py +0 -0
  255. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/handler.py +0 -0
  256. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/listeners.py +0 -0
  257. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/logger.py +0 -0
  258. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/managers/LoggingManager.py +0 -0
  259. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/managers/__init__.py +0 -0
  260. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/providers/__init__.py +0 -0
  261. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/logging/providers/log_provider.py +0 -0
  262. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/__init__.py +0 -0
  263. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/collection/Collection.py +0 -0
  264. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/collection/__init__.py +0 -0
  265. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/DBMigrateCommand.py +0 -0
  266. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MakeMigrationCommand.py +0 -0
  267. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MakeSeedCommand.py +0 -0
  268. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MigrateResetCommand.py +0 -0
  269. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MigrateRollbackCommand.py +0 -0
  270. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/MigrateStatusCommand.py +0 -0
  271. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/ShellCommand.py +0 -0
  272. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/__init__.py +0 -0
  273. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/stubs/create_migration.stub +0 -0
  274. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/commands/stubs/table_migration.stub +0 -0
  275. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/config/__init__.py +0 -0
  276. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/config/config.py +0 -0
  277. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/config/database.py +0 -0
  278. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/connection.py +0 -0
  279. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/factory.py +0 -0
  280. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/manager.py +0 -0
  281. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/manages_transactions.py +0 -0
  282. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/mysql_connection.py +0 -0
  283. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/postgres_connection.py +0 -0
  284. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/sqlite_connection.py +0 -0
  285. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/connections/transactions.py +0 -0
  286. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/expressions/__init__.py +0 -0
  287. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/expressions/expressions.py +0 -0
  288. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/facades/DB.py +0 -0
  289. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/facades/Schema.py +0 -0
  290. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/facades/__init__.py +0 -0
  291. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/factory/__init__.py +0 -0
  292. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/factory/factory.py +0 -0
  293. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/helpers/__init__.py +0 -0
  294. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/helpers/misc.py +0 -0
  295. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/migrations/Migration.py +0 -0
  296. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/migrations/Migrator.py +0 -0
  297. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/migrations/__init__.py +0 -0
  298. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/MigrationModel.py +0 -0
  299. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/__init__.py +0 -0
  300. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/attribute.py +0 -0
  301. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/builder.py +0 -0
  302. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/caster.py +0 -0
  303. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/fields.py +0 -0
  304. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/model.py +0 -0
  305. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/observer.py +0 -0
  306. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/pivot.py +0 -0
  307. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/registry.py +0 -0
  308. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/models/relationship.py +0 -0
  309. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/observers/ObservesEvents.py +0 -0
  310. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/observers/__init__.py +0 -0
  311. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/pagination/BasePaginator.py +0 -0
  312. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/pagination/LengthAwarePaginator.py +0 -0
  313. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/pagination/SimplePaginator.py +0 -0
  314. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/pagination/__init__.py +0 -0
  315. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/providers/__init__.py +0 -0
  316. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/EagerLoadMixin.py +0 -0
  317. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/EagerRelation.py +0 -0
  318. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/__init__.py +0 -0
  319. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/grammars/MSSQLGrammar.py +0 -0
  320. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/grammars/MySQLGrammar.py +0 -0
  321. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/grammars/PostgresGrammar.py +0 -0
  322. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/grammars/SQLiteGrammar.py +0 -0
  323. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/grammars/__init__.py +0 -0
  324. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/processors/MSSQLPostProcessor.py +0 -0
  325. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/processors/MySQLPostProcessor.py +0 -0
  326. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/processors/PostgresPostProcessor.py +0 -0
  327. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/processors/SQLitePostProcessor.py +0 -0
  328. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/processors/__init__.py +0 -0
  329. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/query/support.py +0 -0
  330. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/BaseRelationship.py +0 -0
  331. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/BelongsTo.py +0 -0
  332. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/HasMany.py +0 -0
  333. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/HasManyThrough.py +0 -0
  334. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/HasOne.py +0 -0
  335. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/HasOneThrough.py +0 -0
  336. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/MorphMany.py +0 -0
  337. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/MorphOne.py +0 -0
  338. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/MorphTo.py +0 -0
  339. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/MorphToMany.py +0 -0
  340. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/relationships/__init__.py +0 -0
  341. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/Blueprint.py +0 -0
  342. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/Column.py +0 -0
  343. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/ColumnDiff.py +0 -0
  344. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/Constraint.py +0 -0
  345. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/ForeignKeyConstraint.py +0 -0
  346. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/Index.py +0 -0
  347. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/Table.py +0 -0
  348. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/TableDiff.py +0 -0
  349. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/__init__.py +0 -0
  350. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/platforms/MySQLPlatform.py +0 -0
  351. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/platforms/Platform.py +0 -0
  352. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/platforms/SQLitePlatform.py +0 -0
  353. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/platforms/__init__.py +0 -0
  354. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/schema/schema.py +0 -0
  355. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/seeders/Seeder.py +0 -0
  356. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/seeders/__init__.py +0 -0
  357. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/stubs/create-migration.html +0 -0
  358. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/stubs/table-migration.html +0 -0
  359. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/testing/__init__.py +0 -0
  360. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/masoniteorm/testing/transaction.py +0 -0
  361. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/__init__.py +0 -0
  362. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/argument.py +0 -0
  363. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/prompt.py +0 -0
  364. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/protocol.py +0 -0
  365. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/providers/__init__.py +0 -0
  366. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/providers/mcp_provider.py +0 -0
  367. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/request.py +0 -0
  368. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/resource.py +0 -0
  369. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/response.py +0 -0
  370. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/server.py +0 -0
  371. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/mcp/tool.py +0 -0
  372. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/process/__init__.py +0 -0
  373. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/process/exception.py +0 -0
  374. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/process/fake.py +0 -0
  375. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/process/process.py +0 -0
  376. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/process/result.py +0 -0
  377. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/py.typed +0 -0
  378. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/__init__.py +0 -0
  379. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/config.py +0 -0
  380. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/connection.py +0 -0
  381. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/connector.py +0 -0
  382. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/manager.py +0 -0
  383. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/prefix.py +0 -0
  384. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/redis/provider.py +0 -0
  385. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/__init__.py +0 -0
  386. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/adapters/__init__.py +0 -0
  387. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/adapters/base.py +0 -0
  388. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/adapters/claude.py +0 -0
  389. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/adapters/gemini.py +0 -0
  390. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/commands/__init__.py +0 -0
  391. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/commands/skills.py +0 -0
  392. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/parser.py +0 -0
  393. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/provider.py +0 -0
  394. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/registry.py +0 -0
  395. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/database/SKILL.md +0 -0
  396. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/fastapi/SKILL.md +0 -0
  397. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/inertia/SKILL.md +0 -0
  398. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/__init__.py +0 -0
  399. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/config/__init__.py +0 -0
  400. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/config/storage.py +0 -0
  401. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/data/mime.types +0 -0
  402. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/drivers/__init__.py +0 -0
  403. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/drivers/fake.py +0 -0
  404. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/file.py +0 -0
  405. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/filestream.py +0 -0
  406. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/storage/providers/provider.py +0 -0
  407. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/__init__.py +0 -0
  408. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/collection.py +0 -0
  409. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/config.py +0 -0
  410. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/dataclass.py +0 -0
  411. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/providers/__init__.py +0 -0
  412. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/providers/provider.py +0 -0
  413. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/string.py +0 -0
  414. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/structures.py +0 -0
  415. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/support/uri.py +0 -0
  416. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/testing/__init__.py +0 -0
  417. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/testing/test_case.py +0 -0
  418. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/config/vite.py +0 -0
  419. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/exceptions.py +0 -0
  420. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/stubs/package.json +0 -0
  421. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/stubs/resources/css/app.css +0 -0
  422. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/stubs/resources/js/app.ts +0 -0
  423. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/stubs/resources/templates/index.html +0 -0
  424. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/stubs/tsconfig.json +0 -0
  425. {fastapi_startkit-0.67.0 → fastapi_startkit-0.69.0}/src/fastapi_startkit/vite/stubs/vite.config.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fastapi-startkit
3
- Version: 0.67.0
3
+ Version: 0.69.0
4
4
  Summary: A modular, provider-driven framework for building async Python web applications with FastAPI — bundling a service container, configuration system, an async-first ORM, facades, and a Cleo-powered console.
5
5
  Keywords: fastapi,framework,async,web,asgi,orm,sqlalchemy,pydantic,ioc-container,dependency-injection,service-provider,cli,console,configuration,microservices,starter-kit,boilerplate
6
6
  Author: Bedram Tamang
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastapi-startkit"
3
- version = "0.67.0"
3
+ version = "0.69.0"
4
4
  description = "A modular, provider-driven framework for building async Python web applications with FastAPI — bundling a service container, configuration system, an async-first ORM, facades, and a Cleo-powered console."
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -61,9 +61,8 @@ class Application(Container, Generic[TConfig]):
61
61
  self.resolve_environment()
62
62
  self.configure_config()
63
63
  self.configure_paths()
64
- self.register_providers()
65
-
66
64
  self._fastapi: Optional["FastAPI"] = None
65
+ self.register_providers()
67
66
  self.load_providers()
68
67
 
69
68
  def set_environment(self, env: str):
@@ -8,7 +8,7 @@ from fastapi_startkit.environment import env
8
8
  class ReverbConnectionConfig(Config):
9
9
  driver: str = "reverb"
10
10
  key: str = env("REVERB_APP_KEY", "local")
11
- secret: str = env("REVERB_APP_SECRET", "secret")
11
+ secret: str = env("REVERB_APP_SECRET", "")
12
12
  app_id: str = env("REVERB_APP_ID", "1")
13
13
  host: str = env("REVERB_HOST", "0.0.0.0")
14
14
  port: int = env("REVERB_PORT", 8080)
@@ -2,13 +2,19 @@ from __future__ import annotations
2
2
 
3
3
  import importlib
4
4
  import importlib.util
5
+ import json
5
6
  import sys
7
+ from collections.abc import Mapping
6
8
  from pathlib import Path
7
9
 
8
10
  from fastapi_startkit.support import Provider
9
11
  from .config import BroadcastingConfig
10
12
  from .manager import BroadcastManager
11
13
  from .reverb.server import ReverbServer
14
+ from .reverb.signature import is_presence, requires_auth, sign, valid_channel, valid_socket_id
15
+
16
+ UNSAFE_SECRETS = ("", "secret")
17
+ INSECURE_ENVIRONMENTS = ("local", "testing")
12
18
 
13
19
 
14
20
  class ReverbProvider(Provider):
@@ -36,7 +42,14 @@ class ReverbProvider(Provider):
36
42
  config_data = self.resolve_config(BroadcastingConfig)
37
43
  self.merge_config_from(config_data, "broadcasting")
38
44
 
39
- server = ReverbServer()
45
+ reverb = config_data.get("connections", {}).get("reverb", {})
46
+ secret = reverb.get("secret") or ""
47
+ if secret in UNSAFE_SECRETS and self.app.env not in INSECURE_ENVIRONMENTS:
48
+ raise RuntimeError(
49
+ f"REVERB_APP_SECRET must be set to a private value when APP_ENV is {self.app.env!r}; "
50
+ "private and presence channel signatures would otherwise be forgeable."
51
+ )
52
+ server = ReverbServer(key=reverb.get("key", "local"), secret=secret)
40
53
  manager = BroadcastManager(config_data, server)
41
54
 
42
55
  self.app.bind("broadcasting", manager)
@@ -81,91 +94,75 @@ class ReverbProvider(Provider):
81
94
  spec.loader.exec_module(module) # type: ignore[union-attr]
82
95
 
83
96
  def _mount_websocket(self) -> None:
84
- """Mount the Reverb WebSocket endpoint on the FastAPI application.
85
-
86
- The endpoint path is ``/app/{app_key}``, matching the Pusher protocol
87
- expected by Laravel Echo. The ``app_key`` is taken from the
88
- ``reverb.connections.reverb.key`` config value (default: ``"local"``).
89
- """
90
97
  try:
91
98
  fastapi_app = self.app.fastapi
92
99
  except RuntimeError:
93
- # FastAPI not installed or app not yet booted — skip
94
100
  return
95
101
 
96
- config = self.app.make("config").get("broadcasting") or {}
97
- connections = config.get("connections", {}) if isinstance(config, dict) else {}
98
- reverb_conn = connections.get("reverb", {}) if isinstance(connections, dict) else {}
99
- app_key = reverb_conn.get("key", "local") if isinstance(reverb_conn, dict) else "local"
100
-
101
102
  server: ReverbServer = self.app.make("reverb.server")
102
103
 
103
- from starlette.routing import WebSocketRoute
104
- from starlette.applications import Starlette
105
-
106
104
  async def ws_endpoint(websocket):
107
105
  await server.handle(websocket)
108
106
 
109
- ws_app = Starlette(routes=[WebSocketRoute(f"/app/{app_key}", ws_endpoint)])
110
- fastapi_app.mount("", ws_app)
107
+ fastapi_app.router.add_websocket_route(f"/app/{server.key}", ws_endpoint)
111
108
 
112
109
  def _mount_auth_endpoint(self) -> None:
113
- """Mount the ``/broadcasting/auth`` HTTP endpoint.
114
-
115
- The endpoint handles the Laravel Echo authentication handshake for
116
- private and presence channels. It:
117
-
118
- 1. Reads ``channel_name`` from the POST body (form or JSON).
119
- 2. Resolves the current user via the container's ``auth`` binding (if
120
- available), falling back to ``request.state.user``.
121
- 3. Calls :meth:`~.auth.ChannelAuthRegistry.authorize` with the channel
122
- name and the resolved user.
123
- 4. Returns **200** on success or **403** on denial.
124
- """
125
110
  try:
126
111
  fastapi_app = self.app.fastapi
127
112
  except RuntimeError:
128
113
  return
129
114
 
130
- from fastapi import Request
131
- from fastapi.responses import JSONResponse
115
+ from starlette.requests import Request
116
+ from starlette.responses import JSONResponse
132
117
 
133
118
  application = self.app
134
119
 
135
- @fastapi_app.post("/broadcasting/auth")
136
120
  async def broadcasting_auth(request: Request):
137
- # Resolve channel name from form data or JSON body
138
- content_type = request.headers.get("content-type", "")
139
- if "application/json" in content_type:
140
- body = await request.json()
141
- channel_name = body.get("channel_name", "")
121
+ if "application/json" in request.headers.get("content-type", ""):
122
+ try:
123
+ body = await request.json()
124
+ except ValueError:
125
+ return JSONResponse({"error": "Malformed JSON body"}, status_code=400)
142
126
  else:
143
- form = await request.form()
144
- channel_name = form.get("channel_name", "")
145
-
146
- if not isinstance(channel_name, str):
147
- return JSONResponse({"error": "channel_name must be a string"}, status_code=422)
127
+ body = await request.form()
128
+ if not isinstance(body, Mapping):
129
+ return JSONResponse({"error": "Request body must be an object"}, status_code=422)
130
+
131
+ channel_name = body.get("channel_name", "")
132
+ socket_id = body.get("socket_id", "")
133
+ if not valid_socket_id(socket_id):
134
+ return JSONResponse({"error": "socket_id is invalid"}, status_code=422)
135
+ if not valid_channel(channel_name):
136
+ return JSONResponse({"error": "channel_name is invalid"}, status_code=422)
137
+
138
+ server: ReverbServer = application.make("reverb.server")
139
+ user = self._resolve_user(application, request)
140
+ if not server.secret or not requires_auth(channel_name) or user is None:
141
+ return JSONResponse({"error": "Forbidden"}, status_code=403)
148
142
 
149
- # Resolve authenticated user.
150
- # Prefer request.state.user (set by auth middleware), then fall
151
- # back to the container's "auth" service.
152
- user = getattr(request.state, "user", None)
153
- if user is None:
154
- try:
155
- auth_service = application.make("auth")
156
- user_attr = getattr(auth_service, "user", None)
157
- if callable(user_attr):
158
- user = user_attr()
159
- else:
160
- user = user_attr
161
- except Exception:
162
- user = None
163
-
164
- # Authorise
165
143
  manager: BroadcastManager = application.make("broadcasting")
166
- allowed = await manager.channel_registry.authorize(channel_name, user)
144
+ if not await manager.channel_registry.authorize(channel_name, user):
145
+ return JSONResponse({"error": "Forbidden"}, status_code=403)
146
+
147
+ if not is_presence(channel_name):
148
+ return JSONResponse({"auth": sign(server.key, server.secret, socket_id, channel_name)})
167
149
 
168
- if not allowed:
150
+ user_id = getattr(user, "id", None)
151
+ if user_id is None:
169
152
  return JSONResponse({"error": "Forbidden"}, status_code=403)
153
+ channel_data = json.dumps({"user_id": str(user_id)})
154
+ auth = sign(server.key, server.secret, socket_id, channel_name, channel_data)
155
+ return JSONResponse({"auth": auth, "channel_data": channel_data})
170
156
 
171
- return JSONResponse({"auth": True, "channel_data": {}})
157
+ fastapi_app.router.add_route("/broadcasting/auth", broadcasting_auth, methods=["POST"])
158
+
159
+ @staticmethod
160
+ def _resolve_user(application, request):
161
+ user = getattr(request.state, "user", None)
162
+ if user is not None:
163
+ return user
164
+ try:
165
+ user_attr = getattr(application.make("auth"), "user", None)
166
+ except Exception:
167
+ return None
168
+ return user_attr() if callable(user_attr) else user_attr
@@ -0,0 +1,21 @@
1
+ class ChannelManager:
2
+ def __init__(self):
3
+ self._subscriptions: dict[str, set[str]] = {}
4
+
5
+ def subscribe(self, channel: str, socket_id: str) -> None:
6
+ self._subscriptions.setdefault(channel, set()).add(socket_id)
7
+
8
+ def unsubscribe(self, channel: str, socket_id: str) -> None:
9
+ subscribers = self._subscriptions.get(channel)
10
+ if subscribers is None:
11
+ return
12
+ subscribers.discard(socket_id)
13
+ if not subscribers:
14
+ del self._subscriptions[channel]
15
+
16
+ def remove_socket(self, socket_id: str) -> None:
17
+ for channel in list(self._subscriptions):
18
+ self.unsubscribe(channel, socket_id)
19
+
20
+ def subscribers(self, channel: str) -> set[str]:
21
+ return set(self._subscriptions.get(channel, set()))
@@ -1,4 +1,5 @@
1
- import uuid
1
+ import secrets
2
+
2
3
  from starlette.websockets import WebSocket
3
4
 
4
5
 
@@ -7,15 +8,21 @@ class ConnectionManager:
7
8
  self._connections: dict[str, WebSocket] = {}
8
9
 
9
10
  def add(self, websocket: WebSocket) -> str:
10
- socket_id = str(uuid.uuid4())
11
+ socket_id = self._new_socket_id()
12
+ while socket_id in self._connections:
13
+ socket_id = self._new_socket_id()
11
14
  self._connections[socket_id] = websocket
12
15
  return socket_id
13
16
 
17
+ @staticmethod
18
+ def _new_socket_id() -> str:
19
+ return f"{secrets.randbelow(10**10)}.{secrets.randbelow(10**10)}"
20
+
14
21
  def remove(self, socket_id: str) -> None:
15
22
  self._connections.pop(socket_id, None)
16
23
 
17
24
  def get(self, socket_id: str):
18
25
  return self._connections.get(socket_id)
19
26
 
20
- def all(self) -> dict:
27
+ def all(self) -> dict[str, WebSocket]:
21
28
  return dict(self._connections)
@@ -0,0 +1,119 @@
1
+ import json
2
+
3
+ from starlette.applications import Starlette
4
+ from starlette.routing import WebSocketRoute
5
+ from starlette.websockets import WebSocket, WebSocketDisconnect
6
+
7
+ from .channel_manager import ChannelManager
8
+ from .connection import ConnectionManager
9
+ from .signature import requires_auth, valid_channel, verify
10
+
11
+ MESSAGE_TOO_BIG = 1009
12
+
13
+
14
+ class ReverbServer:
15
+ max_message_size = 10_000
16
+
17
+ def __init__(self, key: str = "local", secret: str = ""):
18
+ self.key = key
19
+ self.secret = secret
20
+ self.connections = ConnectionManager()
21
+ self.channels = ChannelManager()
22
+
23
+ async def handle(self, websocket: WebSocket):
24
+ await websocket.accept()
25
+ socket_id = self.connections.add(websocket)
26
+ try:
27
+ await self._send(
28
+ websocket,
29
+ "pusher:connection_established",
30
+ json.dumps({"socket_id": socket_id, "activity_timeout": 120}),
31
+ )
32
+ await self._receive_loop(socket_id, websocket)
33
+ except WebSocketDisconnect:
34
+ pass
35
+ finally:
36
+ self.connections.remove(socket_id)
37
+ self.channels.remove_socket(socket_id)
38
+
39
+ async def _receive_loop(self, socket_id: str, websocket: WebSocket):
40
+ while True:
41
+ frame = await websocket.receive()
42
+ if frame["type"] == "websocket.disconnect":
43
+ return
44
+ text = frame.get("text")
45
+ if text is not None and len(text) > self.max_message_size:
46
+ await self._error(websocket, "Maximum message size exceeded")
47
+ await websocket.close(code=MESSAGE_TOO_BIG)
48
+ return
49
+ message = self._parse(text)
50
+ if message is None:
51
+ await self._error(websocket, "Invalid message format")
52
+ continue
53
+ await self._handle_message(socket_id, websocket, message)
54
+
55
+ @staticmethod
56
+ def _parse(text: str | None) -> dict[str, object] | None:
57
+ if text is None:
58
+ return None
59
+ try:
60
+ message = json.loads(text)
61
+ except ValueError:
62
+ return None
63
+ if not isinstance(message, dict) or not isinstance(message.get("event", ""), str):
64
+ return None
65
+ return message
66
+
67
+ async def _handle_message(self, socket_id: str, websocket: WebSocket, message: dict[str, object]):
68
+ event = message.get("event", "")
69
+ if event == "pusher:ping":
70
+ await self._send(websocket, "pusher:pong", {})
71
+ elif event == "pusher:subscribe":
72
+ await self._subscribe(socket_id, websocket, message.get("data"))
73
+
74
+ async def _subscribe(self, socket_id: str, websocket: WebSocket, data: object):
75
+ if not isinstance(data, dict) or not isinstance(data.get("channel"), str) or not data["channel"]:
76
+ await self._error(websocket, "Invalid subscription")
77
+ return
78
+ channel = data["channel"]
79
+ if not valid_channel(channel):
80
+ await self._subscription_error(websocket, channel, "InvalidChannel", "Invalid channel name", 400)
81
+ return
82
+ if requires_auth(channel) and not verify(
83
+ self.key, self.secret, socket_id, channel, data.get("auth"), data.get("channel_data")
84
+ ):
85
+ await self._subscription_error(websocket, channel, "AuthError", "Invalid signature", 401)
86
+ return
87
+ self.channels.subscribe(channel, socket_id)
88
+ await self._send(websocket, "pusher_internal:subscription_succeeded", {}, channel)
89
+
90
+ async def _subscription_error(self, websocket: WebSocket, channel: str, kind: str, error: str, status: int):
91
+ await self._send(
92
+ websocket, "pusher:subscription_error", {"type": kind, "error": error, "status": status}, channel
93
+ )
94
+
95
+ async def _error(self, websocket: WebSocket, message: str):
96
+ await self._send(websocket, "pusher:error", {"code": 4200, "message": message})
97
+
98
+ @staticmethod
99
+ async def _send(websocket: WebSocket, event: str, data: object, channel: str | None = None):
100
+ payload: dict[str, object] = {"event": event, "data": data}
101
+ if channel is not None:
102
+ payload["channel"] = channel
103
+ await websocket.send_text(json.dumps(payload))
104
+
105
+ async def broadcast_to_channel(self, channel_name: str, event_name: str, data: dict[str, object]):
106
+ payload = json.dumps({"event": event_name, "channel": channel_name, "data": data})
107
+ for socket_id in self.channels.subscribers(channel_name):
108
+ ws = self.connections.get(socket_id)
109
+ if ws:
110
+ try:
111
+ await ws.send_text(payload)
112
+ except Exception:
113
+ pass
114
+
115
+ def as_starlette_app(self, app_key: str = "local") -> Starlette:
116
+ async def endpoint(ws: WebSocket):
117
+ await self.handle(ws)
118
+
119
+ return Starlette(routes=[WebSocketRoute(f"/app/{app_key}", endpoint)])
@@ -0,0 +1,43 @@
1
+ import hashlib
2
+ import hmac
3
+ import re
4
+
5
+ from typing_extensions import TypeIs
6
+
7
+ SOCKET_ID = re.compile(r"[0-9]+\.[0-9]+", re.ASCII)
8
+ CHANNEL_NAME = re.compile(r"[A-Za-z0-9_\-=@,.;]{1,200}", re.ASCII)
9
+
10
+
11
+ def valid_socket_id(socket_id: object) -> TypeIs[str]:
12
+ return isinstance(socket_id, str) and SOCKET_ID.fullmatch(socket_id) is not None
13
+
14
+
15
+ def valid_channel(channel: object) -> TypeIs[str]:
16
+ return isinstance(channel, str) and CHANNEL_NAME.fullmatch(channel) is not None
17
+
18
+
19
+ def requires_auth(channel: object) -> bool:
20
+ return isinstance(channel, str) and channel.startswith(("private-", "presence-"))
21
+
22
+
23
+ def is_presence(channel: str) -> bool:
24
+ return channel.startswith("presence-")
25
+
26
+
27
+ def sign(key: str, secret: str, socket_id: str, channel: str, channel_data: str | None = None) -> str:
28
+ parts = [socket_id, channel] if channel_data is None else [socket_id, channel, channel_data]
29
+ digest = hmac.new(secret.encode(), ":".join(parts).encode(), hashlib.sha256).hexdigest()
30
+ return f"{key}:{digest}"
31
+
32
+
33
+ def verify(key: str, secret: str, socket_id: str, channel: str, auth: object, channel_data: object = None) -> bool:
34
+ if not secret or not isinstance(auth, str):
35
+ return False
36
+ if not valid_socket_id(socket_id) or not valid_channel(channel) or not requires_auth(channel):
37
+ return False
38
+ if not is_presence(channel):
39
+ channel_data = None
40
+ elif not isinstance(channel_data, str):
41
+ return False
42
+ expected = sign(key, secret, socket_id, channel, channel_data)
43
+ return hmac.compare_digest(auth.encode(), expected.encode())
@@ -0,0 +1,30 @@
1
+ from .array_store import ArrayStore
2
+ from .config import ArrayStoreConfig, CacheConfig, DatabaseStoreConfig, FileStoreConfig, RedisStoreConfig
3
+ from .file_store import FileStore
4
+ from .lock import Lock, LockTimeoutError
5
+ from .manager import CacheManager
6
+ from .memo_store import MemoStore
7
+ from .provider import CacheProvider
8
+ from .repository import Repository
9
+ from .store import MISSING, Store
10
+ from .tagged_store import TaggedStore, TagSet
11
+
12
+ __all__ = [
13
+ "MISSING",
14
+ "ArrayStore",
15
+ "ArrayStoreConfig",
16
+ "CacheConfig",
17
+ "CacheManager",
18
+ "CacheProvider",
19
+ "DatabaseStoreConfig",
20
+ "FileStore",
21
+ "FileStoreConfig",
22
+ "Lock",
23
+ "LockTimeoutError",
24
+ "MemoStore",
25
+ "RedisStoreConfig",
26
+ "Repository",
27
+ "Store",
28
+ "TagSet",
29
+ "TaggedStore",
30
+ ]
@@ -0,0 +1,62 @@
1
+ from typing import Any
2
+
3
+ from .lock import Lock
4
+ from .store import MISSING, expired, expires_at
5
+
6
+
7
+ class ArrayStore:
8
+ def __init__(self) -> None:
9
+ self.storage: dict[str, tuple[float | None, Any]] = {}
10
+ self.locks: dict[str, tuple[str, float | None]] = {}
11
+
12
+ async def get(self, key: str) -> Any:
13
+ entry = self.storage.get(key)
14
+ if entry is None:
15
+ return MISSING
16
+
17
+ expiration, value = entry
18
+ if expired(expiration):
19
+ del self.storage[key]
20
+ return MISSING
21
+ return value
22
+
23
+ async def put(self, key: str, value: Any, seconds: float | None) -> bool:
24
+ self.storage[key] = (expires_at(seconds), value)
25
+ return True
26
+
27
+ async def forget(self, key: str) -> bool:
28
+ return self.storage.pop(key, MISSING) is not MISSING
29
+
30
+ async def flush(self) -> bool:
31
+ self.storage.clear()
32
+ return True
33
+
34
+ def lock(self, name: str, seconds: float = 0, owner: str | None = None) -> "ArrayLock":
35
+ return ArrayLock(self, name, seconds, owner)
36
+
37
+
38
+ class ArrayLock(Lock):
39
+ def __init__(self, store: ArrayStore, name: str, seconds: float = 0, owner: str | None = None):
40
+ super().__init__(name, seconds, owner)
41
+ self.store = store
42
+
43
+ def holder(self) -> str | None:
44
+ entry = self.store.locks.get(self.name)
45
+ if entry is None or expired(entry[1]):
46
+ return None
47
+ return entry[0]
48
+
49
+ async def acquire(self) -> bool:
50
+ if self.holder() is not None:
51
+ return False
52
+ self.store.locks[self.name] = (self.owner, expires_at(self.ttl))
53
+ return True
54
+
55
+ async def release(self) -> bool:
56
+ if self.holder() != self.owner:
57
+ return False
58
+ del self.store.locks[self.name]
59
+ return True
60
+
61
+ async def force_release(self) -> None:
62
+ self.store.locks.pop(self.name, None)
@@ -0,0 +1,45 @@
1
+ from dataclasses import dataclass, field
2
+
3
+ from fastapi_startkit.configuration import Config
4
+ from fastapi_startkit.environment import env
5
+
6
+
7
+ @dataclass
8
+ class ArrayStoreConfig(Config):
9
+ driver: str = "array"
10
+
11
+
12
+ @dataclass
13
+ class FileStoreConfig(Config):
14
+ driver: str = "file"
15
+ path: str = env("CACHE_FILE_PATH", "storage/framework/cache/data")
16
+
17
+
18
+ @dataclass
19
+ class DatabaseStoreConfig(Config):
20
+ driver: str = "database"
21
+ connection: str | None = env("DB_CACHE_CONNECTION", None, cast=False)
22
+ table: str = env("DB_CACHE_TABLE", "caches")
23
+
24
+
25
+ @dataclass
26
+ class RedisStoreConfig(Config):
27
+ driver: str = "redis"
28
+ connection: str = "cache"
29
+
30
+
31
+ @dataclass
32
+ class CacheConfig(Config):
33
+ default: str = env("CACHE_CONNECTION", "database")
34
+ prefix: str = env("CACHE_PREFIX", "cache")
35
+ stores: dict[str, Config] = field(
36
+ default_factory=lambda: {
37
+ "array": ArrayStoreConfig(),
38
+ "file": FileStoreConfig(),
39
+ "database": DatabaseStoreConfig(),
40
+ "redis": RedisStoreConfig(),
41
+ }
42
+ )
43
+
44
+
45
+ __all__ = ["ArrayStoreConfig", "CacheConfig", "DatabaseStoreConfig", "FileStoreConfig", "RedisStoreConfig"]
@@ -0,0 +1,94 @@
1
+ import base64
2
+ import math
3
+ import pickle
4
+ import time
5
+ from collections.abc import Callable
6
+ from typing import Any
7
+
8
+ from sqlalchemy.exc import IntegrityError
9
+
10
+ from fastapi_startkit.masoniteorm.connections.connection import Connection
11
+ from fastapi_startkit.masoniteorm.models.builder import QueryBuilder
12
+
13
+ from .lock import Lock
14
+ from .store import MISSING, expired, expires_at
15
+
16
+
17
+ def encode(value: Any) -> str:
18
+ return base64.b64encode(pickle.dumps(value)).decode()
19
+
20
+
21
+ class DatabaseStore:
22
+ def __init__(self, connection: Callable[[], Connection], table: str = "caches"):
23
+ self.connection = connection
24
+ self.table = table
25
+
26
+ def query(self) -> QueryBuilder[Any]:
27
+ return self.connection().query().table(self.table)
28
+
29
+ async def get(self, key: str) -> Any:
30
+ builder = self.query().where("key", key).limit(1)
31
+ row = await builder.connection.select_one(builder.to_qmark(), list(builder.get_bindings()))
32
+ if row is None:
33
+ return MISSING
34
+
35
+ try:
36
+ value = pickle.loads(base64.b64decode(row["value"]))
37
+ except Exception:
38
+ await self.forget(key)
39
+ return MISSING
40
+
41
+ if expired(row["expiration"]):
42
+ await self.forget(key)
43
+ return MISSING
44
+ return value
45
+
46
+ async def put(self, key: str, value: Any, seconds: float | None) -> bool:
47
+ expiration = expires_at(seconds)
48
+ record = {"value": encode(value), "expiration": None if expiration is None else math.ceil(expiration)}
49
+ if await self.query().where("key", key).update(record):
50
+ return True
51
+ try:
52
+ await self.query().insert({"key": key, **record})
53
+ except IntegrityError:
54
+ await self.query().where("key", key).update(record)
55
+ return True
56
+
57
+ async def forget(self, key: str) -> bool:
58
+ return bool(await self.query().where("key", key).delete())
59
+
60
+ async def flush(self) -> bool:
61
+ await self.query().delete()
62
+ return True
63
+
64
+ def lock(self, name: str, seconds: float = 0, owner: str | None = None) -> "DatabaseLock":
65
+ return DatabaseLock(self, name, seconds, owner)
66
+
67
+
68
+ class DatabaseLock(Lock):
69
+ def __init__(self, store: DatabaseStore, name: str, seconds: float = 0, owner: str | None = None):
70
+ super().__init__(name, seconds, owner)
71
+ self.store = store
72
+ self.key = f"lock:{name}"
73
+
74
+ async def acquire(self) -> bool:
75
+ expiration = expires_at(self.ttl)
76
+ record = {"value": encode(self.owner), "expiration": None if expiration is None else math.ceil(expiration)}
77
+ try:
78
+ await self.store.query().insert({"key": self.key, **record})
79
+ except IntegrityError:
80
+ expired_lock = self.store.query().where("key", self.key).where("expiration", "<=", time.time())
81
+ return bool(await expired_lock.update(record))
82
+ return True
83
+
84
+ async def release(self) -> bool:
85
+ held = (
86
+ self.store.query()
87
+ .where("key", self.key)
88
+ .where("value", encode(self.owner))
89
+ .where(lambda query: query.where_null("expiration").or_where("expiration", ">", time.time()))
90
+ )
91
+ return bool(await held.delete())
92
+
93
+ async def force_release(self) -> None:
94
+ await self.store.query().where("key", self.key).delete()