chore: initial project setup with backend, frontend, and infrastructure
Some checks failed
CI / backend (push) Failing after 31s
CI / frontend (push) Successful in 40s
CI / docker (push) Has been skipped

Add complete NexaPantry application structure including:
- Docker Compose configuration with PostgreSQL, Redis, FastAPI backend, worker, frontend and Caddy
- Environment configuration template with database, auth, and service settings
- GitHub Actions CI workflow for backend/frontend linting, testing, auditing and Docker builds
- AGPL-3.0 license and comprehensive README with setup, development, and security documentation
- Backend
This commit is contained in:
2026-06-04 10:26:38 +02:00
commit 3792ca55e7
74 changed files with 13417 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
import hashlib
import secrets
from datetime import UTC, datetime, timedelta
from typing import Any
from cryptography.fernet import Fernet, InvalidToken
from jose import JWTError, jwt
from passlib.context import CryptContext
from app.core.config import get_settings
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
ALGORITHM = "HS256"
def hash_password(password: str) -> str:
return pwd_context.hash(password)
def verify_password(password: str, password_hash: str | None) -> bool:
return bool(password_hash) and pwd_context.verify(password, password_hash)
def create_session_token(user_id: str, minutes: int = 60 * 24 * 14) -> str:
expires_at = datetime.now(UTC) + timedelta(minutes=minutes)
payload: dict[str, Any] = {"sub": user_id, "exp": expires_at}
return jwt.encode(payload, get_settings().jwt_secret_key, algorithm=ALGORITHM)
def decode_session_token(token: str) -> str | None:
try:
payload = jwt.decode(token, get_settings().jwt_secret_key, algorithms=[ALGORITHM])
return str(payload.get("sub"))
except JWTError:
return None
def new_token() -> str:
return secrets.token_urlsafe(32)
def hash_token(token: str) -> str:
return hashlib.sha256(token.encode("utf-8")).hexdigest()
def encrypt_secret(value: str | None) -> str | None:
if not value:
return None
return Fernet(get_settings().settings_secret_key.encode("utf-8")).encrypt(
value.encode("utf-8")
).decode("utf-8")
def decrypt_secret(value: str | None) -> str | None:
if not value:
return None
try:
return Fernet(get_settings().settings_secret_key.encode("utf-8")).decrypt(
value.encode("utf-8")
).decode("utf-8")
except InvalidToken:
return None
def make_csrf_token() -> str:
return secrets.token_urlsafe(24)