Compare commits

..

3 Commits

Author SHA1 Message Date
berkay b4b9f97690 updated 2025-03-26 11:10:08 +03:00
berkay 637edfadd4 auth api tested 2025-03-25 19:01:46 +03:00
berkay db2cde2f5d api design 2025-03-25 16:38:58 +03:00
65 changed files with 810 additions and 712 deletions

View File

@ -0,0 +1,118 @@
import datetime
class DefaultApiConfig:
app: str
host: str
port: int
log_level: str
reload: bool
@classmethod
def as_dict(cls):
return {
"app": cls.app,
"host": cls.host,
"port": int(cls.port),
"log_level": cls.log_level,
"reload": bool(cls.reload),
}
class ApiConfigs:
"""Base class for all configurations."""
SECRET: str = (
"59f871a2d2194e96adb36b279d2cc21059f871a2d2194e96adb36b279d2cc21059f871a2d2194e96adb36b279d2cc210s"
)
ACCESS_TIME: int = 432000
REFRESH_TIME: int = 864000
DEFAULT_SIZE: int = 10
MIN_SIZE: int = 5
MAX_SIZE: int = 50
ACCESS_TOKEN_TAG: str = "Authorization"
REFRESH_TOKEN_TAG: str = "Refresher"
ACCESS_TOKEN_LENGTH: int = 72
REFRESH_TOKEN_LENGTH: int = 128
class ApiStatic:
PLACEHOLDER = "https://s.tmimgcdn.com/scr/800x500/276800/building-home-nature-logo-vector-template-3_276851-original.jpg"
FORGOT_LINK = "https://www.evyos.com.tr/password/create?tokenUrl="
BLACKLIST_LINK = "https://www.evyos.com.tr/support/unknown-login-notice/"
APP_DIR = "/home/berkay/git-evyos/api-managment-backend/"
@classmethod
def forgot_link(cls, forgot_key):
return cls.FORGOT_LINK + forgot_key
@classmethod
def blacklist_login(cls, record_id):
return cls.BLACKLIST_LINK + record_id
class MainConfig:
# Date and Time Configuration
DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss Z"
DATETIME_FORMAT_JS = "YYYY-MM-DD HH:mm:ss +0"
# Timezone Configuration
DEFAULT_TIMEZONE = "GMT+3" # Default timezone for the application
SYSTEM_TIMEZONE = "GMT+0" # System timezone (used for internal operations)
SUPPORTED_TIMEZONES = ["GMT+0", "GMT+3"] # List of supported timezones
class LanguageConfig:
SUPPORTED_LANGUAGES = ["en", "tr"]
DEFAULT_LANGUAGE = "tr"
class ValidationsConfig:
SUPPORTED_VALIDATIONS = ["header", "validation", "all"]
DEFAULT_VALIDATION = "all"
class ApiConfig(DefaultApiConfig):
# Application Information
APP_NAME = "evyos-auth-api-gateway"
TITLE = "WAG API Auth Api Gateway"
DESCRIPTION = (
"This api is serves as web auth api gateway only to evyos web services."
)
APP_URL = "https://www.auth.eys.gen.tr"
# Server Configuration
app = "app:app"
host = "0.0.0.0"
port = 41575
log_level = "info"
reload = True
class Auth:
ACCESS_EMAIL_EXT = "evyos.com.tr"
ACCESS_TOKEN_TAG = "evyos-session-key"
REFRESHER_TOKEN_TAG = "eys-session-refresher"
SECRET_KEY_72 = (
"t3sUAmjTGeTgDc6dAUrB41u2SNg0ZHzj4HTjem95y3fRH1nZXOHIBj163kib6iLybT0gLaxq"
)
SECRET_KEY_96 = "7ct8VpiwaP1hR2bVSet4dEEAgepuTZUOnO1QxOgKyDqBR2PkqNhcubSrbUUigQKoQA1PBoeeQn5ZCo24pESmVtKs76nA4EKq"
SECRET_KEY_144 = (
"R2p5Rq6KCr6PCfjFYUeH1keF2VWHFEuqINVjBGGnvRA2m10pYUKqfOtIGBcaj2v5wZmElDndzSHGOS7roQsoTelPSok0"
+ "qqMucurMWE0FGexGpFuJkfPEm9tH2OjMOqegvEetpSVywH0W4Kh4"
)
ALGORITHM = "HS256"
ACCESS_TOKEN_LENGTH: int = 90
REFRESHER_TOKEN_LENGTH: int = 144
PASSWORD_EXPIRE_DAY = datetime.timedelta(days=30)
TOKEN_EXPIRE_MINUTES_1 = datetime.timedelta(minutes=1)
TOKEN_EXPIRE_MINUTES_15 = datetime.timedelta(minutes=15)
TOKEN_EXPIRE_MINUTES_30 = datetime.timedelta(minutes=30)
TOKEN_EXPIRE_DAY_1 = datetime.timedelta(days=1)
TOKEN_EXPIRE_DAY_5 = datetime.timedelta(days=5)
TOKEN_EXPIRE_DAY_15 = datetime.timedelta(days=15)
TOKEN_EXPIRE_DAY_30 = datetime.timedelta(days=30)

View File

@ -5,19 +5,21 @@ from ApiLayers.AllConfigs import HostConfig
class Config:
MAILBOX: str = os.getenv('MAILBOX', "bilgilendirme@ileti.isbank.com.tr")
MAIN_MAIL: str = os.getenv('MAIN_MAIL', "karatay.berkay@gmail.com")
INFO_MAIL: str = os.getenv('INFO_MAIL', "mehmet.karatay@hotmail.com")
EMAIL_HOST: str = os.getenv('EMAIL_HOST', "10.10.2.34")
EMAIL_SENDER_USERNAME: str = os.getenv('EMAIL_SENDER_USERNAME', "karatay@mehmetkaratay.com.tr")
EMAIL_USERNAME: str = os.getenv('EMAIL_USERNAME', "isbank@mehmetkaratay.com.tr")
EMAIL_PASSWORD: str = os.getenv('EMAIL_PASSWORD', "system")
AUTHORIZE_IBAN: str = os.getenv('AUTHORIZE_IBAN', "4245-0093333")
SERVICE_TIMING: int = int(os.getenv('SERVICE_TIMING', 900))
EMAIL_PORT: int = int(os.getenv('EMAIL_PORT', 993))
EMAIL_SEND_PORT: int = int(os.getenv('EMAIL_SEND_PORT', 587))
EMAIL_SLEEP: int = int(os.getenv('EMAIL_SLEEP', 60))
EMAIL_SEND: bool = bool(os.getenv('EMAIL_SEND', False))
MAILBOX: str = os.getenv("MAILBOX", "bilgilendirme@ileti.isbank.com.tr")
MAIN_MAIL: str = os.getenv("MAIN_MAIL", "karatay.berkay@gmail.com")
INFO_MAIL: str = os.getenv("INFO_MAIL", "mehmet.karatay@hotmail.com")
EMAIL_HOST: str = os.getenv("EMAIL_HOST", "10.10.2.34")
EMAIL_SENDER_USERNAME: str = os.getenv(
"EMAIL_SENDER_USERNAME", "karatay@mehmetkaratay.com.tr"
)
EMAIL_USERNAME: str = os.getenv("EMAIL_USERNAME", "isbank@mehmetkaratay.com.tr")
EMAIL_PASSWORD: str = os.getenv("EMAIL_PASSWORD", "system")
AUTHORIZE_IBAN: str = os.getenv("AUTHORIZE_IBAN", "4245-0093333")
SERVICE_TIMING: int = int(os.getenv("SERVICE_TIMING", 900))
EMAIL_PORT: int = int(os.getenv("EMAIL_PORT", 993))
EMAIL_SEND_PORT: int = int(os.getenv("EMAIL_SEND_PORT", 587))
EMAIL_SLEEP: int = int(os.getenv("EMAIL_SLEEP", 60))
EMAIL_SEND: bool = bool(os.getenv("EMAIL_SEND", False))
class EmailConfig:

View File

@ -3,7 +3,7 @@ from ApiLayers.AllConfigs import HostConfig
class WagDatabase:
HOST: str = HostConfig.MAIN_HOST
PORT: int = 5434
PORT: int = 5444
SQL: str = "postgresql+psycopg2"
USERNAME: str = "berkay_wag_user"
PASSWORD: str = "berkay_wag_user_password"

View File

@ -1,5 +1,3 @@
class HostConfig:
MAIN_HOST = "10.10.2.36" # http://10.10.2.36 Database and other services

View File

@ -1,5 +1,4 @@
from fastapi import APIRouter
import uuid
from Events.Engine.abstract_class import CategoryCluster, MethodToEvent

View File

@ -1,4 +1,6 @@
from Services.Redis import RedisActions, AccessToken
from Services.Redis.Models.cluster import RedisList
from Services.RedisService.Actions.actions import RedisActions
from Services.RedisService.Models.access import AccessToken
from Services.RedisService.Models.cluster import RedisList
redis_list = RedisList(redis_key="test")

View File

@ -23,13 +23,12 @@ class UserLoginModule:
}
@staticmethod
def check_user_exists(access_key: str):
def check_user_exists(access_key: str, db_session):
from ApiLayers.Schemas import Users
"""
Check if the user exists in the database.
"""
db_session = Users.new_session() # Check if user exists.
if "@" in access_key:
found_user: Users = Users.filter_one(
Users.email == access_key.lower(), db=db_session
@ -54,42 +53,46 @@ class UserLoginModule:
"""
Login the user via the credentials.
"""
# Get the actual data from the BaseRequestModel if needed
found_user: Users = self.check_user_exists(access_key=access_data.access_key)
if len(found_user.hash_password) < 5:
with Users.new_session() as db_session: # Check if user exists.
# Get the actual data from the BaseRequestModel if needed
found_user: Users = self.check_user_exists(
access_key=access_data.access_key, db_session=db_session
)
if len(found_user.hash_password) < 5:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=found_user.lang,
loc=get_line_number_for_error(),
sys_msg="login_user_via_credentials: Invalid password create a password to user first",
)
# Check if the password is correct
if PasswordModule.check_password(
domain=access_data.domain,
id_=found_user.uu_id,
password=access_data.password,
password_hashed=found_user.hash_password,
):
found_user_dict = found_user.get_dict()
# Set the access token to the redis
token_response = TokenService.set_access_token_to_redis(
request=self.request,
user=found_user,
domain=access_data.domain,
remember=access_data.remember_me,
db_session=db_session
)
# Set the user and token information to the instance
self.user = found_user_dict
self.access_token = token_response.get("access_token")
self.refresh_token = token_response.get("refresh_token")
self.access_object = {
"user_type": token_response.get("user_type", None),
"selection_list": token_response.get("selection_list", {}),
}
return None
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=found_user.lang,
lang="tr",
loc=get_line_number_for_error(),
sys_msg="login_user_via_credentials: Invalid password create a password to user first",
sys_msg="login_user_via_credentials: raised an unknown error",
)
# Check if the password is correct
if PasswordModule.check_password(
domain=access_data.domain,
id_=found_user.uu_id,
password=access_data.password,
password_hashed=found_user.hash_password,
):
# Set the access token to the redis
token_response = TokenService.set_access_token_to_redis(
request=self.request,
user=found_user,
domain=access_data.domain,
remember=access_data.remember_me,
)
# Set the user and token information to the instance
self.user = found_user.get_dict()
self.access_token = token_response.get("access_token")
self.refresh_token = token_response.get("refresh_token")
self.access_object = {
"user_type": token_response.get("user_type", None),
"selection_list": token_response.get("selection_list", {}),
}
return None
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang="tr",
loc=get_line_number_for_error(),
sys_msg="login_user_via_credentials: raised an unknown error",
)

View File

@ -30,8 +30,9 @@ from ApiLayers.Schemas import (
Departments,
OccupantTypes,
)
from Services.Redis.Models.response import RedisResponse
from Services.Redis import RedisActions, AccessToken
from Services.RedisService.Models.response import RedisResponse
from Services.RedisService.Actions.actions import RedisActions
from Services.RedisService.Models.access import AccessToken
if TYPE_CHECKING:
@ -63,78 +64,78 @@ class TokenService:
"""Handle employee login process and return login information."""
from ApiLayers.Schemas.identity.identity import UsersTokens, People
db_session = Employees.new_session()
list_employee = Employees.filter_all(
Employees.people_id == user.person_id, db=db_session
).data
companies_uu_id_list: List[str] = []
companies_id_list: List[int] = []
companies_list: List[Dict[str, Any]] = []
duty_uu_id_list: List[str] = []
duty_id_list: List[int] = []
for employee in list_employee:
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db_session).data
if duties := Duties.filter_one(
Duties.id == staff.duties_id, db=db_session
).data:
if duty_found := Duty.filter_by_one(
id=duties.duties_id, db=db_session
).data:
duty_uu_id_list.append(str(duty_found.uu_id))
duty_id_list.append(duty_found.id)
department = Departments.filter_one(
Departments.id == duties.department_id, db=db_session
with Employees.new_session() as db_session:
list_employee = Employees.filter_all(
Employees.people_id == user.person_id, db=db_session
).data
if company := Companies.filter_one(
Companies.id == department.company_id, db=db_session
).data:
companies_uu_id_list.append(str(company.uu_id))
companies_id_list.append(company.id)
company_address = Addresses.filter_by_one(
id=company.official_address_id, db=db_session
companies_uu_id_list: List[str] = []
companies_id_list: List[int] = []
companies_list: List[Dict[str, Any]] = []
duty_uu_id_list: List[str] = []
duty_id_list: List[int] = []
for employee in list_employee:
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db_session).data
if duties := Duties.filter_one(
Duties.id == staff.duties_id, db=db_session
).data:
if duty_found := Duty.filter_by_one(
id=duties.duties_id, db=db_session
).data:
duty_uu_id_list.append(str(duty_found.uu_id))
duty_id_list.append(duty_found.id)
department = Departments.filter_one(
Departments.id == duties.department_id, db=db_session
).data
companies_list.append(
{
"uu_id": str(company.uu_id),
"public_name": company.public_name,
"company_type": company.company_type,
"company_address": company_address,
}
)
person = People.filter_one(People.id == user.person_id, db=db_session).data
model_value = EmployeeTokenObject(
domain=domain,
user_type=UserType.employee.value,
user_uu_id=str(user.uu_id),
credentials=user.credentials(),
user_id=user.id,
person_id=person.id,
person_uu_id=str(person.uu_id),
full_name=person.full_name,
request=dict(request.headers),
companies_uu_id_list=companies_uu_id_list,
companies_id_list=companies_id_list,
duty_uu_id_list=duty_uu_id_list,
duty_id_list=duty_id_list,
timezone=user.local_timezone or "GMT+0",
lang="tr",
).model_dump()
if access_token := cls.set_object_to_redis(user, model_value):
return {
"access_token": access_token,
"user_type": UserType.employee.name,
"selection_list": companies_list,
}
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg="Creating Token failed...",
)
if company := Companies.filter_one(
Companies.id == department.company_id, db=db_session
).data:
companies_uu_id_list.append(str(company.uu_id))
companies_id_list.append(company.id)
company_address = Addresses.filter_by_one(
id=company.official_address_id, db=db_session
).data
companies_list.append(
{
"uu_id": str(company.uu_id),
"public_name": company.public_name,
"company_type": company.company_type,
"company_address": company_address,
}
)
person = People.filter_one(People.id == user.person_id, db=db_session).data
model_value = EmployeeTokenObject(
domain=domain,
user_type=UserType.employee.value,
user_uu_id=str(user.uu_id),
credentials=user.credentials(db_session=db_session),
user_id=user.id,
person_id=person.id,
person_uu_id=str(person.uu_id),
full_name=person.full_name,
request=dict(request.headers),
companies_uu_id_list=companies_uu_id_list,
companies_id_list=companies_id_list,
duty_uu_id_list=duty_uu_id_list,
duty_id_list=duty_id_list,
timezone=user.local_timezone or "GMT+0",
lang="tr",
).model_dump()
if access_token := cls.set_object_to_redis(user, model_value):
return {
"access_token": access_token,
"user_type": UserType.employee.name,
"selection_list": companies_list,
}
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg="Creating Token failed...",
)
@classmethod
def do_occupant_login(
@ -285,14 +286,15 @@ class TokenService:
user: Users,
domain: str,
remember: bool,
db_session
) -> Dict[str, Any]:
"""Set access token to redis and handle user session."""
from ApiLayers.AllConfigs.Token.config import Auth
from ApiLayers.Schemas.identity.identity import UsersTokens, People
user_id, user_dict = user.id, user.get_dict()
cls.remove_token_with_domain(user=user, domain=domain)
# Users.client_arrow = DateTimeLocal(is_client=True, timezone=user.local_timezone)
login_dict, db_session = {}, UsersTokens.new_session()
login_dict = {}
if user.is_occupant: # Handle login based on user type
login_dict = cls.do_occupant_login(
request=request, user=user, domain=domain
@ -309,7 +311,7 @@ class TokenService:
login_dict["refresh_token"] = users_token_created
users_token = UsersTokens.find_or_create(
db=db_session,
user_id=user.id,
user_id=user_id,
token_type="RememberMe",
domain=domain,
)
@ -329,15 +331,15 @@ class TokenService:
login_dict["refresh_token"] = users_token.token
else:
already_refresher = UsersTokens.filter_all(
UsersTokens.user_id == user.id,
UsersTokens.user_id == user_id,
UsersTokens.token_type == "RememberMe",
UsersTokens.domain == domain,
db=db_session,
)
if already_refresher.count:
already_refresher.query.delete(synchronize_session=False)
already_refresher.core_query.delete(synchronize_session=False)
user.save(db=db_session)
return {**login_dict, "user": user.get_dict()}
return {**login_dict, "user": user_dict}
@classmethod
def update_token_at_redis(

View File

@ -1,4 +1,3 @@
import json
from typing import Any, Union, Awaitable
from pydantic import ValidationError

View File

@ -1,4 +1,4 @@
from Services.Redis.Actions.actions import RedisActions
from Services.RedisService.Actions.actions import RedisActions
from ApiLayers.AllConfigs.Redis.configs import RedisValidationKeys

View File

@ -4,7 +4,7 @@ from ApiLayers.AllConfigs.Redis.configs import (
)
from ApiLayers.AllConfigs.main import LanguageConfig
from Events.Engine.set_defaults.category_cluster_models import CategoryClusterController
from Services.Redis.Actions.actions import RedisActions
from Services.RedisService.Actions.actions import RedisActions
class SetDefaultLanguageModelsRedis:

View File

@ -1,5 +1,5 @@
from typing import Optional
from Services.Redis import RedisActions
from Services.RedisService.Actions.actions import RedisActions
from ApiLayers.AllConfigs.Redis.configs import RedisValidationKeysAction

View File

@ -16,7 +16,7 @@ from ApiLayers.ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
from ApiLayers.Schemas import Events, EndpointRestriction
from ApiLayers.AllConfigs.Redis.configs import RedisCategoryKeys
from Services.Redis.Actions.actions import RedisActions
from Services.RedisService.Actions.actions import RedisActions
from .auth_middleware import MiddlewareModule

View File

@ -10,7 +10,7 @@ from sqlalchemy import (
SmallInteger,
)
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
from ApiLayers.LanguageModels.Database.account.account import (
AccountBooksLanguageModel,
AccountCodesLanguageModel,

View File

@ -1,7 +1,7 @@
from sqlalchemy import String, ForeignKey, Index, TIMESTAMP, SmallInteger, Identity
from sqlalchemy.orm import mapped_column, Mapped
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class BuildIbans(CrudCollection):

View File

@ -6,7 +6,7 @@ from sqlalchemy import String
from sqlalchemy.orm import mapped_column, Mapped
from cryptography.fernet import Fernet, MultiFernet
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class CrypterEngine(CrudCollection):

View File

@ -9,7 +9,7 @@ from sqlalchemy import (
Integer,
)
from sqlalchemy.orm import mapped_column, Mapped
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class DecisionBookBudgetBooks(CrudCollection):

View File

@ -16,7 +16,7 @@ from sqlalchemy import (
)
from ApiLayers.ApiLibrary import system_arrow, SelectActionWithEmployee
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
from ApiLayers.ApiValidations.Request import (
InsertBuild,
InsertBuildParts,

View File

@ -25,7 +25,7 @@ from ApiLayers.ApiValidations.Request import (
InsertBuildDecisionBookItemDebits,
InsertBuildDecisionBookProjects,
)
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
from ApiLayers.LanguageModels.Database.building.decision_book import (
BuildDecisionBookLanguageModel,
BuildDecisionBookInvitationsLanguageModel,

View File

@ -23,7 +23,7 @@ from ApiLayers.LanguageModels.Database.company.company import (
CompaniesLanguageModel,
# CompanyDutiesLanguageModel,
)
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class RelationshipDutyCompany(CrudCollection):

View File

@ -1,7 +1,7 @@
from sqlalchemy import String, Integer, ForeignKey, Index, Boolean, Identity
from sqlalchemy.orm import mapped_column, Mapped
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class Departments(CrudCollection):

View File

@ -12,7 +12,7 @@ from ApiLayers.LanguageModels.Database.company.employee import (
EmployeeHistoryLanguageModel,
EmployeesSalariesLanguageModel,
)
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
from ApiLayers.ApiValidations.Request import InsertCompanyEmployees

View File

@ -1,4 +1,4 @@
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
from ApiLayers.LanguageModels.Database.event.event import (
EventsLanguageModel,
ModulesLanguageModel,
@ -250,29 +250,28 @@ class Event2Employee(CrudCollection):
)
@classmethod
def get_event_codes(cls, employee_id: int) -> list:
db = cls.new_session()
def get_event_codes(cls, employee_id: int, db_session) -> list:
employee_events = cls.filter_all(
cls.employee_id == employee_id,
db=db,
db=db_session,
).data
active_event_ids = Service2Events.filter_all_system(
Service2Events.service_id.in_(
[event.event_service_id for event in employee_events]
),
db=db,
db=db_session,
).data
active_events = Events.filter_all(
Events.id.in_([event.event_id for event in active_event_ids]),
db=db,
db=db_session,
).data
if extra_events := Event2EmployeeExtra.filter_all(
Event2EmployeeExtra.employee_id == employee_id,
db=db,
db=db_session,
).data:
events_extra = Events.filter_all(
Events.id.in_([event.event_id for event in extra_events]),
db=db,
db=db_session,
).data
active_events.extend(events_extra)
return [event.function_code for event in active_events]
@ -349,29 +348,28 @@ class Event2Occupant(CrudCollection):
)
@classmethod
def get_event_codes(cls, build_living_space_id) -> list:
db = cls.new_session()
def get_event_codes(cls, build_living_space_id, db_session) -> list:
occupant_events = cls.filter_all(
cls.build_living_space_id == build_living_space_id,
db=db,
db=db_session,
).data
active_event_ids = Service2Events.filter_all_system(
Service2Events.service_id.in_(
[event.event_service_id for event in occupant_events]
),
db=db,
db=db_session,
).data
active_events = Events.filter_all(
Events.id.in_([event.event_id for event in active_event_ids]),
db=db,
db=db_session,
).data
if extra_events := Event2OccupantExtra.filter_all(
Event2OccupantExtra.build_living_space_id == build_living_space_id,
db=db,
db=db_session,
).data:
events_extra = Events.filter_all(
Events.id.in_([event.event_id for event in extra_events]),
db=db,
db=db_session,
).data
active_events.extend(events_extra)
return [event.function_code for event in active_events]

View File

@ -15,7 +15,7 @@ from sqlalchemy import (
)
from sqlalchemy.orm import mapped_column, relationship, Mapped
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
from config import ApiStatic
from ApiLayers.ApiLibrary.date_time_actions.date_functions import system_arrow
@ -144,8 +144,7 @@ class Users(CrudCollection, SelectAction):
return "Occupant" if self.is_occupant else "Employee"
@classmethod
def credentials(cls):
db_session = cls.new_session()
def credentials(cls, db_session):
person_object: People = People.filter_by_one(
db=db_session, system=True, id=cls.person_id
).data

View File

@ -9,7 +9,7 @@ from sqlalchemy.orm import (
Mapped,
mapped_column,
)
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class ApiEnumDropdown(CrudCollection):

View File

@ -4,7 +4,7 @@ from sqlalchemy.orm import mapped_column, Mapped
from ApiLayers.LanguageModels.Database.rules.rules import (
EndpointRestrictionLanguageModel,
)
from Services.PostgresDb import CrudCollection
from Services.PostgresService.controllers.mixin_controllers import CrudCollection
class EndpointRestriction(CrudCollection):

View File

@ -5,6 +5,7 @@ description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"arrow>=1.3.0",
"cryptography>=44.0.2",
"fastapi>=0.115.11",
"prometheus-fastapi-instrumentator>=7.1.0",

View File

@ -41,24 +41,26 @@ def parse_excel_file(excel_frame: DataFrame) -> list[dict]:
iban = str(row[5]).replace(" ", "")
if not str(row[1]) == "nan" and not str(row[2]) == "nan":
if len(str(row[1]).split("/")) > 2:
data_list.append(dict(
iban=str(iban),
bank_date=arrow.get(
datetime.datetime.strptime(str(row[1]), "%d/%m/%Y-%H:%M:%S")
).__str__(),
channel_branch=unidecode(str(row[3])),
currency_value=(
float(str(row[4]).replace(",", "")) if row[4] else 0
),
balance=float(str(row[5]).replace(",", "")) if row[5] else 0,
additional_balance=(
float(str(row[6]).replace(",", "")) if row[6] else 0
),
process_name=str(row[7]),
process_type=unidecode(str(row[8])),
process_comment=unidecode(str(row[9])),
bank_reference_code=str(row[15]),
))
data_list.append(
dict(
iban=str(iban),
bank_date=arrow.get(
datetime.datetime.strptime(str(row[1]), "%d/%m/%Y-%H:%M:%S")
).__str__(),
channel_branch=unidecode(str(row[3])),
currency_value=(
float(str(row[4]).replace(",", "")) if row[4] else 0
),
balance=float(str(row[5]).replace(",", "")) if row[5] else 0,
additional_balance=(
float(str(row[6]).replace(",", "")) if row[6] else 0
),
process_name=str(row[7]),
process_type=unidecode(str(row[8])),
process_comment=unidecode(str(row[9])),
bank_reference_code=str(row[15]),
)
)
return data_list

View File

@ -43,36 +43,39 @@ def set_account_records_to_send_email():
"""
from app import set_account_records_to_send_email
"""
db_session = AccountRecords.new_session()
account_records = AccountRecords.filter_all(db=db_session).core_query
account_records = (
account_records.order_by(
AccountRecords.bank_date.desc(), AccountRecords.bank_reference_code.desc()
)
.limit(3)
.all()
)
with AccountRecords.new_session() as db_session:
account_records = AccountRecords.filter_all(db=db_session).core_query
account_records = (
account_records.order_by(
AccountRecords.bank_date.desc(), AccountRecords.bank_reference_code.desc()
).limit(3).all()
first_record, second_record, balance_error = (
account_records[0],
account_records[1],
False,
)
second_balance = first_record.bank_balance - first_record.currency_value
if second_balance != second_record.bank_balance:
balance_error = True
list_of_rows = list()
for record in account_records:
list_of_rows.append(
[record.bank_date, record.process_comment, f"{record.currency_value:.4f}"]
)
first_record, second_record, balance_error = (
account_records[0], account_records[1], False
)
second_balance = first_record.bank_balance - first_record.currency_value
if second_balance != second_record.bank_balance:
balance_error = True
list_of_rows = list()
for record in account_records:
list_of_rows.append(
[record.bank_date, record.process_comment, f"{record.currency_value:.4f}"]
)
send_to = "karatay@mehmetkaratay.com.tr"
html_template = render_email_template(
headers=["Ulaştığı Tarih", "Banka Transaksiyonu Ek Bilgi", "Aktarım Değeri"],
rows=list_of_rows,
balance_error=balance_error,
bank_balance=account_records[0].bank_balance,
)
send_email_to_given_address(send_to=send_to, html_template=html_template)
send_to = "karatay@mehmetkaratay.com.tr"
html_template = render_email_template(
headers=["Ulaştığı Tarih", "Banka Transaksiyonu Ek Bilgi", "Aktarım Değeri"],
rows=list_of_rows,
balance_error=balance_error,
bank_balance=account_records[0].bank_balance,
)
send_email_to_given_address(send_to=send_to, html_template=html_template)
if __name__ == "__main__":

View File

@ -86,6 +86,7 @@ if __name__ == "__main__":
results = check_any_written_stage_in_mongo_database(mongo_provider=provider)
for result in results:
send_email_and_update_mongo_database(
mongo_provider=provider, email_data=result,
mongo_provider=provider,
email_data=result,
)
time.sleep(60)

View File

@ -1,5 +1,3 @@
def main():
print("Hello from seperatorservice!")

View File

@ -1,9 +1,9 @@
import time
import arrow
from model import BankReceive
from Schemas import AccountRecords, BuildIbans
from Services.MongoService.provider import MongoProvider
from model import BankReceive
from Configs.mongo import MongoConfig
@ -46,7 +46,9 @@ def write_parsed_data_to_account_records(
).data:
print("already @database record", found_record.id)
else:
new_account_record = AccountRecords.find_or_create(db=db_session, **data_dict)
new_account_record = AccountRecords.find_or_create(
db=db_session, **data_dict
)
new_account_record.is_confirmed = True
new_account_record.save(db=db_session)
mongo_provider.update_one(

View File

@ -1,4 +1,6 @@
from typing import Any, Union
import arrow
from typing import Any
from fastapi import Request
from ApiLayers.ApiLibrary.common.line_number import get_line_number_for_error
@ -10,6 +12,7 @@ from ApiLayers.ApiValidations.Response.default_response import (
EndpointNotAcceptableResponse,
EndpointBadRequestResponse,
)
from ApiLayers.AllConfigs.Redis.configs import RedisAuthKeys
from ApiLayers.ErrorHandlers import HTTPExceptionApi
from ApiLayers.Schemas import (
BuildLivingSpace,
@ -27,9 +30,9 @@ from ApiLayers.Schemas import (
Users,
UsersTokens,
)
from Events.base_request_model import TokenDictType, BaseRouteModel
from Services.Redis.Actions.actions import RedisActions
from ApiLayers.AllConfigs.Redis.configs import RedisAuthKeys
from Services.RedisService.Actions.actions import RedisActions
class Handlers:
@ -39,178 +42,180 @@ class Handlers:
def handle_employee_selection(
cls, request: Request, data: Any, token_dict: TokenDictType
):
db = Users.new_session()
if data.company_uu_id not in token_dict.companies_uu_id_list:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Company not found in token",
)
selected_company: Companies = Companies.filter_one(
Companies.uu_id == data.company_uu_id, db=db
).data
if not selected_company:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Company not found in token",
)
# Get duties IDs for the company
duties_ids = [
duty.id
for duty in Duties.filter_all(
Duties.company_id == selected_company.id, db=db
with Users.new_session() as db:
if data.company_uu_id not in token_dict.companies_uu_id_list:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Company not found in token",
)
selected_company: Companies = Companies.filter_one(
Companies.uu_id == data.company_uu_id, db=db
).data
]
if not selected_company:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Company not found in token",
)
# Get staff IDs
staff_ids = [
staff.id
for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids), db=db).data
]
# Get duties IDs for the company
duties_ids = [
duty.id
for duty in Duties.filter_all(
Duties.company_id == selected_company.id, db=db
).data
]
# Get employee
employee: Employees = Employees.filter_one(
Employees.people_id == token_dict.person_id,
Employees.staff_id.in_(staff_ids),
db=db,
).data
# Get staff IDs
staff_ids = [
staff.id
for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids), db=db).data
]
if not employee:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Employee not found in token",
# Get employee
employee: Employees = Employees.filter_one(
Employees.people_id == token_dict.person_id,
Employees.staff_id.in_(staff_ids),
db=db,
).data
if not employee:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Employee not found in token",
)
# Get reachable events
reachable_event_codes = Event2Employee.get_event_codes(
employee_id=employee.id, db_session=db
)
# Get reachable events
reachable_event_codes = Event2Employee.get_event_codes(employee_id=employee.id)
# Get staff and duties
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db).data
duties = Duties.filter_one(Duties.id == staff.duties_id, db=db).data
department = Departments.filter_one(
Departments.id == duties.department_id, db=db
).data
# Get staff and duties
staff = Staff.filter_one(Staff.id == employee.staff_id, db=db).data
duties = Duties.filter_one(Duties.id == staff.duties_id, db=db).data
department = Departments.filter_one(
Departments.id == duties.department_id, db=db
).data
# Get bulk duty
bulk_id = Duty.filter_by_one(system=True, duty_code="BULK", db=db).data
bulk_duty_id = Duties.filter_by_one(
company_id=selected_company.id,
duties_id=bulk_id.id,
db=db,
).data
# Get bulk duty
bulk_id = Duty.filter_by_one(system=True, duty_code="BULK", db=db).data
bulk_duty_id = Duties.filter_by_one(
company_id=selected_company.id,
duties_id=bulk_id.id,
db=db,
).data
# Create company token
company_token = CompanyToken(
company_uu_id=selected_company.uu_id.__str__(),
company_id=selected_company.id,
department_id=department.id,
department_uu_id=department.uu_id.__str__(),
duty_id=duties.id,
duty_uu_id=duties.uu_id.__str__(),
bulk_duties_id=bulk_duty_id.id,
staff_id=staff.id,
staff_uu_id=staff.uu_id.__str__(),
employee_id=employee.id,
employee_uu_id=employee.uu_id.__str__(),
reachable_event_codes=reachable_event_codes,
)
try: # Update Redis
return TokenService.update_token_at_redis(
request=request, add_payload=company_token
)
except Exception as e:
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg=f"{e}",
# Create company token
company_token = CompanyToken(
company_uu_id=selected_company.uu_id.__str__(),
company_id=selected_company.id,
department_id=department.id,
department_uu_id=department.uu_id.__str__(),
duty_id=duties.id,
duty_uu_id=duties.uu_id.__str__(),
bulk_duties_id=bulk_duty_id.id,
staff_id=staff.id,
staff_uu_id=staff.uu_id.__str__(),
employee_id=employee.id,
employee_uu_id=employee.uu_id.__str__(),
reachable_event_codes=reachable_event_codes,
)
try: # Update Redis
return TokenService.update_token_at_redis(
request=request, add_payload=company_token
)
except Exception as e:
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg=f"{e}",
)
@classmethod # Requires no auth context
def handle_occupant_selection(
cls, request: Request, data: Any, token_dict: TokenDictType
):
"""Handle occupant type selection"""
db = BuildLivingSpace.new_session()
# Get selected occupant type
selected_build_living_space: BuildLivingSpace = BuildLivingSpace.filter_one(
BuildLivingSpace.uu_id == data.build_living_space_uu_id,
db=db,
).data
if not selected_build_living_space:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Selected occupant type not found",
with BuildLivingSpace.new_session() as db:
# Get selected occupant type
selected_build_living_space: BuildLivingSpace = BuildLivingSpace.filter_one(
BuildLivingSpace.uu_id == data.build_living_space_uu_id,
db=db,
).data
if not selected_build_living_space:
raise HTTPExceptionApi(
error_code="HTTP_400_BAD_REQUEST",
lang=token_dict.lang,
loc=get_line_number_for_error(),
sys_msg="Selected occupant type not found",
)
# Get reachable events
reachable_event_codes = Event2Occupant.get_event_codes(
build_living_space_id=selected_build_living_space.id, db_session=db
)
occupant_type = OccupantTypes.filter_one_system(
OccupantTypes.id == selected_build_living_space.occupant_type_id,
db=db,
).data
build_part = BuildParts.filter_one(
BuildParts.id == selected_build_living_space.build_parts_id,
db=db,
).data
build = BuildParts.filter_one(
BuildParts.id == build_part.build_id,
db=db,
).data
responsible_employee = Employees.filter_one(
Employees.id == build_part.responsible_employee_id,
db=db,
).data
related_company = RelationshipEmployee2Build.filter_one(
RelationshipEmployee2Build.member_id == build.id,
db=db,
).data
# Get company
company_related = Companies.filter_one(
Companies.id == related_company.company_id,
db=db,
).data
# Create occupant token
occupant_token = OccupantToken(
living_space_id=selected_build_living_space.id,
living_space_uu_id=selected_build_living_space.uu_id.__str__(),
occupant_type_id=occupant_type.id,
occupant_type_uu_id=occupant_type.uu_id.__str__(),
occupant_type=occupant_type.occupant_type,
build_id=build.id,
build_uuid=build.uu_id.__str__(),
build_part_id=build_part.id,
build_part_uuid=build_part.uu_id.__str__(),
responsible_employee_id=responsible_employee.id,
responsible_employee_uuid=responsible_employee.uu_id.__str__(),
responsible_company_id=company_related.id,
responsible_company_uuid=company_related.uu_id.__str__(),
reachable_event_codes=reachable_event_codes,
)
# Get reachable events
reachable_event_codes = Event2Occupant.get_event_codes(
build_living_space_id=selected_build_living_space.id
)
occupant_type = OccupantTypes.filter_one_system(
OccupantTypes.id == selected_build_living_space.occupant_type_id,
db=db,
).data
build_part = BuildParts.filter_one(
BuildParts.id == selected_build_living_space.build_parts_id,
db=db,
).data
build = BuildParts.filter_one(
BuildParts.id == build_part.build_id,
db=db,
).data
responsible_employee = Employees.filter_one(
Employees.id == build_part.responsible_employee_id,
db=db,
).data
related_company = RelationshipEmployee2Build.filter_one(
RelationshipEmployee2Build.member_id == build.id,
db=db,
).data
# Get company
company_related = Companies.filter_one(
Companies.id == related_company.company_id,
db=db,
).data
# Create occupant token
occupant_token = OccupantToken(
living_space_id=selected_build_living_space.id,
living_space_uu_id=selected_build_living_space.uu_id.__str__(),
occupant_type_id=occupant_type.id,
occupant_type_uu_id=occupant_type.uu_id.__str__(),
occupant_type=occupant_type.occupant_type,
build_id=build.id,
build_uuid=build.uu_id.__str__(),
build_part_id=build_part.id,
build_part_uuid=build_part.uu_id.__str__(),
responsible_employee_id=responsible_employee.id,
responsible_employee_uuid=responsible_employee.uu_id.__str__(),
responsible_company_id=company_related.id,
responsible_company_uuid=company_related.uu_id.__str__(),
reachable_event_codes=reachable_event_codes,
)
try: # Update Redis
return TokenService.update_token_at_redis(
request=request, add_payload=occupant_token
)
except Exception as e:
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg=f"{e}",
)
try: # Update Redis
return TokenService.update_token_at_redis(
request=request, add_payload=occupant_token
)
except Exception as e:
raise HTTPExceptionApi(
error_code="",
lang="en",
loc=get_line_number_for_error(),
sys_msg=f"{e}",
)
class AuthenticationFunctions(BaseRouteModel):
@ -298,106 +303,109 @@ class AuthenticationFunctions(BaseRouteModel):
def authentication_access_token_user_info(cls):
"""Refresh user info using access token"""
if cls.context_retriever.token:
db = Users.new_session()
if found_user := Users.filter_one(
Users.id == cls.context_retriever.token.user_id, db=db
).data:
return EndpointSuccessResponse(
code="USER_INFO_REFRESHED", lang=cls.context_retriever.token.lang
).as_dict(
{
"access_token": cls.context_retriever.get_token,
"user": found_user.get_dict(),
}
)
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
with Users.new_session() as db:
if found_user := Users.filter_one(
Users.id == cls.context_retriever.token.user_id, db=db
).data:
return EndpointSuccessResponse(
code="USER_INFO_REFRESHED", lang=cls.context_retriever.token.lang
).as_dict(
{
"access_token": cls.context_retriever.get_token,
"user": found_user.get_dict(),
}
)
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
@classmethod # Requires no auth context
def authentication_change_password(cls, data: Any):
"""Change password with access token"""
if cls.context_retriever.token:
db = Users.new_session()
if found_user := Users.filter_one(
Users.id == cls.context_retriever.token.user_id, db=db
).data:
found_user.set_password(data.new_password)
return EndpointSuccessResponse(
code="PASSWORD_CHANGED", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
with Users.new_session() as db:
if found_user := Users.filter_one(
Users.id == cls.context_retriever.token.user_id, db=db
).data:
found_user.set_password(data.new_password)
return EndpointSuccessResponse(
code="PASSWORD_CHANGED", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
@classmethod # Requires not auth context
def authentication_create_password(cls, data: Any):
"""Create password with password reset token requested via email"""
db = Users.new_session()
if not data.re_password == data.password:
return EndpointNotAcceptableResponse(
code="PASSWORD_NOT_MATCH", lang=cls.context_retriever.token.lang
).as_dict(data={"password": data.password, "re_password": data.re_password})
if found_user := Users.filter_one(
Users.password_token == data.password_token, db=db
).data:
found_user.create_password(found_user=found_user, password=data.password)
found_user.password_token = ""
found_user.save()
return EndpointSuccessResponse(
code="CREATED_PASSWORD", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
with Users.new_session() as db:
if not data.re_password == data.password:
return EndpointNotAcceptableResponse(
code="PASSWORD_NOT_MATCH", lang=cls.context_retriever.token.lang
).as_dict(data={"password": data.password, "re_password": data.re_password})
if found_user := Users.filter_one(
Users.password_token == data.password_token, db=db
).data:
found_user.create_password(found_user=found_user, password=data.password)
found_user.password_token = ""
found_user.save()
return EndpointSuccessResponse(
code="CREATED_PASSWORD", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
@classmethod # Requires auth context
def authentication_disconnect_user(cls):
"""Disconnect all sessions of user in access token"""
db = Users.new_session()
found_user = Users.filter_one_system(
Users.id == cls.context_retriever.token.user_id, db=db
).data
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
registered_tokens = UsersTokens.filter_all(
UsersTokens.user_id == cls.context_retriever.token.user_id, db=db
)
if registered_tokens.count:
registered_tokens.query.delete()
UsersTokens.save(db=db)
with Users.new_session() as db:
found_user = Users.filter_one_system(
Users.id == cls.context_retriever.token.user_id, db=db
).data
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
registered_tokens = UsersTokens.filter_all(
UsersTokens.user_id == cls.context_retriever.token.user_id, db=db
)
if registered_tokens.count:
registered_tokens.query.delete()
UsersTokens.save(db=db)
RedisActions.delete(
list_keys=[f"{RedisAuthKeys.AUTH}:*:{str(found_user.uu_id)}"]
)
return EndpointSuccessResponse(
code="DISCONNECTED_USER", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
RedisActions.delete(
list_keys=[f"{RedisAuthKeys.AUTH}:*:{str(found_user.uu_id)}"]
)
return EndpointSuccessResponse(
code="DISCONNECTED_USER", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
@classmethod # Requires auth context
def authentication_logout_user(cls, data: Any):
"""Logout only single session of user which domain is provided"""
db = Users.new_session()
found_user = Users.filter_one_system(
Users.id == cls.context_retriever.token.user_id, db=db
).data
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
registered_tokens = UsersTokens.filter_all_system(
UsersTokens.user_id == cls.context_retriever.token.user_id,
UsersTokens.domain == cls.context_retriever.token.domain,
db=db,
)
if registered_tokens.count:
registered_tokens.query.delete()
UsersTokens.save(db=db)
TokenService.remove_token_with_domain(user=found_user, domain=data.domain)
return EndpointSuccessResponse(
code="LOGOUT_USER", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
with Users.new_session() as db:
found_user = Users.filter_one_system(
Users.id == cls.context_retriever.token.user_id, db=db
).data
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
registered_tokens = UsersTokens.filter_all(
UsersTokens.user_id == cls.context_retriever.token.user_id,
UsersTokens.domain == cls.context_retriever.token.domain,
db=db,
)
if registered_tokens.count:
registered_tokens.core_query.delete()
UsersTokens.save(db=db)
TokenService.remove_token_with_domain(
user=found_user, domain=data.domain
)
return EndpointSuccessResponse(
code="LOGOUT_USER", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
@classmethod # Requires not auth context
def authentication_refresher_token(cls, request: Request, data: Any):
@ -410,89 +418,80 @@ class AuthenticationFunctions(BaseRouteModel):
}
}
"""
import arrow
from ApiLayers.ApiServices.Token.token_handler import TokenService
db = UsersTokens.new_session()
token_refresher: UsersTokens = UsersTokens.filter_by_one(
token=data.refresh_token,
domain=data.domain,
db=db,
).data
language = request.headers.get("evyos-language", "tr")
if not token_refresher:
return EndpointNotAcceptableResponse(
code="REFRESHER_NOT_FOUND", lang=language
).as_dict(data={"refresh_token": data.refresh_token})
with UsersTokens.new_session() as db:
token_refresher: UsersTokens = UsersTokens.filter_one(
UsersTokens.token==data.refresh_token,
UsersTokens.domain==data.domain,
db=db,
).data
language = request.headers.get("evyos-language", "tr")
if not token_refresher:
return EndpointNotAcceptableResponse(
code="REFRESHER_NOT_FOUND", lang=language
).as_dict(data={"refresh_token": data.refresh_token})
if found_user := Users.filter_one(
Users.id == token_refresher.user_id, db=db
).data:
token_created = TokenService.set_access_token_to_redis(
request=request,
user=found_user,
domain=data.domain,
remember=True,
)
found_user.last_agent = request.headers.get("User-Agent", None)
found_user.last_platform = request.headers.get("Origin", None)
found_user.last_remote_addr = getattr(
request, "remote_addr", None
) or request.headers.get("X-Forwarded-For", None)
found_user.last_seen = str(arrow.now())
response_data = {
"access_token": token_created.get("access_token"),
"refresh_token": data.refresh_token,
}
return EndpointSuccessResponse(code="TOKEN_REFRESH", lang=language).as_dict(
data=response_data
)
raise EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=language
).as_dict(data={})
if found_user := Users.filter_one(
Users.id == token_refresher.user_id, db=db
).data:
token_created = TokenService.set_access_token_to_redis(
request=request, user=found_user, domain=data.domain, remember=True, db_session=db
)
found_user.last_agent = request.headers.get("User-Agent", None)
found_user.last_platform = request.headers.get("Origin", None)
found_user.last_remote_addr = getattr(request, "remote_addr", None
) or request.headers.get("X-Forwarded-For", None)
found_user.last_seen = str(arrow.now())
response_data = {
"access_token": token_created.get("access_token"),
"refresh_token": data.refresh_token,
}
return EndpointSuccessResponse(code="TOKEN_REFRESH", lang=language).as_dict(
data=response_data
)
raise EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=language
).as_dict(data={})
@classmethod # Requires not auth context
def authentication_forgot_password(cls, data: Any):
"""Send an email to user for a valid password reset token"""
import arrow
from ApiLayers.ApiServices.Token.token_handler import TokenService
from ApiLayers.AllConfigs.Templates.password_templates import (
change_your_password_template,
)
from Services.Email.send_email import email_sender
from ApiLayers.AllConfigs.Templates.password_templates import change_your_password_template
from Services.EmailService.provider import email_sender
from ApiLayers.AllConfigs.Api.config import ApiStatic
from config import ApiStatic
db = Users.new_session()
request = cls.context_retriever.request
found_user: Users = Users.check_user_exits(
access_key=data.access_key, domain=data.domain
)
forgot_key = TokenService._create_access_token(access=False)
forgot_link = ApiStatic.forgot_link(forgot_key=forgot_key)
send_email_completed = email_sender.send_email(
subject=f"Dear {found_user.user_tag}, your forgot password link has been sent.",
receivers=[str(found_user.email)],
html=change_your_password_template(
user_name=found_user.user_tag, forgot_link=forgot_link
),
)
if not send_email_completed:
return EndpointBadRequestResponse(
code="EMAIL_NOT_SENT", lang=cls.context_retriever.token.lang
).as_dict(data={"email": found_user.email})
found_user.password_token = forgot_key
found_user.password_token_is_valid = str(arrow.now().shift(days=1))
found_user.save(db=db)
return EndpointSuccessResponse(
code="FORGOT_PASSWORD", lang=cls.context_retriever.token.lang
).as_dict(
data={
"user": found_user.get_dict(),
"forgot_link": forgot_link,
"token": forgot_key,
}
)
with Users.new_session() as db:
# request = cls.context_retriever.request
found_user: Users = Users.check_user_exits(
access_key=data.access_key, domain=data.domain
)
forgot_key = TokenService._create_access_token(access=False)
forgot_link = ApiStatic.forgot_link(forgot_key=forgot_key)
send_email_completed = email_sender.send_email(
subject=f"Dear {found_user.user_tag}, your forgot password link has been sent.",
receivers=[str(found_user.email)],
html=change_your_password_template(
user_name=found_user.user_tag, forgot_link=forgot_link
),
)
if not send_email_completed:
return EndpointBadRequestResponse(
code="EMAIL_NOT_SENT", lang=cls.context_retriever.token.lang
).as_dict(data={"email": found_user.email})
found_user.password_token = forgot_key
found_user.password_token_is_valid = str(arrow.now().shift(days=1))
found_user.save(db=db)
return EndpointSuccessResponse(
code="FORGOT_PASSWORD", lang=cls.context_retriever.token.lang
).as_dict(
data={
"user": found_user.get_dict(),
"forgot_link": forgot_link,
"token": forgot_key,
}
)
@classmethod # Requires not auth context
def authentication_reset_password(cls, data: Any):
@ -502,28 +501,26 @@ class AuthenticationFunctions(BaseRouteModel):
@classmethod # Requires not auth context
def authentication_download_avatar(cls):
"""Download avatar icon and profile info of user"""
import arrow
db = Users.new_session()
if found_user := Users.filter_one(
Users.id == cls.context_retriever.token.user_id, db=db
).data:
expired_starts = str(arrow.now() - arrow.get(str(found_user.expiry_ends)))
expired_int = (
arrow.now().datetime - arrow.get(str(found_user.expiry_ends)).datetime
)
user_info = {
"lang": cls.context_retriever.token.lang,
"full_name": found_user.person.full_name,
"avatar": found_user.avatar,
"remember_me": found_user.remember_me,
"expiry_ends": str(found_user.expiry_ends),
"expired_humanized": expired_starts,
"expired_day": int(expired_int.days) * -1,
}
return EndpointSuccessResponse(
code="USER_AVATAR", lang=cls.context_retriever.token.lang
).as_dict(data=user_info)
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})
with Users.new_session() as db:
if found_user := Users.filter_one(
Users.id == cls.context_retriever.token.user_id, db=db
).data:
expired_starts = str(arrow.now() - arrow.get(str(found_user.expiry_ends)))
expired_int = (
arrow.now().datetime - arrow.get(str(found_user.expiry_ends)).datetime
)
user_info = {
"lang": cls.context_retriever.token.lang,
"full_name": found_user.person.full_name,
"avatar": found_user.avatar,
"remember_me": found_user.remember_me,
"expiry_ends": str(found_user.expiry_ends),
"expired_humanized": expired_starts,
"expired_day": int(expired_int.days) * -1,
}
return EndpointSuccessResponse(
code="USER_AVATAR", lang=cls.context_retriever.token.lang
).as_dict(data=user_info)
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(data={})

View File

@ -24,7 +24,7 @@ from ApiLayers.Schemas import (
from ApiLayers.ApiValidations.Response import AccountRecordResponse
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class AccountListEventMethods(BaseRouteModel):

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
from ApiLayers.Schemas import AddressNeighborhood

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -2,7 +2,7 @@ from typing import Union, Optional
from ApiLayers.ApiValidations.Request import ListOptions
from Events.base_request_model import BaseRouteModel, ListOptionsBase
from Services.PostgresDb.Models.pagination import PaginationResult
from Services.PostgresService.controllers.pagination_controllers import PaginationResult
class Handlers:

View File

@ -3,19 +3,17 @@ Validation function handlers
"""
from typing import Dict, Any, Optional
from fastapi import Request
from ApiLayers.AllConfigs.Redis.configs import (
RedisCategoryKeys,
RedisValidationKeysAction,
RedisCategoryPageInfoKeysAction,
)
from ApiLayers.ApiLibrary.common.line_number import get_line_number_for_error
from ApiLayers.ErrorHandlers.Exceptions.api_exc import HTTPExceptionApi
from Services.Redis.Actions.actions import RedisActions
from Services.RedisService.Actions.actions import RedisActions
from Events.base_request_model import BaseRouteModel
from config import ValidationsConfig
from ApiLayers.AllConfigs.Api.config import ValidationsConfig
class ValidateBase:

View File

@ -10,7 +10,10 @@ from Events.abstract_class import (
if TYPE_CHECKING:
from fastapi import Request, HTTPException, status, Body
from ApiLayers.ApiValidations.Custom.token_objects import EmployeeTokenObject, OccupantTokenObject
from ApiLayers.ApiValidations.Custom.token_objects import (
EmployeeTokenObject,
OccupantTokenObject,
)
# Type aliases for common types

View File

@ -5,7 +5,7 @@ from pydantic import BaseModel
# from application.validations.request.list_options.list_options import ListOptions
from Services.PostgresService.controllers.response_controllers import PostgresResponse
from Configs.api import ApiConfigs
from ApiLayers.AllConfigs.Api.config import ApiConfigs
class ListOptions: ...

View File

@ -2,7 +2,7 @@ from contextlib import contextmanager
from functools import lru_cache
from typing import Generator
from Configs.postgres import Database
from ApiLayers.AllConfigs.SqlDatabase.configs import WagDatabase as Database
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker, scoped_session, Session

View File

@ -1,24 +1,24 @@
services:
mongo_service:
container_name: mongo_service
# image: "bitnami/mongodb:latest"
image: "bitnami/mongodb:4.4.1-debian-10-r3"
networks:
- network_store_services
environment:
- MONGODB_DISABLE_ENFORCE_AUTH=true
- MONGODB_ROOT_PASSWORD=root
- MONGODB_DATABASE=mongo_database
- MONGODB_USERNAME=mongo_user
- MONGODB_PASSWORD=mongo_password
- MONGO_INITDB_ROOT_USERNAME=mongo_user
- MONGO_INITDB_ROOT_PASSWORD=mongo_password
- MONGO_INITDB_DATABASE=mongo_database
volumes:
- wag_commercial_mongodb_data:/bitnami/mongodb
ports:
- "11117:27017"
# mongo_service:
# container_name: mongo_service
## image: "bitnami/mongodb:latest"
# image: "bitnami/mongodb:4.4.1-debian-10-r3"
# networks:
# - network_store_services
# environment:
# - MONGODB_DISABLE_ENFORCE_AUTH=true
# - MONGODB_ROOT_PASSWORD=root
# - MONGODB_DATABASE=mongo_database
# - MONGODB_USERNAME=mongo_user
# - MONGODB_PASSWORD=mongo_password
# - MONGO_INITDB_ROOT_USERNAME=mongo_user
# - MONGO_INITDB_ROOT_PASSWORD=mongo_password
# - MONGO_INITDB_DATABASE=mongo_database
# volumes:
# - wag_commercial_mongodb_data:/bitnami/mongodb
# ports:
# - "11117:27017"
#
# commercial_memory_service:
@ -52,70 +52,70 @@ services:
# volumes:
# - wag_postgres_commercial_data:/bitnami/postgresql
email_service:
container_name: email_service
restart: on-failure
build:
context: .
dockerfile: BankServices/EmailService/Dockerfile
networks:
- network_store_services
depends_on:
- mongo_service
env_file:
- email.env
parser_service:
container_name: parser_service
restart: on-failure
build:
context: .
dockerfile: BankServices/ParserService/Dockerfile
networks:
- network_store_services
depends_on:
- mongo_service
env_file:
- email.env
writer_service:
container_name: writer_service
restart: on-failure
build:
context: .
dockerfile: BankServices/WriterService/Dockerfile
networks:
- network_store_services
depends_on:
- mongo_service
env_file:
- email.env
routine_email_service:
container_name: routine_email_service
restart: on-failure
build:
context: .
dockerfile: BankServices/RoutineEmailService/Dockerfile
networks:
- network_store_services
depends_on:
- mongo_service
env_file:
- email.env
sender_service:
container_name: sender_service
restart: on-failure
build:
context: .
dockerfile: BankServices/SenderService/Dockerfile
networks:
- network_store_services
depends_on:
- mongo_service
env_file:
- email.env
# email_service:
# container_name: email_service
# restart: on-failure
# build:
# context: .
# dockerfile: BankServices/EmailService/Dockerfile
# networks:
# - network_store_services
# depends_on:
# - mongo_service
# env_file:
# - email.env
#
# parser_service:
# container_name: parser_service
# restart: on-failure
# build:
# context: .
# dockerfile: BankServices/ParserService/Dockerfile
# networks:
# - network_store_services
# depends_on:
# - mongo_service
# env_file:
# - email.env
#
# writer_service:
# container_name: writer_service
# restart: on-failure
# build:
# context: .
# dockerfile: BankServices/WriterService/Dockerfile
# networks:
# - network_store_services
# depends_on:
# - mongo_service
# env_file:
# - email.env
#
# routine_email_service:
# container_name: routine_email_service
# restart: on-failure
# build:
# context: .
# dockerfile: BankServices/RoutineEmailService/Dockerfile
# networks:
# - network_store_services
# depends_on:
# - mongo_service
# env_file:
# - email.env
#
# sender_service:
# container_name: sender_service
# restart: on-failure
# build:
# context: .
# dockerfile: BankServices/SenderService/Dockerfile
# networks:
# - network_store_services
# depends_on:
# - mongo_service
# env_file:
# - email.env
# initservice:
# container_name: initservice
@ -123,17 +123,17 @@ services:
# context: .
# dockerfile: ApiServices/InitServiceApi/Dockerfile
# authservice:
# container_name: authservice
# build:
# context: .
# dockerfile: ApiServices/AuthApiService/Dockerfile
# ports:
# - "8081:8888"
authservice:
container_name: authservice
build:
context: .
dockerfile: ApiServices/AuthApiService/Dockerfile
ports:
- "8081:8888"
# depends_on:
# - initservice
# networks:
# - wag-network
networks:
- wag-network
# validationservice:
# container_name: validationservice

31
uv.lock
View File

@ -67,38 +67,32 @@ name = "authapiservice"
version = "0.1.0"
source = { virtual = "ApiServices/AuthApiService" }
dependencies = [
{ name = "arrow" },
{ name = "cryptography" },
{ name = "faker" },
{ name = "fastapi" },
{ name = "pandas" },
{ name = "prometheus-fastapi-instrumentator" },
{ name = "psycopg2-binary" },
{ name = "pymongo" },
{ name = "redis" },
{ name = "redmail" },
{ name = "requests" },
{ name = "rsa" },
{ name = "sqlalchemy-mixins" },
{ name = "textdistance" },
{ name = "unidecode" },
{ name = "uvicorn" },
]
[package.metadata]
requires-dist = [
{ name = "arrow", specifier = ">=1.3.0" },
{ name = "cryptography", specifier = ">=44.0.2" },
{ name = "faker", specifier = ">=37.0.2" },
{ name = "fastapi", specifier = ">=0.115.11" },
{ name = "pandas", specifier = ">=2.2.3" },
{ name = "prometheus-fastapi-instrumentator", specifier = ">=7.1.0" },
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
{ name = "pymongo", specifier = ">=4.11.3" },
{ name = "redis", specifier = ">=5.2.1" },
{ name = "redmail", specifier = ">=0.6.0" },
{ name = "requests", specifier = ">=2.32.3" },
{ name = "rsa", specifier = ">=4.9" },
{ name = "sqlalchemy-mixins", specifier = ">=2.0.5" },
{ name = "textdistance", specifier = ">=4.6.3" },
{ name = "unidecode", specifier = ">=1.3.8" },
{ name = "uvicorn", specifier = ">=0.34.0" },
]
@ -547,15 +541,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
]
[[package]]
name = "pyasn1"
version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 },
]
[[package]]
name = "pycparser"
version = "2.22"
@ -750,18 +735,6 @@ requires-dist = [
{ name = "sqlalchemy-mixins", specifier = ">=2.0.5" },
]
[[package]]
name = "rsa"
version = "4.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyasn1" },
]
sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 },
]
[[package]]
name = "senderservice"
version = "0.1.0"