added pipeline
This commit is contained in:
parent
842e8a12d4
commit
6e24614c54
13 changed files with 87 additions and 15 deletions
60
.forgejo/workflows/deploy_backend.yml
Normal file
60
.forgejo/workflows/deploy_backend.yml
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
name: Build and Deploy Backend
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "backend/**"
|
||||||
|
- "pyproject.toml"
|
||||||
|
- "uv.lock"
|
||||||
|
- "deploy/deploy-backend.sh"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_deploy:
|
||||||
|
runs-on: roadrunner
|
||||||
|
|
||||||
|
container:
|
||||||
|
volumes:
|
||||||
|
- /opt/blauwasser-api:/opt/blauwasser-api
|
||||||
|
- /var/lib/blauwasser-api:/var/lib/blauwasser-api
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Repository auschecken
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Python einrichten
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.14"
|
||||||
|
|
||||||
|
# - name: Backend testen
|
||||||
|
# working-directory: backend
|
||||||
|
# run: |
|
||||||
|
# python -m pip install -U pip
|
||||||
|
# python -m pip install -e .
|
||||||
|
# pytest
|
||||||
|
|
||||||
|
- name: Backend kopieren
|
||||||
|
run: |
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude '.venv' \
|
||||||
|
--exclude '__pycache__' \
|
||||||
|
--exclude '*.db' \
|
||||||
|
backend/ /opt/blauwasser-api/backend/
|
||||||
|
cp pyproject.toml /opt/blauwasser-api/
|
||||||
|
cp uv.lock /opt/blauwasser-api/
|
||||||
|
|
||||||
|
- name: Abhängigkeiten aktualisieren
|
||||||
|
run: |
|
||||||
|
cd /opt/blauwasser-api
|
||||||
|
uv sync --frozen
|
||||||
|
|
||||||
|
- name: Backend neu starten
|
||||||
|
run: |
|
||||||
|
systemctl restart blauwasser-api
|
||||||
|
systemctl is-active --quiet blauwasser-api
|
||||||
|
|
||||||
|
- name: Deployment prüfen
|
||||||
|
run: |
|
||||||
|
curl --fail https://api.blauwasser-mausis.de/health
|
||||||
|
|
@ -17,6 +17,7 @@ dependencies = [
|
||||||
"fastapi-crons==2.5.0",
|
"fastapi-crons==2.5.0",
|
||||||
"feedparser==6.0.14",
|
"feedparser==6.0.14",
|
||||||
"PyJWT==2.15.0",
|
"PyJWT==2.15.0",
|
||||||
|
"python-dotenv>=1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
BACKEND_URL="http://localhost:8000"
|
BACKEND_URL="http://localhost:8000"
|
||||||
FRONTEND_URL="http://localhost:4321"
|
FRONTEND_URL="http://localhost:4321"
|
||||||
SECRET="IamALocalSecretAndMustBeReplacedInProduction"
|
JWT_SECRET="IamALocalSecretAndMustBeReplacedInProduction"
|
||||||
ENVIRONMENT="local"
|
ENVIRONMENT="local"
|
||||||
MAILTRAP_API_TOKEN="FakedyFake"
|
MAILTRAP_API_TOKEN="FakedyFake"
|
||||||
DB_NAME="dev"
|
DATABASE_URL="sqlite:///dev.db"
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,8 +1,8 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
ENVIRONMENT = os.environ["ENVIRONMENT"]
|
ENVIRONMENT = os.environ["ENVIRONMENT"]
|
||||||
SECRET_KEY = os.environ["SECRET"]
|
JWT_SECRET = os.environ["JWT_SECRET"]
|
||||||
FRONTEND_URL = os.environ["FRONTEND_URL"]
|
FRONTEND_URL = os.environ["FRONTEND_URL"]
|
||||||
BACKEND_URL = os.environ["BACKEND_URL"]
|
BACKEND_URL = os.environ["BACKEND_URL"]
|
||||||
MAILTRAP_API_TOKEN = os.environ["MAILTRAP_API_TOKEN"]
|
MAILTRAP_API_TOKEN = os.environ["MAILTRAP_API_TOKEN"]
|
||||||
DB_NAME = os.environ["DB_NAME"]
|
DATABASE_URL = os.environ["DATABASE_URL"]
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import Any
|
|
||||||
from backend.config import SECRET_KEY
|
from backend.config import JWT_SECRET
|
||||||
import os
|
|
||||||
from fastapi.security import HTTPBasicCredentials
|
from fastapi.security import HTTPBasicCredentials
|
||||||
from fastapi import Security
|
from fastapi import Security
|
||||||
from fastapi.security import HTTPBearer
|
from fastapi.security import HTTPBearer
|
||||||
|
|
@ -19,13 +18,13 @@ from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
ALGORITHM = "HS256"
|
ALGORITHM = "HS256"
|
||||||
ACCESS_TOKEN_EXPIRE_DAYS = 360
|
ACCESS_TOKEN_EXPIRE_DAYS = 180
|
||||||
|
|
||||||
def create_access_token(data: dict, expires_delta: timedelta | None = None):
|
def create_access_token(data: dict, expires_delta: timedelta | None = None):
|
||||||
to_encode = data.copy()
|
to_encode = data.copy()
|
||||||
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS))
|
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS))
|
||||||
to_encode.update({"exp": expire})
|
to_encode.update({"exp": expire})
|
||||||
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
return jwt.encode(to_encode, JWT_SECRET, algorithm=ALGORITHM)
|
||||||
|
|
||||||
security = HTTPBearer()
|
security = HTTPBearer()
|
||||||
|
|
||||||
|
|
@ -36,7 +35,7 @@ def get_token(credentials = Security(security)) -> str:
|
||||||
raise HTTPException(status_code=403, detail="Invalid authorization format")
|
raise HTTPException(status_code=403, detail="Invalid authorization format")
|
||||||
|
|
||||||
def decode_token(token: str)-> str | None:
|
def decode_token(token: str)-> str | None:
|
||||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
payload = jwt.decode(token, JWT_SECRET, algorithms=[ALGORITHM])
|
||||||
return payload.get("sub")
|
return payload.get("sub")
|
||||||
|
|
||||||
async def get_current_user(token: str = Depends(get_token), session: Session = Depends(get_session)):
|
async def get_current_user(token: str = Depends(get_token), session: Session = Depends(get_session)):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from backend.config import DB_NAME
|
from backend.config import DATABASE_URL
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
|
|
||||||
sqlite_url = f"sqlite:///{DB_NAME}.db"
|
sqlite_url = DATABASE_URL
|
||||||
|
|
||||||
connect_args = {"check_same_thread": False}
|
connect_args = {"check_same_thread": False}
|
||||||
engine = create_engine(sqlite_url, connect_args=connect_args)
|
engine = create_engine(sqlite_url, connect_args=connect_args)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
from dotenv.main import load_dotenv
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
from .routers import users, comments, newsletters
|
from .routers import users, comments, newsletters, health
|
||||||
from backend.internal.database import create_db_and_tables
|
from backend.internal.database import create_db_and_tables
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi_crons import Crons
|
from fastapi_crons import Crons
|
||||||
|
|
@ -26,4 +27,5 @@ def on_startup():
|
||||||
|
|
||||||
app.include_router(users.router)
|
app.include_router(users.router)
|
||||||
app.include_router(comments.router)
|
app.include_router(comments.router)
|
||||||
app.include_router(newsletters.router)
|
app.include_router(newsletters.router)
|
||||||
|
app.include_router(health.router)
|
||||||
8
backend/src/backend/routers/health.py
Normal file
8
backend/src/backend/routers/health.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/health", include_in_schema=False)
|
||||||
|
def health():
|
||||||
|
return {"status": "ok"}
|
||||||
2
backend/uv.lock
generated
2
backend/uv.lock
generated
|
|
@ -152,6 +152,7 @@ dependencies = [
|
||||||
{ name = "mailtrap" },
|
{ name = "mailtrap" },
|
||||||
{ name = "pyjwt" },
|
{ name = "pyjwt" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
|
{ name = "python-dotenv" },
|
||||||
{ name = "sqlmodel" },
|
{ name = "sqlmodel" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -165,6 +166,7 @@ requires-dist = [
|
||||||
{ name = "mailtrap", specifier = "==2.10.0" },
|
{ name = "mailtrap", specifier = "==2.10.0" },
|
||||||
{ name = "pyjwt", specifier = "==2.15.0" },
|
{ name = "pyjwt", specifier = "==2.15.0" },
|
||||||
{ name = "pytest", specifier = ">=9.1.1" },
|
{ name = "pytest", specifier = ">=9.1.1" },
|
||||||
|
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||||
{ name = "sqlmodel", specifier = ">=0.0.42" },
|
{ name = "sqlmodel", specifier = ">=0.0.42" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue