mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-11 21:05:15 -05:00
feature/debug-info (#286)
* rename 'ENV' to 'PRODUCTION' and default to true * set env PRODUCTION * refactor file download process * add last_recipe.json and log downloads * changelog + version bump * set env on workflows * bump version Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
@@ -5,7 +5,7 @@ from mealie.core import root_logger
|
||||
|
||||
# import utils.startup as startup
|
||||
from mealie.core.config import APP_VERSION, settings
|
||||
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes
|
||||
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
|
||||
from mealie.routes.groups import groups
|
||||
from mealie.routes.mealplans import mealplans
|
||||
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, tag_routes
|
||||
@@ -29,6 +29,7 @@ def start_scheduler():
|
||||
|
||||
def api_routers():
|
||||
# Authentication
|
||||
app.include_router(utility_routes.router)
|
||||
app.include_router(users.router)
|
||||
app.include_router(groups.router)
|
||||
# Recipes
|
||||
@@ -36,7 +37,6 @@ def api_routers():
|
||||
app.include_router(category_routes.router)
|
||||
app.include_router(tag_routes.router)
|
||||
app.include_router(recipe_crud_routes.router)
|
||||
|
||||
# Meal Routes
|
||||
app.include_router(mealplans.router)
|
||||
# Settings Routes
|
||||
|
||||
@@ -3,16 +3,19 @@ import secrets
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
import dotenv
|
||||
from pydantic import BaseSettings, Field, validator
|
||||
|
||||
APP_VERSION = "v0.4.1"
|
||||
APP_VERSION = "v0.4.2"
|
||||
DB_VERSION = "v0.4.0"
|
||||
|
||||
CWD = Path(__file__).parent
|
||||
BASE_DIR = CWD.parent.parent
|
||||
|
||||
ENV = BASE_DIR.joinpath(".env")
|
||||
PRODUCTION = os.getenv("ENV", "False").lower() in ["true", "1"]
|
||||
|
||||
dotenv.load_dotenv(ENV)
|
||||
PRODUCTION = os.getenv("PRODUCTION", "True").lower() in ["true", "1"]
|
||||
|
||||
|
||||
def determine_data_dir(production: bool) -> Path:
|
||||
@@ -83,7 +86,7 @@ app_dirs = AppDirectories(CWD, DATA_DIR)
|
||||
|
||||
class AppSettings(BaseSettings):
|
||||
global DATA_DIR
|
||||
PRODUCTION: bool = Field(False, env="ENV")
|
||||
PRODUCTION: bool = Field(True, env="PRODUCTION")
|
||||
IS_DEMO: bool = False
|
||||
API_PORT: int = 9000
|
||||
API_DOCS: bool = True
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from datetime import datetime, timedelta
|
||||
from mealie.schema.user import UserInDB
|
||||
from pathlib import Path
|
||||
|
||||
from jose import jwt
|
||||
from mealie.core.config import settings
|
||||
from mealie.db.database import db
|
||||
from mealie.schema.user import UserInDB
|
||||
from passlib.context import CryptContext
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
@@ -20,6 +21,11 @@ def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
|
||||
return jwt.encode(to_encode, settings.SECRET, algorithm=ALGORITHM)
|
||||
|
||||
|
||||
def create_file_token(file_path: Path) -> bool:
|
||||
token_data = {"file": str(file_path)}
|
||||
return create_access_token(token_data, expires_delta=timedelta(minutes=30))
|
||||
|
||||
|
||||
def authenticate_user(session, email: str, password: str) -> UserInDB:
|
||||
user: UserInDB = db.users.get(session, email, "email")
|
||||
if not user:
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import operator
|
||||
import shutil
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
||||
from mealie.core.config import app_dirs
|
||||
from mealie.core.security import create_file_token
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.deps import get_current_user
|
||||
from mealie.routes.deps import get_current_user, validate_file_token
|
||||
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
||||
from mealie.schema.snackbar import SnackResponse
|
||||
from mealie.services.backups import imports
|
||||
@@ -68,13 +70,10 @@ def upload_backup_file(archive: UploadFile = File(...)):
|
||||
|
||||
@router.get("/{file_name}/download")
|
||||
async def download_backup_file(file_name: str):
|
||||
""" Upload a .zip File to later be imported into Mealie """
|
||||
""" Returns a token to download a file """
|
||||
file = app_dirs.BACKUP_DIR.joinpath(file_name)
|
||||
|
||||
if file.is_file:
|
||||
return FileResponse(file, media_type="application/octet-stream", filename=file_name)
|
||||
else:
|
||||
return SnackResponse.error("No File Found")
|
||||
return {"fileToken": create_file_token(file)}
|
||||
|
||||
|
||||
@router.post("/{file_name}/import", status_code=200)
|
||||
|
||||
@@ -3,6 +3,7 @@ import json
|
||||
from fastapi import APIRouter, Depends
|
||||
from mealie.core.config import APP_VERSION, app_dirs, settings
|
||||
from mealie.core.root_logger import LOGGER_FILE
|
||||
from mealie.core.security import create_file_token
|
||||
from mealie.routes.deps import get_current_user
|
||||
from mealie.schema.debug import AppInfo, DebugInfo
|
||||
|
||||
@@ -37,10 +38,8 @@ async def get_mealie_version():
|
||||
|
||||
@router.get("/last-recipe-json")
|
||||
async def get_last_recipe_json(current_user=Depends(get_current_user)):
|
||||
""" Doc Str """
|
||||
|
||||
with open(app_dirs.DEBUG_DIR.joinpath("last_recipe.json"), "r") as f:
|
||||
return json.loads(f.read())
|
||||
""" Returns a token to download a file """
|
||||
return {"fileToken": create_file_token(app_dirs.DEBUG_DIR.joinpath("last_recipe.json"))}
|
||||
|
||||
|
||||
@router.get("/log/{num}")
|
||||
@@ -51,6 +50,12 @@ async def get_log(num: int, current_user=Depends(get_current_user)):
|
||||
return log_text
|
||||
|
||||
|
||||
@router.get("/log")
|
||||
async def get_log_file():
|
||||
""" Returns a token to download a file """
|
||||
return {"fileToken": create_file_token(LOGGER_FILE)}
|
||||
|
||||
|
||||
def tail(f, lines=20):
|
||||
total_lines_wanted = lines
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
@@ -25,7 +28,25 @@ async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(
|
||||
token_data = TokenData(username=username)
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
user = db.users.get(session, token_data.username, "email")
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
|
||||
async def validate_file_token(token: Optional[str] = None) -> Path:
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="could not validate file token",
|
||||
)
|
||||
if not token:
|
||||
return None
|
||||
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
|
||||
file_path = Path(payload.get("file"))
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
return file_path
|
||||
|
||||
20
mealie/routes/utility_routes.py
Normal file
20
mealie/routes/utility_routes.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from mealie.routes.deps import validate_file_token
|
||||
from mealie.schema.snackbar import SnackResponse
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
|
||||
|
||||
|
||||
@router.get("/download")
|
||||
async def download_file(file_path: Optional[Path] = Depends(validate_file_token)):
|
||||
""" Uses a file token obtained by an active user to retrieve a file from the operating
|
||||
system. """
|
||||
print("File Name:", file_path)
|
||||
if file_path.is_file():
|
||||
return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name)
|
||||
else:
|
||||
return SnackResponse.error("No File Found")
|
||||
Reference in New Issue
Block a user