Compare commits
	
		
			4 Commits
		
	
	
		
			main
			...
			301cf5922e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					301cf5922e | ||
| 
						 | 
					479ec4f917 | ||
| 
						 | 
					3f40a6126c | ||
| 
						 | 
					dd9e9ce110 | 
@@ -1,5 +1,6 @@
 | 
			
		||||
FROM python:3.11.7-slim-bookworm
 | 
			
		||||
FROM python:3.11.7-alpine
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
COPY api .
 | 
			
		||||
RUN apk add --no-cache curl
 | 
			
		||||
RUN pip install -r requirements.txt
 | 
			
		||||
CMD python3 app.py
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ from flask_jwt_extended import JWTManager
 | 
			
		||||
from jwt import ExpiredSignatureError
 | 
			
		||||
from models import db, RevokedToken
 | 
			
		||||
import os
 | 
			
		||||
from tech_views import tech_bp
 | 
			
		||||
from utils import init_db, wait_for_db
 | 
			
		||||
from views import user_bp
 | 
			
		||||
from werkzeug.exceptions import HTTPException
 | 
			
		||||
@@ -26,6 +27,7 @@ def create_app(config_name="default"):
 | 
			
		||||
 | 
			
		||||
    # Blueprints registration
 | 
			
		||||
    app.register_blueprint(user_bp)
 | 
			
		||||
    app.register_blueprint(tech_bp)
 | 
			
		||||
 | 
			
		||||
    # Database and JWT initialization
 | 
			
		||||
    db.init_app(app)
 | 
			
		||||
@@ -53,7 +55,7 @@ def create_app(config_name="default"):
 | 
			
		||||
 | 
			
		||||
    # Fill database by initial values (only if we are not testing)
 | 
			
		||||
    with app.app_context():
 | 
			
		||||
        wait_for_db()
 | 
			
		||||
        wait_for_db(max_retries=100)
 | 
			
		||||
        db.create_all()
 | 
			
		||||
        if config_name != "testing":
 | 
			
		||||
            init_db()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								api/tech_views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								api/tech_views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
from flask import Blueprint, jsonify
 | 
			
		||||
from models import db
 | 
			
		||||
from sqlalchemy import text
 | 
			
		||||
from utils import db_ready
 | 
			
		||||
 | 
			
		||||
# Blueprint with technical endpoints
 | 
			
		||||
tech_bp = Blueprint('tech_bp', __name__)
 | 
			
		||||
 | 
			
		||||
@tech_bp.route('/health', methods=['GET'])
 | 
			
		||||
def health_check():
 | 
			
		||||
    "Check if service works and database is functional"
 | 
			
		||||
    try:
 | 
			
		||||
        with db.engine.connect() as connection:
 | 
			
		||||
            connection.execute(text("SELECT 1"))
 | 
			
		||||
        return jsonify(status="healthy"), 200
 | 
			
		||||
    except Exception:
 | 
			
		||||
        if db_ready:
 | 
			
		||||
            return jsonify(status="unhealthy"), 500
 | 
			
		||||
        else:
 | 
			
		||||
            return jsonify(status="starting"), 503
 | 
			
		||||
							
								
								
									
										18
									
								
								api/utils.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								api/utils.py
									
									
									
									
									
								
							@@ -7,15 +7,17 @@ from sqlalchemy.exc import DatabaseError
 | 
			
		||||
import time
 | 
			
		||||
from werkzeug.security import generate_password_hash
 | 
			
		||||
 | 
			
		||||
db_ready = False
 | 
			
		||||
 | 
			
		||||
def admin_required(user_id, message='Access denied.'):
 | 
			
		||||
    "Check if common user try to make administrative action."
 | 
			
		||||
    user = db.session.get(User, user_id)
 | 
			
		||||
    if user is None or user.role != "Administrator":
 | 
			
		||||
        abort(403, message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_access(owner_id, message='Access denied.'):
 | 
			
		||||
    # Check if user try to access or edit resource that does not belong to them 
 | 
			
		||||
    "Check if user try to access or edit resource that does not belong to them."
 | 
			
		||||
    logged_user_id = int(get_jwt_identity())
 | 
			
		||||
    logged_user_role = db.session.get(User, logged_user_id).role
 | 
			
		||||
    if logged_user_role != "Administrator" and logged_user_id != owner_id:
 | 
			
		||||
@@ -30,20 +32,18 @@ def get_user_or_404(user_id):
 | 
			
		||||
    return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MAX_RETRIES = 100
 | 
			
		||||
 | 
			
		||||
def wait_for_db():
 | 
			
		||||
    for retries in range(MAX_RETRIES):
 | 
			
		||||
def wait_for_db(max_retries):
 | 
			
		||||
    "Try to connect with database <max_retries> times."
 | 
			
		||||
    global db_ready
 | 
			
		||||
    for _ in range(max_retries):
 | 
			
		||||
        try:
 | 
			
		||||
            with db.engine.connect() as connection:
 | 
			
		||||
                connection.execute(text("SELECT 1"))
 | 
			
		||||
            print("Successfully connected with database.")
 | 
			
		||||
            db_ready = True
 | 
			
		||||
            return
 | 
			
		||||
        except DatabaseError:
 | 
			
		||||
            print(f"Waiting for database... (retry {retries + 1})")
 | 
			
		||||
            time.sleep(3)
 | 
			
		||||
    print("Failed to connect to database.")
 | 
			
		||||
    raise Exception("Database not ready after multiple retries.")
 | 
			
		||||
    raise Exception("Failed to connect to database.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init_db():
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,24 @@ services:
 | 
			
		||||
    build: .
 | 
			
		||||
    env_file:
 | 
			
		||||
      - api/.env
 | 
			
		||||
    ports:
 | 
			
		||||
      - 80:80
 | 
			
		||||
    healthcheck:
 | 
			
		||||
      test: ["CMD", "curl", "-f", "http://localhost/health"]
 | 
			
		||||
      interval: 10s
 | 
			
		||||
      timeout: 5s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      start_period: 15s
 | 
			
		||||
  db:
 | 
			
		||||
    container_name: db
 | 
			
		||||
    hostname: db
 | 
			
		||||
    image: mysql:latest
 | 
			
		||||
    env_file:
 | 
			
		||||
      - db/.env
 | 
			
		||||
    ports:
 | 
			
		||||
      - 3306:3306
 | 
			
		||||
    healthcheck:
 | 
			
		||||
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
 | 
			
		||||
      interval: 10s
 | 
			
		||||
      timeout: 5s
 | 
			
		||||
      retries: 5
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user