diff --git a/ApiServices/AuthService/Dockerfile b/ApiServices/AuthService/Dockerfile index d77d8b8..2886638 100644 --- a/ApiServices/AuthService/Dockerfile +++ b/ApiServices/AuthService/Dockerfile @@ -17,9 +17,8 @@ RUN poetry config virtualenvs.create false \ # Copy application code COPY /ApiServices/AuthService /ApiServices/AuthService COPY /Controllers /Controllers -COPY /Schemas/building /Schemas/building -COPY /Schemas/company /Schemas/company -COPY /Schemas/identity /Schemas/identity +COPY /Modules /Modules +COPY /Schemas /Schemas # Set Python path to include app directory ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 diff --git a/ApiServices/AuthService/create_route.py b/ApiServices/AuthService/create_route.py index e2796c7..bc8545c 100644 --- a/ApiServices/AuthService/create_route.py +++ b/ApiServices/AuthService/create_route.py @@ -8,10 +8,7 @@ class RouteRegisterController: self.router_list = router_list self.app = app - - def register_routes(self): for router in self.router_list: self.app.include_router(router) - self.add_router_to_database(router) return self.app diff --git a/ApiServices/AuthService/endpoints/__init__.py b/ApiServices/AuthService/endpoints/__init__.py index 55137e0..0f99b12 100644 --- a/ApiServices/AuthService/endpoints/__init__.py +++ b/ApiServices/AuthService/endpoints/__init__.py @@ -2,4 +2,4 @@ from .auth.route import auth_route __all__ = [ "auth_route", -] \ No newline at end of file +] diff --git a/ApiServices/AuthService/endpoints/auth/route.py b/ApiServices/AuthService/endpoints/auth/route.py index 573304c..a3208fe 100644 --- a/ApiServices/AuthService/endpoints/auth/route.py +++ b/ApiServices/AuthService/endpoints/auth/route.py @@ -13,13 +13,12 @@ from ApiServices.AuthService.validations.request.authentication.login_post impor RequestChangePassword, RequestForgotPasswordPhone, RequestForgotPasswordEmail, + RequestVerifyOTP, ) +from ApiServices.AuthService.events.auth.auth import AuthHandlers -auth_route = APIRouter( - prefix="/authentication", - tags=["Authentication Cluster"], -) +auth_route = APIRouter(prefix="/authentication", tags=["Authentication Cluster"]) @auth_route.post( @@ -49,8 +48,12 @@ def authentication_login_post( status_code=status.HTTP_406_NOT_ACCEPTABLE, headers=headers, ) + result = AuthHandlers.LoginHandler.authentication_login_with_domain_and_creds( + request=request, + data=data, + ) return JSONResponse( - content={**data.model_dump()}, + content=result, status_code=status.HTTP_202_ACCEPTED, headers=headers, ) @@ -84,9 +87,12 @@ def authentication_select_post( status_code=status.HTTP_406_NOT_ACCEPTABLE, headers=headers, ) - + result = AuthHandlers.LoginHandler.authentication_select_company_or_occupant_type( + request=request, + data=data, + ) return JSONResponse( - content=data.model_dump(), + content=result, status_code=status.HTTP_202_ACCEPTED, headers=headers, ) @@ -107,13 +113,15 @@ def authentication_password_create_post( """ Authentication create password Route with Post Method """ - token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None) headers = { "language": language or "", "domain": domain or "", "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: return JSONResponse( content={"error": "EYS_0001"}, @@ -121,7 +129,7 @@ def authentication_password_create_post( headers=headers, ) return JSONResponse( - content={**data.model_dump()}, + content={}, status_code=status.HTTP_202_ACCEPTED, headers=headers, ) @@ -333,3 +341,41 @@ def authentication_token_refresh_post( status_code=status.HTTP_202_ACCEPTED, 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, + ) diff --git a/ApiServices/AuthService/endpoints/routes.py b/ApiServices/AuthService/endpoints/routes.py index c49f031..b747e29 100644 --- a/ApiServices/AuthService/endpoints/routes.py +++ b/ApiServices/AuthService/endpoints/routes.py @@ -16,4 +16,7 @@ def get_safe_endpoint_urls() -> list[tuple[str, str]]: ("/auth/login", "POST"), ("/metrics", "GET"), ("/authentication/login", "POST"), + ("/authentication/password/reset", "POST"), + ("/authentication/password/create", "POST"), + ("/authentication/password/verify-otp", "POST"), ] diff --git a/ApiServices/AuthService/events/auth/auth.py b/ApiServices/AuthService/events/auth/auth.py index 17b2135..7b50e45 100644 --- a/ApiServices/AuthService/events/auth/auth.py +++ b/ApiServices/AuthService/events/auth/auth.py @@ -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 ( EmployeeTokenObject, OccupantTokenObject, @@ -6,7 +9,7 @@ from ApiServices.AuthService.validations.custom.token import ( OccupantToken, UserType, ) -from ApiServices.TemplateService.config import api_config +from ApiServices.AuthService.config import api_config from Schemas import ( Users, People, @@ -25,12 +28,14 @@ from Schemas import ( Event2Employee, ) from Modules.Token.password_module import PasswordModule -from Controllers.Redis.database import RedisActions from Schemas.building.build import RelationshipEmployee2Build from Schemas.event.event import Event2Occupant +from Controllers.Redis.database import RedisActions +from Controllers.Mongo.database import mongo_handler TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject] + class RedisHandlers: AUTH_TOKEN: str = "AUTH_TOKEN" @@ -63,7 +68,7 @@ class RedisHandlers: result_delete = RedisActions.delete( 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() keys = [RedisHandlers.AUTH_TOKEN, generated_access_token, str(user.uu_id)] RedisActions.set_json( @@ -74,20 +79,27 @@ class RedisHandlers: return generated_access_token @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( list_keys=[RedisHandlers.AUTH_TOKEN, token, "*"] ).first: - already_token = cls.process_redis_object(**already_token_data) - if already_token.is_employee: + already_token = cls.process_redis_object(already_token_data) + if already_token.is_employee and isinstance(add_payload, CompanyToken): 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 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(), expires={"hours": 1, "minutes": 30}, ) + print("result.first", result.first) return result.first raise ValueError("Something went wrong") @@ -118,6 +130,14 @@ class UserHandlers: """ Check if the password is valid. """ + print( + dict( + domain=domain, + id_=id_, + password=password, + password_hashed=password_hashed, + ) + ) if PasswordModule.check_password( domain=domain, id_=id_, password=password, password_hashed=password_hashed ): @@ -135,10 +155,9 @@ class LoginHandler: def is_employee(email: str): return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT - @classmethod 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 ): """ Handle employee login. @@ -154,10 +173,10 @@ class LoginHandler: ) if not user_handler.check_password_valid( - domain=data.domain, + domain=domain or "", id_=str(found_user.uu_id), password=data.password, - password_hashed=found_user.hash_password + password_hashed=found_user.hash_password, ): raise ValueError("EYS_0005") @@ -230,13 +249,27 @@ class LoginHandler: return { "access_token": access_token, "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, } raise ValueError("Something went wrong") @classmethod def do_employee_occupant( - cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None + cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None ): """ Handle occupant login. @@ -254,7 +287,7 @@ class LoginHandler: domain=data.domain, id_=str(found_user.uu_id), password=data.password, - password_hashed=found_user.hash_password + password_hashed=found_user.hash_password, ): raise ValueError("EYS_0005") @@ -300,7 +333,9 @@ class LoginHandler: "occupants": [occupant_data], } else: - occupants_selection_dict[build_key]["occupants"].append(occupant_data) + occupants_selection_dict[build_key]["occupants"].append( + occupant_data + ) person = found_user.person model_value = OccupantTokenObject( @@ -344,9 +379,9 @@ class LoginHandler: Returns: SuccessResponse containing authentication token and user info """ - language = request.headers("language", "tr") - domain = request.headers("domain", None) - timezone = request.headers("tz", None) or "GMT+3" + language = request.headers.get("language", "tr") + domain = request.headers.get("domain", None) + timezone = request.headers.get("tz", None) or "GMT+3" if cls.is_employee(data.access_key): return cls.do_employee_login( @@ -386,7 +421,9 @@ class LoginHandler: return request.headers.get(api_config.ACCESS_TOKEN_TAG) @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: if data.company_uu_id not in token_dict.companies_uu_id_list: ValueError("EYS_0011") @@ -407,7 +444,9 @@ class LoginHandler: # Get staff IDs staff_ids = [ 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 @@ -456,13 +495,12 @@ class LoginHandler: reachable_event_codes=reachable_event_codes, ) redis_handler = RedisHandlers() - try: # Update Redis - return redis_handler.update_token_at_redis( - token=access_token, add_payload=company_token - ) - except Exception as e: - err = e - ValueError("EYS_0008") + redis_result = redis_handler.update_token_at_redis( + token=access_token, add_payload=company_token + ) + return { + "selected_uu_id": data.company_uu_id, + } @classmethod def handle_occupant_selection( @@ -526,12 +564,12 @@ class LoginHandler: reachable_event_codes=reachable_event_codes, ) redis_handler = RedisHandlers() - try: # Update Redis - return redis_handler.update_token_at_redis( - token=access_token, add_payload=occupant_token - ) - except Exception as e: - raise ValueError("EYS_0008") + redis_handler.update_token_at_redis( + token=access_token, add_payload=occupant_token + ) + return { + "selected_uu_id": data.company_uu_id, + } @classmethod # Requires auth context def authentication_select_company_or_occupant_type(cls, request: Any, data: Any): @@ -549,16 +587,119 @@ class LoginHandler: if not access_token: raise ValueError("EYS_0001") - token_object = RedisHandlers().get_object_from_redis(access_token=access_token) - if token_object.is_employee and isinstance(data, CompanyToken): + token_object = RedisHandlers.get_object_from_redis(access_token=access_token) + if token_object.is_employee: 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( - 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: LoginHandler: LoginHandler = LoginHandler() + PasswordHandler: PasswordHandler = PasswordHandler() diff --git a/ApiServices/AuthService/events/auth/model.py b/ApiServices/AuthService/events/auth/model.py new file mode 100644 index 0000000..fbbdab2 --- /dev/null +++ b/ApiServices/AuthService/events/auth/model.py @@ -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 diff --git a/ApiServices/AuthService/middlewares/token_middleware.py b/ApiServices/AuthService/middlewares/token_middleware.py index 6ca6f23..fd9d93d 100644 --- a/ApiServices/AuthService/middlewares/token_middleware.py +++ b/ApiServices/AuthService/middlewares/token_middleware.py @@ -5,8 +5,7 @@ from ..config import api_config 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()] if base_url in safe_endpoints: return await call_next(request) diff --git a/ApiServices/AuthService/validations/request/authentication/login_post.py b/ApiServices/AuthService/validations/request/authentication/login_post.py index 54cd5f7..c298948 100644 --- a/ApiServices/AuthService/validations/request/authentication/login_post.py +++ b/ApiServices/AuthService/validations/request/authentication/login_post.py @@ -9,6 +9,11 @@ class RequestLogin(BaseModel): remember_me: Optional[bool] +class RequestVerifyOTP(BaseModel): + token: str + otp: str + + class RequestSelectOccupant(BaseModel): company_uu_id: str diff --git a/ApiServices/InitialService/Dockerfile b/ApiServices/InitialService/Dockerfile index 40dfc17..fa17734 100644 --- a/ApiServices/InitialService/Dockerfile +++ b/ApiServices/InitialService/Dockerfile @@ -16,10 +16,10 @@ RUN poetry config virtualenvs.create false \ # Copy application code COPY /ApiServices/InitialService /ApiServices/InitialService +COPY /ApiServices/InitialService / COPY /Controllers /Controllers -COPY /Schemas/building /Schemas/building -COPY /Schemas/company /Schemas/company -COPY /Schemas/identity /Schemas/identity +COPY /Modules /Modules +COPY /Schemas /Schemas # Set Python path to include app directory ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 diff --git a/ApiServices/InitialService/alembic.ini b/ApiServices/InitialService/alembic.ini new file mode 100644 index 0000000..df27668 --- /dev/null +++ b/ApiServices/InitialService/alembic.ini @@ -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 diff --git a/ApiServices/InitialService/alembic/README b/ApiServices/InitialService/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/ApiServices/InitialService/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/ApiServices/InitialService/alembic/env.py b/ApiServices/InitialService/alembic/env.py new file mode 100644 index 0000000..f28b8a8 --- /dev/null +++ b/ApiServices/InitialService/alembic/env.py @@ -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() diff --git a/ApiServices/InitialService/alembic/script.py.mako b/ApiServices/InitialService/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/ApiServices/InitialService/alembic/script.py.mako @@ -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"} diff --git a/ApiServices/InitialService/app.py b/ApiServices/InitialService/app.py index 7dc0395..52f56f9 100644 --- a/ApiServices/InitialService/app.py +++ b/ApiServices/InitialService/app.py @@ -1,18 +1,21 @@ -from Schemas import ( - BuildLivingSpace, - BuildParts, - Companies, - Departments, - Duties, - Duty, - Staff, - Employees, - Event2Employee, - Event2Occupant, - OccupantTypes, - Users, - UsersTokens, -) +from Controllers.Postgres.database import get_db + +from init_app_defaults import create_application_defaults +from init_enums import init_api_enums_build_types +from init_alembic import generate_alembic +from init_occupant_types import create_occupant_types_defaults +from init_services import create_modules_and_services_and_actions +from init_address import create_one_address + +set_alembic = True if __name__ == "__main__": - pass \ No newline at end of file + + 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) diff --git a/ApiServices/InitialService/init_address.py b/ApiServices/InitialService/init_address.py new file mode 100644 index 0000000..c71178b --- /dev/null +++ b/ApiServices/InitialService/init_address.py @@ -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 diff --git a/ApiServices/InitialService/init_alembic.py b/ApiServices/InitialService/init_alembic.py new file mode 100644 index 0000000..9a5712e --- /dev/null +++ b/ApiServices/InitialService/init_alembic.py @@ -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) diff --git a/ApiServices/InitialService/init_app_defaults.py b/ApiServices/InitialService/init_app_defaults.py new file mode 100644 index 0000000..59591ed --- /dev/null +++ b/ApiServices/InitialService/init_app_defaults.py @@ -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") diff --git a/ApiServices/InitialService/init_enums.py b/ApiServices/InitialService/init_enums.py new file mode 100644 index 0000000..91500e7 --- /dev/null +++ b/ApiServices/InitialService/init_enums.py @@ -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": "Açı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) diff --git a/ApiServices/InitialService/init_occupant_types.py b/ApiServices/InitialService/init_occupant_types.py new file mode 100644 index 0000000..678c1f2 --- /dev/null +++ b/ApiServices/InitialService/init_occupant_types.py @@ -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}") diff --git a/ApiServices/InitialService/init_services.py b/ApiServices/InitialService/init_services.py new file mode 100644 index 0000000..879d24e --- /dev/null +++ b/ApiServices/InitialService/init_services.py @@ -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) diff --git a/ApiServices/TemplateService/config.py b/ApiServices/TemplateService/config.py index 7b81f87..3e3cc2b 100644 --- a/ApiServices/TemplateService/config.py +++ b/ApiServices/TemplateService/config.py @@ -8,9 +8,9 @@ class Configs(BaseSettings): """ PATH: str = "" - HOST: str = ("",) - PORT: int = (0,) - LOG_LEVEL: str = ("info",) + HOST: str = "" + PORT: int = 0 + LOG_LEVEL: str = "info" RELOAD: int = 0 ACCESS_TOKEN_TAG: str = "" diff --git a/ApiServices/TemplateService/create_app.py b/ApiServices/TemplateService/create_app.py index 334a6a3..34d057c 100644 --- a/ApiServices/TemplateService/create_app.py +++ b/ApiServices/TemplateService/create_app.py @@ -10,6 +10,14 @@ from ApiServices.TemplateService.initializer.create_route import RouteRegisterCo 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(): application = FastAPI(**api_config.api_info) @@ -36,6 +44,6 @@ def create_app(): route_register = RouteRegisterController(app=application, router_list=get_routes()) application = route_register.register_routes() - + create_events_if_any_cluster_set() application.openapi = lambda _=application: create_openapi_schema(_) return application diff --git a/ApiServices/TemplateService/endpoints/test_template/route.py b/ApiServices/TemplateService/endpoints/test_template/route.py index d79b7d9..43e46b7 100644 --- a/ApiServices/TemplateService/endpoints/test_template/route.py +++ b/ApiServices/TemplateService/endpoints/test_template/route.py @@ -1,6 +1,6 @@ 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"]) @@ -8,30 +8,23 @@ test_template_route = APIRouter(prefix="/test", tags=["Test"]) @test_template_route.get( path="/template", 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): """ Test Template Route """ - headers = dict(request.headers) - event_cluster_matched = event_cluster.match_event( + 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" + "b8ec6e64-286a-4f60-8554-7a3865454944", ] ) - event_cluster_matched.example_callable() response.headers["X-Header"] = "Test Header GET" - return { - "completed": True, - "message": "Test Template Route", - "info": { - "host": headers.get("host", "Not Found"), - "user_agent": headers.get("user-agent", "Not Found"), - }, - } + if runner_callable := event_cluster_matched.example_callable(): + return runner_callable + raise ValueError("Event key not found or multiple matches found") @test_template_route.post( @@ -42,13 +35,14 @@ def test_template_post(request: Request, response: Response): """ 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" - return { - "completed": True, - "message": "Test Template Route with Post Method", - "info": { - "host": headers.get("host", "Not Found"), - "user_agent": headers.get("user-agent", "Not Found"), - }, - } + if runner_callable := event_cluster_matched.example_callable(): + return runner_callable + raise ValueError("Event key not found or multiple matches found") diff --git a/ApiServices/TemplateService/events/__init__.py b/ApiServices/TemplateService/events/__init__.py index e69de29..1f67273 100644 --- a/ApiServices/TemplateService/events/__init__.py +++ b/ApiServices/TemplateService/events/__init__.py @@ -0,0 +1,5 @@ +from .template.event import template_event_cluster + +__all__ = [ + "template_event_cluster", +] diff --git a/ApiServices/TemplateService/events/template/event.py b/ApiServices/TemplateService/events/template/event.py index 1a6cc88..a0f7205 100644 --- a/ApiServices/TemplateService/events/template/event.py +++ b/ApiServices/TemplateService/events/template/event.py @@ -9,18 +9,21 @@ single_event = Event( description="Example event description", ) + def example_callable(): """ Example callable method """ return { - "completed": True, - "message": "Example callable method 2", - "info": { - "host": "example_host", - "user_agent": "example_user_agent", - }, - } + "completed": True, + "message": "Example callable method 2", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + + single_event.event_callable = example_callable other_event = Event( @@ -30,28 +33,34 @@ other_event = Event( response_validator=None, # TODO: Add response validator description="Example event 2 description", ) + + def example_callable_other(): """ Example callable method """ return { - "completed": True, - "message": "Example callable method 1", - "info": { - "host": "example_host", - "user_agent": "example_user_agent", - }, - } + "completed": True, + "message": "Example callable method 1", + "info": { + "host": "example_host", + "user_agent": "example_user_agent", + }, + } + + other_event.event_callable = example_callable_other tokens_in_redis = [ - "3f510dcf-9f84-4eb9-b919-f582f30adab1", - "9f403034-deba-4e1f-b43e-b25d3c808d39", - "b8ec6e64-286a-4f60-8554-7a3865454944", - "176b829c-7622-4cf2-b474-421e5acb637c", - ] -template_event_cluster = EventCluster(endpoint_uu_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5") + "3f510dcf-9f84-4eb9-b919-f582f30adab1", + "9f403034-deba-4e1f-b43e-b25d3c808d39", + "b8ec6e64-286a-4f60-8554-7a3865454944", + "176b829c-7622-4cf2-b474-421e5acb637c", +] +template_event_cluster = EventCluster( + endpoint_uu_id="bb20c8c6-a289-4cab-9da7-34ca8a36c8e5" +) 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()) diff --git a/ApiServices/TemplateService/initializer/create_route.py b/ApiServices/TemplateService/initializer/create_route.py index 3cc8017..dc73794 100644 --- a/ApiServices/TemplateService/initializer/create_route.py +++ b/ApiServices/TemplateService/initializer/create_route.py @@ -9,30 +9,31 @@ class RouteRegisterController: self.app = app @staticmethod - def add_router_with_event_to_database(route: APIRouter): + def add_router_with_event_to_database(router: APIRouter): from Schemas import EndpointRestriction with EndpointRestriction.new_session() as db_session: - route_path = str(getattr(route, "path")) - route_summary = str(getattr(route, "name")) or "" - operation_id = str(getattr(route, "operation_id")) or "" - if not operation_id: - return + for route in router.routes: + route_path = str(getattr(route, "path")) + route_summary = str(getattr(route, "name")) + operation_id = getattr(route, "operation_id", None) + if not operation_id: + continue - for route_method in [method.lower() for method in getattr(route, "methods")]: - restriction = EndpointRestriction.find_or_create( - **dict( + for route_method in [ + method.lower() for method in getattr(route, "methods") + ]: + restriction = EndpointRestriction.find_or_create( endpoint_method=route_method, endpoint_name=route_path, endpoint_desc=route_summary.replace("_", " "), 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, + db=db_session, ) - ) - if not restriction.meta_data.created: - restriction.endpoint_code = f"AR{str(restriction.id).zfill(3)}" - restriction.save(db=db_session) + if restriction.meta_data.created: + restriction.save(db=db_session) def register_routes(self): for router in self.router_list: diff --git a/ApiServices/TemplateService/initializer/event_clusters.py b/ApiServices/TemplateService/initializer/event_clusters.py index b987e25..f568e80 100644 --- a/ApiServices/TemplateService/initializer/event_clusters.py +++ b/ApiServices/TemplateService/initializer/event_clusters.py @@ -1,4 +1,3 @@ - class EventCluster: def __init__(self, endpoint_uu_id: str): @@ -25,33 +24,39 @@ class EventCluster: def set_events_to_database(self): 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( - EndpointRestriction.uu_id == self.endpoint_uu_id, + EndpointRestriction.operation_uu_id == self.endpoint_uu_id, db=db_session, ).data: for event in self.events: - event_obj = Events.find_or_create( + event_to_save_database = Events.find_or_create( function_code=event.key, function_class=event.name, description=event.description, + endpoint_code=self.endpoint_uu_id, endpoint_id=to_save_endpoint.id, endpoint_uu_id=str(to_save_endpoint.uu_id), is_confirmed=True, active=True, db=db_session, ) - event_obj.save() - print(f'UUID: {event_obj.uu_id} event is saved to {to_save_endpoint.uu_id}') + if event_to_save_database.meta_data.created: + 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": """ Match an event by its key """ - print('set(event_keys)', set(event_keys)) - 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]) + # print('set(event_keys)', set(event_keys)) + # 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] + ) if not len(intersection_of_key) == 1: raise ValueError( f"Event key not found or multiple matches found: {intersection_of_key}" @@ -75,7 +80,6 @@ class Event: self.response_validator = response_validator self.description = description - def event_callable(self): """ Example callable method diff --git a/ApiServices/TemplateService/middlewares/token_middleware.py b/ApiServices/TemplateService/middlewares/token_middleware.py index 6ca6f23..3d2baaf 100644 --- a/ApiServices/TemplateService/middlewares/token_middleware.py +++ b/ApiServices/TemplateService/middlewares/token_middleware.py @@ -6,7 +6,7 @@ from ..config import api_config 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()] if base_url in safe_endpoints: return await call_next(request) diff --git a/ApiServices/TemplateService/open_api_creator.py b/ApiServices/TemplateService/open_api_creator.py index 42baf4c..ee76324 100644 --- a/ApiServices/TemplateService/open_api_creator.py +++ b/ApiServices/TemplateService/open_api_creator.py @@ -3,7 +3,7 @@ from fastapi import FastAPI from fastapi.routing import APIRoute 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 diff --git a/Controllers/Email/config.py b/Controllers/Email/config.py new file mode 100644 index 0000000..1c21e5c --- /dev/null +++ b/Controllers/Email/config.py @@ -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() diff --git a/Controllers/Email/implementations.py b/Controllers/Email/implementations.py new file mode 100644 index 0000000..3bfcbbe --- /dev/null +++ b/Controllers/Email/implementations.py @@ -0,0 +1,29 @@ +from send_email import EmailService, EmailSendModel + + +# Create email parameters +email_params = EmailSendModel( + subject="Test Email", + html="

Hello world!

", + receivers=["recipient@example.com"], + text="Hello world!", +) + +another_email_params = EmailSendModel( + subject="Test Email2", + html="

Hello world!2

", + 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) diff --git a/Controllers/Email/send_email.py b/Controllers/Email/send_email.py new file mode 100644 index 0000000..4054962 --- /dev/null +++ b/Controllers/Email/send_email.py @@ -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) diff --git a/Controllers/Redis/config.py b/Controllers/Redis/config.py index cef1e6f..f9f10b4 100644 --- a/Controllers/Redis/config.py +++ b/Controllers/Redis/config.py @@ -23,4 +23,3 @@ class Configs(BaseSettings): redis_configs = Configs() # singleton instance of the REDIS configuration settings -print(redis_configs.as_dict()) diff --git a/Modules/Token/password_module.py b/Modules/Token/password_module.py index 4c458e1..c1860f6 100644 --- a/Modules/Token/password_module.py +++ b/Modules/Token/password_module.py @@ -3,7 +3,7 @@ import uuid import secrets import random -from config import token_config +from .config import token_config class PasswordModule: diff --git a/Schemas/company/company.py b/Schemas/company/company.py index 1f26e61..85e50ef 100644 --- a/Schemas/company/company.py +++ b/Schemas/company/company.py @@ -13,7 +13,6 @@ from sqlalchemy import ( from sqlalchemy.orm import mapped_column, relationship, Mapped from Controllers.Postgres.mixin import CrudCollection -from Schemas import Duties, Addresses class RelationshipDutyCompany(CrudCollection): @@ -60,6 +59,8 @@ class RelationshipDutyCompany(CrudCollection): @classmethod def match_company_to_company_commercial(cls, data: Any, token): + from Schemas import Duties + with cls.new_session() as db_session: token_duties_id, token_company_id = token.get("duty_id"), token.get( "company_id" @@ -108,6 +109,8 @@ class RelationshipDutyCompany(CrudCollection): @classmethod def match_company_to_company_organization(cls, data: Any, token): + from Schemas import Duties + with cls.new_session() as db_session: token_duties_id, token_company_id = token.get("duty_id"), token.get( "company_id" diff --git a/Schemas/event/event.py b/Schemas/event/event.py index b82f541..b5280b7 100644 --- a/Schemas/event/event.py +++ b/Schemas/event/event.py @@ -9,7 +9,6 @@ from sqlalchemy import ( ) from sqlalchemy.orm import mapped_column, Mapped from Controllers.Postgres.mixin import CrudCollection -from Schemas import OccupantTypes class Events(CrudCollection): @@ -21,26 +20,24 @@ class Events(CrudCollection): __tablename__ = "events" __exclude__fields__ = [] - event_type: Mapped[str] = mapped_column( - String, nullable=False, comment="Event Type" - ) 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( String, nullable=False, comment="class name" ) # name: Mapped[str] = mapped_column(String, nullable=True) # form or page title - description: Mapped[str] = mapped_column( - String, server_default="" - ) # form or page description + 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") 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") + endpoint_code: Mapped[str] = mapped_column( + String, nullable=False, comment="Event Type" + ) endpoint_id: Mapped[int] = mapped_column( ForeignKey("endpoint_restriction.id"), nullable=True ) @@ -108,6 +105,8 @@ class Services(CrudCollection): @classmethod def retrieve_service_via_occupant_code(cls, occupant_code): + from Schemas import OccupantTypes + with cls.new_session() as db_session: occupant_type = OccupantTypes.filter_by_one( system=True, occupant_code=occupant_code, db=db_session diff --git a/Schemas/identity/identity.py b/Schemas/identity/identity.py index 36067fd..f01ad05 100644 --- a/Schemas/identity/identity.py +++ b/Schemas/identity/identity.py @@ -29,7 +29,7 @@ class UsersTokens(CrudCollection): domain: Mapped[str] = mapped_column(String, server_default="") expires_at: Mapped[TIMESTAMP] = mapped_column( 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]) diff --git a/Schemas/rules/rules.py b/Schemas/rules/rules.py index b6c6f9e..309236c 100644 --- a/Schemas/rules/rules.py +++ b/Schemas/rules/rules.py @@ -19,7 +19,7 @@ class EndpointRestriction(CrudCollection): __exclude__fields__ = [] 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( String, server_default="", comment="Function name of the API endpoint" @@ -33,6 +33,3 @@ class EndpointRestriction(CrudCollection): endpoint_desc: Mapped[str] = mapped_column( 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" - ) diff --git a/WebServices/ReadMe.md b/WebServices/ReadMe.md new file mode 100644 index 0000000..aecefbb --- /dev/null +++ b/WebServices/ReadMe.md @@ -0,0 +1,10 @@ +## Next-js service serves with other api services + +### Client-Frontend + +4 Client use only + +### Management-Backend + +4 Management use only + diff --git a/WebServices/client-frontend/.gitignore b/WebServices/client-frontend/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/WebServices/client-frontend/.gitignore @@ -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 diff --git a/WebServices/client-frontend/Dockerfile b/WebServices/client-frontend/Dockerfile new file mode 100644 index 0000000..d627c69 --- /dev/null +++ b/WebServices/client-frontend/Dockerfile @@ -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"] diff --git a/WebServices/client-frontend/README.md b/WebServices/client-frontend/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/WebServices/client-frontend/README.md @@ -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. diff --git a/WebServices/client-frontend/next.config.ts b/WebServices/client-frontend/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/WebServices/client-frontend/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/WebServices/client-frontend/package-lock.json b/WebServices/client-frontend/package-lock.json new file mode 100644 index 0000000..01dba39 --- /dev/null +++ b/WebServices/client-frontend/package-lock.json @@ -0,0 +1,1420 @@ +{ + "name": "client-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client-frontend", + "version": "0.1.0", + "dependencies": { + "next": "15.2.4", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "tailwindcss": "^4", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz", + "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@next/env": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", + "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", + "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", + "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", + "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", + "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", + "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", + "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", + "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz", + "integrity": "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==", + "dev": true, + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz", + "integrity": "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==", + "dev": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-x64": "4.1.3", + "@tailwindcss/oxide-freebsd-x64": "4.1.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-x64-musl": "4.1.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz", + "integrity": "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz", + "integrity": "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz", + "integrity": "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz", + "integrity": "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz", + "integrity": "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz", + "integrity": "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz", + "integrity": "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz", + "integrity": "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz", + "integrity": "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz", + "integrity": "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz", + "integrity": "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.3.tgz", + "integrity": "sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.3", + "@tailwindcss/oxide": "4.1.3", + "postcss": "^8.4.41", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@types/node": { + "version": "20.17.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", + "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", + "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", + "dev": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==", + "dev": true, + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001711", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001711.tgz", + "integrity": "sha512-OpFA8GsKtoV3lCcsI3U5XBAV+oVrMu96OS8XafKqnhOaEAW2mveD1Mx81Sx/02chERwhDakuXs28zbyEc4QMKg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "optional": true + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", + "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", + "dependencies": { + "@next/env": "15.2.4", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.2.4", + "@next/swc-darwin-x64": "15.2.4", + "@next/swc-linux-arm64-gnu": "15.2.4", + "@next/swc-linux-arm64-musl": "15.2.4", + "@next/swc-linux-x64-gnu": "15.2.4", + "@next/swc-linux-x64-musl": "15.2.4", + "@next/swc-win32-arm64-msvc": "15.2.4", + "@next/swc-win32-x64-msvc": "15.2.4", + "sharp": "^0.33.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tailwindcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz", + "integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==", + "dev": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + } + } +} diff --git a/WebServices/client-frontend/package.json b/WebServices/client-frontend/package.json new file mode 100644 index 0000000..4449a92 --- /dev/null +++ b/WebServices/client-frontend/package.json @@ -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" + } +} diff --git a/WebServices/client-frontend/postcss.config.mjs b/WebServices/client-frontend/postcss.config.mjs new file mode 100644 index 0000000..c7bcb4b --- /dev/null +++ b/WebServices/client-frontend/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/WebServices/client-frontend/public/file.svg b/WebServices/client-frontend/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/WebServices/client-frontend/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/client-frontend/public/globe.svg b/WebServices/client-frontend/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/WebServices/client-frontend/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/client-frontend/public/next.svg b/WebServices/client-frontend/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/WebServices/client-frontend/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/client-frontend/public/vercel.svg b/WebServices/client-frontend/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/WebServices/client-frontend/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/client-frontend/public/window.svg b/WebServices/client-frontend/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/WebServices/client-frontend/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/client-frontend/src/app/favicon.ico b/WebServices/client-frontend/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/WebServices/client-frontend/src/app/favicon.ico differ diff --git a/WebServices/client-frontend/src/app/globals.css b/WebServices/client-frontend/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/WebServices/client-frontend/src/app/globals.css @@ -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; +} diff --git a/WebServices/client-frontend/src/app/layout.tsx b/WebServices/client-frontend/src/app/layout.tsx new file mode 100644 index 0000000..f7fa87e --- /dev/null +++ b/WebServices/client-frontend/src/app/layout.tsx @@ -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 ( + + + {children} + + + ); +} diff --git a/WebServices/client-frontend/src/app/page.tsx b/WebServices/client-frontend/src/app/page.tsx new file mode 100644 index 0000000..e68abe6 --- /dev/null +++ b/WebServices/client-frontend/src/app/page.tsx @@ -0,0 +1,103 @@ +import Image from "next/image"; + +export default function Home() { + return ( +
+
+ Next.js logo +
    +
  1. + Get started by editing{" "} + + src/app/page.tsx + + . +
  2. +
  3. + Save and see your changes instantly. +
  4. +
+ +
+ + Vercel logomark + Deploy now + + + Read our docs + +
+
+ +
+ ); +} diff --git a/WebServices/client-frontend/tsconfig.json b/WebServices/client-frontend/tsconfig.json new file mode 100644 index 0000000..c133409 --- /dev/null +++ b/WebServices/client-frontend/tsconfig.json @@ -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"] +} diff --git a/WebServices/management-frontend/.gitignore b/WebServices/management-frontend/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/WebServices/management-frontend/.gitignore @@ -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 diff --git a/WebServices/management-frontend/Dockerfile b/WebServices/management-frontend/Dockerfile new file mode 100644 index 0000000..470d8c2 --- /dev/null +++ b/WebServices/management-frontend/Dockerfile @@ -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"] \ No newline at end of file diff --git a/WebServices/management-frontend/README.md b/WebServices/management-frontend/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/WebServices/management-frontend/README.md @@ -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. diff --git a/WebServices/management-frontend/next.config.ts b/WebServices/management-frontend/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/WebServices/management-frontend/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/WebServices/management-frontend/package-lock.json b/WebServices/management-frontend/package-lock.json new file mode 100644 index 0000000..3edf030 --- /dev/null +++ b/WebServices/management-frontend/package-lock.json @@ -0,0 +1,1420 @@ +{ + "name": "management-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "management-frontend", + "version": "0.1.0", + "dependencies": { + "next": "15.2.4", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "tailwindcss": "^4", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz", + "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@next/env": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", + "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", + "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", + "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", + "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", + "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", + "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", + "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", + "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz", + "integrity": "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==", + "dev": true, + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz", + "integrity": "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==", + "dev": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-x64": "4.1.3", + "@tailwindcss/oxide-freebsd-x64": "4.1.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-x64-musl": "4.1.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz", + "integrity": "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz", + "integrity": "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz", + "integrity": "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz", + "integrity": "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz", + "integrity": "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz", + "integrity": "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz", + "integrity": "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz", + "integrity": "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz", + "integrity": "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz", + "integrity": "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz", + "integrity": "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.3.tgz", + "integrity": "sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.3", + "@tailwindcss/oxide": "4.1.3", + "postcss": "^8.4.41", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@types/node": { + "version": "20.17.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", + "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", + "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", + "dev": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==", + "dev": true, + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001711", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001711.tgz", + "integrity": "sha512-OpFA8GsKtoV3lCcsI3U5XBAV+oVrMu96OS8XafKqnhOaEAW2mveD1Mx81Sx/02chERwhDakuXs28zbyEc4QMKg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "optional": true + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", + "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", + "dependencies": { + "@next/env": "15.2.4", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.2.4", + "@next/swc-darwin-x64": "15.2.4", + "@next/swc-linux-arm64-gnu": "15.2.4", + "@next/swc-linux-arm64-musl": "15.2.4", + "@next/swc-linux-x64-gnu": "15.2.4", + "@next/swc-linux-x64-musl": "15.2.4", + "@next/swc-win32-arm64-msvc": "15.2.4", + "@next/swc-win32-x64-msvc": "15.2.4", + "sharp": "^0.33.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tailwindcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz", + "integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==", + "dev": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + } + } +} diff --git a/WebServices/management-frontend/package.json b/WebServices/management-frontend/package.json new file mode 100644 index 0000000..8fbe569 --- /dev/null +++ b/WebServices/management-frontend/package.json @@ -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" + } +} diff --git a/WebServices/management-frontend/postcss.config.mjs b/WebServices/management-frontend/postcss.config.mjs new file mode 100644 index 0000000..c7bcb4b --- /dev/null +++ b/WebServices/management-frontend/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/WebServices/management-frontend/public/file.svg b/WebServices/management-frontend/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/WebServices/management-frontend/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/management-frontend/public/globe.svg b/WebServices/management-frontend/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/WebServices/management-frontend/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/management-frontend/public/next.svg b/WebServices/management-frontend/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/WebServices/management-frontend/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/management-frontend/public/vercel.svg b/WebServices/management-frontend/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/WebServices/management-frontend/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/management-frontend/public/window.svg b/WebServices/management-frontend/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/WebServices/management-frontend/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/WebServices/management-frontend/src/app/favicon.ico b/WebServices/management-frontend/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/WebServices/management-frontend/src/app/favicon.ico differ diff --git a/WebServices/management-frontend/src/app/globals.css b/WebServices/management-frontend/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/WebServices/management-frontend/src/app/globals.css @@ -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; +} diff --git a/WebServices/management-frontend/src/app/layout.tsx b/WebServices/management-frontend/src/app/layout.tsx new file mode 100644 index 0000000..f7fa87e --- /dev/null +++ b/WebServices/management-frontend/src/app/layout.tsx @@ -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 ( + + + {children} + + + ); +} diff --git a/WebServices/management-frontend/src/app/page.tsx b/WebServices/management-frontend/src/app/page.tsx new file mode 100644 index 0000000..e68abe6 --- /dev/null +++ b/WebServices/management-frontend/src/app/page.tsx @@ -0,0 +1,103 @@ +import Image from "next/image"; + +export default function Home() { + return ( +
+
+ Next.js logo +
    +
  1. + Get started by editing{" "} + + src/app/page.tsx + + . +
  2. +
  3. + Save and see your changes instantly. +
  4. +
+ +
+ + Vercel logomark + Deploy now + + + Read our docs + +
+
+ +
+ ); +} diff --git a/WebServices/management-frontend/tsconfig.json b/WebServices/management-frontend/tsconfig.json new file mode 100644 index 0000000..c133409 --- /dev/null +++ b/WebServices/management-frontend/tsconfig.json @@ -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"] +} diff --git a/api_env.env b/api_env.env index ad2410b..dfe6f43 100644 --- a/api_env.env +++ b/api_env.env @@ -20,7 +20,6 @@ REDIS_HOST=redis_service REDIS_PASSWORD=commercial_redis_password REDIS_PORT=6379 REDIS_DB=0 -API_ACCESS_TOKEN_TAG=evyos-session-key API_ACCESS_EMAIL_EXT=evyos.com.tr API_ALGORITHM=HS256 API_ACCESS_TOKEN_LENGTH=90 @@ -28,4 +27,10 @@ API_REFRESHER_TOKEN_LENGTH=144 API_EMAIL_HOST=10.10.2.36 API_DATETIME_FORMAT=YYYY-MM-DD HH:mm:ss Z API_FORGOT_LINK=https://www.evyos.com.tr/password/create?tokenUrl= -API_VERSION=0.1.001 \ No newline at end of file +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 diff --git a/docker-compose.yml b/docker-compose.yml index b80868a..a9144ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,52 @@ services: ports: - "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: # container_name: template_service # build: @@ -93,7 +139,6 @@ services: - API_PORT=8001 - API_LOG_LEVEL=info - API_RELOAD=1 - - API_ACCESS_TOKEN_TAG=eys-acs-tkn - API_APP_NAME=evyos-auth-api-gateway - API_TITLE=WAG API Auth Api Gateway - API_FORGOT_LINK=https://auth_service/forgot-password @@ -116,6 +161,7 @@ services: # networks: # - wag-services + networks: wag-services: