Fixed docker start up issue

This commit is contained in:
Azeez Muibi
2025-03-22 17:11:46 +01:00
parent 9deb402322
commit 2baf9a22c1
47 changed files with 194 additions and 86 deletions
+47
View File
@@ -0,0 +1,47 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
.venv/
env/
ENV/
*.egg-info/
.installed.cfg
*.egg
# Git
.git
.gitignore
# IDE
.idea/
.vscode/
*.swp
*.swo
# Environment variables
.env
.env.local
# Logs and databases
*.log
logs/
*.sqlite3
*.db
# JMeter (only exclude binary files, keep the test plan)
jmeter/bin/
jmeter/lib/
# Dockerfile
Dockerfile
docker-compose.yaml
.dockerignore
# Testing
.coverage
htmlcov/
.pytest_cache/
+43
View File
@@ -5,7 +5,35 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="934fdd1a-f31d-4df5-906a-7da283ff1489" name="Changes" comment=""> <list default="true" id="934fdd1a-f31d-4df5-906a-7da283ff1489" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.dockerignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/__init__.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/controllers/__init__.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/simbrella_api.csv" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Docker" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/collection.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/collection.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/consent.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/consent.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/disbursement.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/disbursement.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/eligibility.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/eligibility.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/health.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/health.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/lien.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/lien.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/loan.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/loan.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/notification.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/notification.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/offers.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/offers.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/penal.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/penal.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/rac.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/rac.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/repayment.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/repayment.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/sms.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/sms.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/token.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/token.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/controllers/transaction.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/controllers/transaction.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/middleware.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/middleware.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/routes.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/routes.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/wsgi.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config.py" beforeDir="false" afterPath="$PROJECT_DIR$/config.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docker.compose.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/docker-compose.yaml" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -20,6 +48,17 @@
</list> </list>
</option> </option>
</component> </component>
<component name="FlaskConsoleOptions" custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)); sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])&#10;from flask.cli import ScriptInfo, NoAppException&#10;for module in [&quot;main.py&quot;, &quot;wsgi.py&quot;, &quot;app.py&quot;]:&#10; try: locals().update(ScriptInfo(app_import_path=module, create_app=None).load_app().make_shell_context()); print(&quot;\nFlask App: %s&quot; % app.import_name); break&#10; except NoAppException: pass">
<envs>
<env key="FLASK_APP" value="app" />
</envs>
<option name="myCustomStartScript" value="import sys; print('Python %s on %s' % (sys.version, sys.platform)); sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])&#10;from flask.cli import ScriptInfo, NoAppException&#10;for module in [&quot;main.py&quot;, &quot;wsgi.py&quot;, &quot;app.py&quot;]:&#10; try: locals().update(ScriptInfo(app_import_path=module, create_app=None).load_app().make_shell_context()); print(&quot;\nFlask App: %s&quot; % app.import_name); break&#10; except NoAppException: pass" />
<option name="myEnvs">
<map>
<entry key="FLASK_APP" value="app" />
</map>
</option>
</component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
@@ -46,6 +85,7 @@
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;, &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;, &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preference.NewJMeterHomeConfigurable&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot; &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
} }
}</component> }</component>
@@ -93,6 +133,9 @@
<workItem from="1742456797057" duration="6494000" /> <workItem from="1742456797057" duration="6494000" />
<workItem from="1742480617760" duration="420000" /> <workItem from="1742480617760" duration="420000" />
<workItem from="1742541486501" duration="2482000" /> <workItem from="1742541486501" duration="2482000" />
<workItem from="1742559368179" duration="6479000" />
<workItem from="1742575042010" duration="6202000" />
<workItem from="1742659572384" duration="301000" />
</task> </task>
<servers /> <servers />
</component> </component>
-21
View File
@@ -1,21 +0,0 @@
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PORT=5000
# Expose port
EXPOSE 5000
# Run the application with Gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:create_app()"]
+31
View File
@@ -0,0 +1,31 @@
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PORT=8080
# Create non-root user for security
RUN adduser --disabled-password --gecos "" appuser && \
chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/v1/api/salary/health || exit 1
# Run the application with Gunicorn - use wsgi.py instead
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "4", "wsgi:create_app()"]
+11 -5
View File
@@ -25,14 +25,20 @@ This project implements the Simbrella FirstAdvance API as defined in the OpenAPI
The easiest way to run the application is using Docker Compose: The easiest way to run the application is using Docker Compose:
```bash ```bash
# Build and start the containers # Stop any running containers
docker-compose down
# Rebuild the image
docker-compose build
# Start the container
docker-compose up -d docker-compose up -d
# View logs # Check if the container is running
docker-compose logs -f docker ps
# Stop the containers # Check the logs for any errors
docker-compose down docker-compose logs
``` ```
## Manual Setup ## Manual Setup
+3
View File
@@ -0,0 +1,3 @@
"""
App package initialization.
"""
+3
View File
@@ -0,0 +1,3 @@
"""
Controllers package initialization.
"""
@@ -2,8 +2,8 @@
Controller for loan collection endpoints. Controller for loan collection endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import CollectLoanRequest, CollectLoanResponse from app.models import CollectLoanRequest, CollectLoanResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for customer consent endpoints. Controller for customer consent endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required, api_key_required from app.middleware import basic_auth_required, api_key_required
from api.models import ( from app.models import (
CustomerConsentRequest, CustomerConsentResponse, CustomerConsentRequest, CustomerConsentResponse,
RevokeEnableConsentRequest, RevokeEnableConsentResponse RevokeEnableConsentRequest, RevokeEnableConsentResponse
) )
@@ -2,8 +2,8 @@
Controller for loan disbursement endpoints. Controller for loan disbursement endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import DisbursementRequest, DisbursementResponse from app.models import DisbursementRequest, DisbursementResponse
import logging import logging
# Configure logger # Configure logger
@@ -3,8 +3,8 @@ Controller for eligibility check endpoints.
""" """
from flask import Blueprint, request, jsonify, current_app from flask import Blueprint, request, jsonify, current_app
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from api.middleware import basic_auth_required from app.middleware import basic_auth_required
from api.models import EligibilityCheckRequest, EligibilityCheckResponse, EligibleOffer from app.models import EligibilityCheckRequest, EligibilityCheckResponse, EligibleOffer
import logging import logging
from typing import Dict, Any, List from typing import Dict, Any, List
@@ -49,7 +49,7 @@ def eligibility_check() -> ResponseReturnValue:
# Create request model # Create request model
req = EligibilityCheckRequest.from_dict(data) req = EligibilityCheckRequest.from_dict(data)
# Process eligibility check (this would connect to the business logic) # Process eligibility check (this would connect to your business logic)
# For demonstration, we'll return a mock response # For demonstration, we'll return a mock response
# Create sample offers # Create sample offers
@@ -11,13 +11,12 @@ logger = logging.getLogger(__name__)
# Create blueprint # Create blueprint
health_bp = Blueprint('health', __name__) health_bp = Blueprint('health', __name__)
@health_bp.route('/health', methods=['GET']) @health_bp.route('/health', methods=['GET'])
def health_check() -> ResponseReturnValue: def health_check() -> ResponseReturnValue:
""" """
Endpoint to check API health. Endpoint to check API health.
This endpoint is used by Docker healthcheck and monitoring systems. This endpoint is used by Dockerfile healthcheck and monitoring systems.
Returns: Returns:
JSON response with health status JSON response with health status
@@ -2,8 +2,8 @@
Controller for lien check endpoints. Controller for lien check endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import LienCheckRequest, LienCheckResponse from app.models import LienCheckRequest, LienCheckResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for loan-related endpoints. Controller for loan-related endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required from app.middleware import basic_auth_required
from api.models import ( from app.models import (
ProvideLoanRequest, ProvideLoanResponse, ProvideLoanRequest, ProvideLoanResponse,
LoanInformationRequest, LoanInformationResponse, Loan LoanInformationRequest, LoanInformationResponse, Loan
) )
@@ -2,8 +2,8 @@
Controller for notification callback endpoints. Controller for notification callback endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required from app.middleware import basic_auth_required
from api.models import NotificationCallbackRequest, NotificationCallbackResponse from app.models import NotificationCallbackRequest, NotificationCallbackResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for offer selection endpoints. Controller for offer selection endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required from app.middleware import basic_auth_required
from api.models import SelectOffersRequest, SelectOffersResponse, Offer from app.models import SelectOffersRequest, SelectOffersResponse, Offer
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for penal charge endpoints. Controller for penal charge endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import PenalChargeRequest, PenalChargeResponse from app.models import PenalChargeRequest, PenalChargeResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for Risk Acceptance Criteria check endpoints. Controller for Risk Acceptance Criteria check endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import RACCheckRequest, RACCheckResponse, RACResponse from app.models import RACCheckRequest, RACCheckResponse, RACResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for repayment endpoints. Controller for repayment endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import basic_auth_required from app.middleware import basic_auth_required
from api.models import RepaymentRequest, RepaymentResponse from app.models import RepaymentRequest, RepaymentResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for SMS notification endpoints. Controller for SMS notification endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import SMSRequest, SMSResponse from app.models import SMSRequest, SMSResponse
import logging import logging
# Configure logger # Configure logger
@@ -2,8 +2,8 @@
Controller for token validation endpoints. Controller for token validation endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import ValidateTokenRequest, ValidateTokenResponse from app.models import ValidateTokenRequest, ValidateTokenResponse
import logging import logging
# Configure logger # Configure logger
@@ -3,8 +3,8 @@ Controller for transaction check endpoints.
""" """
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from api.middleware import api_key_required from app.middleware import api_key_required
from api.models import ( from app.models import (
TransactionCheckRequest, TransactionCheckResponse, TransactionCheckRequest, TransactionCheckResponse,
NewTransactionCheckRequest, NewTransactionCheckResponse, NewTransactionCheckRequest, NewTransactionCheckResponse,
TransactionData TransactionData
@@ -54,7 +54,7 @@ def transaction_check() -> ResponseReturnValue:
# Create request model # Create request model
req = TransactionCheckRequest.from_dict(data) req = TransactionCheckRequest.from_dict(data)
# Process transaction check (this would connect to the business logic) # Process transaction check (this would connect to your business logic)
# For demonstration, we'll return a mock response # For demonstration, we'll return a mock response
# Create response based on transaction type # Create response based on transaction type
@@ -124,7 +124,7 @@ def new_transaction_check() -> ResponseReturnValue:
# Create request model # Create request model
req = NewTransactionCheckRequest.from_dict(data) req = NewTransactionCheckRequest.from_dict(data)
# Process new transaction check (this would connect to the business logic) # Process new transaction check (this would connect to your business logic)
# For demonstration, we'll return a mock response # For demonstration, we'll return a mock response
# Create transaction data based on transaction type # Create transaction data based on transaction type
-3
View File
@@ -11,9 +11,6 @@ T = TypeVar('T', bound='BaseModel')
class BaseModel: class BaseModel:
"""Base model with serialization capabilities.""" """Base model with serialization capabilities."""
def __init__(self):
self.type_field = None
def to_dict(self) -> Dict[str, Any]: def to_dict(self) -> Dict[str, Any]:
"""Convert model to dictionary.""" """Convert model to dictionary."""
result = {k: v for k, v in asdict(self).items() if v is not None} result = {k: v for k, v in asdict(self).items() if v is not None}
+15 -15
View File
@@ -15,21 +15,21 @@ def register_blueprints(app: Flask) -> None:
app: Flask application instance app: Flask application instance
""" """
# Import controllers # Import controllers
from api.controllers.eligibility import eligibility_bp from app.controllers.eligibility import eligibility_bp
from api.controllers.offers import offers_bp from app.controllers.offers import offers_bp
from api.controllers.loan import loan_bp from app.controllers.loan import loan_bp
from api.controllers.repayment import repayment_bp from app.controllers.repayment import repayment_bp
from api.controllers.consent import consent_bp from app.controllers.consent import consent_bp
from api.controllers.notification import notification_bp from app.controllers.notification import notification_bp
from api.controllers.rac import rac_bp from app.controllers.rac import rac_bp
from api.controllers.disbursement import disbursement_bp from app.controllers.disbursement import disbursement_bp
from api.controllers.collection import collection_bp from app.controllers.collection import collection_bp
from api.controllers.transaction import transaction_bp from app.controllers.transaction import transaction_bp
from api.controllers.penal import penal_bp from app.controllers.penal import penal_bp
from api.controllers.token import token_bp from app.controllers.token import token_bp
from api.controllers.lien import lien_bp from app.controllers.lien import lien_bp
from api.controllers.sms import sms_bp from app.controllers.sms import sms_bp
from api.controllers.health import health_bp from app.controllers.health import health_bp
# Create main API blueprint # Create main API blueprint
api_bp = Blueprint('api', __name__, url_prefix='/v1/api/salary') api_bp = Blueprint('api', __name__, url_prefix='/v1/api/salary')
+1 -1
View File
@@ -14,7 +14,7 @@ class Config:
"""Base configuration class.""" """Base configuration class."""
DEBUG: bool = os.environ.get('DEBUG', 'False').lower() == 'true' DEBUG: bool = os.environ.get('DEBUG', 'False').lower() == 'true'
TESTING: bool = os.environ.get('TESTING', 'False').lower() == 'true' TESTING: bool = os.environ.get('TESTING', 'False').lower() == 'true'
PORT: int = int(os.environ.get('PORT', 8080)) # Changed default port to 8080 PORT: int = int(os.environ.get('PORT', 8080)) # Changed default from 5000 to 8080
# API credentials # API credentials
API_USERNAME: str = os.environ.get('API_USERNAME', 'admin') API_USERNAME: str = os.environ.get('API_USERNAME', 'admin')
+4 -5
View File
@@ -1,13 +1,11 @@
version: '3.8'
services: services:
api: api:
build: . build: .
ports: ports:
- "5000:5000" - "8080:8080"
environment: environment:
- DEBUG=False - DEBUG=False
- PORT=5000 - PORT=8080
- API_USERNAME=admin - API_USERNAME=admin
- API_PASSWORD=password - API_PASSWORD=password
- SIMBRELLA_APP_ID=your_app_id - SIMBRELLA_APP_ID=your_app_id
@@ -17,7 +15,7 @@ services:
- ./logs:/app/logs - ./logs:/app/logs
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/v1/api/salary/health"] test: ["CMD", "curl", "-f", "http://localhost:8080/v1/api/salary/health"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
@@ -34,5 +32,6 @@ services:
# - postgres_data:/var/lib/postgresql/data # - postgres_data:/var/lib/postgresql/data
# restart: unless-stopped # restart: unless-stopped
# Uncomment if you add the database service
# volumes: # volumes:
# postgres_data: # postgres_data:
+1
View File
@@ -0,0 +1 @@
+2 -2
View File
@@ -5,8 +5,8 @@ This module serves as the entry point for the Flask application.
from flask import Flask from flask import Flask
from flask.typing import ResponseReturnValue from flask.typing import ResponseReturnValue
from config import Config from config import Config
from api.routes import register_blueprints from app.routes import register_blueprints
from api.middleware import register_middleware from app.middleware import register_middleware
import logging import logging
def create_app(config_class=Config) -> Flask: def create_app(config_class=Config) -> Flask: