first commit
This commit is contained in:
25
api_services/__init__.py
Normal file
25
api_services/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from .redis.conn import redis_cli
|
||||
from .redis.functions import (
|
||||
get_object_via_access_key,
|
||||
get_object_via_user_uu_id,
|
||||
)
|
||||
from .redis.auth_actions.auth import (
|
||||
save_access_token_to_redis,
|
||||
update_selected_to_redis,
|
||||
)
|
||||
from .email.service import send_email
|
||||
from .templates.password_templates import (
|
||||
password_is_changed_template,
|
||||
change_your_password_template,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"redis_cli",
|
||||
"send_email",
|
||||
"get_object_via_access_key",
|
||||
"get_object_via_user_uu_id",
|
||||
"save_access_token_to_redis",
|
||||
"update_selected_to_redis",
|
||||
"password_is_changed_template",
|
||||
"change_your_password_template",
|
||||
]
|
||||
202
api_services/bank_actions/wag_account_record_parser.py
Normal file
202
api_services/bank_actions/wag_account_record_parser.py
Normal file
@@ -0,0 +1,202 @@
|
||||
import textdistance
|
||||
from unidecode import unidecode
|
||||
from datetime import datetime
|
||||
from databases import (
|
||||
BuildIbanDescription,
|
||||
BuildIbans,
|
||||
BuildDecisionBook,
|
||||
BuildLivingSpace,
|
||||
AccountRecords,
|
||||
)
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class InsertBudgetRecord(BaseModel):
|
||||
iban: str
|
||||
bank_date: str = datetime.now().__str__()
|
||||
receive_debit: str = "debit"
|
||||
budget_type: str = "B"
|
||||
currency_value: float = 0
|
||||
balance: float = 0
|
||||
bank_reference_code: str = ""
|
||||
currency: str = "TL"
|
||||
channel_branch: str = ""
|
||||
process_name: str = ""
|
||||
process_type: str = ""
|
||||
process_comment: str = ""
|
||||
|
||||
add_xcomment: Optional[str] = None
|
||||
project_no: Optional[str] = None
|
||||
company_id: Optional[str] = None
|
||||
customer_id: Optional[str] = None
|
||||
|
||||
send_person_id: Optional[int] = None
|
||||
send_company_id: Optional[int] = None
|
||||
build_id: Optional[int] = None
|
||||
build_decision_book_id: Optional[int] = None
|
||||
build_parts_id: Optional[int] = None
|
||||
build_db_item_id: Optional[int] = None
|
||||
dues_type: Optional[str] = "D"
|
||||
period_time: Optional[str] = ""
|
||||
approving_accounting_record: Optional[bool] = False
|
||||
approving_accounting_person: Optional[str] = None
|
||||
accounting_receipt_date: Optional[str] = "1900-01-01 00:00:00"
|
||||
accounting_receipt_number: Optional[int] = 0
|
||||
|
||||
|
||||
def strip_time_date(date_str):
|
||||
return datetime.strptime(date_str, "%Y-%m-%d")
|
||||
|
||||
|
||||
def strip_date_to_valid(date_str):
|
||||
return datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def find_iban_in_comment(iban: str, comment: str):
|
||||
iban_results, iban_count = BuildIbanDescription.filter_by(iban=iban)
|
||||
sm_dict_extended, sm_dict_digit = {}, {}
|
||||
# is_reference_build = any(
|
||||
# letter in comment.lower() for letter in ["no", "daire", "nolu"]
|
||||
# )
|
||||
if iban_count:
|
||||
for iban_result in iban_results:
|
||||
candidate_parts = comment.split(" ")
|
||||
extended_candidate_parts, digit_part = [], []
|
||||
for part in candidate_parts:
|
||||
if part.lower() not in ["no", "daire", "nolu"]:
|
||||
extended_candidate_parts.append(part)
|
||||
# if part.isdigit():
|
||||
# digit_part.append(part)
|
||||
if extended_candidate_parts:
|
||||
if all(
|
||||
candidate_part.lower() in comment.lower()
|
||||
for candidate_part in extended_candidate_parts
|
||||
):
|
||||
similarity_ratio = textdistance.jaro_winkler(
|
||||
unidecode(str(iban_result.search_word)), comment
|
||||
)
|
||||
found = False
|
||||
name_list = (
|
||||
unidecode(str(iban_result.search_word))
|
||||
.replace(".", " ")
|
||||
.split(" ")
|
||||
)
|
||||
for name in name_list:
|
||||
if len(name) > 3 and name.lower() in comment.lower():
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
similarity_ratio = 0.1
|
||||
sm_dict_extended[f"{iban_result.id}"] = similarity_ratio
|
||||
|
||||
if sm_dict_extended:
|
||||
result = sorted(
|
||||
sm_dict_extended.items(), key=lambda item: item[1], reverse=True
|
||||
)[0]
|
||||
if float(result[1]) >= 0.5:
|
||||
iban_result = BuildIbanDescription.find_one(id=int(result[0]))
|
||||
return {
|
||||
"decision_book_project_id": iban_result.decision_book_project_id,
|
||||
"company_id": iban_result.company_id,
|
||||
"customer_id": iban_result.customer_id,
|
||||
"build_parts_id": iban_result.build_parts_id,
|
||||
"found_from": "Name",
|
||||
"similarity": result[1],
|
||||
}
|
||||
return {
|
||||
"decision_book_project_id": None,
|
||||
"company_id": None,
|
||||
"customer_id": None,
|
||||
"build_parts_id": None,
|
||||
"found_from": None,
|
||||
"similarity": 0.0,
|
||||
}
|
||||
|
||||
|
||||
def parse_comment_with_name(iban: str, comment: str):
|
||||
if "*" in comment:
|
||||
b_comment, a_comment = (
|
||||
unidecode(str(comment)).split("*")[0],
|
||||
unidecode(str(comment)).split("*")[1],
|
||||
)
|
||||
a_result = find_iban_in_comment(iban, a_comment)
|
||||
b_result = find_iban_in_comment(iban, b_comment)
|
||||
if a_result["similarity"] > b_result["similarity"]:
|
||||
a_result["send_person_id"] = a_result["customer_id"]
|
||||
return a_result
|
||||
else:
|
||||
b_result["send_person_id"] = None
|
||||
return b_result
|
||||
else:
|
||||
result = find_iban_in_comment(iban, comment)
|
||||
result["send_person_id"] = result.get("customer_id", None)
|
||||
return result
|
||||
|
||||
|
||||
def wag_insert_budget_record(data):
|
||||
similarity_result = parse_comment_with_name(data["iban"], data["process_comment"])
|
||||
build_iban = BuildIbans.find_one(iban=data["iban"])
|
||||
if payload := InsertBudgetRecord(**data):
|
||||
payload_dict = payload.model_dump(exclude_unset=True, exclude_none=True)
|
||||
decision_book, count = BuildDecisionBook.filter(
|
||||
BuildDecisionBook.period_start_date
|
||||
< strip_date_to_valid(payload_dict["bank_date"]),
|
||||
BuildDecisionBook.period_stop_date
|
||||
> strip_date_to_valid(payload_dict["bank_date"]),
|
||||
)
|
||||
payload_dict["build_id"] = getattr(
|
||||
BuildIbans.find_one(iban=data["iban"]), "build_id", None
|
||||
)
|
||||
living_space, count = BuildLivingSpace.find_living_from_customer_id(
|
||||
similarity_result.get("customer_id", None),
|
||||
strip_date_to_valid(payload_dict["bank_date"]),
|
||||
)
|
||||
# living_space, count = BuildLivingSpace.filter(
|
||||
# or_(
|
||||
# BuildLivingSpace.owner_person_id
|
||||
# == similarity_result.get("customer_id", None),
|
||||
# BuildLivingSpace.life_person_id
|
||||
# == similarity_result.get("customer_id", None),
|
||||
# ),
|
||||
# BuildLivingSpace.start_date
|
||||
# < strip_date_to_valid(payload_dict["bank_date"]) - timedelta(days=30),
|
||||
# BuildLivingSpace.stop_date
|
||||
# > strip_date_to_valid(payload_dict["bank_date"]) + timedelta(days=30),
|
||||
# BuildLivingSpace.active == True,
|
||||
# BuildLivingSpace.deleted == False,
|
||||
# )
|
||||
payload_dict["build_decision_book_id"] = (
|
||||
decision_book[0].id if decision_book else None
|
||||
)
|
||||
payload_dict["company_id"] = similarity_result.get("company_id", None)
|
||||
payload_dict["customer_id"] = similarity_result.get("customer_id", None)
|
||||
payload_dict["send_person_id"] = similarity_result.get("send_person_id", None)
|
||||
|
||||
payload_dict["build_parts_id"] = (
|
||||
living_space[0].build_parts_id if living_space else None
|
||||
)
|
||||
|
||||
payload_dict["bank_date_y"] = strip_date_to_valid(
|
||||
payload_dict["bank_date"]
|
||||
).year
|
||||
payload_dict["bank_date_m"] = strip_date_to_valid(
|
||||
payload_dict["bank_date"]
|
||||
).month
|
||||
payload_dict["bank_date_d"] = strip_date_to_valid(payload_dict["bank_date"]).day
|
||||
payload_dict["bank_date_w"] = strip_date_to_valid(
|
||||
payload_dict["bank_date"]
|
||||
).isocalendar()[2]
|
||||
payload_dict["build_id"] = build_iban.build_id if build_iban else None
|
||||
payload_dict["replication_id"] = 55
|
||||
payload_dict["receive_debit"] = (
|
||||
"R" if payload_dict["currency_value"] < 0 else "D"
|
||||
)
|
||||
data, found = AccountRecords.find_or_create(
|
||||
**payload_dict,
|
||||
found_from=similarity_result.get("found_from", None),
|
||||
similarity=similarity_result.get("similarity", 0.0),
|
||||
)
|
||||
data.payment_budget_record_close()
|
||||
return data, found
|
||||
9
api_services/email/config.py
Normal file
9
api_services/email/config.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from redmail import EmailSender
|
||||
from api_configs import EmailConfig
|
||||
|
||||
email_sender = EmailSender(
|
||||
host=EmailConfig.EMAIL_HOST,
|
||||
port=587,
|
||||
username=EmailConfig.EMAIL_USERNAME,
|
||||
password=EmailConfig.EMAIL_PASSWORD,
|
||||
)
|
||||
31
api_services/email/service.py
Normal file
31
api_services/email/service.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from .config import email_sender
|
||||
|
||||
|
||||
def send_email(
|
||||
subject: str,
|
||||
receivers: list,
|
||||
text: str = "",
|
||||
html: str = "",
|
||||
cc: list = None,
|
||||
bcc: list = None,
|
||||
headers: dict = None,
|
||||
attachments: dict = None,
|
||||
) -> bool:
|
||||
try:
|
||||
email_sender.connect()
|
||||
email_sender.send(
|
||||
subject=subject,
|
||||
receivers=receivers,
|
||||
text=text,
|
||||
html=html,
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
headers=headers or {},
|
||||
attachments=attachments or {},
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error raised at email send :{e}")
|
||||
finally:
|
||||
email_sender.close()
|
||||
return False
|
||||
219
api_services/redis/auth_actions/auth.py
Normal file
219
api_services/redis/auth_actions/auth.py
Normal file
@@ -0,0 +1,219 @@
|
||||
import json
|
||||
import typing
|
||||
|
||||
from fastapi import status
|
||||
from fastapi.exceptions import HTTPException
|
||||
|
||||
from api_configs import Auth
|
||||
from databases import (
|
||||
BuildLivingSpace,
|
||||
BuildParts,
|
||||
Companies,
|
||||
Duties,
|
||||
Departments,
|
||||
Duty,
|
||||
Employees,
|
||||
Staff,
|
||||
)
|
||||
from api_objects import (
|
||||
OccupantTokenObject,
|
||||
EmployeeTokenObject,
|
||||
UserType,
|
||||
)
|
||||
|
||||
from api_services.redis.conn import redis_cli
|
||||
from api_services.redis.functions import get_object_via_user_uu_id, get_object_via_access_key
|
||||
|
||||
|
||||
def save_object_to_redis(
|
||||
access_token, model_object: typing.Union[OccupantTokenObject, EmployeeTokenObject]
|
||||
) -> bool:
|
||||
try:
|
||||
if redis_cli.set(
|
||||
name=str(access_token) + ":" + str(model_object.user_uu_id),
|
||||
value=model_object.model_dump_json(),
|
||||
):
|
||||
return access_token
|
||||
except Exception as e:
|
||||
print("Save Object to Redis Error: ", e)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail=dict(
|
||||
message="Headers are not found in request. Invalid request object. Redis Error: Token is not saved."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def save_access_token_to_redis(
|
||||
request, found_user, domain: str, access_token: str = None
|
||||
):
|
||||
if not found_user:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=dict(message="User is not found."),
|
||||
headers=request.headers,
|
||||
)
|
||||
|
||||
# Check user is already logged in or has a previous session
|
||||
already_tokens = get_object_via_user_uu_id(user_id=found_user.uu_id)
|
||||
for key in already_tokens or []:
|
||||
token_user = json.loads(redis_cli.get(key).decode() or {})
|
||||
if token_user.get("domain", "") == domain:
|
||||
redis_cli.delete(key)
|
||||
|
||||
access_token = (
|
||||
found_user.generate_access_token() if not access_token else access_token
|
||||
)
|
||||
|
||||
# Prepare the user's details to save in Redis Session
|
||||
if found_user.is_occupant: # Check if user is NOT an occupant
|
||||
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.filter_active(
|
||||
BuildLivingSpace.person_id == found_user.person_id, filter_records=False
|
||||
).data
|
||||
if not living_spaces:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=dict(
|
||||
message="NO Living Space is found. This user has no proper account set please contact the admin."
|
||||
),
|
||||
headers=request.headers,
|
||||
)
|
||||
|
||||
occupants_selection_dict = {}
|
||||
for living_space in living_spaces:
|
||||
build_parts_selection = BuildParts.filter_active(
|
||||
BuildParts.id == living_space.build_parts_id
|
||||
)
|
||||
if not build_parts_selection.data:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=dict(
|
||||
message="No build Part is found for the living space. Please contact the admin."
|
||||
),
|
||||
headers=request.headers,
|
||||
)
|
||||
build_part = build_parts_selection.get(1)
|
||||
|
||||
occupant_dict = {
|
||||
"uu_id": str(living_space.occupant_type_uu_id),
|
||||
"id": living_space.occupant_type,
|
||||
}
|
||||
if not str(build_part.uu_id) in occupants_selection_dict:
|
||||
occupants_selection_dict[str(build_part.uu_id)] = [occupant_dict]
|
||||
elif str(build_part.uu_id) in occupants_selection_dict:
|
||||
occupants_selection_dict[str(build_part.uu_id)].append(occupant_dict)
|
||||
|
||||
save_object_to_redis(
|
||||
access_token=access_token,
|
||||
model_object=OccupantTokenObject(
|
||||
domain=domain,
|
||||
user_type=UserType.occupant.value,
|
||||
user_uu_id=str(found_user.uu_id),
|
||||
credentials=found_user.credentials(),
|
||||
user_id=found_user.id,
|
||||
person_id=found_user.person_id,
|
||||
person_uu_id=str(found_user.person.uu_id),
|
||||
request=dict(request.headers),
|
||||
available_occupants=occupants_selection_dict,
|
||||
),
|
||||
)
|
||||
new_occupants_selection_dict = {}
|
||||
for key, value in occupants_selection_dict.items():
|
||||
new_occupants_selection_dict[key] = [
|
||||
occupant.get("uu_id") for occupant in value
|
||||
]
|
||||
|
||||
return dict(
|
||||
user_type=UserType.occupant.name,
|
||||
available_occupants=new_occupants_selection_dict,
|
||||
)
|
||||
|
||||
list_employee = Employees.filter_active(Employees.people_id == found_user.person_id)
|
||||
companies_uu_id_list, companies_id_list = [], []
|
||||
duty_uu_id_list, duty_id_list = [], []
|
||||
for employee in list_employee.data:
|
||||
staff = Staff.find_one(id=employee.staff_id)
|
||||
if duties := Duties.find_one(id=staff.duties_id):
|
||||
if duty_found := Duty.find_one(id=duties.duties_id):
|
||||
duty_uu_id_list.append(str(duty_found.uu_id))
|
||||
duty_id_list.append(duty_found.id)
|
||||
|
||||
department = Departments.find_one(id=duties.department_id)
|
||||
if company := Companies.find_one(id=department.company_id):
|
||||
companies_uu_id_list.append(str(company.uu_id))
|
||||
companies_id_list.append(company.id)
|
||||
|
||||
save_object_to_redis(
|
||||
access_token=access_token,
|
||||
model_object=EmployeeTokenObject(
|
||||
domain=domain,
|
||||
user_type=UserType.employee.value,
|
||||
user_uu_id=str(found_user.uu_id),
|
||||
credentials=found_user.credentials(),
|
||||
user_id=found_user.id,
|
||||
person_id=found_user.person_id,
|
||||
person_uu_id=str(found_user.person.uu_id),
|
||||
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,
|
||||
),
|
||||
)
|
||||
return dict(
|
||||
user_type=UserType.employee.name,
|
||||
companies_uu_id_list=companies_uu_id_list,
|
||||
)
|
||||
|
||||
|
||||
def update_selected_to_redis(request, add_payload):
|
||||
already_tokens = get_object_via_access_key(request=request)
|
||||
if not hasattr(request, "headers"):
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=dict(
|
||||
message="Headers are not found in request. Invalid request object."
|
||||
),
|
||||
)
|
||||
access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG)
|
||||
if already_tokens.user_type == UserType.occupant.value:
|
||||
already_tokens.selected_occupant = add_payload.model_dump()
|
||||
return save_object_to_redis(
|
||||
access_token=access_token,
|
||||
model_object=OccupantTokenObject(**already_tokens.model_dump()),
|
||||
)
|
||||
elif already_tokens.user_type == UserType.employee.value:
|
||||
already_tokens.selected_company = add_payload.model_dump()
|
||||
return save_object_to_redis(
|
||||
access_token=access_token,
|
||||
model_object=EmployeeTokenObject(**already_tokens.model_dump()),
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=dict(
|
||||
message="User type is not found in the token object. Please reach to your administrator."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def update_access_token_to_redis(request, add_payload):
|
||||
already_tokens = get_object_via_access_key(request=request)
|
||||
if not hasattr(request, "headers"):
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=dict(
|
||||
message="Headers are not found in request. Invalid request object."
|
||||
),
|
||||
)
|
||||
payload = {**add_payload, **already_tokens}
|
||||
access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG)
|
||||
if payload.get("user_type") == str(UserType.occupant.value):
|
||||
|
||||
return save_object_to_redis(
|
||||
access_token=access_token,
|
||||
model_object=OccupantTokenObject(**payload),
|
||||
)
|
||||
return save_object_to_redis(
|
||||
access_token=access_token,
|
||||
model_object=EmployeeTokenObject(**payload),
|
||||
)
|
||||
1
api_services/redis/auth_actions/login.py
Normal file
1
api_services/redis/auth_actions/login.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
105
api_services/redis/auth_actions/token.py
Normal file
105
api_services/redis/auth_actions/token.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from fastapi import HTTPException, status
|
||||
from fastapi.requests import Request
|
||||
|
||||
from database_sql_models import Events
|
||||
|
||||
# url_that_not_requires_event_validation = [
|
||||
# "/authentication/login",
|
||||
# "/authentication/select",
|
||||
# "/authentication/valid",
|
||||
# "/authentication/refresh",
|
||||
# "/authentication/change_password",
|
||||
# "/authentication/create_password",
|
||||
# "/authentication/disconnect",
|
||||
# "/authentication/logout",
|
||||
# "/authentication/refresher",
|
||||
# "/authentication/forgot",
|
||||
# "/authentication/avatar",
|
||||
# ]
|
||||
|
||||
|
||||
def parse_token_object_to_dict(request: Request): # from requests import Request
|
||||
from api_services.redis.functions import get_object_via_access_key
|
||||
from databases import EndpointRestriction
|
||||
import api_events.events as events
|
||||
|
||||
if valid_token := get_object_via_access_key(request=request):
|
||||
endpoint_name = str(request.url).replace(str(request.base_url), "/")
|
||||
endpoint_active = EndpointRestriction.filter_active(
|
||||
EndpointRestriction.endpoint_name.ilike(f"%{endpoint_name}%")
|
||||
).data[0]
|
||||
if not endpoint_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="This endpoint is not active for this user, please contact your responsible company for further information.",
|
||||
)
|
||||
|
||||
if valid_token.user_type == 1:
|
||||
if not valid_token.selected_company:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_418_IM_A_TEAPOT,
|
||||
detail="Selected company is not found in the token object.",
|
||||
)
|
||||
selected_event = Events.filter_active(
|
||||
Events.endpoint_id == endpoint_active.id,
|
||||
Events.id.in_(valid_token.selected_company.reachable_event_list_id),
|
||||
)
|
||||
if not selected_event.data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="This endpoint requires event validation. Please contact your responsible company to use this event.",
|
||||
)
|
||||
selected_event = selected_event.data[0]
|
||||
event_function_class = getattr(selected_event, "function_class", None)
|
||||
event_function_code = getattr(selected_event, "function_code", None)
|
||||
function_class = getattr(events, event_function_class, None)
|
||||
active_function = getattr(
|
||||
function_class,
|
||||
function_class.__event_keys__.get(event_function_code, None),
|
||||
None,
|
||||
)
|
||||
if not active_function:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="This endpoint requires event validation. Please contact your responsible company to use this event.",
|
||||
)
|
||||
valid_token.available_event = active_function
|
||||
return valid_token
|
||||
elif valid_token.user_type == 2:
|
||||
if not valid_token.selected_occupant:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_418_IM_A_TEAPOT,
|
||||
detail="Selected occupant is not found in the token object.",
|
||||
)
|
||||
selected_event = Events.filter_active(
|
||||
Events.endpoint_id == endpoint_active.id,
|
||||
Events.id.in_(valid_token.selected_occupant.reachable_event_list_id),
|
||||
)
|
||||
if not selected_event.data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="This endpoint requires event validation. Please contact your responsible company to use this event.",
|
||||
)
|
||||
selected_event = selected_event.data[0]
|
||||
event_function_class = getattr(selected_event, "function_class", None)
|
||||
event_function_code = getattr(selected_event, "function_code", None)
|
||||
function_class = getattr(events, event_function_class, None)
|
||||
active_function = getattr(
|
||||
function_class,
|
||||
function_class.__event_keys__.get(event_function_code, None),
|
||||
None,
|
||||
)
|
||||
if not active_function:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="This endpoint requires event validation. Please contact your responsible company to use this event.",
|
||||
)
|
||||
valid_token.available_event = active_function
|
||||
return valid_token
|
||||
valid_token.available_event = None
|
||||
return valid_token
|
||||
user_type = "Company" if valid_token.user_type == 1 else "Occupant"
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=f"Token of this user is not valid. Please login and refresh {user_type} selection.",
|
||||
)
|
||||
31
api_services/redis/conn.py
Normal file
31
api_services/redis/conn.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from redis import Redis
|
||||
|
||||
# from configs import WagRedis
|
||||
from api_configs import WagRedis
|
||||
|
||||
|
||||
class RedisConn:
|
||||
|
||||
def __init__(self):
|
||||
self.redis = Redis(
|
||||
host=WagRedis.REDIS_HOST,
|
||||
password=WagRedis.REDIS_PASSWORD,
|
||||
port=WagRedis.REDIS_PORT,
|
||||
db=WagRedis.REDIS_DB,
|
||||
)
|
||||
if not self.check_connection():
|
||||
raise Exception("Connection error")
|
||||
|
||||
def check_connection(self):
|
||||
return self.redis.ping()
|
||||
|
||||
def set_connection(self, host, password, port, db):
|
||||
self.redis = Redis(host=host, password=password, port=port, db=db)
|
||||
return self.redis
|
||||
|
||||
|
||||
try:
|
||||
redis_conn = RedisConn()
|
||||
redis_cli = redis_conn.redis
|
||||
except Exception as e:
|
||||
print("Redis Connection Error", e)
|
||||
71
api_services/redis/functions.py
Normal file
71
api_services/redis/functions.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import json
|
||||
import typing
|
||||
|
||||
from fastapi import status
|
||||
from fastapi.exceptions import HTTPException
|
||||
|
||||
from .conn import redis_cli
|
||||
|
||||
from api_configs import Auth
|
||||
from api_objects import EmployeeTokenObject, OccupantTokenObject
|
||||
|
||||
|
||||
def get_object_via_access_key(
|
||||
request,
|
||||
) -> typing.Union[EmployeeTokenObject, OccupantTokenObject, None]:
|
||||
if not hasattr(request, "headers"):
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=dict(
|
||||
message="Headers are not found in request. Invalid request object."
|
||||
),
|
||||
)
|
||||
if not request.headers.get(Auth.ACCESS_TOKEN_TAG):
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=dict(message="Unauthorized user, please login..."),
|
||||
)
|
||||
already_tokens = redis_cli.scan_iter(
|
||||
match=str(request.headers.get(Auth.ACCESS_TOKEN_TAG) + ":*")
|
||||
)
|
||||
|
||||
if already_tokens := list(already_tokens):
|
||||
try:
|
||||
if redis_object := json.loads(
|
||||
redis_cli.get(already_tokens[0].decode()) or {}
|
||||
):
|
||||
if redis_object.get("user_type") == 1:
|
||||
if not redis_object.get("selected_company", None):
|
||||
redis_object["selected_company"] = None
|
||||
return EmployeeTokenObject(**redis_object)
|
||||
elif redis_object.get("user_type") == 2:
|
||||
if not redis_object.get("selected_occupant", None):
|
||||
redis_object["selected_occupant"] = None
|
||||
return OccupantTokenObject(**redis_object)
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=dict(
|
||||
message="User type is not found in the token object. Please reach to your administrator."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail={
|
||||
"message": "Redis Service raised an exception.",
|
||||
"error": str(e),
|
||||
},
|
||||
)
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid credentials. Please login again.",
|
||||
)
|
||||
|
||||
|
||||
def get_object_via_user_uu_id(user_id: str) -> typing.Union[list, None]:
|
||||
already_tokens = redis_cli.scan_iter(match=str("*:" + str(user_id)))
|
||||
already_tokens = list(already_tokens)
|
||||
if list(already_tokens):
|
||||
return list(already_tokens)
|
||||
return None
|
||||
243
api_services/templates/password_templates.py
Normal file
243
api_services/templates/password_templates.py
Normal file
@@ -0,0 +1,243 @@
|
||||
import datetime
|
||||
|
||||
|
||||
def change_your_password_template(**kwargs):
|
||||
user_name, forgot_link, current_year = (
|
||||
kwargs["user_name"],
|
||||
kwargs["forgot_link"],
|
||||
str(datetime.datetime.now().year),
|
||||
)
|
||||
template = """<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Bootstrap demo</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
.header img {
|
||||
max-width: 100px;
|
||||
}
|
||||
.content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
color: #777777;
|
||||
}
|
||||
.btn-success {
|
||||
color: #fff;
|
||||
background-color: #198754;
|
||||
border-color: #198754;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
border-radius: 5px;
|
||||
font-weight: 400;
|
||||
padding: .375rem .75rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="header">
|
||||
<img src="" alt="Company Logo">
|
||||
<h2>Reset Password</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Dear %s,</p>
|
||||
<p>We have received a request to reset your password for your account with Let's Program Blog. To complete the password reset process, please click on the button below:</p>
|
||||
<p>Please note that this link is only valid for a day only. If you did not request a password reset, please disregard this message.</p>
|
||||
<a href="%s"><button type="button" class="btn-success">Reset Password</button></a>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© %s Evyos Ltd Şti. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""" % (
|
||||
user_name,
|
||||
forgot_link,
|
||||
current_year,
|
||||
)
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def password_is_changed_template(**kwargs):
|
||||
user_name, current_year = kwargs["user_name"], str(datetime.datetime.now().year)
|
||||
template = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thank You for Changing Your Password</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
.header img {
|
||||
max-width: 100px;
|
||||
}
|
||||
.content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
color: #777777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="header">
|
||||
<img src="" alt="Company Logo">
|
||||
<h2>Your Password has changed</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Dear %s,</p>
|
||||
<p>We wanted to let you know that your password has been successfully updated.
|
||||
If you did not make this change or if you believe an unauthorized person has accessed your account,
|
||||
please contact our support team immediately.</p>
|
||||
<p>Thank you for helping us keep your account secure.</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© %s Evyos Ltd Şti. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""" % (
|
||||
user_name,
|
||||
current_year,
|
||||
)
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def invalid_ip_or_address_found(**kwargs):
|
||||
user_name, current_year, address = (
|
||||
kwargs["user_name"],
|
||||
str(datetime.datetime.now().year),
|
||||
kwargs.get("address"),
|
||||
)
|
||||
template = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thank You for Changing Your Password</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
.header img {
|
||||
max-width: 100px;
|
||||
}
|
||||
.content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
color: #777777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="header">
|
||||
<img src="" alt="Company Logo">
|
||||
<h2>An Unknown login has been attempted</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Dear %s,</p>
|
||||
<p>We wanted to let you know that an unusual login attempt has been tried from address below.
|
||||
If you have login from address below please ignore this message</p>
|
||||
<p>Thank you for helping us keep your account secure.</p>
|
||||
<h1>Address of ip attempt</h1>
|
||||
<p>City : %s</p>
|
||||
<p>Zip Code : %s</p>
|
||||
<p>Country : %s</p>
|
||||
<p>Region : %s</p>
|
||||
<p>Region Name : %s</p>
|
||||
<p>If you are not login from this address lets us now by clicking link below</p>
|
||||
<a href="%s"><button type="button" class="btn-success">Reset Password</button></a>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© %s Evyos Ltd Şti. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""" % (
|
||||
user_name,
|
||||
address["city"],
|
||||
address["zip"],
|
||||
address["country"],
|
||||
address["region"],
|
||||
address["regionName"],
|
||||
kwargs["notice_link"],
|
||||
current_year,
|
||||
)
|
||||
return template
|
||||
Reference in New Issue
Block a user