middle way of refactoring the code

This commit is contained in:
Mike Liao 2024-10-30 08:44:37 -07:00
parent c8361e71c7
commit 58a2e122aa
61 changed files with 905 additions and 94 deletions

23
apps/authentication/.env Normal file
View File

@ -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

View File

@ -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}"]

View File

@ -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:

View File

@ -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,
)

View File

@ -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:

View File

@ -1,4 +1,4 @@
from infra.log.module_logger import ModuleLogger
from common.log.module_logger import ModuleLogger
from typing import Optional

View File

@ -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:

View File

@ -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,
)

View File

@ -1,5 +1,5 @@
from enum import IntEnum
from infra.models.constants import UserRegion
from common.constants.region import UserRegion
class NewUserMethod(IntEnum):

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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:

View File

@ -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"

View File

@ -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()

View File

@ -0,0 +1,8 @@
from enum import IntEnum
class UserContractRole(IntEnum):
VISITER = 0x1
REQUESTER = 0x2
PROVIDER = 0x4

View File

@ -0,0 +1,5 @@
from enum import IntEnum
class PaymentPlanType(IntEnum):
STAGED = 0

View File

@ -0,0 +1,5 @@
from enum import IntEnum
class UserRegion(IntEnum):
OTHER = 0
ZH_CN = 1

View File

@ -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

View File

@ -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,
)

View File

@ -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)

View File

@ -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"
)

View File

@ -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
},
)

View File

@ -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

View File

@ -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,
)

View File

@ -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
)

View File

@ -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"
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -1,4 +1,4 @@
from app.authentication.webapi.routes import api_router
from webapi.routes import api_router
from starlette import routing

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
export AZURE_STORAGE_DOCUMENT_API_KEY=xbiFtFeQ6v5dozgVM99fZ9huUomL7QcLu6s0y8zYHtIXZ8XdneKDMcg4liQr/9oNlVoRFcZhWjLY+ASt9cjICQ==
export JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0

View File

@ -0,0 +1,3 @@
export MONGODB_URI='mongodb+srv://jetli:8IHKx6dZK8BfugGp@freeleaps2.hanbj.mongodb.net/'
export FREELEAPS_ENV=alpha

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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=<prod | alpha | dev>\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=<prod | alpha | dev>\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

View File

@ -0,0 +1,3 @@
export MONGODB_URI=mongodb://freeleaps2-mongodb:27017/
export FREELEAPS_ENV=dev

View File

@ -0,0 +1,3 @@
export MONGODB_URI=mongodb://localhost:27017/
export FREELEAPS_ENV=local

View File

@ -0,0 +1,2 @@
export MONGODB_URI='mongodb+srv://freeadmin:0eMV0bt8oyaknA0m@freeleaps2.zmsmpos.mongodb.net/?retryWrites=true&w=majority'
export FREELEAPS_ENV=prod