From 58a2e122aa23568cfd296411b699ec5a199808cd Mon Sep 17 00:00:00 2001 From: Mike Liao Date: Wed, 30 Oct 2024 08:44:37 -0700 Subject: [PATCH] middle way of refactoring the code --- apps/authentication/.env | 23 +++ apps/authentication/Dockerfile | 45 ++++-- .../backend/application/signin_hub.py | 6 +- .../backend/business/signin_manager.py | 22 +-- .../backend/infra/auth/user_auth_handler.py | 10 +- .../infra/code_management/depot_handler.py | 2 +- .../user_profile/user_profile_handler.py | 12 +- .../backend/models/gitea/models.py | 2 +- .../backend/models/user/constants.py | 2 +- .../backend/models/user/models.py | 4 +- .../backend/models/user_profile/models.py | 2 +- .../services/auth/user_auth_service.py | 4 +- .../services/user/user_management_service.py | 16 +- .../common/config/app_settings.py | 15 +- .../common/config/log_settings.py | 17 +++ .../common/constants/__init__.py | 0 .../common/constants/contract.py | 8 + .../common/constants/payment.py | 5 + .../authentication/common/constants/region.py | 5 + .../common/exception/__init__.py | 0 .../common/exception/exceptions.py | 23 +++ apps/authentication/common/log/__init__.py | 0 .../common/log/application_logger.py | 14 ++ apps/authentication/common/log/base_logger.py | 139 ++++++++++++++++++ .../common/log/business_metric_logger.py | 25 ++++ .../common/log/function_logger.py | 50 +++++++ apps/authentication/common/log/log_utils.py | 25 ++++ .../common/log/module_logger.py | 46 ++++++ apps/authentication/common/log/user_logger.py | 14 ++ .../common/token/token_manager.py | 80 ++++++++++ apps/authentication/common/utils/date.py | 22 +++ apps/authentication/common/utils/region.py | 13 ++ apps/authentication/common/utils/string.py | 87 +++++++++++ apps/authentication/start_fastapi.sh | 40 +++++ .../webapi/bootstrap/application.py | 12 +- apps/authentication/webapi/main.py | 4 +- .../authentication/webapi/providers/common.py | 2 +- .../webapi/providers/database.py | 4 +- .../authentication/webapi/providers/logger.py | 2 +- .../authentication/webapi/providers/router.py | 2 +- .../webapi/routes/auth/send_email_code.py | 4 +- .../webapi/routes/auth/send_mobile_code.py | 4 +- .../signin/reset_password_through_email.py | 2 +- .../webapi/routes/signin/sign_out.py | 4 +- .../signin/signin_with_email_and_code.py | 4 +- .../signin/signin_with_email_and_password.py | 4 +- .../routes/signin/try_signin_with_email.py | 2 +- .../routes/signin/update_new_user_flid.py | 4 +- .../routes/signin/update_user_password.py | 4 +- .../webapi/routes/tokens/generate_tokens.py | 2 +- .../webapi/routes/tokens/refresh_token.py | 2 +- .../webapi/routes/tokens/verify_token.py | 2 +- sites/authentication/.env | 2 - sites/authentication/deploy/alpha/.env | 3 + sites/authentication/deploy/common/.env | 9 ++ sites/authentication/deploy/common/.host.env | 7 + .../deploy/common/docker-compose.yaml | 37 +++++ sites/authentication/deploy/deploy.sh | 91 ++++++++++++ sites/authentication/deploy/dev/.env | 3 + sites/authentication/deploy/local/.env | 3 + sites/authentication/deploy/prod/.env | 2 + 61 files changed, 905 insertions(+), 94 deletions(-) create mode 100644 apps/authentication/.env create mode 100644 apps/authentication/common/config/log_settings.py create mode 100644 apps/authentication/common/constants/__init__.py create mode 100644 apps/authentication/common/constants/contract.py create mode 100644 apps/authentication/common/constants/payment.py create mode 100644 apps/authentication/common/constants/region.py create mode 100644 apps/authentication/common/exception/__init__.py create mode 100644 apps/authentication/common/exception/exceptions.py create mode 100644 apps/authentication/common/log/__init__.py create mode 100644 apps/authentication/common/log/application_logger.py create mode 100644 apps/authentication/common/log/base_logger.py create mode 100644 apps/authentication/common/log/business_metric_logger.py create mode 100644 apps/authentication/common/log/function_logger.py create mode 100644 apps/authentication/common/log/log_utils.py create mode 100644 apps/authentication/common/log/module_logger.py create mode 100644 apps/authentication/common/log/user_logger.py create mode 100644 apps/authentication/common/token/token_manager.py create mode 100644 apps/authentication/common/utils/date.py create mode 100644 apps/authentication/common/utils/region.py create mode 100644 apps/authentication/common/utils/string.py create mode 100755 apps/authentication/start_fastapi.sh delete mode 100644 sites/authentication/.env create mode 100755 sites/authentication/deploy/alpha/.env create mode 100755 sites/authentication/deploy/common/.env create mode 100755 sites/authentication/deploy/common/.host.env create mode 100755 sites/authentication/deploy/common/docker-compose.yaml create mode 100755 sites/authentication/deploy/deploy.sh create mode 100755 sites/authentication/deploy/dev/.env create mode 100644 sites/authentication/deploy/local/.env create mode 100755 sites/authentication/deploy/prod/.env diff --git a/apps/authentication/.env b/apps/authentication/.env new file mode 100644 index 0000000..afa895c --- /dev/null +++ b/apps/authentication/.env @@ -0,0 +1,23 @@ +APP_NAME=authentication +export SERVICE_API_ACCESS_HOST=0.0.0.0 +export SERVICE_API_ACCESS_PORT=8004 +export CONTAINER_APP_ROOT=/app +export LOG_BASE_PATH=$CONTAINER_APP_ROOT/log/$APP_NAME +export BACKEND_LOG_FILE_NAME=$APP_NAME +export APPLICATION_ACTIVITY_LOG=$APP_NAME-activity +export MONGODB_NAME=freeleaps2 +export MONGODB_PORT=27017 +export JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0 +GIT_REPO_ROOT=/mnt/freeleaps/freeleaps-service-hub +CODEBASE_ROOT=/mnt/freeleaps/freeleaps-service-hub/apps/authentication +SITE_DEPLOY_FOLDER=/mnt/freeleaps/freeleaps-service-hub/sites/authentication/deploy +#!/bin/bash +export VENV_DIR=venv_t +export VENV_ACTIVATE=venv_t/bin/activate +export DOCKER_HOME=/var/lib/docker +export DOCKER_APP_HOME=$DOCKER_HOME/app +export DOCKER_BACKEND_HOME=$DOCKER_APP_HOME/$APP_NAME +export DOCKER_BACKEND_LOG_HOME=$DOCKER_BACKEND_HOME/log +export MONGODB_URI=mongodb://localhost:27017/ +export FREELEAPS_ENV=local + diff --git a/apps/authentication/Dockerfile b/apps/authentication/Dockerfile index f1cbf8c..3f6755d 100644 --- a/apps/authentication/Dockerfile +++ b/apps/authentication/Dockerfile @@ -1,19 +1,38 @@ -# Dockerfile for Python Service -FROM python:3.10-slim +FROM python:3.10-slim-buster -# Set the working directory inside the container -WORKDIR /app +# docker settings +ARG CONTAINER_APP_ROOT= +ENV APP_NAME= -# Copy the requirements.txt to the working directory and install dependencies -COPY requirements.txt ./ +ENV AZURE_STORAGE_DOCUMENT_API_KEY="" +ENV AZURE_STORAGE_DOCUMENT_API_ENDPOINT="" + +#site_settings +ENV SERVICE_API_ACCESS_HOST=0.0.0.0 +ENV SERVICE_API_ACCESS_PORT=8004 +ENV MONGODB_NAME= +ENV MONGODB_PORT= +ENV MONGODB_URI= + + + +#log_settings +ENV LOG_BASE_PATH= +ENV BACKEND_LOG_FILE_NAME= +ENV APPLICATION_ACTIVITY_LOG= + + +WORKDIR ${CONTAINER_APP_ROOT} +COPY requirements.txt . + +RUN pip install --upgrade pip RUN pip install --no-cache-dir -r requirements.txt -# Copy the application code to the working directory -COPY . /app +COPY . ${CONTAINER_APP_ROOT} -# Expose the port used by the FastAPI app -EXPOSE 8004 +RUN apt update +RUN apt install -y netcat +RUN ln -s /bin/bash /usr/bin/bash - -# Run the application using the start script -CMD ["uvicorn", "app.authentication.webapi.main:app", "--reload", "--port=8004", "--host=0.0.0.0"] +EXPOSE ${SERVICE_API_ACCESS_PORT} +CMD ["uvicorn", "webapi.main:app", "--reload", "--port=${SERVICE_API_ACCESS_PORT}", "--host=${SERVICE_API_ACCESS_HOST}"] diff --git a/apps/authentication/backend/application/signin_hub.py b/apps/authentication/backend/application/signin_hub.py index a0494a9..f5f6b79 100644 --- a/apps/authentication/backend/application/signin_hub.py +++ b/apps/authentication/backend/application/signin_hub.py @@ -1,7 +1,7 @@ from typing import Optional, Tuple -from infra.log.log_utils import log_entry_exit_async -from app.authentication.backend.business.signin_manager import SignInManager -from app.authentication.backend.models.user.constants import UserLoginAction +from common.log.log_utils import log_entry_exit_async +from backend.business.signin_manager import SignInManager +from backend.models.user.constants import UserLoginAction class SignInHub: diff --git a/apps/authentication/backend/business/signin_manager.py b/apps/authentication/backend/business/signin_manager.py index 5016f14..a83c290 100644 --- a/apps/authentication/backend/business/signin_manager.py +++ b/apps/authentication/backend/business/signin_manager.py @@ -1,25 +1,25 @@ import random from typing import Tuple, Optional -from app.authentication.backend.services.auth.user_auth_service import UserAuthService -from infra.i18n.region_handler import RegionHandler -from app.authentication.backend.models.user.constants import ( +from backend.services.auth.user_auth_service import UserAuthService +from common.utils.region import RegionHandler +from backend.models.user.constants import ( UserLoginAction, NewUserMethod, ) -from app.authentication.backend.models.user.constants import UserLoginAction -from app.authentication.backend.services.user.user_management_service import ( +from backend.models.user.constants import UserLoginAction +from backend.services.user.user_management_service import ( UserManagementService, ) -from app.authentication.backend.services.code_depot.code_depot_service import ( +from backend.services.code_depot.code_depot_service import ( CodeDepotService, ) -from infra.log.module_logger import ModuleLogger -from infra.utils.string import check_password_complexity -from infra.exception.exceptions import InvalidDataError -from app.authentication.backend.services.notification.notification_service import ( +from common.log.module_logger import ModuleLogger +from common.utils.string import check_password_complexity +from common.exception.exceptions import InvalidDataError +from backend.services.notification.notification_service import ( NotificationService, ) -from app.authentication.backend.models.user.constants import ( +from backend.models.user.constants import ( AuthType, ) diff --git a/apps/authentication/backend/infra/auth/user_auth_handler.py b/apps/authentication/backend/infra/auth/user_auth_handler.py index 57f2b83..9fc7012 100644 --- a/apps/authentication/backend/infra/auth/user_auth_handler.py +++ b/apps/authentication/backend/infra/auth/user_auth_handler.py @@ -2,21 +2,21 @@ import bcrypt from datetime import datetime, timedelta, timezone from typing import Optional -from infra.utils.string import generate_auth_code -from app.authentication.backend.infra.code_management.depot_handler import ( +from common.utils.string import generate_auth_code +from backend.infra.code_management.depot_handler import ( CodeDepotHandler, ) -from app.authentication.backend.models.user.constants import ( +from backend.models.user.constants import ( AuthType, ) -from app.authentication.backend.models.user.models import ( +from backend.models.user.models import ( AuthCodeDoc, UserEmailDoc, UserMobileDoc, UserPasswordDoc, ) -from app.authentication.backend.models.user_profile.models import BasicProfileDoc +from backend.models.user_profile.models import BasicProfileDoc class UserAuthHandler: diff --git a/apps/authentication/backend/infra/code_management/depot_handler.py b/apps/authentication/backend/infra/code_management/depot_handler.py index 4513669..59f335e 100644 --- a/apps/authentication/backend/infra/code_management/depot_handler.py +++ b/apps/authentication/backend/infra/code_management/depot_handler.py @@ -1,4 +1,4 @@ -from infra.log.module_logger import ModuleLogger +from common.log.module_logger import ModuleLogger from typing import Optional diff --git a/apps/authentication/backend/infra/user_profile/user_profile_handler.py b/apps/authentication/backend/infra/user_profile/user_profile_handler.py index e8227be..168f84c 100644 --- a/apps/authentication/backend/infra/user_profile/user_profile_handler.py +++ b/apps/authentication/backend/infra/user_profile/user_profile_handler.py @@ -1,15 +1,15 @@ -from infra.models.constants import UserRegion +from common.constants.region import UserRegion from datetime import datetime, timedelta, timezone -from app.authentication.backend.models.user.models import UserAccountDoc -from app.authentication.backend.models.user.constants import ( +from backend.models.user.models import UserAccountDoc +from backend.models.user.constants import ( UserAccountProperty, ) -from app.authentication.backend.models.permission.constants import ( +from backend.models.permission.constants import ( AdministrativeRole, Capability, ) from typing import Optional -from app.authentication.backend.models.user_profile.models import ( +from backend.models.user_profile.models import ( SelfIntro, Tags, Photo, @@ -22,7 +22,7 @@ from app.authentication.backend.models.user_profile.models import ( ExpectedSalary, ) -from app.authentication.backend.models.user.constants import UserRegionToCurrency +from backend.models.user.constants import UserRegionToCurrency class UserProfileHandler: diff --git a/apps/authentication/backend/models/gitea/models.py b/apps/authentication/backend/models/gitea/models.py index b55df68..20c3431 100644 --- a/apps/authentication/backend/models/gitea/models.py +++ b/apps/authentication/backend/models/gitea/models.py @@ -1,7 +1,7 @@ from typing import Dict, Optional from datetime import datetime, timezone from beanie import Document -from app.authentication.backend.models.gitea.constants import ( +from backend.models.gitea.constants import ( DepotStatus, UserAccountStatus, ) diff --git a/apps/authentication/backend/models/user/constants.py b/apps/authentication/backend/models/user/constants.py index e7c44ea..031c7d1 100644 --- a/apps/authentication/backend/models/user/constants.py +++ b/apps/authentication/backend/models/user/constants.py @@ -1,5 +1,5 @@ from enum import IntEnum -from infra.models.constants import UserRegion +from common.constants.region import UserRegion class NewUserMethod(IntEnum): diff --git a/apps/authentication/backend/models/user/models.py b/apps/authentication/backend/models/user/models.py index da129dc..826c2be 100644 --- a/apps/authentication/backend/models/user/models.py +++ b/apps/authentication/backend/models/user/models.py @@ -3,12 +3,12 @@ from typing import Optional from beanie import Document from .constants import UserAccountProperty -from app.authentication.backend.models.permission.constants import ( +from backend.models.permission.constants import ( AdministrativeRole, Capability, ) from datetime import datetime -from infra.models.constants import UserRegion +from common.constants.region import UserRegion from .constants import AuthType diff --git a/apps/authentication/backend/models/user_profile/models.py b/apps/authentication/backend/models/user_profile/models.py index 5fb19f3..c51cb51 100644 --- a/apps/authentication/backend/models/user_profile/models.py +++ b/apps/authentication/backend/models/user_profile/models.py @@ -5,7 +5,7 @@ from pydantic import BaseModel, EmailStr import re from decimal import Decimal -from infra.models.constants import UserRegion +from common.constants.region import UserRegion class Tags(BaseModel): diff --git a/apps/authentication/backend/services/auth/user_auth_service.py b/apps/authentication/backend/services/auth/user_auth_service.py index 4700264..bab173f 100644 --- a/apps/authentication/backend/services/auth/user_auth_service.py +++ b/apps/authentication/backend/services/auth/user_auth_service.py @@ -1,7 +1,7 @@ -from app.authentication.backend.infra.auth.user_auth_handler import ( +from backend.infra.auth.user_auth_handler import ( UserAuthHandler, ) -from app.authentication.backend.models.user.constants import ( +from backend.models.user.constants import ( AuthType, ) from typing import Optional diff --git a/apps/authentication/backend/services/user/user_management_service.py b/apps/authentication/backend/services/user/user_management_service.py index c0b58aa..bccdacd 100644 --- a/apps/authentication/backend/services/user/user_management_service.py +++ b/apps/authentication/backend/services/user/user_management_service.py @@ -1,23 +1,23 @@ -from infra.log.module_logger import ModuleLogger +from common.log.module_logger import ModuleLogger from typing import Optional -from app.authentication.backend.models.user.constants import ( +from backend.models.user.constants import ( NewUserMethod, UserAccountProperty, ) -from app.authentication.backend.models.user.models import UserAccountDoc -from app.authentication.backend.models.permission.constants import ( +from backend.models.user.models import UserAccountDoc +from backend.models.permission.constants import ( AdministrativeRole, Capability, ) -from app.authentication.backend.infra.auth.user_auth_handler import ( +from backend.infra.auth.user_auth_handler import ( UserAuthHandler, ) -from app.authentication.backend.infra.user_profile.user_profile_handler import ( +from backend.infra.user_profile.user_profile_handler import ( UserProfileHandler, ) -from infra.log.log_utils import log_entry_exit_async -from infra.models.constants import UserRegion +from common.log.log_utils import log_entry_exit_async +from common.constants.region import UserRegion class UserManagementService: diff --git a/apps/authentication/common/config/app_settings.py b/apps/authentication/common/config/app_settings.py index 353a887..2f7e92f 100644 --- a/apps/authentication/common/config/app_settings.py +++ b/apps/authentication/common/config/app_settings.py @@ -3,14 +3,17 @@ from pydantic_settings import BaseSettings class AppSettings(BaseSettings): NAME: str = "central_storage" + APP_NAME:str = NAME - GITEA_URL: str = "" - GITEA_TOKEN: str = "" - GITEA_DEPOT_ORGANIZATION: str = "" + JWT_SECRET_KEY: str = "" + ACCESS_TOKEN_EXPIRE_MINUTES:int = 3600 + REFRESH_TOKEN_EXPIRE_DAYS:int = 1 - CODE_DEPOT_DOMAIN_NAME: str = "" - CODE_DEPOT_SSH_PORT: str = "" - CODE_DEPOT_HTTP_PORT: str = "" + MONGODB_URI:str= "" + MONGODB_NAME:str= "" + + APPLICATION_ACTIVITY_LOG: str = APP_NAME + "-application-activity" + BUSINESS_METRIC_LOG: str = APP_NAME + "-business-metrics" class Config: env_file = ".myapp.env" diff --git a/apps/authentication/common/config/log_settings.py b/apps/authentication/common/config/log_settings.py new file mode 100644 index 0000000..856281d --- /dev/null +++ b/apps/authentication/common/config/log_settings.py @@ -0,0 +1,17 @@ + + +class LogSettings(): + LOG_LEVEL: str = "DEBUG" + LOG_PATH_BASE: str = ( + "./logs" + ) + LOG_PATH: str = LOG_PATH_BASE + '/' + "app" + '.log' + LOG_RETENTION: str = "14 days" + LOG_ROTATION: str = "00:00" # mid night + + class Config: + env_file = ".log.env" + env_file_encoding = "utf-8" + + +log_settings = LogSettings() diff --git a/apps/authentication/common/constants/__init__.py b/apps/authentication/common/constants/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authentication/common/constants/contract.py b/apps/authentication/common/constants/contract.py new file mode 100644 index 0000000..8d1ad3f --- /dev/null +++ b/apps/authentication/common/constants/contract.py @@ -0,0 +1,8 @@ +from enum import IntEnum + + +class UserContractRole(IntEnum): + VISITER = 0x1 + REQUESTER = 0x2 + PROVIDER = 0x4 + diff --git a/apps/authentication/common/constants/payment.py b/apps/authentication/common/constants/payment.py new file mode 100644 index 0000000..65c76a7 --- /dev/null +++ b/apps/authentication/common/constants/payment.py @@ -0,0 +1,5 @@ +from enum import IntEnum + +class PaymentPlanType(IntEnum): + STAGED = 0 + diff --git a/apps/authentication/common/constants/region.py b/apps/authentication/common/constants/region.py new file mode 100644 index 0000000..ac87f72 --- /dev/null +++ b/apps/authentication/common/constants/region.py @@ -0,0 +1,5 @@ +from enum import IntEnum + +class UserRegion(IntEnum): + OTHER = 0 + ZH_CN = 1 diff --git a/apps/authentication/common/exception/__init__.py b/apps/authentication/common/exception/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authentication/common/exception/exceptions.py b/apps/authentication/common/exception/exceptions.py new file mode 100644 index 0000000..2c489c3 --- /dev/null +++ b/apps/authentication/common/exception/exceptions.py @@ -0,0 +1,23 @@ +class DoesNotExistError(Exception): + def __init__(self, message: str = "Does Not Exist"): + self.message = message + + +class AuthenticationError(Exception): + def __init__(self, message: str = "Unauthorized"): + self.message = message + + +class AuthorizationError(Exception): + def __init__(self, message: str = "Forbidden"): + self.message = message + + +class InvalidOperationError(Exception): + def __init__(self, message: str = "Invalid Operation"): + self.message = message + + +class InvalidDataError(Exception): + def __init__(self, message: str = "Invalid Data"): + self.message = message diff --git a/apps/authentication/common/log/__init__.py b/apps/authentication/common/log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authentication/common/log/application_logger.py b/apps/authentication/common/log/application_logger.py new file mode 100644 index 0000000..c1222bb --- /dev/null +++ b/apps/authentication/common/log/application_logger.py @@ -0,0 +1,14 @@ +from .base_logger import LoggerBase +from common.config.app_settings import app_settings +import json + + +class ApplicationLogger(LoggerBase): + def __init__(self, application_activities: dict[str, any] = {}) -> None: + extra_fileds = {} + if application_activities: + extra_fileds.update(application_activities) + super().__init__( + logger_name=app_settings.APPLICATION_ACTIVITY_LOG, + extra_fileds=extra_fileds, + ) diff --git a/apps/authentication/common/log/base_logger.py b/apps/authentication/common/log/base_logger.py new file mode 100644 index 0000000..122dfdb --- /dev/null +++ b/apps/authentication/common/log/base_logger.py @@ -0,0 +1,139 @@ +from loguru import logger as guru_logger +from common.config.log_settings import log_settings +from typing import List +import socket +import json +import logging +import threading + + +class LoggerBase: + binded_loggers = {} + logger_lock = threading.Lock() + + def __init__( + self, logger_name: str, extra_fileds: dict[str, any] + ) -> None: + self.__logger_name = logger_name + self.extra_fileds = extra_fileds + with LoggerBase.logger_lock: + if self.__logger_name in LoggerBase.binded_loggers: + self.logger = LoggerBase.binded_loggers[self.__logger_name] + return + + log_filename = ( + log_settings.LOG_PATH_BASE + "/" + self.__logger_name + ".log" + ) + log_retention = log_settings.LOG_RETENTION + log_rotation = log_settings.LOG_ROTATION + log_level = "INFO" + log_message_format = "{message}" + + guru_logger.add( + sink=log_filename, + level=log_level, + retention=log_retention, + rotation=log_rotation, + format=log_message_format, + serialize=True, + filter=lambda record: "extra" in record + and "topic" in record["extra"] + and record["extra"]["topic"] == self.__logger_name, + ) + host_name = socket.gethostname() + host_ip = socket.gethostbyname(host_name) + self.logger = guru_logger.bind( + topic=self.__logger_name, + host_ip=host_ip, + host_name=host_name, + ) + with LoggerBase.logger_lock: + LoggerBase.binded_loggers[self.__logger_name] = self.logger + + async def log_event( + self, + sender_id: str, + receiver_id: str, + subject: str, + event: str, + properties: dict[str, any], + text: str = "" + ) -> None: + local_logger = self.logger.bind( + sender_id=sender_id, + receiver_id=receiver_id, + subject=subject, + event=event, + properties=properties + ) + local_logger.info(text) + + async def log_exception( + self, + sender_id: str, + receiver_id: str, + subject: str, + exception: Exception, + text: str = "", + properties: dict[str, any] = None, + ) -> None: + local_logger = self.logger.bind( + sender_id=sender_id, + receiver_id=receiver_id, + subject=subject, + event="exception", + properties=properties, + exception=exception + ) + local_logger.exception(text) + + async def log_info( + self, + sender_id: str, + receiver_id: str, + subject: str, + text: str = "", + properties: dict[str, any] = None, + ) -> None: + local_logger = self.logger.bind( + sender_id=sender_id, + receiver_id=receiver_id, + subject=subject, + event="information", + properties=properties, + ) + local_logger.info(text) + + async def log_warning( + self, + sender_id: str, + receiver_id: str, + subject: str, + text: str = "", + properties: dict[str, any] = None, + ) -> None: + local_logger = self.logger.bind( + sender_id=sender_id, + receiver_id=receiver_id, + subject=subject, + event="warning", + properties=properties, + ) + local_logger.warning(text) + + async def log_error( + self, + sender_id: str, + receiver_id: str, + subject: str, + text: str = "", + properties: dict[str, any] = None, + ) -> None: + local_logger = self.logger.bind( + sender_id=sender_id, + receiver_id=receiver_id, + subject=subject, + event="error", + properties=properties, + ) + local_logger.error(text) diff --git a/apps/authentication/common/log/business_metric_logger.py b/apps/authentication/common/log/business_metric_logger.py new file mode 100644 index 0000000..95383ab --- /dev/null +++ b/apps/authentication/common/log/business_metric_logger.py @@ -0,0 +1,25 @@ +from .base_logger import LoggerBase +from common.config.app_settings import app_settings +import json + + +class BusinessMetricLogger(LoggerBase): + def __init__(self, business_metrics: dict[str, any] = {}) -> None: + extra_fileds = {} + if business_metrics: + extra_fileds.update(business_metrics) + super().__init__( + logger_name=app_settings.BUSINESS_METRIC_LOG, + extra_fileds=extra_fileds, + ) + + + async def log_metrics(self, business_metrics: dict[str, any] = {}) -> None: + return await super().log_event( + sender_id="business_metric_manager", + receiver_id="business_metric_logger", + subject="metrics", + event="logging", + properties=business_metrics, + text="business metric logged" + ) diff --git a/apps/authentication/common/log/function_logger.py b/apps/authentication/common/log/function_logger.py new file mode 100644 index 0000000..4388a5e --- /dev/null +++ b/apps/authentication/common/log/function_logger.py @@ -0,0 +1,50 @@ +from .application_logger import ApplicationLogger + + +class FunctionLogger(ApplicationLogger): + def __init__(self, sender_id: str, receiver_id:str) -> None: + super().__init__() + self.event_sender_id = sender_id + self.event_receiver_id = receiver_id + self.event_subject = "function" + + async def log_enter(self, function: str, file: str): + return await super().log_event( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + event="enter", + properties={ + "function": function, + "file": file, + }, + text="Enter:{} of {}".format(function, file) + ) + + async def log_exit(self, function: str, file: str, excution_time_in_ns: int): + return await super().log_event( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + event="exit", + properties={ + "function": function, + "file": file, + "excution_time_in_ns": excution_time_in_ns + }, + text="Exit:{} of {}".format(function, file) + ) + + async def log_exception(self, exception: Exception, function: str, file: str, excution_time_in_ns: int) -> None: + return await super().log_exception( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + exception=exception, + text="Exception:{} of {}".format(function, file), + properties={ + "function": function, + "file": file, + "excution_time_in_ns": excution_time_in_ns + }, + ) diff --git a/apps/authentication/common/log/log_utils.py b/apps/authentication/common/log/log_utils.py new file mode 100644 index 0000000..579dee8 --- /dev/null +++ b/apps/authentication/common/log/log_utils.py @@ -0,0 +1,25 @@ +import os +from .function_logger import FunctionLogger +import time +import functools + + +def log_entry_exit_async(func): + @functools.wraps(func) + async def wrapper(*args, **kwargs): + file_path = os.path.relpath(func.__code__.co_filename) + function_logger = FunctionLogger(sender_id="log_entry_exit_async", receiver_id="function_logger") + start_time = time.process_time_ns() + try: + await function_logger.log_enter(func.__name__, file_path) + result = await func(*args, **kwargs) + await function_logger.log_exit(func.__name__, file_path, time.process_time_ns() - start_time) + return result + except Exception as exception: + await function_logger.log_exception( + exception=exception, + function=func.__name__, + file=file_path, + excution_time_in_ns=time.process_time_ns() - start_time) + raise + return wrapper diff --git a/apps/authentication/common/log/module_logger.py b/apps/authentication/common/log/module_logger.py new file mode 100644 index 0000000..3426b0b --- /dev/null +++ b/apps/authentication/common/log/module_logger.py @@ -0,0 +1,46 @@ +from .application_logger import ApplicationLogger + + +class ModuleLogger(ApplicationLogger): + def __init__(self, sender_id: str) -> None: + super().__init__() + self.event_sender_id = sender_id + self.event_receiver_id = "ModuleLogger" + self.event_subject = "module" + + async def log_exception(self, exception: Exception, text: str = "Exception", properties: dict[str, any] = None) -> None: + return await super().log_exception( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + exception=exception, + text=text, + properties=properties, + ) + + async def log_info(self, info: str, properties: dict[str, any] = None) -> None: + return await super().log_info( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + text=info, + properties=properties, + ) + + async def log_warning(self, warning: str, properties: dict[str, any] = None) -> None: + return await super().log_warning( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + text=warning, + properties=properties, + ) + + async def log_error(self, error: str, properties: dict[str, any] = None) -> None: + return await super().log_error( + sender_id=self.event_sender_id, + receiver_id=self.event_receiver_id, + subject=self.event_subject, + text=error, + properties=properties, + ) diff --git a/apps/authentication/common/log/user_logger.py b/apps/authentication/common/log/user_logger.py new file mode 100644 index 0000000..d931975 --- /dev/null +++ b/apps/authentication/common/log/user_logger.py @@ -0,0 +1,14 @@ +from .base_logger import LoggerBase +from common.config.app_settings import app_settings + +import json + + +class UserLogger(LoggerBase): + def __init__(self, user_activities: dict[str, any] = {}) -> None: + extra_fileds = {} + if user_activities: + extra_fileds.update(user_activities) + super().__init__( + logger_name=app_settings.USER_ACTIVITY_LOG, extra_fileds=extra_fileds + ) diff --git a/apps/authentication/common/token/token_manager.py b/apps/authentication/common/token/token_manager.py new file mode 100644 index 0000000..3034e1f --- /dev/null +++ b/apps/authentication/common/token/token_manager.py @@ -0,0 +1,80 @@ +from datetime import datetime, timedelta, timezone +from typing import Dict +from jose import jwt, JWTError +from common.config.app_settings import app_settings +from fastapi import Depends, HTTPException +from fastapi.security import OAuth2PasswordBearer +from starlette.status import HTTP_401_UNAUTHORIZED + + +class TokenManager: + def __init__(self): + self.secret_key = app_settings.JWT_SECRET_KEY + self.algorithm = "HS256" + self.access_token_expire_minutes = app_settings.ACCESS_TOKEN_EXPIRE_MINUTES + self.refresh_token_expire_days = app_settings.REFRESH_TOKEN_EXPIRE_DAYS + + def create_access_token(self, subject: Dict[str, str]) -> str: + """ + Generates an access token with a short expiration time. + """ + expire = datetime.now(timezone.utc) + timedelta( + minutes=self.access_token_expire_minutes + ) + to_encode = subject.copy() + to_encode.update({"exp": expire}) + return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm) + + def create_refresh_token(self, subject: Dict[str, str]) -> str: + """ + Generates a refresh token with a longer expiration time. + """ + expire = datetime.now(timezone.utc) + timedelta( + days=self.refresh_token_expire_days + ) + to_encode = subject.copy() + to_encode.update({"exp": expire}) + return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm) + + def decode_token(self, token: str) -> Dict: + """ + Decodes a JWT token and returns the payload. + """ + try: + payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm]) + return payload + except JWTError: + raise ValueError("Invalid token") + + def verify_refresh_token(self, token: str) -> bool: + """ + Verifies a refresh token to ensure it is valid and not expired. + """ + try: + payload = self.decode_token(token) + return True + except ValueError: + return False + + def refresh_access_token(self, refresh_token: str, subject: Dict[str, str]) -> str: + """ + Verifies the refresh token and creates a new access token. + """ + if self.verify_refresh_token(refresh_token): + return self.create_access_token(subject) + else: + raise ValueError("Invalid refresh token") + + async def get_current_user( + self, token: str = Depends(OAuth2PasswordBearer(tokenUrl="token")) + ) -> Dict: + """ + Extract and validate user information from the JWT token. + """ + try: + payload = self.decode_token(token) # Decode JWT token + return payload + except ValueError: + raise HTTPException( + status_code=HTTP_401_UNAUTHORIZED, detail="Invalid or expired token" + ) diff --git a/apps/authentication/common/utils/date.py b/apps/authentication/common/utils/date.py new file mode 100644 index 0000000..79451d1 --- /dev/null +++ b/apps/authentication/common/utils/date.py @@ -0,0 +1,22 @@ +import datetime +from datetime import timedelta, timezone + + +def get_sunday(date): + return date - datetime.timedelta(days=date.weekday()) + timedelta(days=6) + + +def get_last_sunday_dates(number, include_current_week=True): + now_utc = datetime.datetime.now(timezone.utc) + today = datetime.datetime(now_utc.year, now_utc.month, now_utc.day) + if include_current_week: + days_to_last_sunday = (6 - today.weekday()) % 7 + last_sunday = today + datetime.timedelta(days=days_to_last_sunday) + else: + days_to_last_sunday = (today.weekday() - 6) % 7 + last_sunday = today - datetime.timedelta(days=days_to_last_sunday) + last_n_sundays = [] + for i in range(number): + sunday = last_sunday - datetime.timedelta(days=i * 7) + last_n_sundays.append(sunday.date()) + return last_n_sundays diff --git a/apps/authentication/common/utils/region.py b/apps/authentication/common/utils/region.py new file mode 100644 index 0000000..4212e02 --- /dev/null +++ b/apps/authentication/common/utils/region.py @@ -0,0 +1,13 @@ +from common.constants.region import UserRegion + + +class RegionHandler: + def __init__(self): + self._zh_cn_patterns = [".cn", "cn.", "host"] + + def detect_from_host(self, host: str) -> UserRegion: + # Now we set user preferred region based on host + for parttern in self._zh_cn_patterns: + if parttern in host.lower(): + return UserRegion.ZH_CN + return UserRegion.OTHER \ No newline at end of file diff --git a/apps/authentication/common/utils/string.py b/apps/authentication/common/utils/string.py new file mode 100644 index 0000000..359c6b4 --- /dev/null +++ b/apps/authentication/common/utils/string.py @@ -0,0 +1,87 @@ +import random +import re +import jieba +from typing import List + +SKILL_TAGS = [ + "C++", + "Java", + "Python", + "TypeScript", + "iOS", + "Android", + "Web", + "Javascript", + "Vue", + "Go", +] + +# dynamically update skill tags? maybe based on the most commonly extracted keywords to help the system adapt to change +def updateSkillTags(string): + SKILL_TAGS.append(string) + + +def generate_auth_code(): + filtered = "0123456789" + code = "".join(random.choice(filtered) for i in range(6)) + return code + + +# TODO: Need to optimize +def generate_self_intro_summary(content_html: str) -> str: + element_html = re.compile("<.*?>") + content_text = re.sub(element_html, "", content_html).strip() + return content_text[:50] + + +# TODO: Need to optimize +def extract_skill_tags(content_html: str) -> List[str]: + element_html = re.compile("<.*?>") + content_text = re.sub(element_html, "", content_html).strip() + words = set([word.lower() for word in jieba.cut(content_text) if word.strip()]) + + results = [] + for tag in SKILL_TAGS: + if tag.lower() in words: + results.append(tag) + return results + + +def extract_title(content_html: str) -> List[str]: + element_html = re.compile("<.*?>") + content_text = re.sub(element_html, "\n", content_html).strip() + + cut_point_indexes = [] + for cut_point in [".", ",", ";", "\r", "\n"]: + result = content_text.find(cut_point) + if result > 0: + cut_point_indexes.append(result) + + title = ( + content_text[: min(cut_point_indexes)] + if len(cut_point_indexes) > 0 + else content_text + ) + return title + + +def check_password_complexity(password): + lowercase_pattern = r"[a-z]" + uppercase_pattern = r"[A-Z]" + digit_pattern = r"\d" + special_pattern = r'[!@#$%^&*(),.?":{}|<>]' + + password_lowercase_one = bool(re.search(lowercase_pattern, password)) + password_uppercase_one = bool(re.search(uppercase_pattern, password)) + password_digit_one = bool(re.search(digit_pattern, password)) + password_special_one = bool(re.search(special_pattern, password)) + + if ( + password_lowercase_one + and password_uppercase_one + and password_digit_one + and password_special_one + ): + return True + else: + return False diff --git a/apps/authentication/start_fastapi.sh b/apps/authentication/start_fastapi.sh new file mode 100755 index 0000000..99cf493 --- /dev/null +++ b/apps/authentication/start_fastapi.sh @@ -0,0 +1,40 @@ +#! /bin/bash +rp=$(dirname "$(realpath '$1'))") +pushd $rp + +APP_NAME=authentication +APP_PARENT_FOLDER=apps + +GIT_REPO_ROOT=$(git rev-parse --show-toplevel) +CODEBASE_ROOT=$GIT_REPO_ROOT/$APP_PARENT_FOLDER/$APP_NAME +SITE_DEPLOY_FOLDER=$GIT_REPO_ROOT/sites/$APP_NAME/deploy + + +echo APP_NAME=$APP_NAME > .env +cat $SITE_DEPLOY_FOLDER/common/.env >> .env +echo GIT_REPO_ROOT=$(git rev-parse --show-toplevel) >> .env +echo CODEBASE_ROOT=$GIT_REPO_ROOT/$APP_PARENT_FOLDER/$APP_NAME >> .env +echo SITE_DEPLOY_FOLDER=$GIT_REPO_ROOT/sites/$APP_NAME/deploy >> .env +cat $SITE_DEPLOY_FOLDER/common/.host.env >> .env +cat $SITE_DEPLOY_FOLDER/local/.env >> .env + +. .env + +if [ -d "$VENV_DIR" ] +then + echo "Folder $VENV_DIR exists. Proceed to next steps" +else + echo "Folder $VENV_DIR dosen't exist. create it" + sudo apt install python3-pip + python3 -m pip install virtualenv + python3 -m virtualenv $VENV_DIR +fi + +source $VENV_DIR/bin/activate +pip install --upgrade pip +pip install -r requirements.txt + +uvicorn webapi.main:app --reload --host 0.0.0.0 --port $SERVICE_API_ACCESS_PORT + + +popd diff --git a/apps/authentication/webapi/bootstrap/application.py b/apps/authentication/webapi/bootstrap/application.py index d670c50..fd9f1b1 100644 --- a/apps/authentication/webapi/bootstrap/application.py +++ b/apps/authentication/webapi/bootstrap/application.py @@ -2,13 +2,13 @@ import logging from fastapi import FastAPI from fastapi.openapi.utils import get_openapi -from app.authentication.webapi.providers import common -from app.authentication.webapi.providers import logger -from app.authentication.webapi.providers import router -from app.authentication.webapi.providers import database +from webapi.providers import common +from webapi.providers import logger +from webapi.providers import router +from webapi.providers import database -# from app.authentication.webapi.providers import scheduler -from app.authentication.webapi.providers import exception_handler +# from webapi.providers import scheduler +from webapi.providers import exception_handler from .freeleaps_app import FreeleapsApp diff --git a/apps/authentication/webapi/main.py b/apps/authentication/webapi/main.py index 53d716a..c35ffa3 100755 --- a/apps/authentication/webapi/main.py +++ b/apps/authentication/webapi/main.py @@ -1,5 +1,5 @@ -from app.authentication.webapi.bootstrap.application import create_app -from app.authentication.webapi.config.site_settings import site_settings +from webapi.bootstrap.application import create_app +from webapi.config.site_settings import site_settings from fastapi.responses import RedirectResponse from fastapi.middleware.cors import CORSMiddleware import uvicorn diff --git a/apps/authentication/webapi/providers/common.py b/apps/authentication/webapi/providers/common.py index f1eed99..1dd849f 100644 --- a/apps/authentication/webapi/providers/common.py +++ b/apps/authentication/webapi/providers/common.py @@ -1,5 +1,5 @@ from fastapi.middleware.cors import CORSMiddleware -from app.authentication.webapi.config.site_settings import site_settings +from webapi.config.site_settings import site_settings def register(app): diff --git a/apps/authentication/webapi/providers/database.py b/apps/authentication/webapi/providers/database.py index 211ea30..70d5246 100644 --- a/apps/authentication/webapi/providers/database.py +++ b/apps/authentication/webapi/providers/database.py @@ -1,7 +1,7 @@ -from infra.config.app_settings import app_settings +from common.config.app_settings import app_settings from beanie import init_beanie from motor.motor_asyncio import AsyncIOMotorClient -from app.authentication.backend.models import backend_models +from backend.models import backend_models def register(app): diff --git a/apps/authentication/webapi/providers/logger.py b/apps/authentication/webapi/providers/logger.py index 0b37c27..9bc8adb 100644 --- a/apps/authentication/webapi/providers/logger.py +++ b/apps/authentication/webapi/providers/logger.py @@ -1,7 +1,7 @@ import logging import sys from loguru import logger -from infra.config.log_settings import log_settings +from common.config.log_settings import log_settings def register(app=None): diff --git a/apps/authentication/webapi/providers/router.py b/apps/authentication/webapi/providers/router.py index 2dabd7e..3ad11ae 100644 --- a/apps/authentication/webapi/providers/router.py +++ b/apps/authentication/webapi/providers/router.py @@ -1,4 +1,4 @@ -from app.authentication.webapi.routes import api_router +from webapi.routes import api_router from starlette import routing diff --git a/apps/authentication/webapi/routes/auth/send_email_code.py b/apps/authentication/webapi/routes/auth/send_email_code.py index 1285339..050b791 100644 --- a/apps/authentication/webapi/routes/auth/send_email_code.py +++ b/apps/authentication/webapi/routes/auth/send_email_code.py @@ -1,6 +1,6 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse from fastapi import APIRouter, Depends, HTTPException diff --git a/apps/authentication/webapi/routes/auth/send_mobile_code.py b/apps/authentication/webapi/routes/auth/send_mobile_code.py index cd421a3..29bdd5b 100644 --- a/apps/authentication/webapi/routes/auth/send_mobile_code.py +++ b/apps/authentication/webapi/routes/auth/send_mobile_code.py @@ -1,6 +1,6 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse from fastapi import APIRouter, Depends, HTTPException diff --git a/apps/authentication/webapi/routes/signin/reset_password_through_email.py b/apps/authentication/webapi/routes/signin/reset_password_through_email.py index 15a15ba..e4f1519 100644 --- a/apps/authentication/webapi/routes/signin/reset_password_through_email.py +++ b/apps/authentication/webapi/routes/signin/reset_password_through_email.py @@ -1,4 +1,4 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel from fastapi import APIRouter from fastapi.encoders import jsonable_encoder diff --git a/apps/authentication/webapi/routes/signin/sign_out.py b/apps/authentication/webapi/routes/signin/sign_out.py index 2a4a32d..18395b2 100644 --- a/apps/authentication/webapi/routes/signin/sign_out.py +++ b/apps/authentication/webapi/routes/signin/sign_out.py @@ -1,7 +1,7 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel from fastapi import APIRouter -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager from fastapi import APIRouter, Depends from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse diff --git a/apps/authentication/webapi/routes/signin/signin_with_email_and_code.py b/apps/authentication/webapi/routes/signin/signin_with_email_and_code.py index a8400b0..b2b2865 100644 --- a/apps/authentication/webapi/routes/signin/signin_with_email_and_code.py +++ b/apps/authentication/webapi/routes/signin/signin_with_email_and_code.py @@ -7,8 +7,8 @@ from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse from pydantic import BaseModel -from app.authentication.backend.application.signin_hub import SignInHub -from infra.token.token_manager import TokenManager +from backend.application.signin_hub import SignInHub +from common.token.token_manager import TokenManager router = APIRouter() diff --git a/apps/authentication/webapi/routes/signin/signin_with_email_and_password.py b/apps/authentication/webapi/routes/signin/signin_with_email_and_password.py index 41c48e1..ff2a598 100644 --- a/apps/authentication/webapi/routes/signin/signin_with_email_and_password.py +++ b/apps/authentication/webapi/routes/signin/signin_with_email_and_password.py @@ -9,8 +9,8 @@ from fastapi.responses import JSONResponse from fastapi import Depends, HTTPException from starlette.status import HTTP_401_UNAUTHORIZED -from app.authentication.backend.application.signin_hub import SignInHub -from infra.token.token_manager import TokenManager +from backend.application.signin_hub import SignInHub +from common.token.token_manager import TokenManager router = APIRouter() token_manager = TokenManager() diff --git a/apps/authentication/webapi/routes/signin/try_signin_with_email.py b/apps/authentication/webapi/routes/signin/try_signin_with_email.py index 5db70c4..ab750f7 100644 --- a/apps/authentication/webapi/routes/signin/try_signin_with_email.py +++ b/apps/authentication/webapi/routes/signin/try_signin_with_email.py @@ -1,4 +1,4 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel from fastapi import APIRouter from fastapi.encoders import jsonable_encoder diff --git a/apps/authentication/webapi/routes/signin/update_new_user_flid.py b/apps/authentication/webapi/routes/signin/update_new_user_flid.py index 4c56c61..8ccaa3e 100644 --- a/apps/authentication/webapi/routes/signin/update_new_user_flid.py +++ b/apps/authentication/webapi/routes/signin/update_new_user_flid.py @@ -1,7 +1,7 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel from fastapi import APIRouter -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager from fastapi import APIRouter, Depends from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse diff --git a/apps/authentication/webapi/routes/signin/update_user_password.py b/apps/authentication/webapi/routes/signin/update_user_password.py index c7587aa..9c2c3cf 100644 --- a/apps/authentication/webapi/routes/signin/update_user_password.py +++ b/apps/authentication/webapi/routes/signin/update_user_password.py @@ -1,7 +1,7 @@ -from app.authentication.backend.application.signin_hub import SignInHub +from backend.application.signin_hub import SignInHub from pydantic import BaseModel from fastapi import APIRouter -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager from fastapi import APIRouter, Depends from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse diff --git a/apps/authentication/webapi/routes/tokens/generate_tokens.py b/apps/authentication/webapi/routes/tokens/generate_tokens.py index ceec2fd..29d6389 100644 --- a/apps/authentication/webapi/routes/tokens/generate_tokens.py +++ b/apps/authentication/webapi/routes/tokens/generate_tokens.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from pydantic import BaseModel from datetime import datetime, timedelta, timezone -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager router = APIRouter() token_manager = TokenManager() # Initialize TokenManager diff --git a/apps/authentication/webapi/routes/tokens/refresh_token.py b/apps/authentication/webapi/routes/tokens/refresh_token.py index 137265d..e1eeb84 100644 --- a/apps/authentication/webapi/routes/tokens/refresh_token.py +++ b/apps/authentication/webapi/routes/tokens/refresh_token.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, HTTPException from pydantic import BaseModel -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager router = APIRouter() token_manager = TokenManager() # Initialize TokenManager diff --git a/apps/authentication/webapi/routes/tokens/verify_token.py b/apps/authentication/webapi/routes/tokens/verify_token.py index 82b7f19..bc18cca 100644 --- a/apps/authentication/webapi/routes/tokens/verify_token.py +++ b/apps/authentication/webapi/routes/tokens/verify_token.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, HTTPException from pydantic import BaseModel -from infra.token.token_manager import TokenManager +from common.token.token_manager import TokenManager router = APIRouter() token_manager = TokenManager() # Initialize TokenManager diff --git a/sites/authentication/.env b/sites/authentication/.env deleted file mode 100644 index 4df0fdd..0000000 --- a/sites/authentication/.env +++ /dev/null @@ -1,2 +0,0 @@ -export AZURE_STORAGE_DOCUMENT_API_KEY=xbiFtFeQ6v5dozgVM99fZ9huUomL7QcLu6s0y8zYHtIXZ8XdneKDMcg4liQr/9oNlVoRFcZhWjLY+ASt9cjICQ== -export JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0 \ No newline at end of file diff --git a/sites/authentication/deploy/alpha/.env b/sites/authentication/deploy/alpha/.env new file mode 100755 index 0000000..68beb14 --- /dev/null +++ b/sites/authentication/deploy/alpha/.env @@ -0,0 +1,3 @@ +export MONGODB_URI='mongodb+srv://jetli:8IHKx6dZK8BfugGp@freeleaps2.hanbj.mongodb.net/' +export FREELEAPS_ENV=alpha + diff --git a/sites/authentication/deploy/common/.env b/sites/authentication/deploy/common/.env new file mode 100755 index 0000000..3114bc8 --- /dev/null +++ b/sites/authentication/deploy/common/.env @@ -0,0 +1,9 @@ +export SERVICE_API_ACCESS_HOST=0.0.0.0 +export SERVICE_API_ACCESS_PORT=8004 +export CONTAINER_APP_ROOT=/app +export LOG_BASE_PATH=$CONTAINER_APP_ROOT/log/$APP_NAME +export BACKEND_LOG_FILE_NAME=$APP_NAME +export APPLICATION_ACTIVITY_LOG=$APP_NAME-activity +export MONGODB_NAME=freeleaps2 +export MONGODB_PORT=27017 +export JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0 diff --git a/sites/authentication/deploy/common/.host.env b/sites/authentication/deploy/common/.host.env new file mode 100755 index 0000000..73c2468 --- /dev/null +++ b/sites/authentication/deploy/common/.host.env @@ -0,0 +1,7 @@ +#!/bin/bash +export VENV_DIR=venv_t +export VENV_ACTIVATE=venv_t/bin/activate +export DOCKER_HOME=/var/lib/docker +export DOCKER_APP_HOME=$DOCKER_HOME/app +export DOCKER_BACKEND_HOME=$DOCKER_APP_HOME/$APP_NAME +export DOCKER_BACKEND_LOG_HOME=$DOCKER_BACKEND_HOME/log diff --git a/sites/authentication/deploy/common/docker-compose.yaml b/sites/authentication/deploy/common/docker-compose.yaml new file mode 100755 index 0000000..5a0e61d --- /dev/null +++ b/sites/authentication/deploy/common/docker-compose.yaml @@ -0,0 +1,37 @@ +services: + central_storage: + container_name: $APP_NAME + build: + context: ${CODEBASE_ROOT} + args: + CONTAINER_APP_ROOT: ${CONTAINER_APP_ROOT} + profiles: [prod,alpha,dev] + restart: always + environment: + - APP_NAME=${APP_NAME} + - MONGODB_NAME=${MONGODB_NAME} + - MONGODB_PORT=${MONGODB_PORT} + - MONGODB_URI=${MONGODB_URI} + - SERVICE_API_ACCESS_HOST=${SERVICE_API_ACCESS_HOST} + - SERVICE_API_ACCESS_PORT=${SERVICE_API_ACCESS_PORT} + - AZURE_STORAGE_DOCUMENT_API_KEY=${AZURE_STORAGE_DOCUMENT_API_KEY} + - AZURE_STORAGE_DOCUMENT_API_ENDPOINT=${AZURE_STORAGE_DOCUMENT_API_ENDPOINT} + - LOG_BASE_PATH=${LOG_BASE_PATH} + - BACKEND_LOG_FILE_NAME=${BACKEND_LOG_FILE_NAME} + - APPLICATION_ACTIVITY_LOG=${APPLICATION_ACTIVITY_LOG} + ports: + - ${SERVICE_API_ACCESS_PORT}:${SERVICE_API_ACCESS_PORT} + command: + - /bin/sh + - -c + - | + uvicorn webapi.main:app --reload --port=${SERVICE_API_ACCESS_PORT} --host=${SERVICE_API_ACCESS_HOST} + networks: + - devbox_freeleaps2-network + volumes: + - type: bind + source: $DOCKER_BACKEND_LOG_HOME + target: $LOG_BASE_PATH +networks: + devbox_freeleaps2-network: + external: true \ No newline at end of file diff --git a/sites/authentication/deploy/deploy.sh b/sites/authentication/deploy/deploy.sh new file mode 100755 index 0000000..470f1ef --- /dev/null +++ b/sites/authentication/deploy/deploy.sh @@ -0,0 +1,91 @@ +#!/bin/bash +DW_BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +APP_NAME=authentication +APP_PARENT_FOLDER=apps +PROJECT_NAME=authentication + +while [ $# -gt 0 ]; do + case "$1" in + --target* | -u*) + if [[ "$1" != *=* ]]; then shift; fi # Value is next arg if no `=` + TARGET_ENV="${1#*=}" + ;; + --help | -h) + printf "$BASE_NAME --target=\n" # Flag argument + return 0 + ;; + *) + printf >&2 "Error: Invalid argument\n" + return 1 + ;; + esac + shift +done + +if [[ "${TARGET_ENV}" != "prod" && "${TARGET_ENV}" != "alpha" && "${TARGET_ENV}" != "dev" ]]; then + printf "$BASE_NAME --target=\n" # Flag argument + return 0 +fi +if git rev-parse --git-dir > /dev/null 2>&1; then + # git repo! + GIT_REPO_ROOT=$(git rev-parse --show-toplevel) + CODEBASE_ROOT=$GIT_REPO_ROOT/$APP_PARENT_FOLDER/$APP_NAME + WORKING_DIR=$GIT_REPO_ROOT/sites/$APP_NAME/deploy + +else + # NOT a git repo! + printf "Please run this command under a git repo" + return 0 +fi + +. $DW_BASE_DIR/common/.host.env + + +DW_PUSHD_COUNTER=0 + +ENV_FOLDER=$WORKING_DIR/$TARGET_ENV +COMMON_ENV_FOLDER=$WORKING_DIR/common + + +pushd $WORKING_DIR +DW_PUSHD_COUNTER=$((DW_PUSHD_COUNTER + 1)) + +echo export APP_NAME=$APP_NAME > $WORKING_DIR/.env +echo export GIT_REPO_ROOT=$GIT_REPO_ROOT >> $WORKING_DIR/.env +echo export APP_PARENT_FOLDER=$APP_PARENT_FOLDER >> $WORKING_DIR/.env + +cat $COMMON_ENV_FOLDER/.env >> $WORKING_DIR/.env +echo export CODEBASE_ROOT=$CODEBASE_ROOT >> $WORKING_DIR/.env +echo export WORKING_DIR=$WORKING_DIR >> $WORKING_DIR/.env +cat $COMMON_ENV_FOLDER/.host.env >> $WORKING_DIR/.env +cat $ENV_FOLDER/.env >>$WORKING_DIR/.env + +DOCKER_COMPOSE_YAML=$WORKING_DIR/docker-compose-$APP_NAME.yaml +cp $DW_BASE_DIR/common/docker-compose.yaml $DOCKER_COMPOSE_YAML -u + +. $WORKING_DIR/.env + +sudo mkdir $DOCKER_BACKEND_LOG_HOME -p + +sudo docker compose -p $PROJECT_NAME -f $DOCKER_COMPOSE_YAML --profile $TARGET_ENV down --remove-orphans +sudo docker compose -p $PROJECT_NAME -f $DOCKER_COMPOSE_YAML --profile $TARGET_ENV build --no-cache + +# Clean up any previous resources that are not needed +# sudo docker system prune -f --volumes +# sudo docker image prune -f +# sudo docker container prune -f +# sudo docker network prune -f + +# Start up the Docker containers in detached mode and remove orphans +sudo docker compose -p $PROJECT_NAME -f $DOCKER_COMPOSE_YAML --profile $TARGET_ENV up --detach --remove-orphans +echo 'You can use "sudo docker compose logs -f" to check the output of the containers' +sudo docker ps -a + +rm $WORKING_DIR/.env +rm $DOCKER_COMPOSE_YAML + +while [[ "$DW_PUSHD_COUNTER" -gt 0 ]]; do + DW_PUSHD_COUNTER=$((DW_PUSHD_COUNTER - 1)) + popd +done + diff --git a/sites/authentication/deploy/dev/.env b/sites/authentication/deploy/dev/.env new file mode 100755 index 0000000..a43a6fe --- /dev/null +++ b/sites/authentication/deploy/dev/.env @@ -0,0 +1,3 @@ +export MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ +export FREELEAPS_ENV=dev + diff --git a/sites/authentication/deploy/local/.env b/sites/authentication/deploy/local/.env new file mode 100644 index 0000000..4938c5e --- /dev/null +++ b/sites/authentication/deploy/local/.env @@ -0,0 +1,3 @@ +export MONGODB_URI=mongodb://localhost:27017/ +export FREELEAPS_ENV=local + diff --git a/sites/authentication/deploy/prod/.env b/sites/authentication/deploy/prod/.env new file mode 100755 index 0000000..0167e99 --- /dev/null +++ b/sites/authentication/deploy/prod/.env @@ -0,0 +1,2 @@ +export MONGODB_URI='mongodb+srv://freeadmin:0eMV0bt8oyaknA0m@freeleaps2.zmsmpos.mongodb.net/?retryWrites=true&w=majority' +export FREELEAPS_ENV=prod