From 249a9040d20db7c845c2cb40e5ae66f7a8da6391 Mon Sep 17 00:00:00 2001 From: poduck Date: Thu, 27 Nov 2025 02:16:05 -0500 Subject: [PATCH] Implement Platform Superuser UI and Fix API Role Casing - Update API to return lowercase roles for frontend compatibility - Fix Tenant owner lookup in platform admin serializer - Update frontend App.tsx to match tarball implementation - Prioritize vite.config.js for HMR support - Include pending CSP and CORS configuration updates --- frontend/src/App.tsx | 4 +- ...vite.config.ts => vite.config.ts.disabled} | 0 smoothschedule/blank_screen_debug.png | Bin 0 -> 39344 bytes smoothschedule/config/settings/local.py | 5 ++ .../config/settings/multitenancy.py | 39 +++++++++ smoothschedule/platform_admin/serializers.py | 8 +- smoothschedule/pyproject.toml | 2 + smoothschedule/schedule/api_views.py | 2 +- .../smoothschedule/users/api_views.py | 10 ++- smoothschedule/uv.lock | 81 ++++++++++++++++++ 10 files changed, 143 insertions(+), 8 deletions(-) rename frontend/{vite.config.ts => vite.config.ts.disabled} (100%) create mode 100644 smoothschedule/blank_screen_debug.png diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2aebedb..8bb1a53 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -10,7 +10,6 @@ import { useCurrentUser, useMasquerade, useLogout } from './hooks/useAuth'; import { useCurrentBusiness } from './hooks/useBusiness'; import { useUpdateBusiness } from './hooks/useBusiness'; import { setCookie } from './utils/cookies'; -import { DevQuickLogin } from './components/DevQuickLogin'; // Import Login Page import LoginPage from './pages/LoginPage'; @@ -549,9 +548,8 @@ const App: React.FC = () => { - ); }; -export default App; +export default App; \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts.disabled similarity index 100% rename from frontend/vite.config.ts rename to frontend/vite.config.ts.disabled diff --git a/smoothschedule/blank_screen_debug.png b/smoothschedule/blank_screen_debug.png new file mode 100644 index 0000000000000000000000000000000000000000..11946ac5e38b063b921a06a9b0d098089f85fd39 GIT binary patch literal 39344 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�}e_9BX>t#HVtTv9f@FHUK)6?7l1gb~e zE83flGv@HJ>EP1E*lGK?x#m}aiiz`HpV{sa>jq|wmbSf9tF#u7bTHJUIo|Q_M=mbmY8p(QLY&mx(gZC$JEZ@O^O7P`4u0 zJ?85+Ub17=4}8itQMDdxrt9q?n>u<|lazl+s7c$j;K_x-?$Iw)VXVhAWVoj4yRJL8 zNz8+vD|P^W{l})@cd#!cX5uqXybm^N~FCK4)R z`{R7=T0AtMJd&vYuADQlIY=&i6Wpk~PKU#HAaxPfL{fMR%GM=|O3#Pa@hJNPK)ru& zkxKpHBZ9*^_ogN#_K6HT?qq9xPfu^!nN7z#aK{%y*rjZ~y_NYAzK8=#{wY7xG85Q1 zZ<1ERgq?!%*pW$VrRVr)4alj_bjJ8%6%Wirl0*r(dnb0!+UeX4s4H!pxlbU{Cf#&* zu0s7Tp1tgVVso){)4_cm#ZPZsjUOeB4>Ax{ycQUN$PZy*A?OeI1iQd6BAV+cD)dXRBDSQ??c1P`{}lvFZm z$-@q73qk_q;Vw08lQ=uW@nDguCxk&vp3&2j?a;8n?1y+_mOd*zlw`ACJi1Gt$j+PM z0$>G@RM|RiK6jefXO14B(~JlQ<&+Bjk`Ey>RIak(x*IxlIMZdl`K~~Q9AUAR+9Uqq zY>_lJ(E#}QpHaa|b40tP8$@3362XTz+<$<($SHpv%AA z8J=n^Q2$kV=k?q-KRFv?qIN{79y7ERkM>%okLN_p*eAbM8y;fA(B|PcBlWV^EZ zCanaV%qHfPzqt(d+AF;a^{@9wB}5xO_eD#&*4dkGdrxe(EeNq$T8{qJ#iIw-g#=?u&Ua@T*<$s%qgthuBv->= z4wRlPb9Jle5@Zy?tzqt~8ey|SvDUM1Zcl=m=c6+$dj57E%P(ZzN-u;1k-R!sP~`xB z%38cf)T{xqr8B%JDxaC3YHIPZd#kMDbNKuBT7`@f`qN5B7yNus*sEFtR6BXOH3EEz z*2CuD96~1epy@{5S;{KUg^HXF49n^7~Ih`c6Vju+6#t#He<(omA2F z!lYsRxEUz+qaTVa#m@|O>F;qWb{2?x&vBbp5>}j5s;VK0jU488@ssb8EUU=;u4W=L zRAd?FEz^pus0E;`X1XsbQQQ85Ty)bP{M_P3B>fMYcZk-lgFPWwaT9=e%VrA`xNi;0 zcqYaV+e(7U3$(dc&ec7nWOs~aHYZ5_j_aHIouJpb{6+qxMWuOGuM2Hxld#FzxR`sA zZzjSkYpY15Ml&Gipyj*xDqb7(yQhWcz4|=@7QLSQm-MOi2`Rc{LEXY+JL{aNbw~HL zd0vHInjX3nNue%0F>9vK^FHz$Cz-wA9nL3tVVkPY(LT5! zryb7==faZ+9m@Hv_NHs~glu!o-{eKnoC`lBE_3n@-aX?ipPvA`@46Ym@8jY=9wYiH z{#`>r-~2GiUQha2Ei^sr+TXz{|JOZUpS^74Lph*z8>*#82vNZ*CHd0>pwlicJec|$ z=S-6zFS^DJTt6&9MA<%ikt?cw>BWLu$DS@7)x*XQoUcP0x6e~rS@djhL#?2h@g^0|1Au%d

|2hRwsGUk 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.56.0", "pre-commit==4.5.0", "psycopg[c]==3.2.13", "pytest==9.0.1", @@ -192,4 +193,5 @@ dependencies = [ "sentry-sdk==2.46.0", "whitenoise==6.11.0", "stripe>=7.0.0", + "django-csp==3.8.0", ] diff --git a/smoothschedule/schedule/api_views.py b/smoothschedule/schedule/api_views.py index d2bdfec..58377bb 100644 --- a/smoothschedule/schedule/api_views.py +++ b/smoothschedule/schedule/api_views.py @@ -25,7 +25,7 @@ def current_business_view(request): # Get subdomain from primary domain subdomain = None - primary_domain = tenant.domain_set.filter(is_primary=True).first() + primary_domain = tenant.domains.filter(is_primary=True).first() if primary_domain: # Extract subdomain from domain (e.g., "mybusiness.lvh.me" -> "mybusiness") domain_parts = primary_domain.domain.split('.') diff --git a/smoothschedule/smoothschedule/users/api_views.py b/smoothschedule/smoothschedule/users/api_views.py index 8b42eca..693c0fd 100644 --- a/smoothschedule/smoothschedule/users/api_views.py +++ b/smoothschedule/smoothschedule/users/api_views.py @@ -23,14 +23,20 @@ def current_user_view(request): business_subdomain = None if user.tenant: business_name = user.tenant.name - business_subdomain = user.tenant.subdomain + # user.tenant.subdomain does not exist. Fetch from domains relation. + # Assuming 'domains' is the related_name from Domain model to Tenant + primary_domain = user.tenant.domains.filter(is_primary=True).first() + if primary_domain: + business_subdomain = primary_domain.domain.split('.')[0] + else: + business_subdomain = user.tenant.schema_name user_data = { 'id': user.id, 'username': user.username, 'email': user.email, 'name': user.full_name, - 'role': user.role, + 'role': user.role.lower(), 'avatar_url': None, # TODO: Implement avatar 'email_verified': False, # TODO: Implement email verification 'is_staff': user.is_staff, diff --git a/smoothschedule/uv.lock b/smoothschedule/uv.lock index db8b4a0..0ba68d5 100644 --- a/smoothschedule/uv.lock +++ b/smoothschedule/uv.lock @@ -520,6 +520,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ac3a11950baaf75c1f3242e3af9dfe45201f6ee10c113dd37a9c000876d2/django_crispy_forms-2.5-py3-none-any.whl", hash = "sha256:adc99d5901baca09479c53bf536b3909e80a9f2bb299438a223de4c106ebf1f9", size = 31464, upload-time = "2025-11-06T20:44:00.795Z" }, ] +[[package]] +name = "django-csp" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/16/c3c65ad59997284402e54d00797c7aca96572df911aede3e1f2cc2e029f8/django_csp-3.8.tar.gz", hash = "sha256:ef0f1a9f7d8da68ae6e169c02e9ac661c0ecf04db70e0d1d85640512a68471c0", size = 13341, upload-time = "2024-03-01T14:00:30.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/ff/2c7a4b6706125a17bd0071802e4894c28772cfcdea20a086a2be3c5fafda/django_csp-3.8-py3-none-any.whl", hash = "sha256:19b2978b03fcd73517d7d67acbc04fbbcaec0facc3e83baa502965892d1e0719", size = 17410, upload-time = "2024-03-01T14:00:28.135Z" }, +] + [[package]] name = "django-debug-toolbar" version = "6.1.0" @@ -822,6 +834,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a6/ff/ee2f67c0ff146ec98b5df1df637b2bc2d17beeb05df9f427a67bd7a7d79c/flower-2.0.1-py2.py3-none-any.whl", hash = "sha256:9db2c621eeefbc844c8dd88be64aef61e84e2deb29b271e02ab2b5b9f01068e2", size = 383553, upload-time = "2023-08-13T14:37:41.552Z" }, ] +[[package]] +name = "greenlet" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, + { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, + { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, + { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, + { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, + { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" }, + { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" }, + { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, +] + [[package]] name = "gunicorn" version = "23.0.0" @@ -1222,6 +1253,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, ] +[[package]] +name = "playwright" +version = "1.56.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/31/a5362cee43f844509f1f10d8a27c9cc0e2f7bdce5353d304d93b2151c1b1/playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c", size = 40611424, upload-time = "2025-11-11T18:39:10.175Z" }, + { url = "https://files.pythonhosted.org/packages/ef/95/347eef596d8778fb53590dc326c344d427fa19ba3d42b646fce2a4572eb3/playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b", size = 39400228, upload-time = "2025-11-11T18:39:13.915Z" }, + { url = "https://files.pythonhosted.org/packages/b9/54/6ad97b08b2ca1dfcb4fbde4536c4f45c0d9d8b1857a2d20e7bbfdf43bf15/playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65", size = 40611424, upload-time = "2025-11-11T18:39:17.093Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/6d409e37e82cdd5dda3df1ab958130ae32b46e42458bd4fc93d7eb8749cb/playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c", size = 46263122, upload-time = "2025-11-11T18:39:20.619Z" }, + { url = "https://files.pythonhosted.org/packages/4f/84/fb292cc5d45f3252e255ea39066cd1d2385c61c6c1596548dfbf59c88605/playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34", size = 46110645, upload-time = "2025-11-11T18:39:24.005Z" }, + { url = "https://files.pythonhosted.org/packages/61/bd/8c02c3388ae14edc374ac9f22cbe4e14826c6a51b2d8eaf86e89fabee264/playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721", size = 35639837, upload-time = "2025-11-11T18:39:27.174Z" }, + { url = "https://files.pythonhosted.org/packages/64/27/f13b538fbc6b7a00152f4379054a49f6abc0bf55ac86f677ae54bc49fb82/playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94", size = 35639843, upload-time = "2025-11-11T18:39:30.851Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c7/3ee8b556107995846576b4fe42a08ed49b8677619421f2afacf6ee421138/playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f", size = 31248959, upload-time = "2025-11-11T18:39:33.998Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -1318,6 +1368,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] +[[package]] +name = "pyee" +version = "13.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -1633,6 +1695,7 @@ dependencies = [ { name = "django-celery-beat" }, { name = "django-cors-headers" }, { name = "django-crispy-forms" }, + { name = "django-csp" }, { name = "django-environ" }, { name = "django-hijack" }, { name = "django-model-utils" }, @@ -1649,6 +1712,7 @@ dependencies = [ { name = "python-slugify" }, { name = "redis" }, { name = "sentry-sdk" }, + { name = "stripe" }, { name = "whitenoise" }, ] @@ -1664,6 +1728,7 @@ dev = [ { name = "factory-boy" }, { name = "ipdb" }, { name = "mypy" }, + { name = "playwright" }, { name = "pre-commit" }, { name = "psycopg", extra = ["c"] }, { name = "pytest" }, @@ -1687,6 +1752,7 @@ requires-dist = [ { name = "django-celery-beat", specifier = "==2.8.1" }, { name = "django-cors-headers", specifier = "==4.9.0" }, { name = "django-crispy-forms", specifier = "==2.5" }, + { name = "django-csp", specifier = "==3.8.0" }, { name = "django-environ", specifier = "==0.12.0" }, { name = "django-hijack", specifier = ">=3.4" }, { name = "django-model-utils", specifier = "==5.0.0" }, @@ -1703,6 +1769,7 @@ requires-dist = [ { name = "python-slugify", specifier = "==8.0.4" }, { name = "redis", specifier = "==7.1.0" }, { name = "sentry-sdk", specifier = "==2.46.0" }, + { name = "stripe", specifier = ">=7.0.0" }, { name = "whitenoise", specifier = "==6.11.0" }, ] @@ -1718,6 +1785,7 @@ dev = [ { name = "factory-boy", specifier = "==3.3.2" }, { name = "ipdb", specifier = "==0.13.13" }, { name = "mypy", specifier = "==1.18.2" }, + { name = "playwright", specifier = ">=1.56.0" }, { name = "pre-commit", specifier = "==4.5.0" }, { name = "psycopg", extras = ["c"], specifier = "==3.2.13" }, { name = "pytest", specifier = "==9.0.1" }, @@ -1882,6 +1950,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] +[[package]] +name = "stripe" +version = "14.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/49/08df0acc094587f4d76c2ab31ebbecb8a37312ab558cddaa6a4c2ff19579/stripe-14.0.1.tar.gz", hash = "sha256:f2d56345bf5d41c1f21f814b00174a3173a0b5eb4e8fc46a8f779e3d7a2efc6e", size = 1362960, upload-time = "2025-11-22T01:07:48.862Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/88/0db878a84d333a188714f4ade57c9ae765a14a0b81862eb133ad7864711c/stripe-14.0.1-py3-none-any.whl", hash = "sha256:ff25c5e5f085beaa98b6b9c2c729d22ad99068196cbd83fdf82669fd08311b76", size = 1970603, upload-time = "2025-11-22T01:07:47.309Z" }, +] + [[package]] name = "termcolor" version = "3.2.0"