Vorbereitung Release 0.2.0 #83

Merged
tpeetz merged 178 commits from develop/0.2.0 into main 2026-01-29 22:50:42 +00:00
10 changed files with 55 additions and 18 deletions
Showing only changes of commit 4a2048c378 - Show all commits
+6 -2
View File
@@ -41,6 +41,11 @@ services:
- kontor-api:0.2.0-SNAPSHOT - kontor-api:0.2.0-SNAPSHOT
image: kontor-api:0.2.0-SNAPSHOT image: kontor-api:0.2.0-SNAPSHOT
restart: unless-stopped restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://kontor-api:8800/health"]
interval: 10s
timeout: 5s
retries: 3
networks: networks:
- database - database
- integration - integration
@@ -67,7 +72,7 @@ services:
ports: ports:
- 8200:80 - 8200:80
depends_on: depends_on:
postgres: kontor-api:
condition: service_healthy condition: service_healthy
kontor-vue: kontor-vue:
build: build:
@@ -95,4 +100,3 @@ networks:
volumes: volumes:
activemq-data: activemq-data:
images-data: images-data:
+2
View File
@@ -34,6 +34,8 @@ FROM python:3.13-slim-bookworm AS production
#RUN --mount=type=secret,id=secret-key,target=secrets.json #RUN --mount=type=secret,id=secret-key,target=secrets.json
RUN apt-get update && apt-get install --no-install-recommends -y curl
RUN useradd --create-home appuser RUN useradd --create-home appuser
USER appuser USER appuser
-1
View File
@@ -7,7 +7,6 @@ from fastapi.security import OAuth2PasswordRequestForm
from src.core.config import settings from src.core.config import settings
from src.core.security import create_access_token, authenticate_user, get_current_active_user from src.core.security import create_access_token, authenticate_user, get_current_active_user
from src.db.models.admin import Profile from src.db.models.admin import Profile
from src.db.session import SessionDep
from src.schema.admin import Token, ProfileModel from src.schema.admin import Token, ProfileModel
from src.webapps.auth.forms import LoginForm from src.webapps.auth.forms import LoginForm
@@ -0,0 +1,25 @@
from fastapi import APIRouter, status
from src.schema.admin import HealthCheck
health_router = APIRouter()
@health_router.get(
"/health",
tags=["healthcheck"],
summary="Perform a health check",
response_description="Return HTTP status code 200 (OK)",
status_code=status.HTTP_200_OK
)
def health_check() -> HealthCheck:
"""
## Perform a health check
Endpoint to perform a healthcheck on. This endpoint can primarily be used Docker
to ensure a robust container orchestration and management is in place. Other
services which rely on proper functioning of the API service will not deploy if this
endpoint returns any other HTTP status code except 200 (OK).
:return:
HealthCheck: Returns a JSON response with the health status
"""
return HealthCheck(status="ok")
+3 -2
View File
@@ -34,11 +34,12 @@ LOGGING_CONFIG: dict[str, Any] = {
}, },
"loggers": { "loggers": {
"root": {"handlers": ["default"], "level": "INFO", "propagate": False}, "root": {"handlers": ["default"], "level": "INFO", "propagate": False},
"kontor": {"handlers": ["default"], "level": "INFO", "propagate": True},
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False}, "uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
"uvicorn.error": {"level": "INFO"}, "uvicorn.error": {"level": "INFO"},
"uvicorn.access": {"handlers": ["default"], "level": "INFO", "propagate": False}, "uvicorn.access": {"handlers": ["default"], "level": "WARNING", "propagate": False},
}, },
} }
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger(__name__) logger = logging.getLogger('kontor')
+3 -3
View File
@@ -65,7 +65,7 @@ class OAuth2PasswordBearerWithCookie(OAuth2):
def authenticate_user(username: str, password: str) -> Optional[Profile]: def authenticate_user(username: str, password: str) -> Optional[Profile]:
with SessionLocal() as db: with SessionLocal() as db:
user = get_profile(username=username, db=db) user = get_profile(username=username, db=db)
print(user) logger.info(user)
if not user: if not user:
return None return None
if bcrypt.checkpw(password.encode(), user.password.encode()): if bcrypt.checkpw(password.encode(), user.password.encode()):
@@ -103,7 +103,7 @@ async def get_current_user(security_scopes: SecurityScopes, token: Annotated[str
try: try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub") username: str = payload.get("sub")
print("username/email extracted is ", username) logger.info("username/email extracted is ", username)
if username is None: if username is None:
raise credentials_exception raise credentials_exception
scope: str = payload.get("scope", "") scope: str = payload.get("scope", "")
@@ -147,7 +147,7 @@ def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
) )
username: str = payload.get("sub") username: str = payload.get("sub")
print("username/email extracted is ", username) logger.info("username/email extracted is ", username)
if username is None: if username is None:
raise credentials_exception raise credentials_exception
except JWTError: except JWTError:
+4 -2
View File
@@ -1,4 +1,6 @@
import databases import databases
from src.core.log_conf import logger
from src.db.session import SQLALCHEMY_DATABASE_URL from src.db.session import SQLALCHEMY_DATABASE_URL
@@ -9,7 +11,7 @@ async def check_db_connected():
if not database.is_connected: if not database.is_connected:
await database.connect() await database.connect()
await database.execute("SELECT 1") await database.execute("SELECT 1")
print("Database is connected (^_^)") logger.info("Database is connected (^_^)")
except Exception as e: except Exception as e:
print( print(
"Looks like db is missing or is there is some problem in connection,see below traceback" "Looks like db is missing or is there is some problem in connection,see below traceback"
@@ -23,6 +25,6 @@ async def check_db_disconnected():
database = databases.Database(SQLALCHEMY_DATABASE_URL) database = databases.Database(SQLALCHEMY_DATABASE_URL)
if database.is_connected: if database.is_connected:
await database.disconnect() await database.disconnect()
print("Database is Disconnected (-_-) zZZ") logger.info("Database is Disconnected (-_-) zZZ")
except Exception as e: except Exception as e:
raise e raise e
+3 -3
View File
@@ -1,5 +1,3 @@
import logging
import logging.config
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi import FastAPI from fastapi import FastAPI
@@ -7,7 +5,8 @@ from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from src.apis.base import api_router from src.apis.base import api_router
from src.core.log_conf import LOGGING_CONFIG, logger from src.apis.version1.healthcheck import health_router
from src.core.log_conf import logger
from src.db.session import engine from src.db.session import engine
from src.db.utils import check_db_connected, check_db_disconnected from src.db.utils import check_db_connected, check_db_disconnected
from src.webapps.base import api_router as web_app_router from src.webapps.base import api_router as web_app_router
@@ -24,6 +23,7 @@ async def lifespan(app: FastAPI):
def include_router(app: FastAPI): def include_router(app: FastAPI):
app.include_router(api_router) app.include_router(api_router)
app.include_router(web_app_router) app.include_router(web_app_router)
app.include_router(health_router)
def configure_static(app: FastAPI): def configure_static(app: FastAPI):
app.mount("/static", StaticFiles(directory="src/static"), name="static") app.mount("/static", StaticFiles(directory="src/static"), name="static")
+6
View File
@@ -19,3 +19,9 @@ class ProfileModel(BaseModel):
first_name: str first_name: str
last_name: str last_name: str
active: bool active: bool
class HealthCheck(BaseModel):
"""
Health check model
"""
status: str = "ok"
+2 -4
View File
@@ -1,12 +1,10 @@
from typing import AnyStr
from fastapi import APIRouter, Request, status, responses from fastapi import APIRouter, Request, status, responses
from fastapi.security.utils import get_authorization_scheme_param from fastapi.security.utils import get_authorization_scheme_param
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from src.core.security import get_current_user_from_token
from src.db.models.media import MediaVideo from src.db.models.media import MediaVideo
from src.db.repository.media import create_new_video from src.db.repository.media import create_new_video
#from src.apis.version1.admin import get_current_user_from_token
from src.db.models.admin import Profile from src.db.models.admin import Profile
from src.db.session import SessionDep from src.db.session import SessionDep
from src.schema.media.video import AddLink from src.schema.media.video import AddLink
@@ -28,7 +26,7 @@ def get_mediavideos(db: SessionDep, request: Request, msg: str = None):
return templates.TemplateResponse("media/videos.html", {"request": request, "msg": msg, "user": None, "mediavideos": mediavideos}) return templates.TemplateResponse("media/videos.html", {"request": request, "msg": msg, "user": None, "mediavideos": mediavideos})
@router.get("/videos/{video_id}") @router.get("/videos/{video_id}")
def video_details(video_id: AnyStr, request: Request, db: SessionDep): def video_details(video_id: str, request: Request, db: SessionDep):
mediavideo = db.get(MediaVideo, video_id) mediavideo = db.get(MediaVideo, video_id)
return templates.TemplateResponse("media/video_detail.html", {"request": request, "mediavideo":mediavideo}) return templates.TemplateResponse("media/video_detail.html", {"request": request, "mediavideo":mediavideo})