mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
imrpove api docs
This commit is contained in:
parent
cbb68a7ea8
commit
048114f2f2
20
.github/workflows/build-docs.yml
vendored
Normal file
20
.github/workflows/build-docs.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: Publish docs via GitHub Pages
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Deploy docs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout main
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Deploy docs
|
||||||
|
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CONFIG_FILE: docs/mkdocs.yml
|
||||||
|
EXTRA_PACKAGES: build-base
|
5
docs/docs/api/api-examples.md
Normal file
5
docs/docs/api/api-examples.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# API Examples
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
Have Ideas? Submit a PR!
|
@ -1,3 +0,0 @@
|
|||||||
# API Introduction
|
|
||||||
|
|
||||||
TODO
|
|
26
docs/docs/api/demo/index.html
Normal file
26
docs/docs/api/demo/index.html
Normal file
File diff suppressed because one or more lines are too long
26
docs/docs/html/api.html
Normal file
26
docs/docs/html/api.html
Normal file
File diff suppressed because one or more lines are too long
@ -7,7 +7,6 @@ theme:
|
|||||||
logo: material/silverware-variant
|
logo: material/silverware-variant
|
||||||
features:
|
features:
|
||||||
- navigation.expand
|
- navigation.expand
|
||||||
- navigation.instant
|
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- pymdownx.emoji:
|
- pymdownx.emoji:
|
||||||
@ -35,7 +34,8 @@ nav:
|
|||||||
- Backups and Exports: "getting-started/backups-and-exports.md"
|
- Backups and Exports: "getting-started/backups-and-exports.md"
|
||||||
- Recipe Migration: "getting-started/migration-imports.md"
|
- Recipe Migration: "getting-started/migration-imports.md"
|
||||||
- API Reference:
|
- API Reference:
|
||||||
- Swagger/OpenAPI: "api/api-intro.md"
|
- API Documentation: "api/demo/index.html"
|
||||||
|
- Usage Examples: "api/api-examples.md"
|
||||||
- Contributors Guide:
|
- Contributors Guide:
|
||||||
- Non-Code: "contributors/non-coders.md"
|
- Non-Code: "contributors/non-coders.md"
|
||||||
- Developers Guide:
|
- Developers Guide:
|
||||||
|
198
frontend/src/components/RecipeEditor/PrintRecipe.vue
Normal file
198
frontend/src/components/RecipeEditor/PrintRecipe.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-card flat class="d-print-none">
|
||||||
|
<v-card-text>
|
||||||
|
<v-row align="center" justify="center">
|
||||||
|
<v-btn
|
||||||
|
left
|
||||||
|
color="accent lighten-1 "
|
||||||
|
class="ma-1 image-action"
|
||||||
|
@click="$emit('exit')"
|
||||||
|
>
|
||||||
|
<v-icon> mdi-arrow-left </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-card flat class="text-center" align-center>
|
||||||
|
<v-card-text>Font Size</v-card-text>
|
||||||
|
<v-card-text>
|
||||||
|
<v-btn
|
||||||
|
class="mx-2"
|
||||||
|
fab
|
||||||
|
dark
|
||||||
|
x-small
|
||||||
|
color="primary"
|
||||||
|
@click="subtractFontSize"
|
||||||
|
>
|
||||||
|
<v-icon dark> mdi-minus </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
class="mx-2"
|
||||||
|
fab
|
||||||
|
dark
|
||||||
|
x-small
|
||||||
|
color="primary"
|
||||||
|
@click="addFontSize"
|
||||||
|
>
|
||||||
|
<v-icon dark> mdi-plus </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
<v-card flat>
|
||||||
|
<v-row dense align="center">
|
||||||
|
<v-col md="10" sm="10">
|
||||||
|
<v-card flat>
|
||||||
|
<v-card-title> {{ recipe.name }} </v-card-title>
|
||||||
|
|
||||||
|
<v-card-text> {{ recipe.description }} </v-card-text>
|
||||||
|
|
||||||
|
<v-divider></v-divider>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
<v-col md="1" sm="1" justify-end>
|
||||||
|
<v-img :src="getImage(recipe.image)" max-height="200" max-width="300">
|
||||||
|
</v-img>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card>
|
||||||
|
<v-card flat align>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row class="mt-n6">
|
||||||
|
<v-col>
|
||||||
|
<v-btn
|
||||||
|
v-if="recipe.recipeYield"
|
||||||
|
dense
|
||||||
|
small
|
||||||
|
:hover="false"
|
||||||
|
type="label"
|
||||||
|
:ripple="false"
|
||||||
|
elevation="0"
|
||||||
|
color="secondary darken-1"
|
||||||
|
class="rounded-sm static"
|
||||||
|
>
|
||||||
|
{{ recipe.recipeYield }}
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-rating
|
||||||
|
class="mr-2 align-end static"
|
||||||
|
color="secondary darken-1"
|
||||||
|
background-color="secondary lighten-3"
|
||||||
|
length="5"
|
||||||
|
:value="recipe.rating"
|
||||||
|
></v-rating>
|
||||||
|
</v-row>
|
||||||
|
<h2 class="mt-1">Ingredients</h2>
|
||||||
|
<v-row>
|
||||||
|
<v-list dense class="column-wrapper align-start">
|
||||||
|
<v-list-item
|
||||||
|
v-for="(ingredient, index) in recipe.recipeIngredient"
|
||||||
|
:key="generateKey('ingredient', index)"
|
||||||
|
hide-details
|
||||||
|
class="mb-n3 print-text"
|
||||||
|
:label="ingredient"
|
||||||
|
>
|
||||||
|
<v-list-item-icon class="mr-1">
|
||||||
|
<v-icon> mdi-minus </v-icon>
|
||||||
|
</v-list-item-icon>
|
||||||
|
{{ ingredient }}
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-row>
|
||||||
|
<v-row dense>
|
||||||
|
<v-col cols="12">
|
||||||
|
<div v-if="recipe.categories[0]">
|
||||||
|
<h2 class="mt-4">Categories</h2>
|
||||||
|
<v-chip
|
||||||
|
class="ma-1"
|
||||||
|
color="primary"
|
||||||
|
dark
|
||||||
|
v-for="category in recipe.categories"
|
||||||
|
:key="category"
|
||||||
|
>
|
||||||
|
{{ category }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="recipe.tags[0]">
|
||||||
|
<h2 class="mt-4">Tags</h2>
|
||||||
|
<v-chip
|
||||||
|
class="ma-1"
|
||||||
|
color="primary"
|
||||||
|
dark
|
||||||
|
v-for="tag in recipe.tags"
|
||||||
|
:key="tag"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 v-if="recipe.notes[0]" class="my-2">Notes</h2>
|
||||||
|
<v-card
|
||||||
|
flat
|
||||||
|
class="mt-1"
|
||||||
|
v-for="(note, index) in recipe.notes"
|
||||||
|
:key="generateKey('note', index)"
|
||||||
|
>
|
||||||
|
<v-card-title> {{ note.title }}</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
{{ note.text }}
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<h2 class="mb-4">Instructions</h2>
|
||||||
|
|
||||||
|
<v-card
|
||||||
|
v-for="(step, index) in recipe.recipeInstructions"
|
||||||
|
:key="generateKey('step', index)"
|
||||||
|
class="my-n4"
|
||||||
|
flat
|
||||||
|
>
|
||||||
|
<v-card-title class="my-n4">Step: {{ index + 1 }}</v-card-title>
|
||||||
|
<v-card-text class="my-n4">{{ step.text }}</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import utils from "../../utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
recipe: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fontSize: 1.0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getImage(image) {
|
||||||
|
if (image) {
|
||||||
|
return utils.getImageURL(image) + "?rnd=" + this.imageKey;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
generateKey(item, index) {
|
||||||
|
return utils.generateUniqueKey(item, index);
|
||||||
|
},
|
||||||
|
addFontSize() {
|
||||||
|
this.fontSize += 0.2;
|
||||||
|
},
|
||||||
|
subtractFontSize() {
|
||||||
|
this.fontSize -= 0.2;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.column-wrapper {
|
||||||
|
column-count: 2;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,5 +1,4 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os
|
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@ -15,8 +14,8 @@ from routes import (
|
|||||||
static_routes,
|
static_routes,
|
||||||
user_routes,
|
user_routes,
|
||||||
)
|
)
|
||||||
from routes.setting_routes import scheduler
|
from routes.setting_routes import scheduler # ! This has to be imported for scheduling
|
||||||
from settings import PORT
|
from settings import PORT, PRODUCTION
|
||||||
from utils.logger import logger
|
from utils.logger import logger
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
CWD = Path(__file__).parent
|
||||||
@ -26,10 +25,10 @@ app = FastAPI()
|
|||||||
|
|
||||||
|
|
||||||
# Mount Vue Frontend only in production
|
# Mount Vue Frontend only in production
|
||||||
env = os.environ.get("ENV")
|
if PRODUCTION:
|
||||||
if(env == "prod"):
|
|
||||||
app.mount("/static", StaticFiles(directory=WEB_PATH, html=True))
|
app.mount("/static", StaticFiles(directory=WEB_PATH, html=True))
|
||||||
|
|
||||||
|
|
||||||
# API Routes
|
# API Routes
|
||||||
app.include_router(recipe_routes.router)
|
app.include_router(recipe_routes.router)
|
||||||
app.include_router(meal_routes.router)
|
app.include_router(meal_routes.router)
|
||||||
@ -49,6 +48,9 @@ app.include_router(static_routes.router)
|
|||||||
startup.ensure_dirs()
|
startup.ensure_dirs()
|
||||||
startup.generate_default_theme()
|
startup.generate_default_theme()
|
||||||
|
|
||||||
|
# Generate API Documentation
|
||||||
|
if not PRODUCTION:
|
||||||
|
startup.generate_api_docs(app)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
logger.info("-----SYSTEM STARTUP-----")
|
logger.info("-----SYSTEM STARTUP-----")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# from datetime import datetime
|
# from datetime import datetime
|
||||||
from typing import Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@ -7,3 +7,24 @@ from pydantic import BaseModel
|
|||||||
class BackupJob(BaseModel):
|
class BackupJob(BaseModel):
|
||||||
tag: Optional[str]
|
tag: Optional[str]
|
||||||
template: Optional[str]
|
template: Optional[str]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
schema_extra = {
|
||||||
|
"example": {
|
||||||
|
"tag": "July 23rd 2021",
|
||||||
|
"template": "recipes.md",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Imports(BaseModel):
|
||||||
|
imports: List[str]
|
||||||
|
templates: List[str]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
schema_extra = {
|
||||||
|
"example": {
|
||||||
|
"imports": ["sample_data.zip", "sampe_data2.zip"],
|
||||||
|
"templates": ["recipes.md", "custom_template.md"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from models.backup_models import BackupJob
|
from models.backup_models import BackupJob, Imports
|
||||||
from services.backup_services import (BACKUP_DIR, TEMPLATE_DIR, export_db,
|
from pydantic.main import BaseModel
|
||||||
import_from_archive)
|
from services.backup_services import (
|
||||||
|
BACKUP_DIR,
|
||||||
|
TEMPLATE_DIR,
|
||||||
|
export_db,
|
||||||
|
import_from_archive,
|
||||||
|
)
|
||||||
from utils.snackbar import SnackResponse
|
from utils.snackbar import SnackResponse
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/backups/available/", tags=["Import / Export"])
|
@router.get("/api/backups/available/", tags=["Import / Export"], response_model=Imports)
|
||||||
async def available_imports():
|
async def available_imports():
|
||||||
|
"""Returns a list of avaiable .zip files for import into Mealie."""
|
||||||
imports = []
|
imports = []
|
||||||
templates = []
|
templates = []
|
||||||
for archive in BACKUP_DIR.glob("*.zip"):
|
for archive in BACKUP_DIR.glob("*.zip"):
|
||||||
@ -17,12 +23,12 @@ async def available_imports():
|
|||||||
for template in TEMPLATE_DIR.glob("*.md"):
|
for template in TEMPLATE_DIR.glob("*.md"):
|
||||||
templates.append(template.name)
|
templates.append(template.name)
|
||||||
|
|
||||||
return {"imports": imports, "templates": templates}
|
return Imports(imports=imports, templates=templates)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/backups/export/database/", tags=["Import / Export"], status_code=201)
|
@router.post("/api/backups/export/database/", tags=["Import / Export"], status_code=201)
|
||||||
async def export_database(data: BackupJob):
|
async def export_database(data: BackupJob):
|
||||||
|
"""Generates a backup of the recipe database in json format."""
|
||||||
try:
|
try:
|
||||||
export_path = export_db(data.tag, data.template)
|
export_path = export_db(data.tag, data.template)
|
||||||
except:
|
except:
|
||||||
@ -38,6 +44,7 @@ async def export_database(data: BackupJob):
|
|||||||
"/api/backups/{file_name}/import/", tags=["Import / Export"], status_code=200
|
"/api/backups/{file_name}/import/", tags=["Import / Export"], status_code=200
|
||||||
)
|
)
|
||||||
async def import_database(file_name: str):
|
async def import_database(file_name: str):
|
||||||
|
""" Import a database backup file generated from Mealie. """
|
||||||
imported = import_from_archive(file_name)
|
imported = import_from_archive(file_name)
|
||||||
return imported
|
return imported
|
||||||
|
|
||||||
@ -48,6 +55,7 @@ async def import_database(file_name: str):
|
|||||||
status_code=200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
async def delete_backup(backup_name: str):
|
async def delete_backup(backup_name: str):
|
||||||
|
""" Removes a database backup from the file system """
|
||||||
|
|
||||||
try:
|
try:
|
||||||
BACKUP_DIR.joinpath(backup_name).unlink()
|
BACKUP_DIR.joinpath(backup_name).unlink()
|
||||||
|
@ -16,13 +16,10 @@ async def get_all_meals():
|
|||||||
|
|
||||||
@router.post("/api/meal-plan/create/", tags=["Meal Plan"])
|
@router.post("/api/meal-plan/create/", tags=["Meal Plan"])
|
||||||
async def set_meal_plan(data: MealPlan):
|
async def set_meal_plan(data: MealPlan):
|
||||||
""" Creates Mealplan from Frontend Data"""
|
""" Creates a mealplan database entry"""
|
||||||
data.process_meals()
|
data.process_meals()
|
||||||
data.save_to_db()
|
data.save_to_db()
|
||||||
|
|
||||||
# try:
|
|
||||||
|
|
||||||
# except:
|
|
||||||
# raise HTTPException(
|
# raise HTTPException(
|
||||||
# status_code=404,
|
# status_code=404,
|
||||||
# detail=SnackResponse.error("Unable to Create Mealplan See Log"),
|
# detail=SnackResponse.error("Unable to Create Mealplan See Log"),
|
||||||
|
@ -8,6 +8,7 @@ ENV = CWD.joinpath(".env")
|
|||||||
dotenv.load_dotenv(ENV)
|
dotenv.load_dotenv(ENV)
|
||||||
|
|
||||||
# General
|
# General
|
||||||
|
PRODUCTION = os.environ.get("ENV")
|
||||||
PORT = int(os.getenv("mealie_port", 9000))
|
PORT = int(os.getenv("mealie_port", 9000))
|
||||||
|
|
||||||
# Mongo Database
|
# Mongo Database
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from services.settings_services import Colors, SiteTheme
|
from services.settings_services import Colors, SiteTheme
|
||||||
@ -37,5 +38,43 @@ def generate_default_theme():
|
|||||||
default_theme.save_to_db()
|
default_theme.save_to_db()
|
||||||
|
|
||||||
|
|
||||||
|
"""Script to export the ReDoc documentation page into a standalone HTML file."""
|
||||||
|
|
||||||
|
HTML_TEMPLATE = """<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||||
|
<title>My Project - ReDoc</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" href="https://fastapi.tiangolo.com/img/favicon.png">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style data-styled="" data-styled-version="4.4.1"></style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="redoc-container"></div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js"> </script>
|
||||||
|
<script>
|
||||||
|
var spec = %s;
|
||||||
|
Redoc.init(spec, {}, document.getElementById("redoc-container"));
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
CWD = Path(__file__).parent
|
||||||
|
out_path = CWD.joinpath("temp", "api.html")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_api_docs(app):
|
||||||
|
with open(out_path, "w") as fd:
|
||||||
|
print(HTML_TEMPLATE % json.dumps(app.openapi()), file=fd)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
26
mealie/temp/api.html
Normal file
26
mealie/temp/api.html
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
import datetime
|
|
@ -1 +0,0 @@
|
|||||||
// Test Notify
|
|
Loading…
x
Reference in New Issue
Block a user