Web service initiated

This commit is contained in:
berkay 2025-04-05 14:59:10 +03:00
parent b1c8203a33
commit fa4df11323
76 changed files with 5385 additions and 171 deletions

View File

@ -17,9 +17,8 @@ RUN poetry config virtualenvs.create false \
# Copy application code # Copy application code
COPY /ApiServices/AuthService /ApiServices/AuthService COPY /ApiServices/AuthService /ApiServices/AuthService
COPY /Controllers /Controllers COPY /Controllers /Controllers
COPY /Schemas/building /Schemas/building COPY /Modules /Modules
COPY /Schemas/company /Schemas/company COPY /Schemas /Schemas
COPY /Schemas/identity /Schemas/identity
# Set Python path to include app directory # Set Python path to include app directory
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1

View File

@ -8,10 +8,7 @@ class RouteRegisterController:
self.router_list = router_list self.router_list = router_list
self.app = app self.app = app
def register_routes(self): def register_routes(self):
for router in self.router_list: for router in self.router_list:
self.app.include_router(router) self.app.include_router(router)
self.add_router_to_database(router)
return self.app return self.app

View File

@ -13,13 +13,12 @@ from ApiServices.AuthService.validations.request.authentication.login_post impor
RequestChangePassword, RequestChangePassword,
RequestForgotPasswordPhone, RequestForgotPasswordPhone,
RequestForgotPasswordEmail, RequestForgotPasswordEmail,
RequestVerifyOTP,
) )
from ApiServices.AuthService.events.auth.auth import AuthHandlers
auth_route = APIRouter( auth_route = APIRouter(prefix="/authentication", tags=["Authentication Cluster"])
prefix="/authentication",
tags=["Authentication Cluster"],
)
@auth_route.post( @auth_route.post(
@ -49,8 +48,12 @@ def authentication_login_post(
status_code=status.HTTP_406_NOT_ACCEPTABLE, status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers, headers=headers,
) )
result = AuthHandlers.LoginHandler.authentication_login_with_domain_and_creds(
request=request,
data=data,
)
return JSONResponse( return JSONResponse(
content={**data.model_dump()}, content=result,
status_code=status.HTTP_202_ACCEPTED, status_code=status.HTTP_202_ACCEPTED,
headers=headers, headers=headers,
) )
@ -84,9 +87,12 @@ def authentication_select_post(
status_code=status.HTTP_406_NOT_ACCEPTABLE, status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers, headers=headers,
) )
result = AuthHandlers.LoginHandler.authentication_select_company_or_occupant_type(
request=request,
data=data,
)
return JSONResponse( return JSONResponse(
content=data.model_dump(), content=result,
status_code=status.HTTP_202_ACCEPTED, status_code=status.HTTP_202_ACCEPTED,
headers=headers, headers=headers,
) )
@ -107,13 +113,15 @@ def authentication_password_create_post(
""" """
Authentication create password Route with Post Method Authentication create password Route with Post Method
""" """
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = { headers = {
"language": language or "", "language": language or "",
"domain": domain or "", "domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}", "eys-ext": f"{str(uuid.uuid4())}",
"token": token,
} }
result = AuthHandlers.PasswordHandler.create_password(
password=data.password,
password_token=data.password_token,
)
if not domain or not language: if not domain or not language:
return JSONResponse( return JSONResponse(
content={"error": "EYS_0001"}, content={"error": "EYS_0001"},
@ -121,7 +129,7 @@ def authentication_password_create_post(
headers=headers, headers=headers,
) )
return JSONResponse( return JSONResponse(
content={**data.model_dump()}, content={},
status_code=status.HTTP_202_ACCEPTED, status_code=status.HTTP_202_ACCEPTED,
headers=headers, headers=headers,
) )
@ -333,3 +341,41 @@ def authentication_token_refresh_post(
status_code=status.HTTP_202_ACCEPTED, status_code=status.HTTP_202_ACCEPTED,
headers=headers, headers=headers,
) )
@auth_route.get(
path="/password/verify-otp",
summary="Verify OTP for password reset",
description="Verify OTP for password reset",
)
def authentication_password_verify_otp(
request: Request,
data: RequestVerifyOTP,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
tz: str = Header(None, alias="timezone"),
):
"""
Verify OTP for password reset
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"tz": tz or "GMT+3",
"token": token,
}
print('Token&OTP : ', data.otp, data.token)
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0003"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)

View File

@ -16,4 +16,7 @@ def get_safe_endpoint_urls() -> list[tuple[str, str]]:
("/auth/login", "POST"), ("/auth/login", "POST"),
("/metrics", "GET"), ("/metrics", "GET"),
("/authentication/login", "POST"), ("/authentication/login", "POST"),
("/authentication/password/reset", "POST"),
("/authentication/password/create", "POST"),
("/authentication/password/verify-otp", "POST"),
] ]

View File

@ -1,4 +1,7 @@
from typing import Any, List, Dict, Optional, Union import arrow
from typing import Any, Dict, Optional, Union
from ApiServices.AuthService.events.auth.model import PasswordHistoryViaUser
from ApiServices.AuthService.validations.custom.token import ( from ApiServices.AuthService.validations.custom.token import (
EmployeeTokenObject, EmployeeTokenObject,
OccupantTokenObject, OccupantTokenObject,
@ -6,7 +9,7 @@ from ApiServices.AuthService.validations.custom.token import (
OccupantToken, OccupantToken,
UserType, UserType,
) )
from ApiServices.TemplateService.config import api_config from ApiServices.AuthService.config import api_config
from Schemas import ( from Schemas import (
Users, Users,
People, People,
@ -25,12 +28,14 @@ from Schemas import (
Event2Employee, Event2Employee,
) )
from Modules.Token.password_module import PasswordModule from Modules.Token.password_module import PasswordModule
from Controllers.Redis.database import RedisActions
from Schemas.building.build import RelationshipEmployee2Build from Schemas.building.build import RelationshipEmployee2Build
from Schemas.event.event import Event2Occupant from Schemas.event.event import Event2Occupant
from Controllers.Redis.database import RedisActions
from Controllers.Mongo.database import mongo_handler
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject] TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
class RedisHandlers: class RedisHandlers:
AUTH_TOKEN: str = "AUTH_TOKEN" AUTH_TOKEN: str = "AUTH_TOKEN"
@ -63,7 +68,7 @@ class RedisHandlers:
result_delete = RedisActions.delete( result_delete = RedisActions.delete(
list_keys=[RedisHandlers.AUTH_TOKEN, "*", str(user.uu_id)] list_keys=[RedisHandlers.AUTH_TOKEN, "*", str(user.uu_id)]
) )
print('result_delete', result_delete) print("result_delete", result_delete)
generated_access_token = PasswordModule.generate_access_token() generated_access_token = PasswordModule.generate_access_token()
keys = [RedisHandlers.AUTH_TOKEN, generated_access_token, str(user.uu_id)] keys = [RedisHandlers.AUTH_TOKEN, generated_access_token, str(user.uu_id)]
RedisActions.set_json( RedisActions.set_json(
@ -74,20 +79,27 @@ class RedisHandlers:
return generated_access_token return generated_access_token
@classmethod @classmethod
def update_token_at_redis(cls, token: str, add_payload: Union[CompanyToken, OccupantToken]): def update_token_at_redis(
cls, token: str, add_payload: Union[CompanyToken, OccupantToken]
):
if already_token_data := RedisActions.get_json( if already_token_data := RedisActions.get_json(
list_keys=[RedisHandlers.AUTH_TOKEN, token, "*"] list_keys=[RedisHandlers.AUTH_TOKEN, token, "*"]
).first: ).first:
already_token = cls.process_redis_object(**already_token_data) already_token = cls.process_redis_object(already_token_data)
if already_token.is_employee: if already_token.is_employee and isinstance(add_payload, CompanyToken):
already_token.selected_company = add_payload already_token.selected_company = add_payload
elif already_token.is_occupant: elif already_token.is_occupant and isinstance(add_payload, OccupantToken):
already_token.selected_occupant = add_payload already_token.selected_occupant = add_payload
result = RedisActions.set_json( result = RedisActions.set_json(
list_keys=[RedisHandlers.AUTH_TOKEN, token, str(already_token.user_uu_id)], list_keys=[
RedisHandlers.AUTH_TOKEN,
token,
str(already_token.user_uu_id),
],
value=already_token.model_dump(), value=already_token.model_dump(),
expires={"hours": 1, "minutes": 30}, expires={"hours": 1, "minutes": 30},
) )
print("result.first", result.first)
return result.first return result.first
raise ValueError("Something went wrong") raise ValueError("Something went wrong")
@ -118,6 +130,14 @@ class UserHandlers:
""" """
Check if the password is valid. Check if the password is valid.
""" """
print(
dict(
domain=domain,
id_=id_,
password=password,
password_hashed=password_hashed,
)
)
if PasswordModule.check_password( if PasswordModule.check_password(
domain=domain, id_=id_, password=password, password_hashed=password_hashed domain=domain, id_=id_, password=password, password_hashed=password_hashed
): ):
@ -135,7 +155,6 @@ class LoginHandler:
def is_employee(email: str): def is_employee(email: str):
return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
@classmethod @classmethod
def do_employee_login( def do_employee_login(
cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None
@ -154,10 +173,10 @@ class LoginHandler:
) )
if not user_handler.check_password_valid( if not user_handler.check_password_valid(
domain=data.domain, domain=domain or "",
id_=str(found_user.uu_id), id_=str(found_user.uu_id),
password=data.password, password=data.password,
password_hashed=found_user.hash_password password_hashed=found_user.hash_password,
): ):
raise ValueError("EYS_0005") raise ValueError("EYS_0005")
@ -230,6 +249,20 @@ class LoginHandler:
return { return {
"access_token": access_token, "access_token": access_token,
"user_type": UserType.employee.name, "user_type": UserType.employee.name,
"user": found_user.get_dict(
exclude_list=[
Users.hash_password,
Users.cryp_uu_id,
Users.password_token,
Users.created_credentials_token,
Users.updated_credentials_token,
Users.confirmed_credentials_token,
Users.is_confirmed,
Users.is_notification_send,
Users.is_email_send,
Users.remember_me,
]
),
"selection_list": companies_list, "selection_list": companies_list,
} }
raise ValueError("Something went wrong") raise ValueError("Something went wrong")
@ -254,7 +287,7 @@ class LoginHandler:
domain=data.domain, domain=data.domain,
id_=str(found_user.uu_id), id_=str(found_user.uu_id),
password=data.password, password=data.password,
password_hashed=found_user.hash_password password_hashed=found_user.hash_password,
): ):
raise ValueError("EYS_0005") raise ValueError("EYS_0005")
@ -300,7 +333,9 @@ class LoginHandler:
"occupants": [occupant_data], "occupants": [occupant_data],
} }
else: else:
occupants_selection_dict[build_key]["occupants"].append(occupant_data) occupants_selection_dict[build_key]["occupants"].append(
occupant_data
)
person = found_user.person person = found_user.person
model_value = OccupantTokenObject( model_value = OccupantTokenObject(
@ -344,9 +379,9 @@ class LoginHandler:
Returns: Returns:
SuccessResponse containing authentication token and user info SuccessResponse containing authentication token and user info
""" """
language = request.headers("language", "tr") language = request.headers.get("language", "tr")
domain = request.headers("domain", None) domain = request.headers.get("domain", None)
timezone = request.headers("tz", None) or "GMT+3" timezone = request.headers.get("tz", None) or "GMT+3"
if cls.is_employee(data.access_key): if cls.is_employee(data.access_key):
return cls.do_employee_login( return cls.do_employee_login(
@ -386,7 +421,9 @@ class LoginHandler:
return request.headers.get(api_config.ACCESS_TOKEN_TAG) return request.headers.get(api_config.ACCESS_TOKEN_TAG)
@classmethod @classmethod
def handle_employee_selection(cls, access_token: str, data: Any, token_dict: TokenDictType): def handle_employee_selection(
cls, access_token: str, data: Any, token_dict: TokenDictType
):
with Users.new_session() as db: with Users.new_session() as db:
if data.company_uu_id not in token_dict.companies_uu_id_list: if data.company_uu_id not in token_dict.companies_uu_id_list:
ValueError("EYS_0011") ValueError("EYS_0011")
@ -407,7 +444,9 @@ class LoginHandler:
# Get staff IDs # Get staff IDs
staff_ids = [ staff_ids = [
staff.id staff.id
for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids), db=db).data for staff in Staff.filter_all(
Staff.duties_id.in_(duties_ids), db=db
).data
] ]
# Get employee # Get employee
@ -456,13 +495,12 @@ class LoginHandler:
reachable_event_codes=reachable_event_codes, reachable_event_codes=reachable_event_codes,
) )
redis_handler = RedisHandlers() redis_handler = RedisHandlers()
try: # Update Redis redis_result = redis_handler.update_token_at_redis(
return redis_handler.update_token_at_redis(
token=access_token, add_payload=company_token token=access_token, add_payload=company_token
) )
except Exception as e: return {
err = e "selected_uu_id": data.company_uu_id,
ValueError("EYS_0008") }
@classmethod @classmethod
def handle_occupant_selection( def handle_occupant_selection(
@ -526,12 +564,12 @@ class LoginHandler:
reachable_event_codes=reachable_event_codes, reachable_event_codes=reachable_event_codes,
) )
redis_handler = RedisHandlers() redis_handler = RedisHandlers()
try: # Update Redis redis_handler.update_token_at_redis(
return redis_handler.update_token_at_redis(
token=access_token, add_payload=occupant_token token=access_token, add_payload=occupant_token
) )
except Exception as e: return {
raise ValueError("EYS_0008") "selected_uu_id": data.company_uu_id,
}
@classmethod # Requires auth context @classmethod # Requires auth context
def authentication_select_company_or_occupant_type(cls, request: Any, data: Any): def authentication_select_company_or_occupant_type(cls, request: Any, data: Any):
@ -549,16 +587,119 @@ class LoginHandler:
if not access_token: if not access_token:
raise ValueError("EYS_0001") raise ValueError("EYS_0001")
token_object = RedisHandlers().get_object_from_redis(access_token=access_token) token_object = RedisHandlers.get_object_from_redis(access_token=access_token)
if token_object.is_employee and isinstance(data, CompanyToken): if token_object.is_employee:
return cls.handle_employee_selection( return cls.handle_employee_selection(
access_token=access_token, data=data, token_dict=token_object, access_token=access_token,
data=data,
token_dict=token_object,
) )
elif token_object.is_occupant and isinstance(data, OccupantToken): elif token_object.is_occupant:
return cls.handle_occupant_selection( return cls.handle_occupant_selection(
access_token=access_token, data=data, token_dict=token_object, access_token=access_token,
data=data,
token_dict=token_object,
) )
class PasswordHandler:
@staticmethod
def create_password(password, password_token=None):
with Users.new_session() as db_session:
found_user = Users.filter_one(
Users.password_token == password_token, db=db_session
).data
if not found_user:
raise ValueError("EYS_0031")
if found_user.password_token:
replace_day = 0
try:
replace_day = int(
str(found_user.password_expires_day or 0)
.split(",")[0]
.replace(" days", "")
)
except Exception as e:
err = e
token_is_expired = arrow.now() >= arrow.get(
str(found_user.password_expiry_begins)
).shift(days=replace_day)
if not password_token == found_user.password_token and token_is_expired:
raise ValueError("")
collection_name = f"{found_user.related_company}*Domain"
print("collection_name", collection_name)
with mongo_handler.collection(collection_name) as mongo_engine:
print({"user_uu_id": str(found_user.uu_id)})
domain_via_user = mongo_engine.find_one(
{"user_uu_id": str(found_user.uu_id)}
)
print("domain_via_user", domain_via_user)
if not domain_via_user:
raise ValueError("EYS_0024")
domain_via_user = domain_via_user.get("main_domain", None)
new_password_dict = {
"password": PasswordModule.create_hashed_password(
domain=domain_via_user,
id_=str(found_user.uu_id),
password=password,
),
"date": str(arrow.now().date()),
}
history_dict = PasswordHistoryViaUser(
user_uu_id=str(found_user.uu_id),
password_add=new_password_dict,
access_history_detail={"request": "", "ip": ""},
)
found_user.password_expiry_begins = str(arrow.now())
found_user.hash_password = new_password_dict.get("password")
found_user.password_token = "" if found_user.password_token else ""
collection_name = f"{found_user.related_company}*PasswordHistory"
with mongo_handler.collection(collection_name) as mongo_engine_sc:
password_history_item = mongo_engine_sc.find_one(
{"user_uu_id": str(found_user.uu_id)}
)
if not password_history_item:
mongo_engine_sc.insert_one(
document={
"user_uu_id": str(found_user.uu_id),
"password_history": [],
}
)
password_history_item = mongo_engine_sc.find_one(
{"user_uu_id": str(found_user.uu_id)}
)
password_history_list = password_history_item.get(
"password_history", []
)
hashed_password = history_dict.password_add.get("password")
for password_in_history in password_history_list:
if str(password_in_history.get("password")) == str(
hashed_password
):
raise ValueError("EYS_0032")
if len(password_history_list) > 3:
password_history_list.pop(0)
password_history_list.append(history_dict.password_add)
return mongo_engine_sc.update_one(
filter={"user_uu_id": str(found_user.uu_id)},
update={
"$set": {
"password_history": password_history_list,
"modified_at": arrow.now().timestamp(),
"access_history_detail": history_dict.access_history_detail,
}
},
upsert=True,
)
found_user.save(db=db_session)
return found_user
class AuthHandlers: class AuthHandlers:
LoginHandler: LoginHandler = LoginHandler() LoginHandler: LoginHandler = LoginHandler()
PasswordHandler: PasswordHandler = PasswordHandler()

View File

@ -0,0 +1,19 @@
from typing import Optional
from pydantic import BaseModel
class DomainViaUser(BaseModel):
user_uu_id: str
main_domain: str
other_domains_list: Optional[list] = None
class PasswordHistoryViaUser(BaseModel):
user_uu_id: str
password_add: dict
access_history_detail: Optional[dict]
class AccessHistoryViaUser(BaseModel):
user_uu_id: str
access_history: dict

View File

@ -5,8 +5,7 @@ from ..config import api_config
async def token_middleware(request: Request, call_next): async def token_middleware(request: Request, call_next):
base_url = request.url.path
base_url = "/".join(request.url.path.split("/")[:3])
safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()] safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()]
if base_url in safe_endpoints: if base_url in safe_endpoints:
return await call_next(request) return await call_next(request)

View File

@ -9,6 +9,11 @@ class RequestLogin(BaseModel):
remember_me: Optional[bool] remember_me: Optional[bool]
class RequestVerifyOTP(BaseModel):
token: str
otp: str
class RequestSelectOccupant(BaseModel): class RequestSelectOccupant(BaseModel):
company_uu_id: str company_uu_id: str

View File

@ -16,10 +16,10 @@ RUN poetry config virtualenvs.create false \
# Copy application code # Copy application code
COPY /ApiServices/InitialService /ApiServices/InitialService COPY /ApiServices/InitialService /ApiServices/InitialService
COPY /ApiServices/InitialService /
COPY /Controllers /Controllers COPY /Controllers /Controllers
COPY /Schemas/building /Schemas/building COPY /Modules /Modules
COPY /Schemas/company /Schemas/company COPY /Schemas /Schemas
COPY /Schemas/identity /Schemas/identity
# Set Python path to include app directory # Set Python path to include app directory
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1

View File

@ -0,0 +1,119 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
# Use forward slashes (/) also on windows to provide an os agnostic path
script_location = alembic
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
# string value is passed to ZoneInfo()
# leave blank for localtime
# timezone =
# max length of characters to apply to the "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
# version_path_separator = newline
#
# Use os.pathsep. Default configuration used for new projects.
version_path_separator = os
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = postgresql+psycopg2://berkay_wag_user:berkay_wag_user_password@postgres-service:5432/wag_database
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
# hooks = ruff
# ruff.type = exec
# ruff.executable = %(here)s/.venv/bin/ruff
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARNING
handlers = console
qualname =
[logger_sqlalchemy]
level = WARNING
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@ -0,0 +1 @@
Generic single-database configuration.

View File

@ -0,0 +1,74 @@
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from Schemas import *
from Controllers.Postgres.database import Base
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@ -0,0 +1,28 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
"""Upgrade schema."""
${upgrades if upgrades else "pass"}
def downgrade() -> None:
"""Downgrade schema."""
${downgrades if downgrades else "pass"}

View File

@ -1,18 +1,21 @@
from Schemas import ( from Controllers.Postgres.database import get_db
BuildLivingSpace,
BuildParts, from init_app_defaults import create_application_defaults
Companies, from init_enums import init_api_enums_build_types
Departments, from init_alembic import generate_alembic
Duties, from init_occupant_types import create_occupant_types_defaults
Duty, from init_services import create_modules_and_services_and_actions
Staff, from init_address import create_one_address
Employees,
Event2Employee, set_alembic = True
Event2Occupant,
OccupantTypes,
Users,
UsersTokens,
)
if __name__ == "__main__": if __name__ == "__main__":
pass
with get_db() as db_session:
if set_alembic:
generate_alembic(session=db_session)
init_api_enums_build_types(db_session=db_session)
create_application_defaults(db_session=db_session)
create_occupant_types_defaults(db_session=db_session)
create_modules_and_services_and_actions(db_session=db_session)
create_one_address(db_session=db_session)

View File

@ -0,0 +1,83 @@
from Schemas import (
AddressCity,
AddressStreet,
AddressLocality,
AddressDistrict,
AddressNeighborhood,
AddressState,
AddressCountry,
)
def create_one_address(db_session):
address_list = []
country = AddressCountry.find_or_create(
country_name="TÜRKİYE", country_code="TR", db=db_session, is_confirmed=True
)
address_list.append(country)
state = AddressState.find_or_create(
state_name="TÜRKİYE",
state_code="TR",
phone_code="90",
country_id=country.id,
country_uu_id=str(country.uu_id),
is_confirmed=True,
db=db_session,
)
address_list.append(state)
city = AddressCity.find_or_create(
city_name="ANKARA",
city_code="6",
licence_plate="06",
state_id=state.id,
state_uu_id=str(state.uu_id),
is_confirmed=True,
db=db_session,
)
address_list.append(city)
district = AddressDistrict.find_or_create(
district_name="ÇANKAYA",
district_code="1231",
city_id=city.id,
city_uu_id=str(city.uu_id),
is_confirmed=True,
db=db_session,
)
address_list.append(district)
locality = AddressLocality.find_or_create(
locality_name="MERKEZ",
locality_code="2431",
type_code="3",
type_description=None,
district_id=district.id,
district_uu_id=str(district.uu_id),
is_confirmed=True,
db=db_session,
)
address_list.append(locality)
neighborhood = AddressNeighborhood.find_or_create(
neighborhood_name="AYRANCI MAHALLESİ",
neighborhood_code="1522",
type_code="1",
type_description="MAHALLESİ",
locality_id=locality.id,
locality_uu_id=str(locality.uu_id),
is_confirmed=True,
db=db_session,
)
address_list.append(neighborhood)
street = AddressStreet.find_or_create(
street_name="REŞAT NURİ CADDESİ",
type_description="CADDESİ",
type_code="3",
street_code="52270",
neighborhood_id=neighborhood.id,
neighborhood_uu_id=str(neighborhood.uu_id),
is_confirmed=True,
db=db_session,
)
address_list.append(street)
for address_single in address_list:
address_single.save(db=db_session)
return

View File

@ -0,0 +1,24 @@
import os
def generate_alembic(session):
from sqlalchemy import text
try:
result = session.execute(
text(
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = "
"'alembic_version') AS table_existence;"
)
)
if result.first()[0]:
session.execute(text("delete from alembic_version;"))
session.commit()
except Exception as e:
print(e)
finally:
run_command = "python -m alembic stamp head;"
run_command += (
"python -m alembic revision --autogenerate;python -m alembic upgrade head;"
)
os.system(run_command)

View File

@ -0,0 +1,410 @@
import arrow
from Modules.Token.password_module import PasswordModule
from Controllers.Mongo.database import mongo_handler
def create_application_defaults(db_session):
from Schemas import (
Companies,
Departments,
Duty,
Duties,
Employees,
People,
Users,
Staff,
RelationshipDutyCompany,
)
created_list = []
created_by, confirmed_by = "System", "System"
company_management = Companies.find_or_create(
**{
"formal_name": "Evyos LTD",
"public_name": "Evyos Verimlilik Sistemleri",
"company_type": "LTD",
"commercial_type": "Commercial",
"tax_no": "123132123132",
"company_tag": "Evyos",
"default_lang_type": "TR",
"default_money_type": "TL",
"is_commercial": True,
"is_confirmed": True,
},
db=db_session,
)
created_list.append(company_management)
active_row = dict(
is_confirmed=True, active=True, deleted=False, is_notification_send=True
)
company_id, company_uu_id = company_management.id, str(company_management.uu_id)
created_list = []
execution = Departments.find_or_create(
department_name="Execution Office",
department_code="EO001",
company_id=company_id,
company_uu_id=str(company_uu_id),
**active_row,
db=db_session,
)
created_list.append(execution)
it_dept = Departments.find_or_create(
department_name="IT Department",
department_code="ITD001",
company_id=company_id,
company_uu_id=str(company_uu_id),
**active_row,
db=db_session,
)
created_list.append(it_dept)
bm_duty = Duty.find_or_create(
duty_name="Business Manager",
duty_code="BM0001",
duty_description="Business Manager",
**active_row,
db=db_session,
)
created_list.append(bm_duty)
it_duty = Duty.find_or_create(
duty_name="IT Manager",
duty_code="IT0001",
duty_description="IT Manager",
**active_row,
db=db_session,
)
created_list.append(it_duty)
bulk_duty = Duty.find_or_create(
duty_name="BULK",
duty_code="BULK",
duty_description="BULK RECORDS OF THE COMPANY",
**active_row,
db=db_session,
)
created_list.append(bulk_duty)
occu_duty = Duty.find_or_create(
duty_name="OCCUPANT",
duty_code="OCCUPANT",
duty_description="OCCUPANT RECORDS OF THE COMPANY",
**active_row,
db=db_session,
)
created_list.append(occu_duty)
duties_created_bm = Duties.find_or_create(
company_id=company_id,
company_uu_id=str(company_uu_id),
duties_id=bm_duty.id,
duties_uu_id=str(bm_duty.uu_id),
department_id=execution.id,
department_uu_id=str(execution.uu_id),
**active_row,
db=db_session,
)
created_list.append(duties_created_bm)
duties_created_it = Duties.find_or_create(
company_id=company_id,
company_uu_id=str(company_uu_id),
duties_id=it_duty.id,
duties_uu_id=str(it_duty.uu_id),
department_id=it_dept.id,
department_uu_id=str(it_dept.uu_id),
**active_row,
db=db_session,
)
created_list.append(duties_created_it)
duties_created__ex = Duties.find_or_create(
company_id=company_id,
company_uu_id=str(company_uu_id),
duties_id=bulk_duty.id,
duties_uu_id=str(bulk_duty.uu_id),
department_id=execution.id,
department_uu_id=str(execution.uu_id),
**active_row,
db=db_session,
)
created_list.append(duties_created__ex)
duties_created_at = Duties.find_or_create(
company_id=company_id,
company_uu_id=str(company_uu_id),
duties_id=occu_duty.id,
duties_uu_id=str(occu_duty.uu_id),
department_id=execution.id,
department_uu_id=str(execution.uu_id),
**active_row,
db=db_session,
)
created_list.append(duties_created_at)
bulk_duty = Duty.filter_by_one(system=True, duty_code="BULK", db=db_session).data
it_dept = Departments.filter_by_one(
system=True,
department_name="IT Department",
department_code="ITD001",
company_id=company_management.id,
company_uu_id=str(company_management.uu_id),
db=db_session,
).data
created_duty = Duty.find_or_create(
duty_name="Database Manager",
duty_code="DM",
duty_description="Database Manager",
created_by=created_by,
confirmed_by=confirmed_by,
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(created_duty)
created_duty = Duty.find_or_create(
duty_name="Network Manager",
duty_code="NM",
duty_description="Network Manager",
created_by=created_by,
confirmed_by=confirmed_by,
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(created_duty)
application_manager_duty = Duty.find_or_create(
duty_name="Application Manager",
duty_code="AM",
duty_description="Application Manager",
created_by=created_by,
confirmed_by=confirmed_by,
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(application_manager_duty)
application_super_user_duty = Duty.find_or_create(
duty_name="Super User",
duty_code="SUE",
duty_description="Super User",
created_by=created_by,
confirmed_by=confirmed_by,
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(application_super_user_duty)
application_manager_duties = Duties.find_or_create(
department_id=it_dept.id,
department_uu_id=str(it_dept.uu_id),
duties_id=application_manager_duty.id,
duties_uu_id=str(application_manager_duty.uu_id),
company_id=company_management.id,
company_uu_id=str(company_management.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(application_manager_duties)
super_user_duties = Duties.find_or_create(
department_id=it_dept.id,
department_uu_id=str(it_dept.uu_id),
duties_id=application_super_user_duty.id,
duties_uu_id=str(application_manager_duty.uu_id),
company_id=company_management.id,
company_uu_id=str(company_management.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(super_user_duties)
RelationshipDutyCompany.find_or_create(
duties_id=application_manager_duties.id,
owner_id=company_management.id,
member_id=company_management.id,
parent_id=None,
child_count=0,
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(application_manager_duties)
RelationshipDutyCompany.find_or_create(
duties_id=super_user_duties.id,
owner_id=company_management.id,
member_id=company_management.id,
parent_id=None,
child_count=0,
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(super_user_duties)
app_manager = People.find_or_create(
**{
"person_tag": "BAM-System",
"firstname": "Berkay Application Manager",
"surname": "Karatay",
"sex_code": "M",
"middle_name": "",
"father_name": "Father",
"mother_name": "Mother",
"country_code": "TR",
"national_identity_id": "12312312312",
"birth_place": "Ankara",
"birth_date": "01.07.1990",
"tax_no": "1231231231",
"is_confirmed": True,
},
db=db_session,
)
created_list.append(app_manager)
sup_manager = People.find_or_create(
**{
"person_tag": "BSU-System",
"firstname": "Berkay Super User",
"surname": "Karatay",
"sex_code": "M",
"middle_name": "",
"father_name": "Father",
"mother_name": "Mother",
"country_code": "TR",
"national_identity_id": "12312312313",
"birth_place": "Ankara",
"birth_date": "01.07.1990",
"tax_no": "1231231232",
},
db=db_session,
)
created_list.append(sup_manager)
application_manager_staff = Staff.find_or_create(
staff_description="Application Manager",
staff_name="Application Manager Employee",
staff_code="AME",
duties_id=application_manager_duties.id,
duties_uu_id=str(application_manager_duty.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(application_manager_staff)
super_user_staff = Staff.find_or_create(
staff_description="Super User",
staff_name="Super User Employee",
staff_code="SUE",
duties_id=super_user_duties.id,
duties_uu_id=str(application_manager_duty.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(super_user_staff)
app_manager_employee = Employees.find_or_create(
staff_id=application_manager_staff.id,
staff_uu_id=str(application_manager_staff.uu_id),
people_id=app_manager.id,
people_uu_id=str(app_manager.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(app_manager_employee)
super_user_employee = Employees.find_or_create(
staff_id=super_user_staff.id,
staff_uu_id=str(super_user_staff.uu_id),
people_id=sup_manager.id,
people_uu_id=str(sup_manager.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(super_user_employee)
app_manager_user = Users.find_or_create(
person_id=app_manager.id,
person_uu_id=str(app_manager.uu_id),
user_tag=app_manager.person_tag,
email="karatay.berkay.man@evyos.com.tr",
phone_number="+901111111111",
avatar="https://s.tmimgcdn.com/scr/800x500/276800/building-home-nature-logo-vector-template-3_276851-original.jpg",
related_company=str(company_management.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(app_manager_user)
app_manager_user.password_expiry_begins = str(arrow.now())
app_manager_user.password_token = PasswordModule.generate_refresher_token()
main_domain, collection_name = (
"evyos.com.tr",
f"{str(company_management.uu_id)}*Domain",
)
with mongo_handler.collection(collection_name) as mongo_engine:
mongo_engine.insert_one(
document={
"user_uu_id": str(app_manager_user.uu_id),
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
)
sup_manager_employee = Users.find_or_create(
person_id=sup_manager.id,
person_uu_id=str(sup_manager.uu_id),
user_tag=sup_manager.person_tag,
email="karatay.berkay.sup@evyos.com.tr",
phone_number="+901111111112",
avatar="https://s.tmimgcdn.com/scr/800x500/276800/building-home-nature-logo-vector-template-3_276851-original.jpg",
created_by=created_by,
confirmed_by=confirmed_by,
related_company=str(company_management.uu_id),
is_confirmed=True,
active=True,
deleted=False,
is_notification_send=True,
db=db_session,
)
created_list.append(sup_manager_employee)
sup_manager_employee.password_expiry_begins = str(arrow.now())
sup_manager_employee.password_token = PasswordModule.generate_refresher_token()
with mongo_handler.collection(collection_name) as mongo_engine:
mongo_engine.insert_one(
document={
"user_uu_id": str(sup_manager_employee.uu_id),
"other_domains_list": [main_domain],
"main_domain": main_domain,
"modified_at": arrow.now().timestamp(),
}
)
db_session.commit()
print("All Defaults Create is now completed")

View File

@ -0,0 +1,253 @@
from pydantic import BaseModel
class InsertBuildTypes(BaseModel):
function_code: str
type_code: str
lang: str
type_name: str
def init_api_enums_build_types(db_session):
from Schemas import BuildTypes, ApiEnumDropdown
insert_types = [
{
"function_code": "EVYOS",
"type_code": "APT_KZN",
"type_name": "Apartman Kazan Dairesi",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT_GRJ",
"type_name": "Apartman Garaj",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT_DP",
"type_name": "Apartman Depo",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "DAIRE",
"type_name": "Apartman Dairesi",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT",
"type_name": "Apartman Binası",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT_YNT",
"type_name": "Apartman Yönetimi",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT_PRK",
"type_name": "Apartman Açık Park Alanı",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT_YSL",
"type_name": "Apartman Yeşil Alan",
"lang": "TR",
},
{
"function_code": "EVYOS",
"type_code": "APT_YOL",
"type_name": "Apartman Ara Yol",
"lang": "TR",
},
]
for insert_type in insert_types:
build_types = InsertBuildTypes(
function_code="EVYOS",
lang=insert_type["lang"],
type_code=str(insert_type["type_code"]).upper(),
type_name=insert_type["type_name"],
)
created_build_type = BuildTypes.find_or_create(
**build_types.model_dump(), is_confirmed=True, db=db_session
)
created_build_type.save(db=db_session)
insert_enums = [
{"enum_class": "BuildDuesTypes", "type_code": "BDT-D", "type_name": "Debit"},
{
"enum_class": "BuildDuesTypes",
"type_code": "BDT-A",
"type_name": "Add Debit",
},
{
"enum_class": "BuildDuesTypes",
"type_code": "BDT-R",
"type_name": "Renovation",
},
{
"enum_class": "BuildDuesTypes",
"type_code": "BDT-L",
"type_name": "Lawyer expence",
},
{
"enum_class": "BuildDuesTypes",
"type_code": "BDT-S",
"type_name": "Service fee",
},
{
"enum_class": "BuildDuesTypes",
"type_code": "BDT-I",
"type_name": "Information",
},
{
"enum_class": "AccountingReceiptTypes",
"type_code": "ART-A",
"type_name": "Kasa Tahsil Fişi",
},
{
"enum_class": "AccountingReceiptTypes",
"type_code": "ART-E",
"type_name": "Kasa Tediye Fişi",
},
{
"enum_class": "AccountingReceiptTypes",
"type_code": "ART-M",
"type_name": "Mahsup Fişi",
},
{
"enum_class": "AccountingReceiptTypes",
"type_code": "ART-O",
"type_name": "ılış Fişi",
},
{
"enum_class": "AccountingReceiptTypes",
"type_code": "ART-C",
"type_name": "Kapanış Fişi",
},
{"enum_class": "IbanBudgetType", "type_code": "IBT-I", "type_name": "Iban"},
{"enum_class": "IbanBudgetType", "type_code": "IBT-B", "type_name": "Budget"},
{
"enum_class": "IbanBudgetType",
"type_code": "IBT-TR",
"type_name": "Transaction records",
},
{"enum_class": "ProjectTypes", "type_code": "R", "type_name": "Tadilat"},
{
"enum_class": "ProjectTypes",
"type_code": "PT-C",
"type_name": "Mahkeme süreçleri",
},
{
"enum_class": "ProjectTypes",
"type_code": "PT-Z",
"type_name": "Sıfır Bakiye",
},
{
"enum_class": "EdmBudgetType",
"type_code": "PT-B",
"type_name": "Banka records",
},
{
"enum_class": "EdmBudgetType",
"type_code": "PT-S",
"type_name": "Sistem kaydı",
},
{
"enum_class": "EdmBudgetType",
"type_code": "EBT-C",
"type_name": "Build, Flat or Site records",
},
{"enum_class": "ExpireType", "type_code": "1", "type_name": "daily"},
{"enum_class": "ExpireType", "type_code": "7", "type_name": "weekly"},
{"enum_class": "ExpireType", "type_code": "30", "type_name": "monthly"},
{"enum_class": "ExpireType", "type_code": "90", "type_name": "quarter"},
{"enum_class": "ExpireType", "type_code": "180", "type_name": "six_month"},
{"enum_class": "ExpireType", "type_code": "365", "type_name": "yearly"},
{"enum_class": "PhoneType", "type_code": "M", "type_name": "cep tel"},
{"enum_class": "PhoneType", "type_code": "L", "type_name": "sabit telefon"},
{"enum_class": "PhoneType", "type_code": "F", "type_name": "fax"},
{"enum_class": "PhoneType", "type_code": "C", "type_name": "santral"},
{
"enum_class": "PhoneType",
"type_code": "G",
"type_name": "ülke genelindeki hatlar 444",
},
{"enum_class": "PerComType", "type_code": "1", "type_name": "Person"},
{"enum_class": "PerComType", "type_code": "2", "type_name": "Company"},
{"enum_class": "Directions", "type_code": "NN", "type_name": "North"},
{"enum_class": "Directions", "type_code": "EE", "type_name": "East"},
{"enum_class": "Directions", "type_code": "SS", "type_name": "South"},
{"enum_class": "Directions", "type_code": "WW", "type_name": "West"},
{"enum_class": "Directions", "type_code": "NE", "type_name": "North East"},
{"enum_class": "Directions", "type_code": "NW", "type_name": "North West"},
{"enum_class": "Directions", "type_code": "SE", "type_name": "South East"},
{"enum_class": "Directions", "type_code": "SW", "type_name": "South West"},
{
"enum_class": "MeetingTypes",
"type_code": "MT-RBM",
"type_name": "Regular Building Meeting",
},
{
"enum_class": "MeetingTypes",
"type_code": "MT-DBM",
"type_name": "Disaster Building Meeting",
},
{
"enum_class": "MeetingTypes",
"type_code": "MT-EBM",
"type_name": "Emergency Building Meeting",
},
{
"enum_class": "DebitTypes",
"type_code": "DT-D",
"type_name": "Debit Sender",
},
{
"enum_class": "DebitTypes",
"type_code": "DT-R",
"type_name": "Credit Receiver",
},
{
"enum_class": "DebitTypes",
"type_code": "DT-Z",
"type_name": "Zero Balance",
},
{
"enum_class": "TimePeriod",
"type_code": "TP-W",
"type_name": "Weekly",
},
{
"enum_class": "TimePeriod",
"type_code": "TP-M",
"type_name": "Monthly",
},
{
"enum_class": "TimePeriod",
"type_code": "TP-Q",
"type_name": "Quarterly",
},
{
"enum_class": "TimePeriod",
"type_code": "TP-Y",
"type_name": "Yearly",
},
]
for insert_enum in insert_enums:
created_api_enum = ApiEnumDropdown.find_or_create(
enum_class=insert_enum["enum_class"],
value=insert_enum["type_name"],
key=str(insert_enum["type_code"]).upper(),
description=insert_enum["type_name"],
is_confirmed=True,
db=db_session,
)
created_api_enum.save(db=db_session)

View File

@ -0,0 +1,218 @@
def create_occupant_types_defaults(db_session):
from Schemas import OccupantTypes
"""
occupant_category = mapped_column(String, server_default="")
occupant_category_type = mapped_column(String, server_default="")
occupant_is_unique = mapped_column(Boolean, server_default="0")
"""
list_occupant_types = [
{
"occupant_type": "Toplantı Başkanı",
"occupant_description": "Toplantı Başkanı",
"occupant_code": "MT-PRS",
"occupant_category": "Toplantı",
"occupant_category_type": "MT",
"occupant_is_unique": True,
},
{
"occupant_type": "Toplantı Katip",
"occupant_description": "Toplantıda tutanak tutan kişi",
"occupant_code": "MT-WRT",
"occupant_category": "Toplantı",
"occupant_category_type": "MT",
"occupant_is_unique": True,
},
{
"occupant_type": "Toplantı Katılımcısı",
"occupant_description": "Toplantıda sadece katılan kişi",
"occupant_code": "MT-ATT",
"occupant_category": "Toplantı",
"occupant_category_type": "MT",
"occupant_is_unique": False,
},
{
"occupant_type": "Toplantı Danışman",
"occupant_description": "Toplantıda danışmanlık yapan kişi",
"occupant_code": "MT-ADV",
"occupant_category": "Toplantı",
"occupant_category_type": "MT",
"occupant_is_unique": False,
},
{
"occupant_type": "Toplantı Seçilmiş Başkanı",
"occupant_description": "Toplantı Seçilmiş Başkanı",
"occupant_code": "MT-VPR",
"occupant_category": "Toplantı",
"occupant_category_type": "MT",
"occupant_is_unique": True,
},
{
"occupant_type": "Daire Sahibi",
"occupant_description": "Daire Sahibi",
"occupant_code": "FL-OWN",
"occupant_category": "Daire",
"occupant_category_type": "FL",
"occupant_is_unique": True,
},
{
"occupant_type": "Daire Kiracısı",
"occupant_description": "Daire Kiracısı",
"occupant_code": "FL-TEN",
"occupant_category": "Daire",
"occupant_category_type": "FL",
"occupant_is_unique": True,
},
{
"occupant_type": "Daire Sakini",
"occupant_description": "Daire Sakini",
"occupant_code": "FL-RES",
"occupant_category": "Daire",
"occupant_category_type": "FL",
"occupant_is_unique": False,
},
{
"occupant_type": "Daire Sakini Vekili",
"occupant_description": "Daire Sakini Vekili",
"occupant_code": "FL-REP",
"occupant_category": "Daire",
"occupant_category_type": "FL",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Avukatı",
"occupant_description": "Bina Avukatı",
"occupant_code": "BU-ATT",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Avukatı Yardımcısı",
"occupant_description": "Bina Avukatı Yardımcısı",
"occupant_code": "BU-ATA",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Denetmen Yardımcısı",
"occupant_description": "Bina Denetmen Yardımcısı",
"occupant_code": "BU-SPA",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Denetmeni",
"occupant_description": "Bina Denetmeni",
"occupant_code": "BU-SPV",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Yönetici Yardımcısı",
"occupant_description": "Bina Yönetici Yardımcısı",
"occupant_code": "BU-MNA",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Yöneticisi",
"occupant_description": "Bina Yöneticisi",
"occupant_code": "BU-MNG",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": True,
},
{
"occupant_type": "Bina Muhasabecisi",
"occupant_description": "Bina Muhasabecisi",
"occupant_code": "BU-ACC",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Proje Lideri",
"occupant_description": "Proje Lideri",
"occupant_code": "PRJ-LDR",
"occupant_category": "Proje",
"occupant_category_type": "PRJ",
"occupant_is_unique": False,
},
{
"occupant_type": "Proje Sorumlusu",
"occupant_description": "Proje Sorumlusu",
"occupant_code": "PRJ-RES",
"occupant_category": "Proje",
"occupant_category_type": "PRJ",
"occupant_is_unique": False,
},
{
"occupant_type": "Proje Ekibi",
"occupant_description": "Proje Ekibi",
"occupant_code": "PRJ-EMP",
"occupant_category": "Proje",
"occupant_category_type": "PRJ",
"occupant_is_unique": False,
},
{
"occupant_type": "Proje Finans Sorumlusu",
"occupant_description": "Proje Finans Sorumlusu",
"occupant_code": "PRJ-FIN",
"occupant_category": "Proje",
"occupant_category_type": "PRJ",
"occupant_is_unique": False,
},
{
"occupant_type": "Proje Teknik Sorumlusu",
"occupant_description": "Proje Teknik Sorumlusu",
"occupant_code": "PRJ-TEC",
"occupant_category": "Proje",
"occupant_category_type": "PRJ",
"occupant_is_unique": False,
},
{
"occupant_type": "Daire Mülkiyet Vekili",
"occupant_description": "Daire Mülkiyet Vekili",
"occupant_code": "FL-DEP", # deputy
"occupant_category": "Daire",
"occupant_category_type": "FL",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Teknik Sorumlusu",
"occupant_description": "Bina Teknik Sorumlusu",
"occupant_code": "BU-TEC",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Teknik Elemanı",
"occupant_description": "Bina Teknik Elemanı",
"occupant_code": "BU-EMP",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
{
"occupant_type": "Bina Teknik Freelancer",
"occupant_description": "Bina Teknik Freelancer",
"occupant_code": "BU-FLC",
"occupant_category": "Bina",
"occupant_category_type": "BU",
"occupant_is_unique": False,
},
]
for list_occupant_type in list_occupant_types:
try:
created_type = OccupantTypes.find_or_create(
**list_occupant_type, is_confirmed=True, db=db_session
)
created_type.save(db=db_session)
except Exception as e:
print(f"Error: {e}")

View File

@ -0,0 +1,83 @@
def create_modules_and_services_and_actions(db_session):
from Schemas import (
Duty,
OccupantTypes,
Modules,
Services,
)
erp_module = Modules.find_or_create(
**{
"module_name": "EVYOS ERP",
"module_description": "EVYOS Enterprise Resource Planning",
"module_code": "EVYOS-ERP",
"module_layer": 1,
"is_default_module": False,
"is_confirmed": True,
},
db=db_session,
)
erp_module.save(db=db_session)
build_module = Modules.find_or_create(
**{
"module_name": "Bina Yönetim Modülü",
"module_description": "Building Management Module",
"module_code": "BLD-MNG",
"module_layer": 1,
"is_default_module": False,
"is_confirmed": True,
},
db=db_session,
)
build_module.save(db=db_session)
user_module = Modules.find_or_create(
**{
"module_name": "Kullancı Modülü",
"module_description": "Kullanıcı Genel Modülü",
"module_code": "USR-PUB",
"module_layer": 1,
"is_default_module": True,
"is_confirmed": True,
},
db=db_session,
)
user_module.save(db=db_session)
erp_module_module_dict = dict(
module_id=erp_module.id,
module_uu_id=str(erp_module.uu_id),
)
build_module_module_dict = dict(
module_id=build_module.id,
module_uu_id=str(build_module.uu_id),
)
duty_objects = Duty.filter_all(
Duty.duty_code.notin_(["BULK", "OCCUPANT", "BM0001"]),
db=db_session,
).data
for duty_object in duty_objects:
created_service = Services.find_or_create(
**erp_module_module_dict,
service_name=duty_object.duty_name,
service_description=duty_object.duty_description,
service_code=f"SRE-{duty_object.duty_code}",
related_responsibility=duty_object.duty_code,
is_confirmed=True,
db=db_session,
)
created_service.save(db=db_session)
occupant_types = OccupantTypes.filter_all(db=db_session).data
for occupant_type in occupant_types:
created_service = Services.find_or_create(
**build_module_module_dict,
service_name=occupant_type.occupant_type,
service_description=occupant_type.occupant_description,
service_code=f"SRO-{occupant_type.occupant_code}",
related_responsibility=occupant_type.occupant_code,
is_confirmed=True,
db=db_session,
)
created_service.save(db=db_session)

View File

@ -8,9 +8,9 @@ class Configs(BaseSettings):
""" """
PATH: str = "" PATH: str = ""
HOST: str = ("",) HOST: str = ""
PORT: int = (0,) PORT: int = 0
LOG_LEVEL: str = ("info",) LOG_LEVEL: str = "info"
RELOAD: int = 0 RELOAD: int = 0
ACCESS_TOKEN_TAG: str = "" ACCESS_TOKEN_TAG: str = ""

View File

@ -10,6 +10,14 @@ from ApiServices.TemplateService.initializer.create_route import RouteRegisterCo
from .config import api_config from .config import api_config
def create_events_if_any_cluster_set():
import events
for event_str in events.__all__:
if to_set_events := getattr(events, event_str, None):
to_set_events.set_events_to_database()
def create_app(): def create_app():
application = FastAPI(**api_config.api_info) application = FastAPI(**api_config.api_info)
@ -36,6 +44,6 @@ def create_app():
route_register = RouteRegisterController(app=application, router_list=get_routes()) route_register = RouteRegisterController(app=application, router_list=get_routes())
application = route_register.register_routes() application = route_register.register_routes()
create_events_if_any_cluster_set()
application.openapi = lambda _=application: create_openapi_schema(_) application.openapi = lambda _=application: create_openapi_schema(_)
return application return application

View File

@ -1,6 +1,6 @@
from fastapi import APIRouter, Request, Response from fastapi import APIRouter, Request, Response
from ApiServices.TemplateService.events.events_setter import event_cluster from ApiServices.TemplateService.events.template.event import template_event_cluster
test_template_route = APIRouter(prefix="/test", tags=["Test"]) test_template_route = APIRouter(prefix="/test", tags=["Test"])
@ -8,30 +8,23 @@ test_template_route = APIRouter(prefix="/test", tags=["Test"])
@test_template_route.get( @test_template_route.get(
path="/template", path="/template",
description="Test Template Route", description="Test Template Route",
operation_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5" operation_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5",
) )
def test_template(request: Request, response: Response): def test_template(request: Request, response: Response):
""" """
Test Template Route Test Template Route
""" """
headers = dict(request.headers) event_cluster_matched = template_event_cluster.match_event(
event_cluster_matched = event_cluster.match_event(
event_keys=[ event_keys=[
"3f510dcf-9f84-4eb9-b919-f582f30adab1", "3f510dcf-9f84-4eb9-b919-f582f30adab1",
"9f403034-deba-4e1f-b43e-b25d3c808d39", "9f403034-deba-4e1f-b43e-b25d3c808d39",
"b8ec6e64-286a-4f60-8554-7a3865454944" "b8ec6e64-286a-4f60-8554-7a3865454944",
] ]
) )
event_cluster_matched.example_callable()
response.headers["X-Header"] = "Test Header GET" response.headers["X-Header"] = "Test Header GET"
return { if runner_callable := event_cluster_matched.example_callable():
"completed": True, return runner_callable
"message": "Test Template Route", raise ValueError("Event key not found or multiple matches found")
"info": {
"host": headers.get("host", "Not Found"),
"user_agent": headers.get("user-agent", "Not Found"),
},
}
@test_template_route.post( @test_template_route.post(
@ -42,13 +35,14 @@ def test_template_post(request: Request, response: Response):
""" """
Test Template Route with Post Method Test Template Route with Post Method
""" """
headers = dict(request.headers) event_cluster_matched = template_event_cluster.match_event(
event_keys=[
"3f510dcf-9f84-4eb9-b919-f582f30adab1",
"9f403034-deba-4e1f-b43e-b25d3c808d39",
"b8ec6e64-286a-4f60-8554-7a3865454944",
]
)
response.headers["X-Header"] = "Test Header POST" response.headers["X-Header"] = "Test Header POST"
return { if runner_callable := event_cluster_matched.example_callable():
"completed": True, return runner_callable
"message": "Test Template Route with Post Method", raise ValueError("Event key not found or multiple matches found")
"info": {
"host": headers.get("host", "Not Found"),
"user_agent": headers.get("user-agent", "Not Found"),
},
}

View File

@ -0,0 +1,5 @@
from .template.event import template_event_cluster
__all__ = [
"template_event_cluster",
]

View File

@ -9,6 +9,7 @@ single_event = Event(
description="Example event description", description="Example event description",
) )
def example_callable(): def example_callable():
""" """
Example callable method Example callable method
@ -21,6 +22,8 @@ def example_callable():
"user_agent": "example_user_agent", "user_agent": "example_user_agent",
}, },
} }
single_event.event_callable = example_callable single_event.event_callable = example_callable
other_event = Event( other_event = Event(
@ -30,6 +33,8 @@ other_event = Event(
response_validator=None, # TODO: Add response validator response_validator=None, # TODO: Add response validator
description="Example event 2 description", description="Example event 2 description",
) )
def example_callable_other(): def example_callable_other():
""" """
Example callable method Example callable method
@ -42,6 +47,8 @@ def example_callable_other():
"user_agent": "example_user_agent", "user_agent": "example_user_agent",
}, },
} }
other_event.event_callable = example_callable_other other_event.event_callable = example_callable_other
tokens_in_redis = [ tokens_in_redis = [
@ -50,8 +57,10 @@ tokens_in_redis = [
"b8ec6e64-286a-4f60-8554-7a3865454944", "b8ec6e64-286a-4f60-8554-7a3865454944",
"176b829c-7622-4cf2-b474-421e5acb637c", "176b829c-7622-4cf2-b474-421e5acb637c",
] ]
template_event_cluster = EventCluster(endpoint_uu_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5") template_event_cluster = EventCluster(
endpoint_uu_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5"
)
template_event_cluster.add_event([single_event, other_event]) template_event_cluster.add_event([single_event, other_event])
matched_event = template_event_cluster.match_event(event_keys=tokens_in_redis) # matched_event = template_event_cluster.match_event(event_keys=tokens_in_redis)
print('event_callable', matched_event.event_callable()) # print('event_callable', matched_event.event_callable())

View File

@ -9,29 +9,30 @@ class RouteRegisterController:
self.app = app self.app = app
@staticmethod @staticmethod
def add_router_with_event_to_database(route: APIRouter): def add_router_with_event_to_database(router: APIRouter):
from Schemas import EndpointRestriction from Schemas import EndpointRestriction
with EndpointRestriction.new_session() as db_session: with EndpointRestriction.new_session() as db_session:
for route in router.routes:
route_path = str(getattr(route, "path")) route_path = str(getattr(route, "path"))
route_summary = str(getattr(route, "name")) or "" route_summary = str(getattr(route, "name"))
operation_id = str(getattr(route, "operation_id")) or "" operation_id = getattr(route, "operation_id", None)
if not operation_id: if not operation_id:
return continue
for route_method in [method.lower() for method in getattr(route, "methods")]: for route_method in [
method.lower() for method in getattr(route, "methods")
]:
restriction = EndpointRestriction.find_or_create( restriction = EndpointRestriction.find_or_create(
**dict(
endpoint_method=route_method, endpoint_method=route_method,
endpoint_name=route_path, endpoint_name=route_path,
endpoint_desc=route_summary.replace("_", " "), endpoint_desc=route_summary.replace("_", " "),
endpoint_function=route_summary, endpoint_function=route_summary,
operation_uu_id=operation_id, # UUID of the endpoint operation_uu_id=operation_id, # UUID of the endpoint
is_confirmed=True, is_confirmed=True,
db=db_session,
) )
) if restriction.meta_data.created:
if not restriction.meta_data.created:
restriction.endpoint_code = f"AR{str(restriction.id).zfill(3)}"
restriction.save(db=db_session) restriction.save(db=db_session)
def register_routes(self): def register_routes(self):

View File

@ -1,4 +1,3 @@
class EventCluster: class EventCluster:
def __init__(self, endpoint_uu_id: str): def __init__(self, endpoint_uu_id: str):
@ -25,33 +24,39 @@ class EventCluster:
def set_events_to_database(self): def set_events_to_database(self):
from Schemas import Events, EndpointRestriction from Schemas import Events, EndpointRestriction
with Events.new_session() as db_session:
with Events.new_session() as db_session:
if to_save_endpoint := EndpointRestriction.filter_one( if to_save_endpoint := EndpointRestriction.filter_one(
EndpointRestriction.uu_id == self.endpoint_uu_id, EndpointRestriction.operation_uu_id == self.endpoint_uu_id,
db=db_session, db=db_session,
).data: ).data:
for event in self.events: for event in self.events:
event_obj = Events.find_or_create( event_to_save_database = Events.find_or_create(
function_code=event.key, function_code=event.key,
function_class=event.name, function_class=event.name,
description=event.description, description=event.description,
endpoint_code=self.endpoint_uu_id,
endpoint_id=to_save_endpoint.id, endpoint_id=to_save_endpoint.id,
endpoint_uu_id=str(to_save_endpoint.uu_id), endpoint_uu_id=str(to_save_endpoint.uu_id),
is_confirmed=True, is_confirmed=True,
active=True, active=True,
db=db_session, db=db_session,
) )
event_obj.save() if event_to_save_database.meta_data.created:
print(f'UUID: {event_obj.uu_id} event is saved to {to_save_endpoint.uu_id}') event_to_save_database.save(db=db_session)
print(
f"UUID: {event_to_save_database.uu_id} event is saved to {to_save_endpoint.uu_id}"
)
def match_event(self, event_keys: list[str]) -> "Event": def match_event(self, event_keys: list[str]) -> "Event":
""" """
Match an event by its key Match an event by its key
""" """
print('set(event_keys)', set(event_keys)) # print('set(event_keys)', set(event_keys))
print('event.keys', set([event.key for event in self.events])) # print('event.keys', set([event.key for event in self.events]))
intersection_of_key: set[str] = set(event_keys) & set([event.key for event in self.events]) intersection_of_key: set[str] = set(event_keys) & set(
[event.key for event in self.events]
)
if not len(intersection_of_key) == 1: if not len(intersection_of_key) == 1:
raise ValueError( raise ValueError(
f"Event key not found or multiple matches found: {intersection_of_key}" f"Event key not found or multiple matches found: {intersection_of_key}"
@ -75,7 +80,6 @@ class Event:
self.response_validator = response_validator self.response_validator = response_validator
self.description = description self.description = description
def event_callable(self): def event_callable(self):
""" """
Example callable method Example callable method

View File

@ -6,7 +6,7 @@ from ..config import api_config
async def token_middleware(request: Request, call_next): async def token_middleware(request: Request, call_next):
base_url = "/".join(request.url.path.split("/")[:3]) base_url = request.url.path
safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()] safe_endpoints = [_[0] for _ in get_safe_endpoint_urls()]
if base_url in safe_endpoints: if base_url in safe_endpoints:
return await call_next(request) return await call_next(request)

View File

@ -3,7 +3,7 @@ from fastapi import FastAPI
from fastapi.routing import APIRoute from fastapi.routing import APIRoute
from fastapi.openapi.utils import get_openapi from fastapi.openapi.utils import get_openapi
from ApiServices.TemplateService.config import template_api_config from .config import api_config as template_api_config
from ApiServices.TemplateService.endpoints.routes import get_safe_endpoint_urls from ApiServices.TemplateService.endpoints.routes import get_safe_endpoint_urls

View File

@ -0,0 +1,32 @@
from pydantic_settings import BaseSettings, SettingsConfigDict
class Configs(BaseSettings):
"""
Email configuration settings.
"""
HOST: str = ""
USERNAME: str = ""
PASSWORD: str = ""
PORT: int = 0
SEND: bool = False
@property
def is_send(self):
return bool(self.SEND)
@classmethod
def as_dict(cls):
return dict(
host=cls.EMAIL_HOST,
port=cls.EMAIL_PORT,
username=cls.EMAIL_USERNAME,
password=cls.EMAIL_PASSWORD,
)
model_config = SettingsConfigDict(env_prefix="EMAIL_")
# singleton instance of the POSTGRESQL configuration settings
email_configs = Configs()

View File

@ -0,0 +1,29 @@
from send_email import EmailService, EmailSendModel
# Create email parameters
email_params = EmailSendModel(
subject="Test Email",
html="<p>Hello world!</p>",
receivers=["recipient@example.com"],
text="Hello world!",
)
another_email_params = EmailSendModel(
subject="Test Email2",
html="<p>Hello world!2</p>",
receivers=["recipient@example.com"],
text="Hello world!2",
)
# The context manager handles connection errors
with EmailService.new_session() as email_session:
# Send email - any exceptions here will propagate up
EmailService.send_email(email_session, email_params)
# Or send directly through the session
email_session.send(email_params)
# Send more emails in the same session if needed
EmailService.send_email(email_session, another_email_params)

View File

@ -0,0 +1,70 @@
from redmail import EmailSender
from typing import List, Optional, Dict
from pydantic import BaseModel
from config import Configs
from contextlib import contextmanager
class EmailSendModel(BaseModel):
subject: str
html: str = ""
receivers: List[str]
text: Optional[str] = ""
cc: Optional[List[str]] = None
bcc: Optional[List[str]] = None
headers: Optional[Dict] = None
attachments: Optional[Dict] = None
class EmailSession:
def __init__(self, email_sender):
self.email_sender = email_sender
def send(self, params: EmailSendModel) -> bool:
"""Send email using this session."""
if not Configs.is_send:
print("Email sending is disabled", params)
return False
receivers = [Configs.USERNAME]
self.email_sender.send(
subject=params.subject,
receivers=receivers,
text=params.text + f" : Gonderilen [{str(receivers)}]",
html=params.html,
cc=params.cc,
bcc=params.bcc,
headers=params.headers or {},
attachments=params.attachments or {},
)
return True
class EmailService:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(EmailService, cls).__new__(cls)
return cls._instance
@classmethod
@contextmanager
def new_session(cls):
"""Create and yield a new email session with active connection."""
email_sender = EmailSender(**Configs.as_dict())
session = EmailSession(email_sender)
try:
email_sender.connect()
yield session
except Exception as e:
print(f"Error with email connection: {e}")
raise
finally:
email_sender.close()
@classmethod
def send_email(cls, session: EmailSession, params: EmailSendModel) -> bool:
"""Send email using the provided session."""
return session.send(params)

View File

@ -23,4 +23,3 @@ class Configs(BaseSettings):
redis_configs = Configs() # singleton instance of the REDIS configuration settings redis_configs = Configs() # singleton instance of the REDIS configuration settings
print(redis_configs.as_dict())

View File

@ -3,7 +3,7 @@ import uuid
import secrets import secrets
import random import random
from config import token_config from .config import token_config
class PasswordModule: class PasswordModule:

View File

@ -13,7 +13,6 @@ from sqlalchemy import (
from sqlalchemy.orm import mapped_column, relationship, Mapped from sqlalchemy.orm import mapped_column, relationship, Mapped
from Controllers.Postgres.mixin import CrudCollection from Controllers.Postgres.mixin import CrudCollection
from Schemas import Duties, Addresses
class RelationshipDutyCompany(CrudCollection): class RelationshipDutyCompany(CrudCollection):
@ -60,6 +59,8 @@ class RelationshipDutyCompany(CrudCollection):
@classmethod @classmethod
def match_company_to_company_commercial(cls, data: Any, token): def match_company_to_company_commercial(cls, data: Any, token):
from Schemas import Duties
with cls.new_session() as db_session: with cls.new_session() as db_session:
token_duties_id, token_company_id = token.get("duty_id"), token.get( token_duties_id, token_company_id = token.get("duty_id"), token.get(
"company_id" "company_id"
@ -108,6 +109,8 @@ class RelationshipDutyCompany(CrudCollection):
@classmethod @classmethod
def match_company_to_company_organization(cls, data: Any, token): def match_company_to_company_organization(cls, data: Any, token):
from Schemas import Duties
with cls.new_session() as db_session: with cls.new_session() as db_session:
token_duties_id, token_company_id = token.get("duty_id"), token.get( token_duties_id, token_company_id = token.get("duty_id"), token.get(
"company_id" "company_id"

View File

@ -9,7 +9,6 @@ from sqlalchemy import (
) )
from sqlalchemy.orm import mapped_column, Mapped from sqlalchemy.orm import mapped_column, Mapped
from Controllers.Postgres.mixin import CrudCollection from Controllers.Postgres.mixin import CrudCollection
from Schemas import OccupantTypes
class Events(CrudCollection): class Events(CrudCollection):
@ -21,26 +20,24 @@ class Events(CrudCollection):
__tablename__ = "events" __tablename__ = "events"
__exclude__fields__ = [] __exclude__fields__ = []
event_type: Mapped[str] = mapped_column(
String, nullable=False, comment="Event Type"
)
function_code: Mapped[str] = mapped_column( function_code: Mapped[str] = mapped_column(
String, nullable=False, comment="function code" String, nullable=False, comment="function code", unique=True
) )
function_class: Mapped[str] = mapped_column( function_class: Mapped[str] = mapped_column(
String, nullable=False, comment="class name" String, nullable=False, comment="class name"
) )
# name: Mapped[str] = mapped_column(String, nullable=True) # form or page title # name: Mapped[str] = mapped_column(String, nullable=True) # form or page title
description: Mapped[str] = mapped_column( description: Mapped[str] = mapped_column(String, server_default="")
String, server_default=""
) # form or page description
property_description: Mapped[str] = mapped_column(String, server_default="") property_description: Mapped[str] = mapped_column(String, server_default="")
marketing_layer = mapped_column(SmallInteger, server_default="3") marketing_layer = mapped_column(SmallInteger, server_default="3")
cost: Mapped[float] = mapped_column(Numeric(20, 2), server_default="0.00") cost: Mapped[float] = mapped_column(Numeric(20, 2), server_default="0.00")
unit_price: Mapped[float] = mapped_column(Numeric(20, 2), server_default="0.00") unit_price: Mapped[float] = mapped_column(Numeric(20, 2), server_default="0.00")
endpoint_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Event Type"
)
endpoint_id: Mapped[int] = mapped_column( endpoint_id: Mapped[int] = mapped_column(
ForeignKey("endpoint_restriction.id"), nullable=True ForeignKey("endpoint_restriction.id"), nullable=True
) )
@ -108,6 +105,8 @@ class Services(CrudCollection):
@classmethod @classmethod
def retrieve_service_via_occupant_code(cls, occupant_code): def retrieve_service_via_occupant_code(cls, occupant_code):
from Schemas import OccupantTypes
with cls.new_session() as db_session: with cls.new_session() as db_session:
occupant_type = OccupantTypes.filter_by_one( occupant_type = OccupantTypes.filter_by_one(
system=True, occupant_code=occupant_code, db=db_session system=True, occupant_code=occupant_code, db=db_session

View File

@ -29,7 +29,7 @@ class UsersTokens(CrudCollection):
domain: Mapped[str] = mapped_column(String, server_default="") domain: Mapped[str] = mapped_column(String, server_default="")
expires_at: Mapped[TIMESTAMP] = mapped_column( expires_at: Mapped[TIMESTAMP] = mapped_column(
TIMESTAMP(timezone=True), TIMESTAMP(timezone=True),
default=str(arrow.now().shift(date=arrow.now(), days=3)), default=str(arrow.now().shift(days=3)),
) )
# users = relationship("Users", back_populates="tokens", foreign_keys=[user_id]) # users = relationship("Users", back_populates="tokens", foreign_keys=[user_id])

View File

@ -19,7 +19,7 @@ class EndpointRestriction(CrudCollection):
__exclude__fields__ = [] __exclude__fields__ = []
operation_uu_id: Mapped[UUID] = mapped_column( operation_uu_id: Mapped[UUID] = mapped_column(
String, comment="UUID of the operation", String, comment="UUID of the operation", nullable=False, unique=True
) )
endpoint_function: Mapped[str] = mapped_column( endpoint_function: Mapped[str] = mapped_column(
String, server_default="", comment="Function name of the API endpoint" String, server_default="", comment="Function name of the API endpoint"
@ -33,6 +33,3 @@ class EndpointRestriction(CrudCollection):
endpoint_desc: Mapped[str] = mapped_column( endpoint_desc: Mapped[str] = mapped_column(
String, server_default="", comment="Description of the endpoint" String, server_default="", comment="Description of the endpoint"
) )
endpoint_code: Mapped[str] = mapped_column(
String, server_default="", unique=True, comment="Unique code for the endpoint"
)

10
WebServices/ReadMe.md Normal file
View File

@ -0,0 +1,10 @@
## Next-js service serves with other api services
### Client-Frontend
4 Client use only
### Management-Backend
4 Management use only

41
WebServices/client-frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -0,0 +1,21 @@
FROM node:18-alpine
WORKDIR /
# Copy package.json and package-lock.json
COPY /WebServices/client-frontend/package*.json ./WebServices/client-frontend/
# Install dependencies
RUN cd ./WebServices/client-frontend && npm install
# Copy the rest of the application
COPY /WebServices/client-frontend ./WebServices/client-frontend
## Build the Next.js app
#RUN cd ./WebServices/client-frontend && npm run dev
# Expose the port the app runs on
EXPOSE 3000
# Command to run the app
CMD ["sh", "-c", "cd /WebServices/client-frontend && npm run dev"]

View File

@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
{
"name": "client-frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.2.4"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4"
}
}

View File

@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;

View File

@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,26 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

View File

@ -0,0 +1,103 @@
import Image from "next/image";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
src/app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -0,0 +1,21 @@
FROM node:18-alpine
WORKDIR /
# Copy package.json and package-lock.json
COPY WebServices/management-frontend/package*.json ./WebServices/management-frontend/
# Install dependencies
RUN cd ./WebServices/management-frontend && npm install
# Copy the rest of the application
COPY WebServices/management-frontend ./WebServices/management-frontend
## Build the Next.js app
#RUN cd ./WebServices/management-frontend && npm run build
# Expose the port the app runs on
EXPOSE 3000
# Command to run the app
CMD ["sh", "-c", "cd /WebServices/management-frontend && npm run dev"]

View File

@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
{
"name": "management-frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.2.4"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4"
}
}

View File

@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;

View File

@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,26 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

View File

@ -0,0 +1,103 @@
import Image from "next/image";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
src/app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@ -20,7 +20,6 @@ REDIS_HOST=redis_service
REDIS_PASSWORD=commercial_redis_password REDIS_PASSWORD=commercial_redis_password
REDIS_PORT=6379 REDIS_PORT=6379
REDIS_DB=0 REDIS_DB=0
API_ACCESS_TOKEN_TAG=evyos-session-key
API_ACCESS_EMAIL_EXT=evyos.com.tr API_ACCESS_EMAIL_EXT=evyos.com.tr
API_ALGORITHM=HS256 API_ALGORITHM=HS256
API_ACCESS_TOKEN_LENGTH=90 API_ACCESS_TOKEN_LENGTH=90
@ -29,3 +28,9 @@ API_EMAIL_HOST=10.10.2.36
API_DATETIME_FORMAT=YYYY-MM-DD HH:mm:ss Z API_DATETIME_FORMAT=YYYY-MM-DD HH:mm:ss Z
API_FORGOT_LINK=https://www.evyos.com.tr/password/create?tokenUrl= API_FORGOT_LINK=https://www.evyos.com.tr/password/create?tokenUrl=
API_VERSION=0.1.001 API_VERSION=0.1.001
API_ACCESS_TOKEN_TAG=eys-acs-tkn
EMAIL_HOST=10.10.2.34
EMAIL_USERNAME=karatay@mehmetkaratay.com.tr
EMAIL_PASSWORD=system
EMAIL_PORT=587
EMAIL_SEND=0

View File

@ -50,6 +50,52 @@ services:
ports: ports:
- "11222:6379" - "11222:6379"
client_frontend:
container_name: client_frontend
build:
context: .
dockerfile: WebServices/client-frontend/Dockerfile
networks:
- wag-services
ports:
- "3000:3000"
volumes:
- .WebServices/client-frontend:WebServices/client-frontend
- WebServices/client-frontend/node_modules
- WebServices/client-frontend/.next
environment:
- NODE_ENV=development
management_frontend:
container_name: management_frontend
build:
context: .
dockerfile: WebServices/management-frontend/Dockerfile
networks:
- wag-services
ports:
- "3001:3000" # Using different host port to avoid conflicts
volumes:
- .WebServices/management-frontend:WebServices/management-frontend
- WebServices/management-frontend/node_modules
- WebServices/management-frontend/.next
environment:
- NODE_ENV=development
# initializer_service:
# container_name: initializer_service
# build:
# context: .
# dockerfile: ApiServices/InitialService/Dockerfile
# networks:
# - wag-services
# env_file:
# - api_env.env
# depends_on:
# - postgres-service
# - mongo_service
# - redis_service
# template_service: # template_service:
# container_name: template_service # container_name: template_service
# build: # build:
@ -93,7 +139,6 @@ services:
- API_PORT=8001 - API_PORT=8001
- API_LOG_LEVEL=info - API_LOG_LEVEL=info
- API_RELOAD=1 - API_RELOAD=1
- API_ACCESS_TOKEN_TAG=eys-acs-tkn
- API_APP_NAME=evyos-auth-api-gateway - API_APP_NAME=evyos-auth-api-gateway
- API_TITLE=WAG API Auth Api Gateway - API_TITLE=WAG API Auth Api Gateway
- API_FORGOT_LINK=https://auth_service/forgot-password - API_FORGOT_LINK=https://auth_service/forgot-password
@ -116,6 +161,7 @@ services:
# networks: # networks:
# - wag-services # - wag-services
networks: networks:
wag-services: wag-services: