diff --git a/apps/payment/.env b/apps/payment/.env new file mode 100644 index 0000000..4f02757 --- /dev/null +++ b/apps/payment/.env @@ -0,0 +1,25 @@ +APP_NAME=payment +export SERVICE_API_ACCESS_HOST=0.0.0.0 +export SERVICE_API_ACCESS_PORT=8006 +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 +GIT_REPO_ROOT=/Users/dongli/workspace/freeleaps-service-hub +CODEBASE_ROOT=/Users/dongli/workspace/freeleaps-service-hub/apps/payment +SITE_DEPLOY_FOLDER=/Users/dongli/workspace/freeleaps-service-hub/sites/payment/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 +export LOG_BASE_PATH=${CODEBASE_ROOT}/log +export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd + + diff --git a/apps/payment/Dockerfile b/apps/payment/Dockerfile index 34bdd04..4d26368 100644 --- a/apps/payment/Dockerfile +++ b/apps/payment/Dockerfile @@ -8,7 +8,9 @@ ENV JWT_SECRET_KEY="" #site_settings ENV SERVICE_API_ACCESS_HOST=0.0.0.0 -ENV SERVICE_API_ACCESS_PORT=8004 +ENV SERVICE_API_ACCESS_PORT=8006 +ENV STRIPE_API_KEY= +ENV STRIPE_WEBHOOK_SECRET= ENV MONGODB_NAME= ENV MONGODB_PORT= ENV MONGODB_URI= diff --git a/apps/payment/backend/business/stripe_manager.py b/apps/payment/backend/business/stripe_manager.py index 9da2ba5..598e397 100644 --- a/apps/payment/backend/business/stripe_manager.py +++ b/apps/payment/backend/business/stripe_manager.py @@ -2,19 +2,19 @@ from datetime import datetime, timezone from typing import Dict, Optional, Tuple from backend.infra.payment.models import StripeTransactionDoc from backend.infra.payment.constants import TransactionStatus -from backend.infra.config.backend import settings +from common.config.app_settings import app_settings import stripe from common.log.module_logger import ModuleLogger from decimal import Decimal import json -stripe.api_key = settings.STRIPE_API_KEY +stripe.api_key = app_settings.STRIPE_API_KEY class StripeManager: def __init__(self) -> None: - self.site_url_root = settings.SITE_URL_ROOT.rstrip("/") + self.site_url_root = app_settings.SITE_URL_ROOT.rstrip("/") self.module_logger = ModuleLogger(sender_id="StripeManager") async def create_stripe_account(self) -> Optional[str]: diff --git a/apps/payment/backend/infra/config/backend.py b/apps/payment/backend/infra/config/backend.py index 672aecb..ca36388 100644 --- a/apps/payment/backend/infra/config/backend.py +++ b/apps/payment/backend/infra/config/backend.py @@ -1,38 +1,38 @@ -import os -from pydantic_settings import BaseSettings - - -class Settings(BaseSettings): - NAME: str = "backend" - - SITE_URL_ROOT: str = "http://localhost/" - - JWT_SECRET_KEY: str = "" - JWT_ALGORITHM: str = "HS256" - - SYSTEM_USER_ID: str = "117f191e810c19729de860aa" - EMAIL_FROM:str = "" - STRIPE_API_KEY: str = "" - STRIPE_WEBHOOK_SECRET: str = "" - - RABBITMQ_HOST: str = "" - RABBITMQ_PORT: str = "" - - FREELEAPS_DEVSVC_ENDPOINT:str = "" - FREELEAPS_CONTENT_ENDPOINT:str = "" - FREELEAPS_CENTRAL_STORAGE_ENDPOINT:str = "" - - KAFKA_SERVER_URL:str="" - - LOG_BASE_PATH: str = "./log" - BACKEND_LOG_FILE_NAME: str = "freeleaps-backend" - APPLICATION_ACTIVITY_LOG: str = "freeleaps-application-activity" - USER_ACTIVITY_LOG: str = "freeleaps-user-activity" - BUSINESS_METRIC_LOG: str = "freeleaps-business-metrics" - - class Config: - env_file = ".freeleaps-backend.env" - env_file_encoding = "utf-8" - - -settings = Settings() +# import os +# from pydantic_settings import BaseSettings +# +# +# class Settings(BaseSettings): +# NAME: str = "backend" +# +# SITE_URL_ROOT: str = "http://localhost/" +# +# JWT_SECRET_KEY: str = "" +# JWT_ALGORITHM: str = "HS256" +# +# SYSTEM_USER_ID: str = "117f191e810c19729de860aa" +# EMAIL_FROM:str = "" +# STRIPE_API_KEY: str = "" +# STRIPE_WEBHOOK_SECRET: str = "" +# +# RABBITMQ_HOST: str = "" +# RABBITMQ_PORT: str = "" +# +# FREELEAPS_DEVSVC_ENDPOINT:str = "" +# FREELEAPS_CONTENT_ENDPOINT:str = "" +# FREELEAPS_CENTRAL_STORAGE_ENDPOINT:str = "" +# +# KAFKA_SERVER_URL:str="" +# +# LOG_BASE_PATH: str = "./log" +# BACKEND_LOG_FILE_NAME: str = "freeleaps-backend" +# APPLICATION_ACTIVITY_LOG: str = "freeleaps-application-activity" +# USER_ACTIVITY_LOG: str = "freeleaps-user-activity" +# BUSINESS_METRIC_LOG: str = "freeleaps-business-metrics" +# +# class Config: +# env_file = ".freeleaps-backend.env" +# env_file_encoding = "utf-8" +# +# +# settings = Settings() diff --git a/apps/payment/backend/infra/config/logging.py b/apps/payment/backend/infra/config/logging.py index 544bc85..ae5503e 100644 --- a/apps/payment/backend/infra/config/logging.py +++ b/apps/payment/backend/infra/config/logging.py @@ -1,18 +1,18 @@ -from backend.infra.config.backend import settings - - -class Settings(): - LOG_LEVEL: str = "DEBUG" - LOG_PATH_BASE: str = ( - settings.LOG_BASE_PATH.removesuffix("/") - ) - LOG_PATH: str = LOG_PATH_BASE + '/' + settings.BACKEND_LOG_FILE_NAME + '.log' - LOG_RETENTION: str = "14 days" - LOG_ROTATION: str = "00:00" # mid night - - class Config: - env_file = ".log.env" - env_file_encoding = "utf-8" - - -settings = Settings() +# from backend.infra.config.backend import settings +# +# +# class Settings(): +# LOG_LEVEL: str = "DEBUG" +# LOG_PATH_BASE: str = ( +# settings.LOG_BASE_PATH.removesuffix("/") +# ) +# LOG_PATH: str = LOG_PATH_BASE + '/' + settings.BACKEND_LOG_FILE_NAME + '.log' +# LOG_RETENTION: str = "14 days" +# LOG_ROTATION: str = "00:00" # mid night +# +# class Config: +# env_file = ".log.env" +# env_file_encoding = "utf-8" +# +# +# settings = Settings() diff --git a/apps/payment/backend/models/__init__.py b/apps/payment/backend/models/__init__.py index ba62d26..b1a6b95 100644 --- a/apps/payment/backend/models/__init__.py +++ b/apps/payment/backend/models/__init__.py @@ -1,8 +1,9 @@ -from .gitea import code_models -from .user import user_models -from .user_profile import profile_models - +# from .gitea import code_models +# from .user import user_models +# from .user_profile import profile_models +# +# TODO: Add all models to backend_models backend_models = [] -backend_models.extend(code_models) -backend_models.extend(user_models) -backend_models.extend(profile_models) +# backend_models.extend(code_models) +# backend_models.extend(user_models) +# backend_models.extend(profile_models) diff --git a/apps/payment/backend/services/common/constants.py b/apps/payment/backend/services/common/constants.py new file mode 100644 index 0000000..db48adf --- /dev/null +++ b/apps/payment/backend/services/common/constants.py @@ -0,0 +1,16 @@ +from enum import IntEnum + + +class UserContractRole(IntEnum): + VISITER = 0x1 + REQUESTER = 0x2 + PROVIDER = 0x4 + + +class PaymentPlanType(IntEnum): + STAGED = 0 + + +class UserRegion(IntEnum): + OTHER = 0 + ZH_CN = 1 diff --git a/apps/payment/backend/services/common/region.py b/apps/payment/backend/services/common/region.py new file mode 100644 index 0000000..6c04749 --- /dev/null +++ b/apps/payment/backend/services/common/region.py @@ -0,0 +1,20 @@ +from backend.services.common.constants import UserRegion +from backend.services.profile.models import BasicProfileDoc + + +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 + + async def get_user_region(self, user_id: str) -> UserRegion: + user_profile = await BasicProfileDoc.find_one( + BasicProfileDoc.user_id == user_id + ) + return user_profile.region if user_profile else UserRegion.OTHER diff --git a/apps/payment/backend/services/user/user_management_service.py b/apps/payment/backend/services/user/user_management_service.py deleted file mode 100644 index bccdacd..0000000 --- a/apps/payment/backend/services/user/user_management_service.py +++ /dev/null @@ -1,99 +0,0 @@ -from common.log.module_logger import ModuleLogger -from typing import Optional - -from backend.models.user.constants import ( - NewUserMethod, - UserAccountProperty, -) -from backend.models.user.models import UserAccountDoc -from backend.models.permission.constants import ( - AdministrativeRole, - Capability, -) -from backend.infra.auth.user_auth_handler import ( - UserAuthHandler, -) -from backend.infra.user_profile.user_profile_handler import ( - UserProfileHandler, -) -from common.log.log_utils import log_entry_exit_async -from common.constants.region import UserRegion - - -class UserManagementService: - def __init__(self) -> None: - self.user_auth_handler = UserAuthHandler() - self.user_profile_handler = UserProfileHandler() - self.module_logger = ModuleLogger(sender_id=UserManagementService) - - @log_entry_exit_async - async def create_new_user_account( - self, method: NewUserMethod, region: UserRegion - ) -> UserAccountDoc: - """create a new user account document in DB - - Args: - method (NewUserMethod): the method the new user came from - region : preferred user region detected via the user log-in website - - Returns: - str: id of user account - """ - if NewUserMethod.EMAIL == method: - user_account = await self.user_profile_handler.create_new_user_account( - UserAccountProperty.EMAIL_VERIFIED, - Capability.VISITOR, - AdministrativeRole.PERSONAL, - region, - ) - - elif NewUserMethod.MOBILE == method: - user_account = await self.user_profile_handler.create_new_user_account( - UserAccountProperty.EMAIL_VERIFIED, - Capability.VISITOR, - AdministrativeRole.PERSONAL, - region, - ) - - # Create other doc in collections for the new user - # TODO: Should convert to notification - # await UserAchievement(str(user_account.id)).create_activeness_achievement() - return user_account - - async def initialize_new_user_data( - self, - user_id: str, - method: NewUserMethod, - email_address: str = None, - mobile_number: str = None, - region: UserRegion = UserRegion.ZH_CN, - time_zone: Optional[str] = "UTC", - ): - """Init data for the new user - - Args: - user_id (str): user id - method (NewUserMethod): the method the new user came from - - Returns: - result: True if initilize data for the new user successfully, else return False - """ - - # create basic and provider profile doc for the new user - if NewUserMethod.EMAIL == method: - await self.user_profile_handler.create_basic_profile( - user_id, email_address, True, None, False, False, region, time_zone - ) - await self.user_auth_handler.save_email_auth_method(user_id, email_address) - elif NewUserMethod.MOBILE == method: - await self.user_profile_handler.create_basic_profile( - user_id, None, False, mobile_number, True, False, region, time_zone - ) - else: - return False - - await self.user_profile_handler.create_provider_profile(user_id) - return True - - async def get_account_by_id(self, user_id: str) -> UserAccountDoc: - return await self.user_profile_handler.get_account_by_id(user_id) diff --git a/apps/payment/common/config/app_settings.py b/apps/payment/common/config/app_settings.py index 809d1b4..0a6557f 100644 --- a/apps/payment/common/config/app_settings.py +++ b/apps/payment/common/config/app_settings.py @@ -2,7 +2,7 @@ from pydantic_settings import BaseSettings class AppSettings(BaseSettings): - NAME: str = "central_storage" + NAME: str = "payment" APP_NAME:str = NAME JWT_SECRET_KEY: str = "" @@ -16,6 +16,9 @@ class AppSettings(BaseSettings): BACKEND_LOG_FILE_NAME: str = APP_NAME APPLICATION_ACTIVITY_LOG: str = APP_NAME + "-application-activity" + STRIPE_API_KEY: str="" + SITE_URL_ROOT: str = "" + class Config: env_file = ".myapp.env" env_file_encoding = "utf-8" diff --git a/apps/payment/common/log/base_logger.py b/apps/payment/common/log/base_logger.py index 122dfdb..f35aaef 100644 --- a/apps/payment/common/log/base_logger.py +++ b/apps/payment/common/log/base_logger.py @@ -22,7 +22,7 @@ class LoggerBase: return log_filename = ( - log_settings.LOG_PATH_BASE + "/" + self.__logger_name + ".log" + log_settings.LOG_BASE_PATH + "/" + self.__logger_name + ".log" ) log_retention = log_settings.LOG_RETENTION log_rotation = log_settings.LOG_ROTATION diff --git a/apps/payment/start_fastapi.sh b/apps/payment/start_fastapi.sh index 99cf493..7b87140 100755 --- a/apps/payment/start_fastapi.sh +++ b/apps/payment/start_fastapi.sh @@ -2,7 +2,7 @@ rp=$(dirname "$(realpath '$1'))") pushd $rp -APP_NAME=authentication +APP_NAME=payment APP_PARENT_FOLDER=apps GIT_REPO_ROOT=$(git rev-parse --show-toplevel) diff --git a/sites/payment/deploy/alpha/.env b/sites/payment/deploy/alpha/.env new file mode 100755 index 0000000..a1cfa87 --- /dev/null +++ b/sites/payment/deploy/alpha/.env @@ -0,0 +1,4 @@ +export MONGODB_URI='mongodb+srv://jetli:8IHKx6dZK8BfugGp@freeleaps2.hanbj.mongodb.net/' +export FREELEAPS_ENV=alpha +export LOG_BASE_PATH=${CODEBASE_ROOT}/log +export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd \ No newline at end of file diff --git a/sites/payment/deploy/common/.env b/sites/payment/deploy/common/.env new file mode 100755 index 0000000..ca344ca --- /dev/null +++ b/sites/payment/deploy/common/.env @@ -0,0 +1,8 @@ +export SERVICE_API_ACCESS_HOST=0.0.0.0 +export SERVICE_API_ACCESS_PORT=8006 +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 diff --git a/sites/payment/deploy/common/.host.env b/sites/payment/deploy/common/.host.env new file mode 100755 index 0000000..73c2468 --- /dev/null +++ b/sites/payment/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/payment/deploy/common/docker-compose.yaml b/sites/payment/deploy/common/docker-compose.yaml new file mode 100755 index 0000000..892d0f3 --- /dev/null +++ b/sites/payment/deploy/common/docker-compose.yaml @@ -0,0 +1,37 @@ +services: + payment: + 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} + - STRIPE_API_KEY=${STRIPE_API_KEY} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - 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/payment/deploy/deploy.sh b/sites/payment/deploy/deploy.sh new file mode 100755 index 0000000..70084a1 --- /dev/null +++ b/sites/payment/deploy/deploy.sh @@ -0,0 +1,91 @@ +#!/bin/bash +DW_BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +APP_NAME=payment +APP_PARENT_FOLDER=apps +PROJECT_NAME=payment + +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/payment/deploy/dev/.env b/sites/payment/deploy/dev/.env new file mode 100755 index 0000000..b391f0d --- /dev/null +++ b/sites/payment/deploy/dev/.env @@ -0,0 +1,4 @@ +export MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ +export FREELEAPS_ENV=dev +export LOG_BASE_PATH=${CODEBASE_ROOT}/log +export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd diff --git a/sites/payment/deploy/local/.env b/sites/payment/deploy/local/.env new file mode 100644 index 0000000..8384b73 --- /dev/null +++ b/sites/payment/deploy/local/.env @@ -0,0 +1,6 @@ +export MONGODB_URI=mongodb://localhost:27017/ +export FREELEAPS_ENV=local +export LOG_BASE_PATH=${CODEBASE_ROOT}/log +export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd + + diff --git a/sites/payment/deploy/prod/.env b/sites/payment/deploy/prod/.env new file mode 100755 index 0000000..0167e99 --- /dev/null +++ b/sites/payment/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