Compare commits

...

9 Commits

Author SHA1 Message Date
bb3a52295d feat: added LowerCaseString field 2025-07-24 22:53:26 +02:00
renovate[bot]
d471b86a25 feat(deps): update dependency stripe to v12.3.0 (#55)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 09:46:11 +02:00
creyD
aa99fc6226 Adjusted files for isort & autopep 2025-06-26 12:50:57 +00:00
vikynoah
30a5e417eb feat: User Password change ticket (#54) 2025-06-26 14:49:56 +02:00
creyD
1f5ba9210f Adjusted files for isort & autopep 2025-06-03 08:01:59 +00:00
vikynoah
f805b3f508 feat: Added Email Sending Service (#52)
* feat: Added Email Sending Service

* changes

* changes
2025-06-03 10:00:49 +02:00
renovate[bot]
8a882cdaae feat(deps): update dependency stripe to v12.2.0 (#51)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 16:31:03 +02:00
renovate[bot]
40176aa3e9 feat(deps): update dependency stripe to v12.1.0 (#50)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 11:09:31 +02:00
vikynoah
be66bbebbf fix: remove optional schema and alter base out (#49)
* fix: remove optional schema and alter base out

* changes
2025-04-15 20:32:02 +02:00
11 changed files with 74 additions and 46 deletions

2
.gitignore vendored
View File

@@ -158,3 +158,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.DS_*

View File

@@ -1,3 +1,4 @@
from .fields import * # noqa
from .groups import * # noqa
from .i18n import * # noqa
from .stripe import * # noqa

17
creyPY/const/fields.py Normal file
View File

@@ -0,0 +1,17 @@
from sqlalchemy import types
class LowerCaseString(types.TypeDecorator):
"""Converts strings to lower case on the way in."""
impl = types.String
cache_ok = True
def process_bind_param(self, value, dialect):
if value is None:
return value
return value.lower()
@property
def python_type(self):
return str

View File

@@ -1,3 +1,2 @@
from .base import * # noqa
from .response_schema import * #noqa
from .schema_optional import * #noqa

View File

@@ -11,6 +11,6 @@ class BaseSchemaModelIN(BaseModel):
class BaseSchemaModelOUT(BaseSchemaModelIN):
id: UUID | str
id: UUID
created_at: datetime
updated_at: datetime

View File

@@ -1,43 +0,0 @@
from typing import List, Optional, Type
from fastapi import Query
from pydantic import BaseModel, create_model
class ResponseModelDependency:
def __init__(self, model_class: Type[BaseModel]):
self.model_class = model_class
def __call__(self, response_fields: Optional[List[str]] = Query(None)) -> Type[BaseModel]:
def process_result(result, fields=None, async_session=False):
if not fields:
if async_session:
return {k: v for k, v in result.__dict__.items() if not k.startswith("_")}
return result
if hasattr(result, "_fields"):
row_fields = result._fields
return dict(zip(row_fields, result))
elif isinstance(result, tuple):
return dict(zip(fields, result))
elif isinstance(result, dict):
return result
else:
return {field: getattr(result, field) for field in fields if hasattr(result, field)}
if not response_fields:
return self.model_class, None, process_result
all_annotations = {}
for cls in self.model_class.__mro__:
if hasattr(cls, "__annotations__"):
all_annotations.update(cls.__annotations__)
fields = {}
for field in response_fields:
if field in all_annotations:
fields[field] = (all_annotations[field], None)
dynamic_model = create_model(f"Dynamic{self.model_class.__name__}", **fields)
return dynamic_model, response_fields, process_result

View File

@@ -1,2 +1,3 @@
from .auth0 import * # noqa
from .stripe import * # noqa
from .aws import * # noqa

View File

@@ -145,3 +145,21 @@ def password_change_mail(email: str) -> bool:
if re.status_code != 200:
raise HTTPException(re.status_code, re.json())
return True
def user_password_change_ticket(user_id: str) -> str:
re = requests.post(
f"https://{AUTH0_DOMAIN}/api/v2/tickets/password-change",
headers={"Authorization": f"Bearer {get_management_token()}"},
json={
"user_id": user_id,
"client_id": AUTH0_CLIENT_ID,
"ttl_sec": 0,
"mark_email_as_verified": False,
"includeEmailInRedirect": False,
},
timeout=5,
)
if re.status_code != 201:
raise HTTPException(re.status_code, re.json())
return re.json()["ticket"]

View File

@@ -0,0 +1 @@
from .email import * # noqa

View File

@@ -0,0 +1,32 @@
import os
import boto3
from botocore.exceptions import ClientError
AWS_CLIENT_ID = os.getenv("AWS_CLIENT_ID")
AWS_CLIENT_SECRET = os.getenv("AWS_CLIENT_SECRET")
AWS_SENDER_EMAIL = os.getenv("AWS_SENDER_EMAIL")
AWS_REGION = os.getenv("AWS_REGION", "eu-central-1")
async def send_email_ses(recipient_email, subject, html_body):
ses_client = boto3.client(
"ses",
aws_access_key_id=AWS_CLIENT_ID,
aws_secret_access_key=AWS_CLIENT_SECRET,
region_name=AWS_REGION,
)
email_message = {
"Source": AWS_SENDER_EMAIL,
"Destination": {"ToAddresses": [recipient_email]},
"Message": {
"Subject": {"Data": subject, "Charset": "UTF-8"},
"Body": {"Html": {"Data": html_body, "Charset": "UTF-8"}},
},
}
try:
response = ses_client.send_email(**email_message)
return response["MessageId"]
except ClientError as e:
return None

View File

@@ -1 +1 @@
stripe==12.0.0 # Stripe
stripe==12.3.0 # Stripe