MukeshKapoor25 commited on
Commit
7326f4e
·
1 Parent(s): 4b40f63

feat: initialize user management service with core functionality

Browse files

- add FastAPI application with health checks and security middleware
- implement MongoDB and Redis connectivity with health monitoring
- include environment validation and configuration settings
- set up Docker and Kubernetes deployment configurations
- add user router with basic health endpoint

.env.example ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ENVIRONMENT=development
2
+ LOG_LEVEL=INFO
3
+ MONGO_URI=mongodb+srv://user:password@cluster.example.mongodb.net/?retryWrites=true&w=majority
4
+ MONGO_DB_NAME=insightfy-bloom
5
+ CACHE_URL=redis://localhost:6379/0
6
+ CACHE_URI=
7
+ CACHE_K=
8
+ CACHE_DB=0
9
+
.gitignore ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+
24
+ # Testing
25
+ .pytest_cache/
26
+ .hypothesis/
27
+ .coverage
28
+ htmlcov/
29
+ .tox/
30
+ .nox/
31
+ .cache
32
+ nosetests.xml
33
+ coverage.xml
34
+ *.cover
35
+ .nyc_output
36
+
37
+ # Environment
38
+ .env
39
+ .venv*
40
+ venv*
41
+ ENV/
42
+ env.bak/
43
+ venv.bak/
44
+ env/
45
+ venv/
46
+ ENV/
47
+ env.bak/
48
+ venv.bak/
49
+
50
+ # IDE
51
+ .vscode/
52
+ .idea/
53
+ *.swp
54
+ *.swo
55
+ *~
56
+
57
+ # OS
58
+ .DS_Store
59
+ .DS_Store?
60
+ ._*
61
+ .Spotlight-V100
62
+ .Trashes
63
+ ehthumbs.db
64
+ Thumbs.db
65
+
66
+ # Logs
67
+ *.log
68
+ logs/
69
+
70
+ # Runtime data
71
+ pids
72
+ *.pid
73
+ *.seed
74
+ *.pid.lock
75
+
76
+ # Temporary files
77
+ *.tmp
78
+ *.temp
79
+
80
+ # API validation reports (generated files)
81
+ *_report.json
82
+ api_validation_report.json
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim-bullseye AS base
2
+ ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 PATH="/home/user/.local/bin:$PATH"
3
+ RUN apt-get update && apt-get install -y openssl ca-certificates && rm -rf /var/lib/apt/lists/*
4
+ RUN useradd -m -u 1000 user
5
+ USER user
6
+ WORKDIR /app
7
+ COPY --chown=user ./requirements.txt requirements.txt
8
+ RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir --upgrade -r requirements.txt
9
+ RUN python -m spacy download en_core_web_sm
10
+ COPY --chown=user . /app
11
+ EXPOSE 7861
12
+ CMD ["uvicorn", "app.app:app", "--host", "0.0.0.0", "--port", "7861", "--workers", "4", "--log-level", "info"]
13
+
app/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ __all__ = ["app"]
2
+
app/app.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from contextlib import asynccontextmanager
2
+ from fastapi import FastAPI, Request
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.responses import JSONResponse
5
+ from insightfy_utils.logging import setup_logging, get_logger
6
+ from insightfy_utils.config import load_env
7
+ from app.routers.user import router as user_router
8
+ from app.middleware.security_middleware import create_security_middleware
9
+ from app.config.validation import validate_environment_variables, validate_database_isolation
10
+ import os
11
+ import time
12
+ import asyncio
13
+
14
+ load_env()
15
+ _env = os.getenv("ENVIRONMENT", "development")
16
+ _log_level = os.getenv("LOG_LEVEL") or ("WARNING" if _env == "development" else "INFO")
17
+ setup_logging(level=_log_level, format_type="json", app_name="insightfy-bms-ms-ums")
18
+ logger = get_logger(__name__)
19
+ startup_time = time.time()
20
+
21
+ @asynccontextmanager
22
+ async def lifespan(app: FastAPI):
23
+ s = time.time()
24
+ try:
25
+ config_result = validate_environment_variables()
26
+ isolation_valid = validate_database_isolation()
27
+ from app.nosql import connect_stores
28
+ await connect_stores()
29
+ startup_duration = time.time() - s
30
+ logger.info("startup_ok", extra={"startup_duration_seconds": round(startup_duration, 3), "service": "insightfy-bms-ms-ums", "config": config_result, "isolation_valid": isolation_valid})
31
+ except Exception as e:
32
+ logger.error("startup_failed", extra={"error": str(e), "service": "insightfy-bms-ms-ums"}, exc_info=True)
33
+ raise
34
+ yield
35
+ try:
36
+ from app.nosql import disconnect_stores
37
+ await disconnect_stores()
38
+ except Exception as e:
39
+ logger.error("shutdown_failed", extra={"error": str(e), "service": "insightfy-bms-ms-ums"}, exc_info=True)
40
+
41
+ app = FastAPI(
42
+ title="Insightfy BMS - User Management Service",
43
+ version="1.0.0",
44
+ description="User Management Service API for Insightfy BMS platform",
45
+ lifespan=lifespan,
46
+ redirect_slashes=False
47
+ )
48
+
49
+ app.add_middleware(
50
+ create_security_middleware,
51
+ enable_security_headers=True
52
+ )
53
+
54
+ allowed_origins = [
55
+ "http://localhost:3000",
56
+ "http://localhost:3001",
57
+ "https://api.insightfybloom.com",
58
+ "https://insightfybloom.com",
59
+ "https://insightfybloom.in",
60
+ ]
61
+ if os.getenv("ENVIRONMENT") == "development":
62
+ allowed_origins.extend([
63
+ "http://localhost:8000",
64
+ "http://127.0.0.1:3000",
65
+ "http://127.0.0.1:3001",
66
+ ])
67
+ app.add_middleware(
68
+ CORSMiddleware,
69
+ allow_origins=allowed_origins,
70
+ allow_credentials=True,
71
+ allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
72
+ allow_headers=[
73
+ "Accept",
74
+ "Accept-Language",
75
+ "Content-Language",
76
+ "Content-Type",
77
+ "Authorization",
78
+ "X-Requested-With",
79
+ "Origin",
80
+ "Cache-Control",
81
+ "Pragma",
82
+ "X-CSRF-Token",
83
+ "X-Request-ID",
84
+ ],
85
+ expose_headers=["X-Request-ID", "X-Service"],
86
+ )
87
+
88
+ @app.middleware("http")
89
+ async def add_service_headers(request: Request, call_next):
90
+ response = await call_next(request)
91
+ response.headers["X-Service"] = "insightfy-bms-ms-ums"
92
+ response.headers["X-Service-Version"] = "1.0.0"
93
+ response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
94
+ response.headers["X-Powered-By"] = "Insightfy"
95
+ return response
96
+
97
+ app.include_router(
98
+ user_router,
99
+ prefix="/ums/v1/users",
100
+ tags=["Users"],
101
+ responses={404: {"description": "Not found"}, 500: {"description": "Internal error"}},
102
+ )
103
+
104
+ @app.get("/", tags=["Health"])
105
+ async def root():
106
+ return {
107
+ "service": "Insightfy BMS - User Management Service",
108
+ "version": "1.0.0",
109
+ "status": "running",
110
+ "description": "User Management Service API for Insightfy BMS platform",
111
+ "docs_url": "/docs",
112
+ "health_check": "/ums/health",
113
+ }
114
+
115
+ @app.get("/ums/health", tags=["Health"])
116
+ async def health_check():
117
+ return {"status": "healthy", "service": "insightfy-bms-ms-ums", "version": "1.0.0", "timestamp": time.time()}
118
+
119
+ @app.get("/ums/health/detailed", tags=["Health"])
120
+ async def detailed_health_check():
121
+ try:
122
+ health_status = await get_comprehensive_health_status()
123
+ return health_status
124
+ except Exception as e:
125
+ logger.error("detailed_health_failed", extra={"error": str(e), "service": "insightfy-bms-ms-ums"}, exc_info=True)
126
+ return {"status": "unhealthy", "service": "insightfy-bms-ms-ums", "version": "1.0.0", "error": "Health check failed", "timestamp": time.time()}
127
+
128
+ @app.get("/ums/health/metrics", tags=["Health"])
129
+ async def health_metrics():
130
+ try:
131
+ from app.nosql import get_connection_metrics
132
+ connection_metrics = await get_connection_metrics()
133
+ return {"service": "insightfy-bms-ms-ums", "version": "1.0.0", "connection_metrics": connection_metrics, "status": "healthy", "timestamp": time.time(), "environment": os.getenv("ENVIRONMENT", "development")}
134
+ except Exception as e:
135
+ logger.error("metrics_failed", extra={"error": str(e), "service": "insightfy-bms-ms-ums"}, exc_info=True)
136
+ return {"service": "insightfy-bms-ms-ums", "version": "1.0.0", "error": "Failed to retrieve metrics", "status": "unhealthy", "timestamp": time.time()}
137
+
138
+ @app.get("/ums/health/readiness", tags=["Health"])
139
+ async def readiness_check():
140
+ try:
141
+ from app.nosql import ping_mongo, ping_redis
142
+ mongo_ready = await ping_mongo()
143
+ redis_ready = await ping_redis()
144
+ if mongo_ready and redis_ready:
145
+ return {"status": "ready", "service": "insightfy-bms-ms-ums", "version": "1.0.0", "timestamp": time.time()}
146
+ else:
147
+ return JSONResponse(status_code=503, content={"status": "not_ready", "service": "insightfy-bms-ms-ums", "version": "1.0.0", "components": {"mongodb": "ready" if mongo_ready else "not_ready", "redis": "ready" if redis_ready else "not_ready"}, "timestamp": time.time()})
148
+ except Exception as e:
149
+ logger.error("readiness_failed", extra={"error": str(e), "service": "insightfy-bms-ms-ums"}, exc_info=True)
150
+ return JSONResponse(status_code=503, content={"status": "not_ready", "service": "insightfy-bms-ms-ums", "error": "Readiness check failed", "timestamp": time.time()})
151
+
152
+ @app.get("/ums/health/liveness", tags=["Health"])
153
+ async def liveness_check():
154
+ current_time = time.time()
155
+ return {"status": "alive", "service": "insightfy-bms-ms-ums", "version": "1.0.0", "timestamp": current_time, "uptime_seconds": round(current_time - startup_time, 2)}
156
+
157
+ async def get_comprehensive_health_status():
158
+ from app.nosql import get_database_status
159
+ from app.config.validation import validate_environment_variables, validate_database_isolation
160
+ db_status = await get_database_status()
161
+ try:
162
+ config_status = validate_environment_variables()
163
+ config_valid = True
164
+ except Exception as e:
165
+ config_status = {"error": str(e)}
166
+ config_valid = False
167
+ isolation_valid = validate_database_isolation()
168
+ overall_healthy = (db_status["overall_status"] == "healthy" and config_valid and isolation_valid)
169
+ return {
170
+ "status": "healthy" if overall_healthy else "unhealthy",
171
+ "service": "insightfy-bms-ms-ums",
172
+ "version": "1.0.0",
173
+ "components": {
174
+ "mongodb": "healthy" if db_status["mongodb"]["connected"] else "unhealthy",
175
+ "redis": "healthy" if db_status["redis"]["connected"] else "unhealthy",
176
+ "configuration": "healthy" if config_valid else "unhealthy",
177
+ "database_isolation": "healthy" if isolation_valid else "unhealthy",
178
+ },
179
+ "database": {"name": db_status["mongodb"]["database"], "connected": db_status["mongodb"]["connected"], "isolation_valid": isolation_valid},
180
+ "configuration": config_status,
181
+ "metrics": {"check_duration_ms": db_status["check_duration_ms"], "timestamp": db_status["timestamp"]},
182
+ "environment": os.getenv("ENVIRONMENT", "development"),
183
+ }
184
+
app/config/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ __all__ = ["validation"]
2
+
app/config/validation.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ def validate_environment_variables():
4
+ env = os.getenv("ENVIRONMENT", "development")
5
+ log_level = os.getenv("LOG_LEVEL", "INFO")
6
+ mongo_uri = os.getenv("MONGO_URI")
7
+ mongo_db = os.getenv("MONGO_DB_NAME", "insightfy-bloom").strip()
8
+ cache_url = os.getenv("CACHE_URL") or os.getenv("REDIS_URL")
9
+ cache_uri = os.getenv("CACHE_URI")
10
+ ok_mongo = bool(mongo_uri and mongo_db)
11
+ ok_cache = bool(cache_url or cache_uri)
12
+ if not ok_mongo:
13
+ raise ValueError("Missing Mongo configuration")
14
+ if not ok_cache:
15
+ raise ValueError("Missing Redis configuration")
16
+ return {
17
+ "environment": env,
18
+ "log_level": log_level,
19
+ "mongo_db": mongo_db,
20
+ "cache_defined": ok_cache
21
+ }
22
+
23
+ def validate_database_isolation():
24
+ expected = "insightfy-bloom"
25
+ mongo_db = os.getenv("MONGO_DB_NAME", "insightfy-bloom").strip()
26
+ return mongo_db == expected
27
+
app/middleware/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from .security_middleware import create_security_middleware
2
+
3
+ __all__ = ["create_security_middleware"]
4
+
app/middleware/security_middleware.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Callable
2
+ from fastapi import Request, Response
3
+ from starlette.middleware.base import BaseHTTPMiddleware
4
+
5
+ class InsightfySecurityMiddleware(BaseHTTPMiddleware):
6
+ def __init__(self, app, enable_security_headers: bool = True):
7
+ super().__init__(app)
8
+ self.enable_security_headers = enable_security_headers
9
+
10
+ async def dispatch(self, request: Request, call_next: Callable):
11
+ response: Response = await call_next(request)
12
+ if self.enable_security_headers:
13
+ response.headers["X-Content-Type-Options"] = "nosniff"
14
+ response.headers["X-Frame-Options"] = "DENY"
15
+ response.headers["X-XSS-Protection"] = "1; mode=block"
16
+ return response
17
+
18
+ def create_security_middleware(app, **kwargs):
19
+ return InsightfySecurityMiddleware(app, enable_security_headers=kwargs.get("enable_security_headers", True))
20
+
app/nosql.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from datetime import datetime, timezone
3
+ from typing import Dict, Any
4
+ from insightfy_utils.logging import get_logger
5
+ from insightfy_utils.db.mongo_connector import create_mongo_connection
6
+ from insightfy_utils.db.redis_connector import create_redis_connection
7
+ from settings import (
8
+ MONGO_URI,
9
+ MONGO_DB_NAME,
10
+ CACHE_URL,
11
+ CACHE_URI,
12
+ CACHE_K,
13
+ CACHE_DB,
14
+ )
15
+
16
+ logger = get_logger(__name__)
17
+
18
+ if not MONGO_URI or not MONGO_DB_NAME:
19
+ raise ValueError("MongoDB settings missing: ensure MONGO_URI and MONGO_DB_NAME are set.")
20
+
21
+ if not CACHE_URL and not CACHE_URI:
22
+ raise ValueError("Cache settings missing: set CACHE_URL or CACHE_URI.")
23
+
24
+ try:
25
+ mongo_client, mongo_db = create_mongo_connection(
26
+ MONGO_URI,
27
+ MONGO_DB_NAME,
28
+ server_selection_timeout_ms=60000,
29
+ socket_timeout_ms=60000,
30
+ connect_timeout_ms=60000,
31
+ maxPoolSize=20,
32
+ retryWrites=True,
33
+ )
34
+ logger.info("MongoDB client initialized", extra={"db_name": MONGO_DB_NAME, "service": "insightfy-bms-ms-ums"})
35
+ except Exception as e:
36
+ logger.error("Failed to initialize MongoDB client", exc_info=e)
37
+ raise
38
+
39
+ def _redis_from_settings():
40
+ if CACHE_URL:
41
+ import redis.asyncio as redis
42
+ return redis.from_url(
43
+ CACHE_URL,
44
+ decode_responses=True,
45
+ socket_timeout=5,
46
+ socket_connect_timeout=5,
47
+ retry_on_timeout=True,
48
+ health_check_interval=30,
49
+ )
50
+ host, port_str = CACHE_URI.split(":")
51
+ port = int(port_str)
52
+ return create_redis_connection(
53
+ host=host,
54
+ port=port,
55
+ password=CACHE_K or None,
56
+ db=CACHE_DB,
57
+ decode_responses=True,
58
+ socket_timeout=5,
59
+ socket_connect_timeout=5,
60
+ retry_on_timeout=True,
61
+ health_check_interval=30,
62
+ )
63
+
64
+ try:
65
+ redis_client = _redis_from_settings()
66
+ logger.info("Redis client initialized", extra={"service": "insightfy-bms-ms-ums"})
67
+ except Exception as e:
68
+ logger.error("Failed to initialize Redis client", exc_info=e)
69
+ raise
70
+
71
+ async def ping_mongo() -> bool:
72
+ try:
73
+ await mongo_db.command("ping")
74
+ return True
75
+ except Exception:
76
+ return False
77
+
78
+ async def ping_redis() -> bool:
79
+ try:
80
+ pong = await redis_client.ping()
81
+ return bool(pong)
82
+ except Exception:
83
+ return False
84
+
85
+ async def get_database_status() -> Dict[str, Any]:
86
+ start_time = datetime.now(timezone.utc)
87
+ mongo_status = await ping_mongo()
88
+ redis_status = await ping_redis()
89
+ end_time = datetime.now(timezone.utc)
90
+ total_check_time = (end_time - start_time).total_seconds() * 1000
91
+ return {
92
+ "mongodb": {"connected": mongo_status, "database": MONGO_DB_NAME, "service": "insightfy-bms-ms-ums"},
93
+ "redis": {"connected": redis_status, "database": CACHE_DB, "service": "insightfy-bms-ms-ums"},
94
+ "overall_status": "healthy" if (mongo_status and redis_status) else "unhealthy",
95
+ "check_duration_ms": total_check_time,
96
+ "timestamp": datetime.now(timezone.utc).isoformat(),
97
+ "service": "insightfy-bms-ms-ums",
98
+ }
99
+
100
+ async def connect_stores() -> None:
101
+ ok_mongo = await ping_mongo()
102
+ ok_redis = await ping_redis()
103
+ if not (ok_mongo and ok_redis):
104
+ raise RuntimeError("Store connectivity check failed")
105
+
106
+ async def disconnect_stores() -> None:
107
+ try:
108
+ mongo_client.close()
109
+ except Exception:
110
+ pass
111
+ try:
112
+ await redis_client.close()
113
+ except Exception:
114
+ pass
115
+
116
+ async def get_connection_metrics() -> Dict[str, Any]:
117
+ try:
118
+ mongo_metrics = {"status": "connected"}
119
+ redis_metrics = {"status": "connected"}
120
+ try:
121
+ mongo_stats = await mongo_db.command("serverStatus")
122
+ mongo_metrics.update({
123
+ "connections": mongo_stats.get("connections", {}),
124
+ "network": mongo_stats.get("network", {}),
125
+ "opcounters": mongo_stats.get("opcounters", {}),
126
+ })
127
+ except Exception:
128
+ mongo_metrics["note"] = "Limited metrics"
129
+ try:
130
+ redis_info = await redis_client.info()
131
+ redis_metrics.update({
132
+ "connected_clients": redis_info.get("connected_clients", 0),
133
+ "used_memory": redis_info.get("used_memory", 0),
134
+ "keyspace_hits": redis_info.get("keyspace_hits", 0),
135
+ "keyspace_misses": redis_info.get("keyspace_misses", 0),
136
+ })
137
+ except Exception:
138
+ redis_metrics["note"] = "Limited metrics"
139
+ return {"mongodb": mongo_metrics, "redis": redis_metrics, "timestamp": datetime.now(timezone.utc).isoformat(), "service": "insightfy-bms-ms-ums"}
140
+ except Exception:
141
+ return {"error": "Failed to retrieve metrics", "timestamp": datetime.now(timezone.utc).isoformat(), "service": "insightfy-bms-ms-ums"}
142
+
143
+ db = mongo_db
144
+
app/routers/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ __all__ = ["user"]
2
+
app/routers/user.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ import time
3
+
4
+ router = APIRouter()
5
+
6
+ @router.get("/health")
7
+ async def user_health():
8
+ return {"status": "healthy", "service": "insightfy-bms-ms-ums", "version": "1.0.0", "timestamp": time.time()}
9
+
deployment/docker-compose.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: "3.9"
2
+ services:
3
+ ms-bms-ums:
4
+ build: .
5
+ image: insightfy/ms-bms-ums:latest
6
+ container_name: ms-bms-ums
7
+ ports:
8
+ - "7861:7861"
9
+ environment:
10
+ - SERVICE_NAME=insightfy-bms-ms-ums
11
+ - MONGO_URI=${MONGO_URI}
12
+ - MONGO_DB_NAME=${MONGO_DB_NAME}
13
+ - CACHE_URL=${CACHE_URL}
14
+ - CACHE_URI=${CACHE_URI}
15
+ - CACHE_K=${CACHE_K}
16
+ - CACHE_DB=${CACHE_DB}
17
+ - ENVIRONMENT=${ENVIRONMENT:-development}
18
+ - LOG_LEVEL=${LOG_LEVEL:-INFO}
19
+ restart: unless-stopped
20
+
deployment/kubernetes/bms-ums-deployment.yaml ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ apiVersion: apps/v1
3
+ kind: Deployment
4
+ metadata:
5
+ name: ms-bms-ums
6
+ namespace: insightfy-app
7
+ labels:
8
+ app: ms-bms-ums
9
+ service: bms-ums
10
+ version: "1.0.0"
11
+ component: microservice
12
+ spec:
13
+ replicas: 2
14
+ strategy:
15
+ type: RollingUpdate
16
+ rollingUpdate:
17
+ maxUnavailable: 1
18
+ maxSurge: 1
19
+ selector:
20
+ matchLabels:
21
+ app: ms-bms-ums
22
+ template:
23
+ metadata:
24
+ labels:
25
+ app: ms-bms-ums
26
+ service: bms-ums
27
+ version: "1.0.0"
28
+ annotations:
29
+ prometheus.io/scrape: "true"
30
+ prometheus.io/port: "7861"
31
+ prometheus.io/path: "/ums/health/metrics"
32
+ spec:
33
+ containers:
34
+ - name: bms-ums
35
+ image: insightfy/ms-bms-ums:latest
36
+ imagePullPolicy: IfNotPresent
37
+ ports:
38
+ - containerPort: 7861
39
+ name: http
40
+ protocol: TCP
41
+ envFrom:
42
+ - configMapRef:
43
+ name: shared-env
44
+ - secretRef:
45
+ name: app-secrets
46
+ env:
47
+ - name: SERVICE_NAME
48
+ value: "insightfy-bms-ms-ums"
49
+ - name: MONGO_DB_NAME
50
+ value: "insightfy-bloom"
51
+ - name: ENVIRONMENT
52
+ value: "production"
53
+ - name: LOG_LEVEL
54
+ value: "INFO"
55
+ readinessProbe:
56
+ httpGet:
57
+ path: /ums/health/readiness
58
+ port: 7861
59
+ initialDelaySeconds: 10
60
+ periodSeconds: 15
61
+ livenessProbe:
62
+ httpGet:
63
+ path: /ums/health/liveness
64
+ port: 7861
65
+ initialDelaySeconds: 20
66
+ periodSeconds: 30
67
+ resources:
68
+ requests:
69
+ cpu: "100m"
70
+ memory: "128Mi"
71
+ limits:
72
+ cpu: "500m"
73
+ memory: "512Mi"
74
+ ---
75
+ apiVersion: policy/v1
76
+ kind: PodDisruptionBudget
77
+ metadata:
78
+ name: ms-bms-ums-pdb
79
+ namespace: insightfy-app
80
+ labels:
81
+ app: ms-bms-ums
82
+ service: bms-ums
83
+ spec:
84
+ minAvailable: 1
85
+ selector:
86
+ matchLabels:
87
+ app: ms-bms-ums
88
+ ---
89
+ apiVersion: networking.k8s.io/v1
90
+ kind: NetworkPolicy
91
+ metadata:
92
+ name: ms-bms-ums-netpol
93
+ namespace: insightfy-app
94
+ labels:
95
+ app: ms-bms-ums
96
+ service: bms-ums
97
+ spec:
98
+ podSelector:
99
+ matchLabels:
100
+ app: ms-bms-ums
101
+ policyTypes:
102
+ - Ingress
103
+ - Egress
104
+ ingress:
105
+ - from:
106
+ - namespaceSelector:
107
+ matchLabels:
108
+ name: insightfy-app
109
+ - namespaceSelector:
110
+ matchLabels:
111
+ name: ingress-nginx
112
+ ports:
113
+ - protocol: TCP
114
+ port: 7861
115
+ egress:
116
+ - to: []
117
+ ports:
118
+ - protocol: TCP
119
+ port: 27017
120
+ - protocol: TCP
121
+ port: 6379
122
+ - protocol: TCP
123
+ port: 443
124
+ - protocol: TCP
125
+ port: 80
126
+ - protocol: UDP
127
+ port: 53
128
+
requirements.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi>=0.104.0
2
+ uvicorn[standard]>=0.24.0
3
+ pydantic>=2.0.0
4
+ pydantic-settings>=2.0.0
5
+ motor>=3.3.0
6
+ redis>=5.0.0
7
+ python-jose[cryptography]>=3.3.0
8
+ passlib[bcrypt]>=1.7.4
9
+ python-multipart>=0.0.6
10
+ python-dotenv>=1.0.0
11
+ insightfy-utils>=0.1.0
12
+ pytest>=7.0.0
13
+ pytest-asyncio>=0.21.0
14
+ hypothesis>=6.0.0
15
+ email-validator>=2.0.0
16
+ httpx>=0.24.0
17
+ databases>=0.8.0
18
+ pandas>=1.5.0
19
+ bleach>=6.0.0
20
+ psutil>=5.9.0
21
+ spacy>=3.5.0
22
+
settings.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from urllib.parse import quote_plus, urlencode
3
+ import os
4
+ from insightfy_utils.config import load_env
5
+ load_env()
6
+ SECRET_KEY = os.getenv("SECRET_KEY")
7
+ ALGORITHM = os.getenv("ALGORITHM", "HS256")
8
+ MONGO_URI = os.getenv('MONGO_URI')
9
+ MONGO_DB_NAME = os.getenv("MONGO_DB_NAME", "insightfy-bloom").strip()
10
+
11
+ def _sanitize_mongo_uri(uri: str | None) -> str | None:
12
+ if not uri:
13
+ return uri
14
+ uri = uri.replace("??", "?")
15
+ if "?" in uri:
16
+ base_uri, options_part = uri.split("?", 1)
17
+ options = options_part.split("&")
18
+ valid_options = []
19
+ for opt in options:
20
+ if not opt:
21
+ continue
22
+ key = opt.split("=")[0] if "=" in opt else opt
23
+ if key in ["?ssl", "ssl_cert_reqs"]:
24
+ continue
25
+ valid_options.append(opt)
26
+ return f"{base_uri}?{'&'.join(valid_options)}"
27
+ return uri
28
+
29
+ MONGO_URI = _sanitize_mongo_uri(os.getenv('MONGO_URI'))
30
+ CACHE_URL = os.getenv("CACHE_URL") or os.getenv("REDIS_URL")
31
+ CACHE_URI = os.getenv('CACHE_URI')
32
+ CACHE_K = os.getenv('CACHE_K')
33
+ CACHE_DB = int(os.getenv("CACHE_DB", "0"))
34
+