django-cfg 1.2.22__py3-none-any.whl → 1.2.23__py3-none-any.whl
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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin/__init__.py +23 -0
- django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
- django_cfg/apps/payments/admin/balance_admin.py +434 -0
- django_cfg/apps/payments/admin/currencies_admin.py +186 -0
- django_cfg/apps/payments/admin/filters.py +259 -0
- django_cfg/apps/payments/admin/payments_admin.py +142 -0
- django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
- django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
- django_cfg/apps/payments/config/__init__.py +87 -0
- django_cfg/apps/payments/config/module.py +162 -0
- django_cfg/apps/payments/config/providers.py +93 -0
- django_cfg/apps/payments/config/settings.py +136 -0
- django_cfg/apps/payments/config/utils.py +198 -0
- django_cfg/apps/payments/decorators.py +291 -0
- django_cfg/apps/payments/middleware/api_access.py +261 -0
- django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
- django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
- django_cfg/apps/payments/migrations/0001_initial.py +32 -11
- django_cfg/apps/payments/models/__init__.py +18 -0
- django_cfg/apps/payments/models/api_keys.py +2 -2
- django_cfg/apps/payments/models/balance.py +2 -2
- django_cfg/apps/payments/models/base.py +16 -0
- django_cfg/apps/payments/models/events.py +2 -2
- django_cfg/apps/payments/models/payments.py +2 -2
- django_cfg/apps/payments/models/subscriptions.py +2 -2
- django_cfg/apps/payments/services/__init__.py +58 -7
- django_cfg/apps/payments/services/billing/__init__.py +8 -0
- django_cfg/apps/payments/services/cache/__init__.py +15 -0
- django_cfg/apps/payments/services/cache/base.py +30 -0
- django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
- django_cfg/apps/payments/services/core/__init__.py +17 -0
- django_cfg/apps/payments/services/core/balance_service.py +449 -0
- django_cfg/apps/payments/services/core/payment_service.py +393 -0
- django_cfg/apps/payments/services/core/subscription_service.py +616 -0
- django_cfg/apps/payments/services/internal_types.py +266 -0
- django_cfg/apps/payments/services/middleware/__init__.py +8 -0
- django_cfg/apps/payments/services/providers/__init__.py +19 -0
- django_cfg/apps/payments/services/providers/base.py +137 -0
- django_cfg/apps/payments/services/providers/cryptapi.py +262 -0
- django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
- django_cfg/apps/payments/services/providers/registry.py +99 -0
- django_cfg/apps/payments/services/validators/__init__.py +8 -0
- django_cfg/apps/payments/signals/__init__.py +13 -0
- django_cfg/apps/payments/signals/api_key_signals.py +150 -0
- django_cfg/apps/payments/signals/payment_signals.py +127 -0
- django_cfg/apps/payments/signals/subscription_signals.py +196 -0
- django_cfg/apps/payments/urls.py +5 -5
- django_cfg/apps/payments/utils/__init__.py +42 -0
- django_cfg/apps/payments/utils/config_utils.py +243 -0
- django_cfg/apps/payments/utils/middleware_utils.py +228 -0
- django_cfg/apps/payments/utils/validation_utils.py +94 -0
- django_cfg/apps/support/signals.py +16 -4
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/models/revolution.py +1 -1
- django_cfg/modules/base.py +1 -1
- django_cfg/modules/django_email.py +42 -4
- django_cfg/modules/django_unfold/dashboard.py +20 -0
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/METADATA +2 -1
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/RECORD +63 -26
- django_cfg/apps/payments/services/base.py +0 -68
- django_cfg/apps/payments/services/nowpayments.py +0 -78
- django_cfg/apps/payments/services/providers.py +0 -77
- django_cfg/apps/payments/services/redis_service.py +0 -215
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
django_cfg/__init__.py,sha256=
|
2
|
+
django_cfg/__init__.py,sha256=UAyyUuLL3rXe_tPmnLDBZA3gajkyvd4DvOdkV8sJKko,1631
|
3
3
|
django_cfg/apps.py,sha256=k84brkeXJI7EgKZLEpTkM9YFZofKI4PzhFOn1cl9Msc,1656
|
4
4
|
django_cfg/config.py,sha256=ME-JKaVzcdmaGhuc1YTkEWoMKSaUasNf1SBlNz-NfrM,1399
|
5
5
|
django_cfg/urls.py,sha256=bpRFjMonQuk4UCUMxx4ueBX3YDNB7HXKFwEghQ3KR3o,793
|
@@ -269,7 +269,21 @@ django_cfg/apps/newsletter/views/subscriptions.py,sha256=marHLbRT6a6e8tMjKVtKG_0
|
|
269
269
|
django_cfg/apps/newsletter/views/tracking.py,sha256=EJlAltpASzc_0nslVPJt-6mnIqI_QCEmz3MQEGPKzmU,2481
|
270
270
|
django_cfg/apps/payments/__init__.py,sha256=Qb8viLJBKCclV9uuoXjxWXeDevXlbdJ76mV8g5hK7tk,237
|
271
271
|
django_cfg/apps/payments/apps.py,sha256=FZgbLQFMeOJPs_892bM6X-3Edt3YPD2CP50HMekH7Sk,551
|
272
|
-
django_cfg/apps/payments/
|
272
|
+
django_cfg/apps/payments/decorators.py,sha256=A8MsH7hzw8i7Cl3Eh5Lcqd9Dbn_nubbb6BAI3eZMEgk,10960
|
273
|
+
django_cfg/apps/payments/urls.py,sha256=8Tr5Sr3FcTgUB4PRYYlb8MOkugAPgKT46Jd0uVyNruk,3473
|
274
|
+
django_cfg/apps/payments/admin/__init__.py,sha256=BUTbE4RfbqX3EBF-9oscrh7wQ2tuwCqofGYqlCz4QM4,666
|
275
|
+
django_cfg/apps/payments/admin/api_keys_admin.py,sha256=nMghMkKcDYvjZ4POVwpS59E6Dc7IVP9dHv-1OltpEes,11436
|
276
|
+
django_cfg/apps/payments/admin/balance_admin.py,sha256=Dwap1VZGwNJ4ZbELMDbiVIxQPmI80MWnsTudyZOvsxo,15386
|
277
|
+
django_cfg/apps/payments/admin/currencies_admin.py,sha256=mMcttxDJlbQNJBhbrUhjJJYY2n9uHg1-7Hn8DCH9pVU,5810
|
278
|
+
django_cfg/apps/payments/admin/filters.py,sha256=MCYFqs1t3XL0U4WZv4ONUbwqv0vLOo7sN7eDuSaVFd0,8504
|
279
|
+
django_cfg/apps/payments/admin/payments_admin.py,sha256=fYKiTgEn1mHNZ2bEamTdf0alyZXYi_GgzjuFB81Ib98,4356
|
280
|
+
django_cfg/apps/payments/admin/subscriptions_admin.py,sha256=mJRSyDd761f0LA64AnG19dzTUmiivqq9wZg4roqKYIo,6913
|
281
|
+
django_cfg/apps/payments/admin/tariffs_admin.py,sha256=6UiQyRRIKrDdmpi2IZOpDpLeJLHrebOGNc3OizlikLI,6422
|
282
|
+
django_cfg/apps/payments/config/__init__.py,sha256=eEfe8z-RKGLmNQaxXdngcUAVnlbq3VKzo9qKMZwh45A,1873
|
283
|
+
django_cfg/apps/payments/config/module.py,sha256=jm8CFJoFoYhUrd1tt7j0uZgFA7oMOTbLQM21UVZNpQw,6547
|
284
|
+
django_cfg/apps/payments/config/providers.py,sha256=NuLCdgKbQyMkyWvKKLsmMO4hwkjqT1ddIynsTBoAWAk,3845
|
285
|
+
django_cfg/apps/payments/config/settings.py,sha256=S0V_DH4EzU21wAGgyodSLGKcSGs3s14GcHAihFgdGMg,6569
|
286
|
+
django_cfg/apps/payments/config/utils.py,sha256=H-4IdmlbpYAknaGWVvuivT_5lyvWY8q2C50Wb8qnkD8,6549
|
273
287
|
django_cfg/apps/payments/managers/__init__.py,sha256=uYiETCw-K5GXQyAwpcOa2Qxzof8sbtTNc_SghyW45YU,672
|
274
288
|
django_cfg/apps/payments/managers/api_key_manager.py,sha256=xhViRKCLC_eR68hankMZQTcbzMgiRJdyr8KYKk01_dQ,928
|
275
289
|
django_cfg/apps/payments/managers/balance_manager.py,sha256=Sm_EETlHO7LtlrWIqF65vFPS4FpPSNYo5oTgQwTFAZ4,13279
|
@@ -278,16 +292,19 @@ django_cfg/apps/payments/managers/payment_manager.py,sha256=udiTFIIVXsjqmlwzFwcT
|
|
278
292
|
django_cfg/apps/payments/managers/subscription_manager.py,sha256=LHrw5INc1nSqfpw76bn7W2poEA-29FXfF5uhaxmq7l8,968
|
279
293
|
django_cfg/apps/payments/managers/tariff_manager.py,sha256=Kor_0ApvROcH0UKlp1-9rYChtHWDeOFgUc1GG3IepxY,799
|
280
294
|
django_cfg/apps/payments/middleware/__init__.py,sha256=ryn3emWBq15-kE8eJxX19gd-eTrk7JlpXqV2Cg8N_Wg,294
|
281
|
-
django_cfg/apps/payments/
|
295
|
+
django_cfg/apps/payments/middleware/api_access.py,sha256=JcAg0ntqjR_r1LTUmJ9DTp1F8Uv62_Hza5mo9Rn6hDw,9406
|
296
|
+
django_cfg/apps/payments/middleware/rate_limiting.py,sha256=MhpHQkTjoHdH-tsJNlZATQQ9d9R9PDTwrdw1gUt_ydo,7407
|
297
|
+
django_cfg/apps/payments/middleware/usage_tracking.py,sha256=i8KT5E0dN6zhQwY8A-wFWtpkc9FJpfTQ4qTnRV8onmw,11176
|
298
|
+
django_cfg/apps/payments/migrations/0001_initial.py,sha256=uLAGM9KXBHLuLt4NN9yY8qcelxZ0gxUQYwv2zfp9nus,40538
|
282
299
|
django_cfg/apps/payments/migrations/__init__.py,sha256=OHlzxEGDJYKZz82orZXnh77xy-Okv6FcG3EKMfg9LzU,21
|
283
|
-
django_cfg/apps/payments/models/__init__.py,sha256=
|
284
|
-
django_cfg/apps/payments/models/api_keys.py,sha256=
|
285
|
-
django_cfg/apps/payments/models/balance.py,sha256=
|
286
|
-
django_cfg/apps/payments/models/base.py,sha256=
|
300
|
+
django_cfg/apps/payments/models/__init__.py,sha256=iYRu56c8oZCG5n52aWJqsKEKX44k0_2BiSFkN8jZ46U,1444
|
301
|
+
django_cfg/apps/payments/models/api_keys.py,sha256=HCtZuvpW7cYRpsSXkS3U3lk_smMQpgLRf7PHaM58MkQ,2484
|
302
|
+
django_cfg/apps/payments/models/balance.py,sha256=F7unVOEVZMUGNvPkvGTWsytxfvQKdV8eQSnEBPZkuDc,7052
|
303
|
+
django_cfg/apps/payments/models/base.py,sha256=iypC2MCPrASunrJepuY0y7fASVLDMDSqOwAWp-ovTDk,793
|
287
304
|
django_cfg/apps/payments/models/currencies.py,sha256=lz6axNRmfoQa3od-EV78vbZQv3vh2FznxHzGBi20qIU,4106
|
288
|
-
django_cfg/apps/payments/models/events.py,sha256=
|
289
|
-
django_cfg/apps/payments/models/payments.py,sha256=
|
290
|
-
django_cfg/apps/payments/models/subscriptions.py,sha256=
|
305
|
+
django_cfg/apps/payments/models/events.py,sha256=flKdY3SRAx5UZyHw6wLh5TflHTyy2lQpdi5aa1NqJ1o,2372
|
306
|
+
django_cfg/apps/payments/models/payments.py,sha256=z_dgYWZBd09wBn5ZEp0jf-3osjJASb4UlWgdELL-f1g,9548
|
307
|
+
django_cfg/apps/payments/models/subscriptions.py,sha256=femsIzWxSZZdu1Ee_MgCYnnpMnXshejwb1_JPhbo8k8,8193
|
291
308
|
django_cfg/apps/payments/models/tariffs.py,sha256=nqM3RPtu4itAHRvT6jAgZgQEdEIN1KgKnEljkaHGFDc,2749
|
292
309
|
django_cfg/apps/payments/serializers/__init__.py,sha256=4l7Tu1NXztXtqceVbC3OxtWJir4n3_dFpVEgRgQ9J4k,1416
|
293
310
|
django_cfg/apps/payments/serializers/api_keys.py,sha256=5qG3PEwIVfoNSC6XpbOBbC3l4umgld3kPJC5l_vsYV0,1396
|
@@ -296,11 +313,31 @@ django_cfg/apps/payments/serializers/currencies.py,sha256=SskSl3rjZBxxLnY_zsaJiF
|
|
296
313
|
django_cfg/apps/payments/serializers/payments.py,sha256=BDhcD9DCea50Cz1PumLNVntgH40dOR55NzCXv4I0YOQ,2258
|
297
314
|
django_cfg/apps/payments/serializers/subscriptions.py,sha256=16OileC0yxM90ERK3Uo5Jqe9DeyNf3tBwK-ZqDtEbPw,2795
|
298
315
|
django_cfg/apps/payments/serializers/tariffs.py,sha256=54N5tFpc5NBIMyXuN7Re6pDNoQLJc2yZBIb5uVRhn-I,1698
|
299
|
-
django_cfg/apps/payments/services/__init__.py,sha256=
|
300
|
-
django_cfg/apps/payments/services/
|
301
|
-
django_cfg/apps/payments/services/
|
302
|
-
django_cfg/apps/payments/services/
|
303
|
-
django_cfg/apps/payments/services/
|
316
|
+
django_cfg/apps/payments/services/__init__.py,sha256=rBXpwd9kR-zcrjDoBPivUjWpw3vPgAVNUGmpATX1T7Y,1786
|
317
|
+
django_cfg/apps/payments/services/internal_types.py,sha256=RgKpbGDIGhtFA4fQGwfI3OCiwaC2mOC8bEJP5ecZjdg,7862
|
318
|
+
django_cfg/apps/payments/services/billing/__init__.py,sha256=YHdL1RWo-VAotOdZaykArOd0NTGI0fuQ0B7nBRKn6Lg,149
|
319
|
+
django_cfg/apps/payments/services/cache/__init__.py,sha256=CA8cUptfUD5LwbAB9kPMDofdkJBD_o7Z3HHG4vXMoyA,318
|
320
|
+
django_cfg/apps/payments/services/cache/base.py,sha256=McWSpr6eNEXbhG2PBkQd_YkL-he-scoWNUVmKI4FohY,696
|
321
|
+
django_cfg/apps/payments/services/cache/simple_cache.py,sha256=N2i0yGOR5Y1SPl2hqsXcGyehCpwXdN9SQVrphO1yat0,4508
|
322
|
+
django_cfg/apps/payments/services/core/__init__.py,sha256=I7x8Ryz4htQ1LwwtWZRK-MeI_SYboCQ7cKtTxmoqfpk,382
|
323
|
+
django_cfg/apps/payments/services/core/balance_service.py,sha256=E1r620C25CzcNdkV8INBR8FbEtl02sPSboYtzLZMU9I,16400
|
324
|
+
django_cfg/apps/payments/services/core/payment_service.py,sha256=CaMGobTU7t7o-i0D_TbfPH15XJ1eilbOFJuBWzAM64g,14634
|
325
|
+
django_cfg/apps/payments/services/core/subscription_service.py,sha256=ORUHYaz9BQXKwyeRSuLSaKp20UDtRxAkl9PmdRwqxt8,22559
|
326
|
+
django_cfg/apps/payments/services/middleware/__init__.py,sha256=SAiK2KW4aFVdfjlNrV3Bv8F0qg_epqwbZXOV0WoS6yA,154
|
327
|
+
django_cfg/apps/payments/services/providers/__init__.py,sha256=A0R28JDaB-sI--wTJ-WegbpKXpVmDizTVTKqOYWvSPM,444
|
328
|
+
django_cfg/apps/payments/services/providers/base.py,sha256=kjWPb7EutZHaByDW3py8srAcnvZdE4oqj6Tp9xS9ADk,3807
|
329
|
+
django_cfg/apps/payments/services/providers/cryptapi.py,sha256=BrAhUHfoQJgmHpUq9I3hWloMLBzyRCqwNG6pMfs5Qlg,10149
|
330
|
+
django_cfg/apps/payments/services/providers/nowpayments.py,sha256=xsylxVaN5L5tYfv1j_OJzX_PSEBEq9pmBeRFG80rcCA,11490
|
331
|
+
django_cfg/apps/payments/services/providers/registry.py,sha256=uS0C9JutkDlhYjlSs_EiK7Qj3gJCEXFt-dgZNqwVVkw,3687
|
332
|
+
django_cfg/apps/payments/services/validators/__init__.py,sha256=_NgbT79oslNvBPPYRpSFnPkvnZuQ48Kk1dzSZIE2ZAU,140
|
333
|
+
django_cfg/apps/payments/signals/__init__.py,sha256=L3danrGQ-9tEfaAbkXOILPb2Bw_T4XRN4PodjMl1hvk,342
|
334
|
+
django_cfg/apps/payments/signals/api_key_signals.py,sha256=1Sj334_o9vTChrUjiH9KSM789T89S4xHywAncGmT4dE,5601
|
335
|
+
django_cfg/apps/payments/signals/payment_signals.py,sha256=XbfW0KLYuqEAl8O8PEkcD8dHxgeLPP6YGUFV5vrNvzQ,4823
|
336
|
+
django_cfg/apps/payments/signals/subscription_signals.py,sha256=FDXlR02fpS6ILzMJQjoK4Ln1v7Kb8vp_Ej9v_lqnVE0,7492
|
337
|
+
django_cfg/apps/payments/utils/__init__.py,sha256=uE3H5xO0cxMyxfM39qwmLWIL0QVzArCy0Ecr-poeL-U,1063
|
338
|
+
django_cfg/apps/payments/utils/config_utils.py,sha256=NvH8thOnlBK5jhgCFzPw6kbYOWIVhiE-YRfWv_U1SQk,8477
|
339
|
+
django_cfg/apps/payments/utils/middleware_utils.py,sha256=MTS9MY_8-VqpBVv8uQYH89rfeG8dTnUru38vVbbE-1o,6682
|
340
|
+
django_cfg/apps/payments/utils/validation_utils.py,sha256=xSxpjz5aAajSOIxDhaXHdB-Uf98MOi3uDKVx0jHmREo,2704
|
304
341
|
django_cfg/apps/payments/views/__init__.py,sha256=00HRO1sDErjnFq2dedFAjBIyIhZZyZmv4DK98lHmx2Q,1613
|
305
342
|
django_cfg/apps/payments/views/api_key_views.py,sha256=fG68roqOF9FShde68Qp_CBf0KJuf1chwK41RU0VqUJk,5542
|
306
343
|
django_cfg/apps/payments/views/balance_views.py,sha256=rA_OJFizP_UBBr8OdWLQZQ51qlPFYoeJCYUzNXSw4S0,2509
|
@@ -314,7 +351,7 @@ django_cfg/apps/support/admin_filters.py,sha256=ZpKtetRxppRAMwIr-pwDbXAyh7qouDfT
|
|
314
351
|
django_cfg/apps/support/apps.py,sha256=t-MzsKjXx6ZXACkG72Wa8GSmRYOERxfhrZUwUwjqCak,304
|
315
352
|
django_cfg/apps/support/models.py,sha256=a_m4CWpfR7A2KlVRUYOGPdpNyNPJflD3Mq7EkiwzICA,2465
|
316
353
|
django_cfg/apps/support/serializers.py,sha256=N-PP8d1H00mHSWT6x5DwDYrJoR3PS3AKrUW80L4lncs,1385
|
317
|
-
django_cfg/apps/support/signals.py,sha256=
|
354
|
+
django_cfg/apps/support/signals.py,sha256=W7lA3o1eoAwZQd92fX0GaiIXp9ptQRYtwn1Siz1FV9g,3706
|
318
355
|
django_cfg/apps/support/urls.py,sha256=t3r4wGRUqQS-BDCQgSXwSF9pbucyvr7lo7_pJ1uZ52Q,899
|
319
356
|
django_cfg/apps/support/admin/__init__.py,sha256=vVoUWfKj6qBHhwr6zG2ZH8ZpUegYNJvzuAxdFoHEo_0,153
|
320
357
|
django_cfg/apps/support/admin/filters.py,sha256=ZpKtetRxppRAMwIr-pwDbXAyh7qouDfTCEZoo1YJfFs,2179
|
@@ -326,7 +363,7 @@ django_cfg/apps/support/migrations/0001_initial.py,sha256=AUUXqMiRbv_bRhogBWKWdr
|
|
326
363
|
django_cfg/apps/support/migrations/0002_alter_message_ticket.py,sha256=MxdjTW7hJ1B0yQloKR3sVOgujOzSA6eZp5YCmCuXht0,571
|
327
364
|
django_cfg/apps/support/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
328
365
|
django_cfg/apps/support/templates/support/chat/access_denied.html,sha256=QdVXR-61aUHmh9Vlag_zXGcS_BG7a807LesIbduqnag,1094
|
329
|
-
django_cfg/apps/support/templates/support/chat/ticket_chat.html,sha256=
|
366
|
+
django_cfg/apps/support/templates/support/chat/ticket_chat.html,sha256=Xz5Z0ADP9aZpiE_cJdcRP9UJpJhY32h1h1sLfI94zgU,14483
|
330
367
|
django_cfg/apps/support/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
331
368
|
django_cfg/apps/support/utils/support_email_service.py,sha256=0l4dXqAG6oN2UR7W3JzuRhlomaWa41IT7ceVcanluKs,5570
|
332
369
|
django_cfg/apps/support/views/__init__.py,sha256=E-Iqiy888SncfBTiSyXyGCjaNrMa5Klvapzb3ItYPXc,448
|
@@ -414,13 +451,13 @@ django_cfg/models/jwt.py,sha256=3R_dpLmVZIcH4zdtwA4qKnuCB8RZQACrgsbbgWY2q4c,9025
|
|
414
451
|
django_cfg/models/limits.py,sha256=nUfvyPykwKR38ZOlIFqlNGRmfV8RO5hiUrhDH6FCHJw,7449
|
415
452
|
django_cfg/models/logging.py,sha256=4vZF-G9rPmXMxxtUx_ad7Esvgbe8a_5Dl692Yg0fL4A,10636
|
416
453
|
django_cfg/models/ngrok.py,sha256=MVgcKWx0DRSW0QcwhiSx2vVwTSG49vbVrzPkZqDK-zw,3575
|
417
|
-
django_cfg/models/revolution.py,sha256=
|
454
|
+
django_cfg/models/revolution.py,sha256=16fQ09iovrmlepNcTr2J0hZ-ShdMnMSQ2rTXa60yrus,8134
|
418
455
|
django_cfg/models/security.py,sha256=Xv19ZVOIenB_-f0wB6fm-Ap4j9kA43bSFaT2XenpSqc,4685
|
419
456
|
django_cfg/models/services.py,sha256=fj9JjrJFrlL4DMnMbx_D8JiiZpz4E5uBqmhquAxau7c,13159
|
420
457
|
django_cfg/models/tasks.py,sha256=2T3apcUFf8zevYJmapbFrh6bWv5PTJPeXR6Bc5vlj4c,15524
|
421
458
|
django_cfg/modules/__init__.py,sha256=Ip9WMpzImEwIAywpFwU056_v0O9oIGG7nCT1YSArxkw,316
|
422
|
-
django_cfg/modules/base.py,sha256=
|
423
|
-
django_cfg/modules/django_email.py,sha256=
|
459
|
+
django_cfg/modules/base.py,sha256=ZFXqGPpZGvsVYp9Cc1aCpvMzGrEE9V2z9g50so2P5Nk,5736
|
460
|
+
django_cfg/modules/django_email.py,sha256=Kk2B1Y73xlbpNKCDkU2uBT01iVANjKpmVlKbRScVCs4,18059
|
424
461
|
django_cfg/modules/django_health.py,sha256=7QzuQ6WyjWYj6lecd4auwRvEyrMUL7N6hiAp-tLyoY4,8923
|
425
462
|
django_cfg/modules/django_logger.py,sha256=3oP9jev0lOcFUJ1tYcpbFnK524zIGA2xIOrrAiTwpb8,6331
|
426
463
|
django_cfg/modules/django_ngrok.py,sha256=LjlAIJprbFhFYwDG93acas8XaB6iFQGu4fWvZiDNhyQ,10482
|
@@ -461,7 +498,7 @@ django_cfg/modules/django_twilio/templates/guide.md,sha256=nZfwx-sgWyK5NApm93zOe
|
|
461
498
|
django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html,sha256=sXR6_D9hmOFfk9CrfPizpLddVhkRirBWpZd_ioEsxVk,6671
|
462
499
|
django_cfg/modules/django_twilio/templates/sendgrid_test_data.json,sha256=fh1VyuSiDELHsS_CIz9gp7tlsMAEjaDOoqbAPSZ3yyo,339
|
463
500
|
django_cfg/modules/django_unfold/__init__.py,sha256=Z91x1iGmkzlRbEb2L9OCFmYDKNAV9C4G3i15j5S0esc,1898
|
464
|
-
django_cfg/modules/django_unfold/dashboard.py,sha256=
|
501
|
+
django_cfg/modules/django_unfold/dashboard.py,sha256=UITRlmqAGpptKGFMdTwh9rcIZqKyu9V1TkyKkGNHlKo,14892
|
465
502
|
django_cfg/modules/django_unfold/models.py,sha256=bY6QSSaH_-r9vOTkSQjxeIkl5RaED7XkxXkT8-W5stk,4014
|
466
503
|
django_cfg/modules/django_unfold/system_monitor.py,sha256=cznZqldRJqiSLSJbs4U7R2rX8ClzoIpqdfXdXqI2iQw,6955
|
467
504
|
django_cfg/modules/django_unfold/tailwind.py,sha256=X9o1K3QL0VwUISgJ26sLb6zkdK-00qiDuekqTw-fydc,10846
|
@@ -544,8 +581,8 @@ django_cfg/utils/path_resolution.py,sha256=C9As6p4Q9l3VeoVkFDRPQWGrzAWf8O8UxLVka
|
|
544
581
|
django_cfg/utils/smart_defaults.py,sha256=MxbUZwn_xbh48li7uLI6W4D9WCD2P2WO48dv85Fra5E,23057
|
545
582
|
django_cfg/utils/toolkit.py,sha256=Td8_iXNaftonF_xdZP4Y3uO65nuA_4_zditn5Q_Pfcw,23310
|
546
583
|
django_cfg/utils/version_check.py,sha256=jI4v3YMdQriUEeb_TvRl511sDghy6I75iKRDUaNpucs,4800
|
547
|
-
django_cfg-1.2.
|
548
|
-
django_cfg-1.2.
|
549
|
-
django_cfg-1.2.
|
550
|
-
django_cfg-1.2.
|
551
|
-
django_cfg-1.2.
|
584
|
+
django_cfg-1.2.23.dist-info/METADATA,sha256=2qgEiOin5w8jT2vBv1cxqAPjKWYo0W3D3OEaXTCfqg0,38435
|
585
|
+
django_cfg-1.2.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
586
|
+
django_cfg-1.2.23.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
|
587
|
+
django_cfg-1.2.23.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
588
|
+
django_cfg-1.2.23.dist-info/RECORD,,
|
@@ -1,68 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Base payment service classes.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from abc import ABC, abstractmethod
|
6
|
-
from typing import Dict, Any, Optional
|
7
|
-
from decimal import Decimal
|
8
|
-
|
9
|
-
|
10
|
-
class PaymentProvider(ABC):
|
11
|
-
"""Abstract base class for payment providers."""
|
12
|
-
|
13
|
-
def __init__(self, config: Dict[str, Any]):
|
14
|
-
"""Initialize provider with config."""
|
15
|
-
self.config = config
|
16
|
-
self.name = self.__class__.__name__.lower().replace('provider', '')
|
17
|
-
|
18
|
-
@abstractmethod
|
19
|
-
def create_payment(self, amount: Decimal, currency: str, **kwargs) -> Dict[str, Any]:
|
20
|
-
"""Create a payment request."""
|
21
|
-
pass
|
22
|
-
|
23
|
-
@abstractmethod
|
24
|
-
def check_payment_status(self, payment_id: str) -> Dict[str, Any]:
|
25
|
-
"""Check payment status."""
|
26
|
-
pass
|
27
|
-
|
28
|
-
@abstractmethod
|
29
|
-
def process_webhook(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
30
|
-
"""Process webhook payload."""
|
31
|
-
pass
|
32
|
-
|
33
|
-
@abstractmethod
|
34
|
-
def get_supported_currencies(self) -> list[str]:
|
35
|
-
"""Get list of supported currencies."""
|
36
|
-
pass
|
37
|
-
|
38
|
-
|
39
|
-
class PaymentService:
|
40
|
-
"""Main payment service with provider management."""
|
41
|
-
|
42
|
-
def __init__(self):
|
43
|
-
"""Initialize payment service."""
|
44
|
-
self.providers: Dict[str, PaymentProvider] = {}
|
45
|
-
|
46
|
-
def register_provider(self, provider: PaymentProvider) -> None:
|
47
|
-
"""Register a payment provider."""
|
48
|
-
self.providers[provider.name] = provider
|
49
|
-
|
50
|
-
def get_provider(self, name: str) -> Optional[PaymentProvider]:
|
51
|
-
"""Get provider by name."""
|
52
|
-
return self.providers.get(name)
|
53
|
-
|
54
|
-
def create_payment(self, provider_name: str, amount: Decimal, currency: str, **kwargs) -> Dict[str, Any]:
|
55
|
-
"""Create payment using specified provider."""
|
56
|
-
provider = self.get_provider(provider_name)
|
57
|
-
if not provider:
|
58
|
-
raise ValueError(f"Provider {provider_name} not found")
|
59
|
-
|
60
|
-
return provider.create_payment(amount, currency, **kwargs)
|
61
|
-
|
62
|
-
def process_webhook(self, provider_name: str, payload: Dict[str, Any]) -> Dict[str, Any]:
|
63
|
-
"""Process webhook for specified provider."""
|
64
|
-
provider = self.get_provider(provider_name)
|
65
|
-
if not provider:
|
66
|
-
raise ValueError(f"Provider {provider_name} not found")
|
67
|
-
|
68
|
-
return provider.process_webhook(payload)
|
@@ -1,78 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
NowPayments provider implementation.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import Dict, Any
|
6
|
-
from decimal import Decimal
|
7
|
-
import requests
|
8
|
-
from .base import PaymentProvider
|
9
|
-
|
10
|
-
|
11
|
-
class NowPaymentsProvider(PaymentProvider):
|
12
|
-
"""NowPayments crypto payment provider."""
|
13
|
-
|
14
|
-
def __init__(self, config: Dict[str, Any]):
|
15
|
-
"""Initialize NowPayments provider."""
|
16
|
-
super().__init__(config)
|
17
|
-
self.api_key = config.get('api_key')
|
18
|
-
self.base_url = config.get('base_url', 'https://api.nowpayments.io/v1')
|
19
|
-
self.headers = {'x-api-key': self.api_key}
|
20
|
-
|
21
|
-
def create_payment(self, amount: Decimal, currency: str, **kwargs) -> Dict[str, Any]:
|
22
|
-
"""Create payment via NowPayments API."""
|
23
|
-
payload = {
|
24
|
-
'price_amount': float(amount),
|
25
|
-
'price_currency': 'USD',
|
26
|
-
'pay_currency': currency.upper(),
|
27
|
-
'order_id': kwargs.get('order_id'),
|
28
|
-
'order_description': kwargs.get('description', 'Payment'),
|
29
|
-
'ipn_callback_url': kwargs.get('callback_url'),
|
30
|
-
'success_url': kwargs.get('success_url'),
|
31
|
-
'cancel_url': kwargs.get('cancel_url'),
|
32
|
-
}
|
33
|
-
|
34
|
-
response = requests.post(
|
35
|
-
f"{self.base_url}/payment",
|
36
|
-
json=payload,
|
37
|
-
headers=self.headers,
|
38
|
-
timeout=30
|
39
|
-
)
|
40
|
-
response.raise_for_status()
|
41
|
-
return response.json()
|
42
|
-
|
43
|
-
def check_payment_status(self, payment_id: str) -> Dict[str, Any]:
|
44
|
-
"""Check payment status via NowPayments API."""
|
45
|
-
response = requests.get(
|
46
|
-
f"{self.base_url}/payment/{payment_id}",
|
47
|
-
headers=self.headers,
|
48
|
-
timeout=30
|
49
|
-
)
|
50
|
-
response.raise_for_status()
|
51
|
-
return response.json()
|
52
|
-
|
53
|
-
def process_webhook(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
54
|
-
"""Process NowPayments webhook."""
|
55
|
-
# Extract important fields from webhook
|
56
|
-
return {
|
57
|
-
'payment_id': payload.get('payment_id'),
|
58
|
-
'payment_status': payload.get('payment_status'),
|
59
|
-
'pay_address': payload.get('pay_address'),
|
60
|
-
'pay_amount': payload.get('pay_amount'),
|
61
|
-
'pay_currency': payload.get('pay_currency'),
|
62
|
-
'price_amount': payload.get('price_amount'),
|
63
|
-
'price_currency': payload.get('price_currency'),
|
64
|
-
'order_id': payload.get('order_id'),
|
65
|
-
'outcome_amount': payload.get('outcome_amount'),
|
66
|
-
'outcome_currency': payload.get('outcome_currency'),
|
67
|
-
}
|
68
|
-
|
69
|
-
def get_supported_currencies(self) -> list[str]:
|
70
|
-
"""Get supported cryptocurrencies from NowPayments."""
|
71
|
-
response = requests.get(
|
72
|
-
f"{self.base_url}/currencies",
|
73
|
-
headers=self.headers,
|
74
|
-
timeout=30
|
75
|
-
)
|
76
|
-
response.raise_for_status()
|
77
|
-
data = response.json()
|
78
|
-
return data.get('currencies', [])
|
@@ -1,77 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Payment provider integrations using Pydantic for external data validation.
|
3
|
-
Only for provider responses and internal service communication.
|
4
|
-
"""
|
5
|
-
|
6
|
-
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
7
|
-
from decimal import Decimal
|
8
|
-
from datetime import datetime, timezone
|
9
|
-
from typing import Optional, Dict, Any
|
10
|
-
|
11
|
-
|
12
|
-
class NowPaymentsWebhook(BaseModel):
|
13
|
-
"""NowPayments webhook data validation."""
|
14
|
-
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
15
|
-
|
16
|
-
payment_id: str
|
17
|
-
payment_status: str
|
18
|
-
pay_address: str
|
19
|
-
pay_amount: Decimal
|
20
|
-
pay_currency: str
|
21
|
-
order_id: str
|
22
|
-
order_description: Optional[str] = None
|
23
|
-
ipn_callback_url: Optional[str] = None
|
24
|
-
created_at: Optional[datetime] = None
|
25
|
-
updated_at: Optional[datetime] = None
|
26
|
-
|
27
|
-
@field_validator('pay_amount')
|
28
|
-
@classmethod
|
29
|
-
def validate_amount(cls, v: Decimal) -> Decimal:
|
30
|
-
if v <= 0:
|
31
|
-
raise ValueError("Payment amount must be positive")
|
32
|
-
return v
|
33
|
-
|
34
|
-
|
35
|
-
class NowPaymentsCreateResponse(BaseModel):
|
36
|
-
"""NowPayments payment creation response."""
|
37
|
-
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
38
|
-
|
39
|
-
payment_id: str
|
40
|
-
payment_status: str
|
41
|
-
pay_address: str
|
42
|
-
pay_amount: Decimal
|
43
|
-
pay_currency: str
|
44
|
-
order_id: str
|
45
|
-
order_description: Optional[str] = None
|
46
|
-
ipn_callback_url: Optional[str] = None
|
47
|
-
created_at: datetime
|
48
|
-
updated_at: datetime
|
49
|
-
|
50
|
-
|
51
|
-
class NowPaymentsStatusResponse(BaseModel):
|
52
|
-
"""NowPayments payment status response."""
|
53
|
-
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
54
|
-
|
55
|
-
payment_id: str
|
56
|
-
payment_status: str
|
57
|
-
pay_address: str
|
58
|
-
pay_amount: Decimal
|
59
|
-
actually_paid: Optional[Decimal] = None
|
60
|
-
pay_currency: str
|
61
|
-
order_id: str
|
62
|
-
outcome_amount: Optional[Decimal] = None
|
63
|
-
outcome_currency: Optional[str] = None
|
64
|
-
|
65
|
-
|
66
|
-
class ProviderWebhookData(BaseModel):
|
67
|
-
"""Generic webhook data for any provider."""
|
68
|
-
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
69
|
-
|
70
|
-
provider: str
|
71
|
-
payment_id: str
|
72
|
-
status: str
|
73
|
-
amount: Optional[Decimal] = None
|
74
|
-
currency: Optional[str] = None
|
75
|
-
raw_data: Dict[str, Any] = Field(default_factory=dict)
|
76
|
-
signature: Optional[str] = None
|
77
|
-
received_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
@@ -1,215 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Redis service for universal payments with circuit breaker.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import logging
|
6
|
-
import redis
|
7
|
-
import json
|
8
|
-
import time
|
9
|
-
from typing import Optional, Any, Dict, List
|
10
|
-
from django_cfg.modules import BaseCfgModule
|
11
|
-
from django.core.cache import cache
|
12
|
-
from django.db import transaction
|
13
|
-
|
14
|
-
logger = logging.getLogger(__name__)
|
15
|
-
|
16
|
-
|
17
|
-
class RedisCircuitBreaker:
|
18
|
-
"""Circuit breaker for Redis operations with database fallback."""
|
19
|
-
|
20
|
-
def __init__(self, failure_threshold: int = 5, recovery_timeout: int = 60):
|
21
|
-
self.failure_threshold = failure_threshold
|
22
|
-
self.recovery_timeout = recovery_timeout
|
23
|
-
self.failure_count = 0
|
24
|
-
self.last_failure_time = None
|
25
|
-
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
|
26
|
-
|
27
|
-
def call(self, redis_func, fallback_func, *args, **kwargs):
|
28
|
-
"""Execute function with circuit breaker pattern."""
|
29
|
-
if self.state == "OPEN":
|
30
|
-
if self._should_attempt_reset():
|
31
|
-
self.state = "HALF_OPEN"
|
32
|
-
else:
|
33
|
-
logger.warning("Circuit breaker OPEN, using fallback")
|
34
|
-
return fallback_func(*args, **kwargs)
|
35
|
-
|
36
|
-
try:
|
37
|
-
result = redis_func(*args, **kwargs)
|
38
|
-
self._on_success()
|
39
|
-
return result
|
40
|
-
except Exception as e:
|
41
|
-
self._on_failure()
|
42
|
-
logger.warning(f"Redis operation failed, using fallback: {e}")
|
43
|
-
return fallback_func(*args, **kwargs)
|
44
|
-
|
45
|
-
def _on_success(self):
|
46
|
-
"""Handle successful operation."""
|
47
|
-
self.failure_count = 0
|
48
|
-
self.state = "CLOSED"
|
49
|
-
|
50
|
-
def _on_failure(self):
|
51
|
-
"""Handle failed operation."""
|
52
|
-
self.failure_count += 1
|
53
|
-
self.last_failure_time = time.time()
|
54
|
-
if self.failure_count >= self.failure_threshold:
|
55
|
-
self.state = "OPEN"
|
56
|
-
|
57
|
-
def _should_attempt_reset(self):
|
58
|
-
"""Check if circuit breaker should attempt reset."""
|
59
|
-
import time
|
60
|
-
return (time.time() - self.last_failure_time) > self.recovery_timeout
|
61
|
-
|
62
|
-
|
63
|
-
class RedisService(BaseCfgModule):
|
64
|
-
"""Redis service with automatic configuration and circuit breaker."""
|
65
|
-
|
66
|
-
def __init__(self):
|
67
|
-
super().__init__()
|
68
|
-
self._client = None
|
69
|
-
self._circuit_breaker = RedisCircuitBreaker()
|
70
|
-
|
71
|
-
@property
|
72
|
-
def client(self) -> Optional[redis.Redis]:
|
73
|
-
"""Get Redis client with lazy initialization."""
|
74
|
-
if self._client is None:
|
75
|
-
self._client = self._create_client()
|
76
|
-
return self._client
|
77
|
-
|
78
|
-
def _create_client(self) -> Optional[redis.Redis]:
|
79
|
-
"""Create Redis client from configuration."""
|
80
|
-
try:
|
81
|
-
config = self.get_config()
|
82
|
-
if not config:
|
83
|
-
logger.warning("No config available, Redis disabled")
|
84
|
-
return None
|
85
|
-
|
86
|
-
# Get Redis config from main config
|
87
|
-
redis_config = getattr(config, 'cache', None)
|
88
|
-
if not redis_config:
|
89
|
-
logger.warning("No Redis config found, using default")
|
90
|
-
redis_url = "redis://localhost:6379/0"
|
91
|
-
else:
|
92
|
-
redis_url = getattr(redis_config, 'redis_url', "redis://localhost:6379/0")
|
93
|
-
|
94
|
-
return redis.Redis.from_url(redis_url, decode_responses=True)
|
95
|
-
|
96
|
-
except Exception as e:
|
97
|
-
logger.error(f"Failed to create Redis client: {e}")
|
98
|
-
return None
|
99
|
-
|
100
|
-
def get_cache(self, key: str) -> Any:
|
101
|
-
"""Get value from cache with circuit breaker."""
|
102
|
-
def redis_get():
|
103
|
-
if not self.client:
|
104
|
-
raise redis.ConnectionError("Redis client not available")
|
105
|
-
data = self.client.get(key)
|
106
|
-
return json.loads(data) if data else None
|
107
|
-
|
108
|
-
def fallback_get():
|
109
|
-
# Fallback to Django cache (database)
|
110
|
-
return cache.get(key)
|
111
|
-
|
112
|
-
return self._circuit_breaker.call(redis_get, fallback_get)
|
113
|
-
|
114
|
-
def set_cache(self, key: str, value: Any, ttl: int = 300) -> bool:
|
115
|
-
"""Set value in cache with circuit breaker."""
|
116
|
-
def redis_set():
|
117
|
-
if not self.client:
|
118
|
-
raise redis.ConnectionError("Redis client not available")
|
119
|
-
data = json.dumps(value) if value is not None else ""
|
120
|
-
return self.client.setex(key, ttl, data)
|
121
|
-
|
122
|
-
def fallback_set():
|
123
|
-
# Fallback to Django cache (database)
|
124
|
-
cache.set(key, value, ttl)
|
125
|
-
return True
|
126
|
-
|
127
|
-
return self._circuit_breaker.call(redis_set, fallback_set)
|
128
|
-
|
129
|
-
def delete_cache(self, key: str) -> bool:
|
130
|
-
"""Delete key from cache."""
|
131
|
-
def redis_delete():
|
132
|
-
if not self.client:
|
133
|
-
raise redis.ConnectionError("Redis client not available")
|
134
|
-
return self.client.delete(key)
|
135
|
-
|
136
|
-
def fallback_delete():
|
137
|
-
cache.delete(key)
|
138
|
-
return True
|
139
|
-
|
140
|
-
return self._circuit_breaker.call(redis_delete, fallback_delete)
|
141
|
-
|
142
|
-
def increment(self, key: str, amount: int = 1, ttl: Optional[int] = None) -> int:
|
143
|
-
"""Increment counter with circuit breaker."""
|
144
|
-
def redis_incr():
|
145
|
-
if not self.client:
|
146
|
-
raise redis.ConnectionError("Redis client not available")
|
147
|
-
result = self.client.incr(key, amount)
|
148
|
-
if ttl:
|
149
|
-
self.client.expire(key, ttl)
|
150
|
-
return result
|
151
|
-
|
152
|
-
def fallback_incr():
|
153
|
-
# Simple fallback - just return amount (no persistence)
|
154
|
-
logger.warning(f"Redis unavailable, counter increment for {key} not persisted")
|
155
|
-
return amount
|
156
|
-
|
157
|
-
return self._circuit_breaker.call(redis_incr, fallback_incr)
|
158
|
-
|
159
|
-
def get_user_access_cache(self, user_id: int, endpoint_group: str) -> Optional[Dict]:
|
160
|
-
"""Get cached user access info."""
|
161
|
-
key = f"access:{user_id}:{endpoint_group}"
|
162
|
-
return self.get_cache(key)
|
163
|
-
|
164
|
-
def set_user_access_cache(self, user_id: int, endpoint_group: str, access_info: Dict, ttl: int = 60) -> bool:
|
165
|
-
"""Set cached user access info."""
|
166
|
-
key = f"access:{user_id}:{endpoint_group}"
|
167
|
-
return self.set_cache(key, access_info, ttl)
|
168
|
-
|
169
|
-
def track_usage(self, user_id: int, endpoint_group: str, response_time_ms: Optional[int] = None) -> None:
|
170
|
-
"""Track API usage with rate limiting."""
|
171
|
-
# Increment request counter
|
172
|
-
usage_key = f"usage:{user_id}:{endpoint_group}"
|
173
|
-
self.increment(usage_key, ttl=3600) # 1 hour window
|
174
|
-
|
175
|
-
# Track response time if provided
|
176
|
-
if response_time_ms:
|
177
|
-
rt_key = f"response_time:{user_id}:{endpoint_group}"
|
178
|
-
def redis_track_rt():
|
179
|
-
if not self.client:
|
180
|
-
raise redis.ConnectionError("Redis client not available")
|
181
|
-
self.client.lpush(rt_key, response_time_ms)
|
182
|
-
self.client.ltrim(rt_key, 0, 99) # Keep last 100
|
183
|
-
self.client.expire(rt_key, 3600)
|
184
|
-
|
185
|
-
def fallback_track_rt():
|
186
|
-
pass # Skip response time tracking in fallback
|
187
|
-
|
188
|
-
self._circuit_breaker.call(redis_track_rt, fallback_track_rt)
|
189
|
-
|
190
|
-
def check_rate_limit(self, user_id: int, endpoint_group: str, limit: int, window: int = 3600) -> Dict:
|
191
|
-
"""Check rate limit for user."""
|
192
|
-
rate_key = f"rate:{user_id}:{endpoint_group}:{window}"
|
193
|
-
|
194
|
-
def redis_check():
|
195
|
-
if not self.client:
|
196
|
-
raise redis.ConnectionError("Redis client not available")
|
197
|
-
current = self.client.get(rate_key) or 0
|
198
|
-
return {
|
199
|
-
'allowed': int(current) < limit,
|
200
|
-
'current': int(current),
|
201
|
-
'limit': limit,
|
202
|
-
'window': window
|
203
|
-
}
|
204
|
-
|
205
|
-
def fallback_check():
|
206
|
-
# Fallback - always allow (no rate limiting)
|
207
|
-
logger.warning(f"Redis unavailable, rate limiting disabled for user {user_id}")
|
208
|
-
return {
|
209
|
-
'allowed': True,
|
210
|
-
'current': 0,
|
211
|
-
'limit': limit,
|
212
|
-
'window': window
|
213
|
-
}
|
214
|
-
|
215
|
-
return self._circuit_breaker.call(redis_check, fallback_check)
|
File without changes
|
File without changes
|
File without changes
|