mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-14 20:03:14 -05:00
Release v0.1.0 Candidate (#85)
* Changed uvicorn port to 80 * Changed port in docker-compose to match dockerfile * Readded environment variables in docker-compose * production image rework * Use opengraph metadata to make basic recipe cards when full recipe metadata is not available * fixed instrucitons on parse * add last_recipe * automated testing * roadmap update * Sqlite (#75) * file structure * auto-test * take 2 * refactor ap scheduler and startup process * fixed scraper error * database abstraction * database abstraction * port recipes over to new schema * meal migration * start settings migration * finale mongo port * backup improvements * migration imports to new DB structure * unused import cleanup * docs strings * settings and theme import logic * cleanup * fixed tinydb error * requirements * fuzzy search * remove scratch file * sqlalchemy models * improved search ui * recipe models almost done * sql modal population * del scratch * rewrite database model mixins * mostly grabage * recipe updates * working sqllite * remove old files and reorganize * final cleanup Co-authored-by: Hayden <hay-kot@pm.me> * Backup card (#78) * backup / import dialog * upgrade to new tag method * New import card * rename settings.py to app_config.py * migrate to poetry for development * fix failing test Co-authored-by: Hayden <hay-kot@pm.me> * added mkdocs to docker-compose * Translations (#72) * Translations + danish * changed back proxy target to use ENV * Resolved more merge conflicts * Removed test in translation * Documentation of translations * Updated translations * removed old packages Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com> * fail to start bug fixes * feature: prep/cook/total time slots (#80) Co-authored-by: Hayden <hay-kot@pm.me> * missing bind attributes * Bug fixes (#81) * fix: url remains after succesful import * docs: changelog + update todos * arm image * arm compose * compose updates * update poetry * arm support Co-authored-by: Hayden <hay-kot@pm.me> * dockerfile hotfix * dockerfile hotfix * Version Release Final Touches (#84) * Remove slim * bug: opacity issues * bug: startup failure with no database * ci/cd on dev branch * formatting * v0.1.0 documentation Co-authored-by: Hayden <hay-kot@pm.me> * db init hotfix * bug: fix crash in mongo * fix mongo bug * fixed version notifier * finale changelog Co-authored-by: kentora <=> Co-authored-by: Hayden <hay-kot@pm.me> Co-authored-by: Richard Mitic <richard.h.mitic@gmail.com> Co-authored-by: kentora <kentora@kentora.dk>
This commit is contained in:
@@ -1,51 +1,59 @@
|
||||
import operator
|
||||
|
||||
from app_config import BACKUP_DIR, TEMPLATE_DIR
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from models.backup_models import BackupJob, Imports
|
||||
from services.backup_services import (
|
||||
BACKUP_DIR,
|
||||
TEMPLATE_DIR,
|
||||
export_db,
|
||||
import_from_archive,
|
||||
)
|
||||
from models.backup_models import BackupJob, ImportJob, Imports, LocalBackup
|
||||
from services.backups.exports import backup_all
|
||||
from services.backups.imports import ImportDatabase
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(tags=["Import / Export"])
|
||||
|
||||
|
||||
@router.get("/api/backups/available/", tags=["Import / Export"], response_model=Imports)
|
||||
async def available_imports():
|
||||
@router.get("/api/backups/available/", response_model=Imports)
|
||||
def available_imports():
|
||||
"""Returns a list of avaiable .zip files for import into Mealie."""
|
||||
imports = []
|
||||
templates = []
|
||||
for archive in BACKUP_DIR.glob("*.zip"):
|
||||
imports.append(archive.name)
|
||||
backup = LocalBackup(name=archive.name, date=archive.stat().st_ctime)
|
||||
imports.append(backup)
|
||||
|
||||
for template in TEMPLATE_DIR.glob("*.md"):
|
||||
templates.append(template.name)
|
||||
|
||||
imports.sort(key=operator.attrgetter("date"), reverse=True)
|
||||
|
||||
return Imports(imports=imports, templates=templates)
|
||||
|
||||
|
||||
@router.post("/api/backups/export/database/", tags=["Import / Export"], status_code=201)
|
||||
async def export_database(data: BackupJob):
|
||||
@router.post("/api/backups/export/database/", status_code=201)
|
||||
def export_database(data: BackupJob):
|
||||
"""Generates a backup of the recipe database in json format."""
|
||||
|
||||
export_path = backup_all(data.tag, data.template)
|
||||
try:
|
||||
export_path = export_db(data.tag, data.template)
|
||||
return SnackResponse.success("Backup Created at " + export_path)
|
||||
except:
|
||||
HTTPException(
|
||||
status_code=400,
|
||||
detail=SnackResponse.error("Error Creating Backup. See Log File"),
|
||||
)
|
||||
|
||||
return SnackResponse.success("Backup Created at " + export_path)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/api/backups/{file_name}/import/", tags=["Import / Export"], status_code=200
|
||||
)
|
||||
async def import_database(file_name: str):
|
||||
@router.post("/api/backups/{file_name}/import/", status_code=200)
|
||||
def import_database(file_name: str, import_data: ImportJob):
|
||||
""" Import a database backup file generated from Mealie. """
|
||||
imported = import_from_archive(file_name)
|
||||
|
||||
import_db = ImportDatabase(
|
||||
zip_archive=import_data.name,
|
||||
import_recipes=import_data.recipes,
|
||||
force_import=import_data.force,
|
||||
rebase=import_data.rebase,
|
||||
import_settings=import_data.settings,
|
||||
import_themes=import_data.themes,
|
||||
)
|
||||
|
||||
imported = import_db.run()
|
||||
return imported
|
||||
|
||||
|
||||
@@ -54,7 +62,7 @@ async def import_database(file_name: str):
|
||||
tags=["Import / Export"],
|
||||
status_code=200,
|
||||
)
|
||||
async def delete_backup(backup_name: str):
|
||||
def delete_backup(backup_name: str):
|
||||
""" Removes a database backup from the file system """
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from models.recipe_models import SlugResponse
|
||||
from services.meal_services import MealPlan
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(tags=["Meal Plan"])
|
||||
|
||||
|
||||
@router.get("/api/meal-plan/all/", tags=["Meal Plan"], response_model=List[MealPlan])
|
||||
async def get_all_meals():
|
||||
@router.get("/api/meal-plan/all/", response_model=List[MealPlan])
|
||||
def get_all_meals():
|
||||
""" Returns a list of all available Meal Plan """
|
||||
|
||||
return MealPlan.get_all()
|
||||
|
||||
|
||||
@router.post("/api/meal-plan/create/", tags=["Meal Plan"])
|
||||
async def set_meal_plan(data: MealPlan):
|
||||
@router.post("/api/meal-plan/create/")
|
||||
def set_meal_plan(data: MealPlan):
|
||||
""" Creates a meal plan database entry """
|
||||
data.process_meals()
|
||||
data.save_to_db()
|
||||
@@ -29,24 +28,25 @@ async def set_meal_plan(data: MealPlan):
|
||||
return SnackResponse.success("Mealplan Created")
|
||||
|
||||
|
||||
@router.post("/api/meal-plan/{plan_id}/update/", tags=["Meal Plan"])
|
||||
async def update_meal_plan(plan_id: str, meal_plan: MealPlan):
|
||||
@router.post("/api/meal-plan/{plan_id}/update/")
|
||||
def update_meal_plan(plan_id: str, meal_plan: MealPlan):
|
||||
""" Updates a meal plan based off ID """
|
||||
|
||||
try:
|
||||
meal_plan.process_meals()
|
||||
meal_plan.update(plan_id)
|
||||
except:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=SnackResponse.error("Unable to Update Mealplan"),
|
||||
)
|
||||
meal_plan.process_meals()
|
||||
meal_plan.update(plan_id)
|
||||
# try:
|
||||
# meal_plan.process_meals()
|
||||
# meal_plan.update(plan_id)
|
||||
# except:
|
||||
# raise HTTPException(
|
||||
# status_code=404,
|
||||
# detail=SnackResponse.error("Unable to Update Mealplan"),
|
||||
# )
|
||||
|
||||
return SnackResponse.success("Mealplan Updated")
|
||||
|
||||
|
||||
@router.delete("/api/meal-plan/{plan_id}/delete/", tags=["Meal Plan"])
|
||||
async def delete_meal_plan(plan_id):
|
||||
@router.delete("/api/meal-plan/{plan_id}/delete/")
|
||||
def delete_meal_plan(plan_id):
|
||||
""" Removes a meal plan from the database """
|
||||
|
||||
MealPlan.delete(plan_id)
|
||||
@@ -58,7 +58,7 @@ async def delete_meal_plan(plan_id):
|
||||
"/api/meal-plan/today/",
|
||||
tags=["Meal Plan"],
|
||||
)
|
||||
async def get_today():
|
||||
def get_today():
|
||||
"""
|
||||
Returns the recipe slug for the meal scheduled for today.
|
||||
If no meal is scheduled nothing is returned
|
||||
@@ -67,8 +67,8 @@ async def get_today():
|
||||
return MealPlan.today()
|
||||
|
||||
|
||||
@router.get("/api/meal-plan/this-week/", tags=["Meal Plan"], response_model=MealPlan)
|
||||
async def get_this_week():
|
||||
@router.get("/api/meal-plan/this-week/", response_model=MealPlan)
|
||||
def get_this_week():
|
||||
""" Returns the meal plan data for this week """
|
||||
|
||||
return MealPlan.this_week()
|
||||
|
||||
@@ -4,15 +4,15 @@ from fastapi import APIRouter, File, HTTPException, UploadFile
|
||||
from models.migration_models import ChowdownURL
|
||||
from services.migrations.chowdown import chowdown_migrate as chowdow_migrate
|
||||
from services.migrations.nextcloud import migrate as nextcloud_migrate
|
||||
from settings import MIGRATION_DIR
|
||||
from app_config import MIGRATION_DIR
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(tags=["Migration"])
|
||||
|
||||
|
||||
# Chowdown
|
||||
@router.post("/api/migration/chowdown/repo/", tags=["Migration"])
|
||||
async def import_chowdown_recipes(repo: ChowdownURL):
|
||||
@router.post("/api/migration/chowdown/repo/")
|
||||
def import_chowdown_recipes(repo: ChowdownURL):
|
||||
""" Import Chowsdown Recipes from Repo URL """
|
||||
try:
|
||||
report = chowdow_migrate(repo.url)
|
||||
@@ -30,8 +30,8 @@ async def import_chowdown_recipes(repo: ChowdownURL):
|
||||
|
||||
|
||||
# Nextcloud
|
||||
@router.get("/api/migration/nextcloud/available/", tags=["Migration"])
|
||||
async def get_avaiable_nextcloud_imports():
|
||||
@router.get("/api/migration/nextcloud/available/")
|
||||
def get_avaiable_nextcloud_imports():
|
||||
""" Returns a list of avaiable directories that can be imported into Mealie """
|
||||
available = []
|
||||
for dir in MIGRATION_DIR.iterdir():
|
||||
@@ -43,15 +43,15 @@ async def get_avaiable_nextcloud_imports():
|
||||
return available
|
||||
|
||||
|
||||
@router.post("/api/migration/nextcloud/{selection}/import/", tags=["Migration"])
|
||||
async def import_nextcloud_directory(selection: str):
|
||||
@router.post("/api/migration/nextcloud/{selection}/import/")
|
||||
def import_nextcloud_directory(selection: str):
|
||||
""" Imports all the recipes in a given directory """
|
||||
|
||||
return nextcloud_migrate(selection)
|
||||
|
||||
|
||||
@router.delete("/api/migration/{file_folder_name}/delete/", tags=["Migration"])
|
||||
async def delete_migration_data(file_folder_name: str):
|
||||
@router.delete("/api/migration/{file_folder_name}/delete/")
|
||||
def delete_migration_data(file_folder_name: str):
|
||||
""" Removes migration data from the file system """
|
||||
|
||||
remove_path = MIGRATION_DIR.joinpath(file_folder_name)
|
||||
@@ -66,8 +66,8 @@ async def delete_migration_data(file_folder_name: str):
|
||||
return SnackResponse.info(f"Migration Data Remove: {remove_path.absolute()}")
|
||||
|
||||
|
||||
@router.post("/api/migration/upload/", tags=["Migration"])
|
||||
async def upload_nextcloud_zipfile(archive: UploadFile = File(...)):
|
||||
@router.post("/api/migration/upload/")
|
||||
def upload_nextcloud_zipfile(archive: UploadFile = File(...)):
|
||||
""" Upload a .zip File to later be imported into Mealie """
|
||||
dest = MIGRATION_DIR.joinpath(archive.filename)
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@ from services.recipe_services import Recipe, read_requested_values
|
||||
from services.scrape_services import create_from_url
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
router = APIRouter(tags=["Recipes"])
|
||||
|
||||
|
||||
@router.get("/api/all-recipes/", tags=["Recipes"], response_model=List[dict])
|
||||
async def get_all_recipes(
|
||||
keys: Optional[List[str]] = Query(...), num: Optional[int] = 100
|
||||
):
|
||||
@router.get("/api/all-recipes/", response_model=List[dict])
|
||||
def get_all_recipes(keys: Optional[List[str]] = Query(...), num: Optional[int] = 100):
|
||||
"""
|
||||
Returns key data for all recipes based off the query paramters provided.
|
||||
For example, if slug, image, and name are provided you will recieve a list of
|
||||
@@ -30,8 +28,8 @@ async def get_all_recipes(
|
||||
return all_recipes
|
||||
|
||||
|
||||
@router.post("/api/all-recipes/", tags=["Recipes"], response_model=List[dict])
|
||||
async def get_all_recipes_post(body: AllRecipeRequest):
|
||||
@router.post("/api/all-recipes/", response_model=List[dict])
|
||||
def get_all_recipes_post(body: AllRecipeRequest):
|
||||
"""
|
||||
Returns key data for all recipes based off the body data provided.
|
||||
For example, if slug, image, and name are provided you will recieve a list of
|
||||
@@ -46,16 +44,16 @@ async def get_all_recipes_post(body: AllRecipeRequest):
|
||||
return all_recipes
|
||||
|
||||
|
||||
@router.get("/api/recipe/{recipe_slug}/", tags=["Recipes"], response_model=Recipe)
|
||||
async def get_recipe(recipe_slug: str):
|
||||
@router.get("/api/recipe/{recipe_slug}/", response_model=Recipe)
|
||||
def get_recipe(recipe_slug: str):
|
||||
""" Takes in a recipe slug, returns all data for a recipe """
|
||||
recipe = Recipe.get_by_slug(recipe_slug)
|
||||
|
||||
return recipe
|
||||
|
||||
|
||||
@router.get("/api/recipe/image/{recipe_slug}/", tags=["Recipes"])
|
||||
async def get_recipe_img(recipe_slug: str):
|
||||
@router.get("/api/recipe/image/{recipe_slug}/")
|
||||
def get_recipe_img(recipe_slug: str):
|
||||
""" Takes in a recipe slug, returns the static image """
|
||||
recipe_image = read_image(recipe_slug)
|
||||
|
||||
@@ -69,7 +67,7 @@ async def get_recipe_img(recipe_slug: str):
|
||||
status_code=201,
|
||||
response_model=str,
|
||||
)
|
||||
async def parse_recipe_url(url: RecipeURLIn):
|
||||
def parse_recipe_url(url: RecipeURLIn):
|
||||
""" Takes in a URL and attempts to scrape data and load it into the database """
|
||||
|
||||
slug = create_from_url(url.url)
|
||||
@@ -77,35 +75,36 @@ async def parse_recipe_url(url: RecipeURLIn):
|
||||
return slug
|
||||
|
||||
|
||||
@router.post("/api/recipe/create/", tags=["Recipes"])
|
||||
async def create_from_json(data: Recipe) -> str:
|
||||
@router.post("/api/recipe/create/")
|
||||
def create_from_json(data: Recipe) -> str:
|
||||
""" Takes in a JSON string and loads data into the database as a new entry"""
|
||||
created_recipe = data.save_to_db()
|
||||
|
||||
return created_recipe
|
||||
|
||||
|
||||
@router.post("/api/recipe/{recipe_slug}/update/image/", tags=["Recipes"])
|
||||
@router.post("/api/recipe/{recipe_slug}/update/image/")
|
||||
def update_recipe_image(
|
||||
recipe_slug: str, image: bytes = File(...), extension: str = Form(...)
|
||||
):
|
||||
""" Removes an existing image and replaces it with the incoming file. """
|
||||
response = write_image(recipe_slug, image, extension)
|
||||
Recipe.update_image(recipe_slug, extension)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.post("/api/recipe/{recipe_slug}/update/", tags=["Recipes"])
|
||||
async def update_recipe(recipe_slug: str, data: Recipe):
|
||||
""" Updates a recipe by existing slug and data. Data should containt """
|
||||
@router.post("/api/recipe/{recipe_slug}/update/")
|
||||
def update_recipe(recipe_slug: str, data: Recipe):
|
||||
""" Updates a recipe by existing slug and data. """
|
||||
|
||||
data.update(recipe_slug)
|
||||
new_slug = data.update(recipe_slug)
|
||||
|
||||
return {"message": "PLACEHOLDER"}
|
||||
return new_slug
|
||||
|
||||
|
||||
@router.delete("/api/recipe/{recipe_slug}/delete/", tags=["Recipes"])
|
||||
async def delete_recipe(recipe_slug: str):
|
||||
@router.delete("/api/recipe/{recipe_slug}/delete/")
|
||||
def delete_recipe(recipe_slug: str):
|
||||
""" Deletes a recipe by slug """
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,99 +1,91 @@
|
||||
from typing import List
|
||||
|
||||
from db.mongo_setup import global_init
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from services.scheduler_services import Scheduler, post_webhooks
|
||||
from services.scheduler_services import post_webhooks
|
||||
from services.settings_services import SiteSettings, SiteTheme
|
||||
from utils.global_scheduler import scheduler
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
global_init()
|
||||
|
||||
scheduler = Scheduler()
|
||||
scheduler.startup_scheduler()
|
||||
router = APIRouter(tags=["Settings"])
|
||||
|
||||
|
||||
@router.get("/api/site-settings/", tags=["Settings"])
|
||||
async def get_main_settings():
|
||||
@router.get("/api/site-settings/")
|
||||
def get_main_settings():
|
||||
""" Returns basic site settings """
|
||||
|
||||
return SiteSettings.get_site_settings()
|
||||
|
||||
|
||||
@router.post("/api/site-settings/webhooks/test/", tags=["Settings"])
|
||||
async def test_webhooks():
|
||||
@router.post("/api/site-settings/webhooks/test/")
|
||||
def test_webhooks():
|
||||
""" Run the function to test your webhooks """
|
||||
|
||||
return post_webhooks()
|
||||
|
||||
|
||||
@router.post("/api/site-settings/update/", tags=["Settings"])
|
||||
async def update_settings(data: SiteSettings):
|
||||
@router.post("/api/site-settings/update/")
|
||||
def update_settings(data: SiteSettings):
|
||||
""" Returns Site Settings """
|
||||
|
||||
try:
|
||||
data.update()
|
||||
except:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=SnackResponse.error("Unable to Save Settings")
|
||||
)
|
||||
data.update()
|
||||
# try:
|
||||
# data.update()
|
||||
# except:
|
||||
# raise HTTPException(
|
||||
# status_code=400, detail=SnackResponse.error("Unable to Save Settings")
|
||||
# )
|
||||
|
||||
scheduler.reschedule_webhooks()
|
||||
return SnackResponse.success("Settings Updated")
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/site-settings/themes/", tags=["Themes"]
|
||||
)
|
||||
async def get_all_themes():
|
||||
@router.get("/api/site-settings/themes/", tags=["Themes"])
|
||||
def get_all_themes():
|
||||
""" Returns all site themes """
|
||||
|
||||
return SiteTheme.get_all()
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/site-settings/themes/{theme_name}/", tags=["Themes"]
|
||||
)
|
||||
async def get_single_theme(theme_name: str):
|
||||
@router.get("/api/site-settings/themes/{theme_name}/", tags=["Themes"])
|
||||
def get_single_theme(theme_name: str):
|
||||
""" Returns a named theme """
|
||||
return SiteTheme.get_by_name(theme_name)
|
||||
|
||||
|
||||
@router.post("/api/site-settings/themes/create/", tags=["Themes"])
|
||||
async def create_theme(data: SiteTheme):
|
||||
def create_theme(data: SiteTheme):
|
||||
""" Creates a site color theme database entry """
|
||||
|
||||
try:
|
||||
data.save_to_db()
|
||||
except:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=SnackResponse.error("Unable to Save Theme")
|
||||
)
|
||||
data.save_to_db()
|
||||
# try:
|
||||
# data.save_to_db()
|
||||
# except:
|
||||
# raise HTTPException(
|
||||
# status_code=400, detail=SnackResponse.error("Unable to Save Theme")
|
||||
# )
|
||||
|
||||
return SnackResponse.success("Theme Saved")
|
||||
|
||||
|
||||
@router.post("/api/site-settings/themes/{theme_name}/update/", tags=["Themes"])
|
||||
async def update_theme(theme_name: str, data: SiteTheme):
|
||||
def update_theme(theme_name: str, data: SiteTheme):
|
||||
""" Update a theme database entry """
|
||||
try:
|
||||
data.update_document()
|
||||
except:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=SnackResponse.error("Unable to Update Theme")
|
||||
)
|
||||
data.update_document()
|
||||
|
||||
# try:
|
||||
# except:
|
||||
# raise HTTPException(
|
||||
# status_code=400, detail=SnackResponse.error("Unable to Update Theme")
|
||||
# )
|
||||
|
||||
return SnackResponse.success("Theme Updated")
|
||||
|
||||
|
||||
@router.delete("/api/site-settings/themes/{theme_name}/delete/", tags=["Themes"])
|
||||
async def delete_theme(theme_name: str):
|
||||
def delete_theme(theme_name: str):
|
||||
""" Deletes theme from the database """
|
||||
try:
|
||||
SiteTheme.delete_theme(theme_name)
|
||||
except:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=SnackResponse.error("Unable to Delete Theme")
|
||||
)
|
||||
SiteTheme.delete_theme(theme_name)
|
||||
# try:
|
||||
# SiteTheme.delete_theme(theme_name)
|
||||
# except:
|
||||
# raise HTTPException(
|
||||
# status_code=400, detail=SnackResponse.error("Unable to Delete Theme")
|
||||
# )
|
||||
|
||||
return SnackResponse.success("Theme Deleted")
|
||||
|
||||
@@ -6,19 +6,19 @@ from fastapi.responses import FileResponse
|
||||
CWD = Path(__file__).parent
|
||||
WEB_PATH = CWD.parent.joinpath("dist")
|
||||
BASE_HTML = WEB_PATH.joinpath("index.html")
|
||||
router = APIRouter()
|
||||
router = APIRouter(include_in_schema=False)
|
||||
|
||||
|
||||
@router.get("/favicon.ico", include_in_schema=False)
|
||||
@router.get("/favicon.ico")
|
||||
def facivon():
|
||||
return responses.RedirectResponse(url="/mealie/favicon.ico")
|
||||
|
||||
|
||||
@router.get("/", include_in_schema=False)
|
||||
@router.get("/")
|
||||
def root():
|
||||
return FileResponse(BASE_HTML)
|
||||
|
||||
|
||||
@router.get("/{full_path:path}", include_in_schema=False)
|
||||
@router.get("/{full_path:path}")
|
||||
def root_plus(full_path):
|
||||
return FileResponse(BASE_HTML)
|
||||
|
||||
Reference in New Issue
Block a user