mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
add server side events
This commit is contained in:
parent
7f63f35ec5
commit
832e95f27e
26 changed files with 393 additions and 34 deletions
59
frontend/src/api/about.js
Normal file
59
frontend/src/api/about.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { baseURL } from "./api-utils";
|
||||
import { apiReq } from "./api-utils";
|
||||
|
||||
const prefix = baseURL + "about";
|
||||
|
||||
const aboutURLs = {
|
||||
version: `${prefix}/version`,
|
||||
debug: `${prefix}`,
|
||||
lastRecipe: `${prefix}/last-recipe-json`,
|
||||
demo: `${prefix}/is-demo`,
|
||||
log: num => `${prefix}/log/${num}`,
|
||||
statistics: `${prefix}/statistics`,
|
||||
events: `${prefix}/events`,
|
||||
event: id => `${prefix}/events/${id}`,
|
||||
};
|
||||
|
||||
export const aboutAPI = {
|
||||
async getEvents() {
|
||||
const resposne = await apiReq.get(aboutURLs.events);
|
||||
return resposne.data;
|
||||
},
|
||||
async deleteEvent(id) {
|
||||
const resposne = await apiReq.delete(aboutURLs.event(id));
|
||||
return resposne.data;
|
||||
},
|
||||
async deleteAllEvents() {
|
||||
const resposne = await apiReq.delete(aboutURLs.events);
|
||||
return resposne.data;
|
||||
},
|
||||
// async getAppInfo() {
|
||||
// const response = await apiReq.get(aboutURLs.version);
|
||||
// return response.data;
|
||||
// },
|
||||
|
||||
// async getDebugInfo() {
|
||||
// const response = await apiReq.get(aboutURLs.debug);
|
||||
// return response.data;
|
||||
// },
|
||||
|
||||
// async getLogText(num) {
|
||||
// const response = await apiReq.get(aboutURLs.log(num));
|
||||
// return response.data;
|
||||
// },
|
||||
|
||||
// async getLastJson() {
|
||||
// const response = await apiReq.get(aboutURLs.lastRecipe);
|
||||
// return response.data;
|
||||
// },
|
||||
|
||||
// async getIsDemo() {
|
||||
// const response = await apiReq.get(aboutURLs.demo);
|
||||
// return response.data;
|
||||
// },
|
||||
|
||||
// async getStatistics() {
|
||||
// const response = await apiReq.get(aboutURLs.statistics);
|
||||
// return response.data;
|
||||
// },
|
||||
};
|
|
@ -11,6 +11,7 @@ import { userAPI } from "./users";
|
|||
import { signupAPI } from "./signUps";
|
||||
import { groupAPI } from "./groups";
|
||||
import { siteSettingsAPI } from "./siteSettings";
|
||||
import { aboutAPI } from "./about";
|
||||
|
||||
/**
|
||||
* The main object namespace for interacting with the backend database
|
||||
|
@ -30,4 +31,5 @@ export const api = {
|
|||
users: userAPI,
|
||||
signUps: signupAPI,
|
||||
groups: groupAPI,
|
||||
about: aboutAPI,
|
||||
};
|
||||
|
|
|
@ -9,5 +9,14 @@
|
|||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"hour12": true
|
||||
}
|
||||
}
|
|
@ -9,5 +9,14 @@
|
|||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"year": "numeric"
|
||||
},
|
||||
"long": {
|
||||
"year": "numeric",
|
||||
"month": "long",
|
||||
"day": "numeric",
|
||||
"weekday": "long",
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"hour12": true
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
<div class="text-truncate">
|
||||
<strong>{{ backup.name }}</strong>
|
||||
</div>
|
||||
<div class="text-truncate">{{ $d(new Date(backup.date), "medium") }}</div>
|
||||
<div class="text-truncate">{{ $d(Date.parse(backup.date), "medium") }}</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</v-toolbar-items>
|
||||
</v-toolbar>
|
||||
<v-card-title> {{ name }} </v-card-title>
|
||||
<v-card-subtitle class="mb-n3"> {{ $d(new Date(date), "medium") }} </v-card-subtitle>
|
||||
<v-card-subtitle class="mb-n3" v-if="date"> {{ $d(new Date(date), "medium") }} </v-card-subtitle>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
|
|
107
frontend/src/pages/Admin/Dashboard/EventViewer.vue
Normal file
107
frontend/src/pages/Admin/Dashboard/EventViewer.vue
Normal file
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<div>
|
||||
<StatCard icon="mdi-bell-ring">
|
||||
<template v-slot:after-heading>
|
||||
<div class="ml-auto text-right">
|
||||
<div class="body-3 grey--text font-weight-light" v-text="'Events'" />
|
||||
|
||||
<h3 class="display-2 font-weight-light text--primary">
|
||||
<small> {{ total }}</small>
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div class="d-flex row py-3 justify-end">
|
||||
<v-btn class="mx-2" small color="primary" @click="deleteAll">
|
||||
<v-icon left> mdi-notification-clear-all </v-icon> Clear
|
||||
</v-btn>
|
||||
</div>
|
||||
<template v-slot:bottom>
|
||||
<v-list subheader two-line>
|
||||
<v-list-item v-for="(event, index) in events" :key="index">
|
||||
<v-list-item-avatar>
|
||||
<v-icon large dark :color="icons[event.category].color">
|
||||
{{ icons[event.category].icon }}
|
||||
</v-icon>
|
||||
</v-list-item-avatar>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="event.title"></v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle v-text="event.text"></v-list-item-subtitle>
|
||||
<v-list-item-subtitle>
|
||||
{{ $d(Date.parse(event.timeStamp), "long") }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
|
||||
<v-list-item-action class="ml-auto">
|
||||
<v-btn large icon @click="deleteEvent(event.id)">
|
||||
<v-icon color="error">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
</StatCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from "@/api";
|
||||
import StatCard from "./StatCard";
|
||||
export default {
|
||||
components: { StatCard },
|
||||
data() {
|
||||
return {
|
||||
total: 0,
|
||||
events: [],
|
||||
icons: {
|
||||
general: {
|
||||
icon: "mdi-information",
|
||||
color: "info",
|
||||
},
|
||||
recipe: {
|
||||
icon: "mdi-silverware-fork-knife",
|
||||
color: "primary",
|
||||
},
|
||||
backup: {
|
||||
icon: "mdi-backup-restore",
|
||||
color: "primary",
|
||||
},
|
||||
schedule: {
|
||||
icon: "mdi-calendar-clock",
|
||||
color: "primary",
|
||||
},
|
||||
migration: {
|
||||
icon: "mdi-database-import",
|
||||
color: "primary",
|
||||
},
|
||||
signup: {
|
||||
icon: "mdi-account",
|
||||
color: "primary",
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getEvents();
|
||||
},
|
||||
methods: {
|
||||
async getEvents() {
|
||||
const events = await api.about.getEvents();
|
||||
this.events = events.events;
|
||||
this.total = events.total;
|
||||
},
|
||||
async deleteEvent(id) {
|
||||
await api.about.deleteEvent(id);
|
||||
this.getEvents();
|
||||
},
|
||||
async deleteAll() {
|
||||
await api.about.deleteAllEvents();
|
||||
this.getEvents();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
|
@ -2,14 +2,11 @@ w<template>
|
|||
<v-card v-bind="$attrs" :class="classes" class="v-card--material pa-3">
|
||||
<div class="d-flex grow flex-wrap">
|
||||
<v-sheet
|
||||
:class="{
|
||||
'pa-7': !$slots.image,
|
||||
}"
|
||||
:color="color"
|
||||
:max-height="icon ? 90 : undefined"
|
||||
:width="icon ? 'auto' : '100%'"
|
||||
elevation="6"
|
||||
class="text-start v-card--material__heading mb-n6"
|
||||
class="text-start v-card--material__heading mb-n6 mt-n10 pa-7"
|
||||
dark
|
||||
>
|
||||
<v-icon v-if="icon" size="32" v-text="icon" />
|
||||
|
@ -30,6 +27,14 @@ w<template>
|
|||
<slot name="actions" />
|
||||
</v-card-actions>
|
||||
</template>
|
||||
|
||||
<template v-if="$slots.bottom">
|
||||
<v-divider class="mt-2" />
|
||||
|
||||
<div class="pb-0">
|
||||
<slot name="bottom" />
|
||||
</div>
|
||||
</template>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
|
@ -71,10 +76,10 @@ export default {
|
|||
};
|
||||
},
|
||||
hasHeading() {
|
||||
return Boolean(this.$slots.heading || this.title || this.icon);
|
||||
return false;
|
||||
},
|
||||
hasAltHeading() {
|
||||
return Boolean(this.$slots.heading || (this.title && this.icon));
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="mt-15">
|
||||
<div class="mt-10">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-col cols="12" sm="12" md="4">
|
||||
<StatCard icon="mdi-silverware-fork-knife">
|
||||
<template v-slot:after-heading>
|
||||
<div class="ml-auto text-right">
|
||||
|
@ -24,7 +24,7 @@
|
|||
</template>
|
||||
</StatCard>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-col cols="12" sm="12" md="4">
|
||||
<StatCard icon="mdi-account">
|
||||
<template v-slot:after-heading>
|
||||
<div class="ml-auto text-right">
|
||||
|
@ -45,7 +45,7 @@
|
|||
</template>
|
||||
</StatCard>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-col cols="12" sm="12" md="4">
|
||||
<StatCard icon="mdi-account-group">
|
||||
<template v-slot:after-heading>
|
||||
<div class="ml-auto text-right">
|
||||
|
@ -67,14 +67,21 @@
|
|||
</StatCard>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class="mt-10">
|
||||
<v-col cols="12" sm="12" lg="6">
|
||||
<EventViewer />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" lg="6"> </v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from "@/api";
|
||||
import StatCard from "./StatCard";
|
||||
import EventViewer from "./EventViewer";
|
||||
export default {
|
||||
components: { StatCard },
|
||||
components: { StatCard, EventViewer },
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
|
@ -92,7 +99,6 @@ export default {
|
|||
methods: {
|
||||
async getStatistics() {
|
||||
this.statistics = await api.meta.getStatistics();
|
||||
console.log(this.statistics);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -160,10 +160,10 @@ export default {
|
|||
methods: {
|
||||
updateClipboard(newClip) {
|
||||
navigator.clipboard.writeText(newClip).then(
|
||||
function() {
|
||||
() => {
|
||||
console.log("Copied", newClip);
|
||||
},
|
||||
function() {
|
||||
() => {
|
||||
console.log("Copy Failed", newClip);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -37,7 +37,6 @@ export default {
|
|||
return this.$store.getters.getSiteSettings;
|
||||
},
|
||||
recentRecipes() {
|
||||
console.log("Recent Recipes");
|
||||
return this.$store.getters.getRecentRecipes;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,11 +4,13 @@ from fastapi import FastAPI
|
|||
from mealie.core import root_logger
|
||||
from mealie.core.config import APP_VERSION, settings
|
||||
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
|
||||
from mealie.routes.about import about_router
|
||||
from mealie.routes.groups import groups
|
||||
from mealie.routes.mealplans import mealplans
|
||||
from mealie.routes.recipe import router as recipe_router
|
||||
from mealie.routes.site_settings import all_settings
|
||||
from mealie.routes.users import users
|
||||
from mealie.services.events import create_general_event
|
||||
|
||||
logger = root_logger.get_logger()
|
||||
|
||||
|
@ -31,6 +33,7 @@ def api_routers():
|
|||
app.include_router(groups.router)
|
||||
# Recipes
|
||||
app.include_router(recipe_router)
|
||||
app.include_router(about_router)
|
||||
# Meal Routes
|
||||
app.include_router(mealplans.router)
|
||||
# Settings Routes
|
||||
|
@ -53,6 +56,7 @@ def system_startup():
|
|||
logger.info("-----SYSTEM STARTUP----- \n")
|
||||
logger.info("------APP SETTINGS------")
|
||||
logger.info(settings.json(indent=4, exclude={"SECRET", "DEFAULT_PASSWORD", "SFTP_PASSWORD", "SFTP_USERNAME"}))
|
||||
create_general_event("Application Startup", f"Mealie API started on port {settings.API_PORT}")
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from logging import getLogger
|
||||
|
||||
from mealie.db.db_base import BaseDocument
|
||||
from mealie.db.models.event import Event
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.mealplan import MealPlanModel
|
||||
from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag
|
||||
|
@ -9,6 +10,7 @@ from mealie.db.models.sign_up import SignUp
|
|||
from mealie.db.models.theme import SiteThemeModel
|
||||
from mealie.db.models.users import User
|
||||
from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse
|
||||
from mealie.schema.events import Event as EventSchema
|
||||
from mealie.schema.meal import MealPlanInDB
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.settings import CustomPageOut
|
||||
|
@ -35,10 +37,10 @@ class _Recipes(BaseDocument):
|
|||
return f"{slug}.{extension}"
|
||||
|
||||
def count_uncategorized(self, session: Session) -> int:
|
||||
return session.query(self.sql_model).filter(RecipeModel.recipe_category == None).count()
|
||||
return session.query(self.sql_model).filter(RecipeModel.recipe_category == None).count() # noqa: 711
|
||||
|
||||
def count_untagged(self, session: Session) -> int:
|
||||
return session.query(self.sql_model).filter(RecipeModel.tags == None).count()
|
||||
return session.query(self.sql_model).filter(RecipeModel.tags == None).count() # noqa: 711
|
||||
|
||||
|
||||
class _Categories(BaseDocument):
|
||||
|
@ -115,8 +117,6 @@ class _Groups(BaseDocument):
|
|||
"""
|
||||
group: GroupInDB = session.query(self.sql_model).filter_by(**{match_key: match_value}).one_or_none()
|
||||
|
||||
# Potentially not needed? column is sorted by SqlAlchemy based on startDate
|
||||
# return sorted(group.mealplans, key=lambda mealplan: mealplan.startDate)
|
||||
return group.mealplans
|
||||
|
||||
|
||||
|
@ -134,6 +134,13 @@ class _CustomPages(BaseDocument):
|
|||
self.schema = CustomPageOut
|
||||
|
||||
|
||||
class _Events(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "id"
|
||||
self.sql_model = Event
|
||||
self.schema = EventSchema
|
||||
|
||||
|
||||
class Database:
|
||||
def __init__(self) -> None:
|
||||
self.recipes = _Recipes()
|
||||
|
@ -146,6 +153,7 @@ class Database:
|
|||
self.sign_ups = _SignUps()
|
||||
self.groups = _Groups()
|
||||
self.custom_pages = _CustomPages()
|
||||
self.events = _Events()
|
||||
|
||||
|
||||
db = Database()
|
||||
|
|
|
@ -23,6 +23,14 @@ class BaseDocument:
|
|||
) -> List[dict]:
|
||||
eff_schema = override_schema or self.schema
|
||||
|
||||
if order_by:
|
||||
order_attr = getattr(self.sql_model, str(order_by))
|
||||
|
||||
return [
|
||||
eff_schema.from_orm(x)
|
||||
for x in session.query(self.sql_model).order_by(order_attr.desc()).offset(start).limit(limit).all()
|
||||
]
|
||||
|
||||
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()]
|
||||
|
||||
def get_all_limit_columns(self, session: Session, fields: List[str], limit: int = None) -> List[SqlAlchemyBase]:
|
||||
|
@ -155,6 +163,10 @@ class BaseDocument:
|
|||
|
||||
return results_as_model
|
||||
|
||||
def delete_all(self, session: Session) -> None:
|
||||
session.query(self.sql_model).delete()
|
||||
session.commit()
|
||||
|
||||
def count_all(self, session: Session, match_key=None, match_value=None) -> int:
|
||||
|
||||
if None in [match_key, match_value]:
|
||||
|
|
|
@ -5,6 +5,7 @@ from mealie.db.database import db
|
|||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.settings import SiteSettings
|
||||
from mealie.schema.theme import SiteTheme
|
||||
from mealie.services.events import create_general_event
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
logger = root_logger.get_logger("init_db")
|
||||
|
@ -58,6 +59,7 @@ def main():
|
|||
else:
|
||||
print("Database Doesn't Exists, Initializing...")
|
||||
init_db()
|
||||
create_general_event("Initialize Database", "Initialize database with default values", session)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from mealie.db.models.event import *
|
||||
from mealie.db.models.group import *
|
||||
from mealie.db.models.mealplan import *
|
||||
from mealie.db.models.recipe.recipe import *
|
||||
from mealie.db.models.settings import *
|
||||
from mealie.db.models.sign_up import *
|
||||
from mealie.db.models.theme import *
|
||||
from mealie.db.models.users import *
|
||||
from mealie.db.models.sign_up import *
|
||||
from mealie.db.models.group import *
|
||||
|
|
17
mealie/db/models/event.py
Normal file
17
mealie/db/models/event.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import sqlalchemy as sa
|
||||
from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
|
||||
|
||||
class Event(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "events"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
title = sa.Column(sa.String)
|
||||
text = sa.Column(sa.String)
|
||||
time_stamp = sa.Column(sa.DateTime)
|
||||
category = sa.Column(sa.String)
|
||||
|
||||
def __init__(self, title, text, time_stamp, category, *args, **kwargs) -> None:
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.time_stamp = time_stamp
|
||||
self.category = category
|
|
@ -4,5 +4,5 @@ SqlAlchemyBase = dec.declarative_base()
|
|||
|
||||
|
||||
class BaseMixins:
|
||||
def _pass_on_me():
|
||||
pass
|
||||
def update(self, *args, **kwarg):
|
||||
self.__init__(*args, **kwarg)
|
||||
|
|
7
mealie/routes/about/__init__.py
Normal file
7
mealie/routes/about/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from fastapi import APIRouter
|
||||
|
||||
from .events import router as events_router
|
||||
|
||||
about_router = APIRouter(prefix="/api/about")
|
||||
|
||||
about_router.include_router(events_router)
|
29
mealie/routes/about/events.py
Normal file
29
mealie/routes/about/events.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.db.models.event import Event
|
||||
from mealie.routes.deps import get_current_user
|
||||
from mealie.schema.events import EventsOut
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
router = APIRouter(prefix="/events", tags=["App Events"])
|
||||
|
||||
|
||||
@router.get("", response_model=EventsOut)
|
||||
async def get_events(session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
||||
""" Get event from the Database """
|
||||
# Get Item
|
||||
return EventsOut(total=db.events.count_all(session), events=db.events.get_all(session, order_by="time_stamp"))
|
||||
|
||||
|
||||
@router.delete("")
|
||||
async def get_events(session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
||||
""" Get event from the Database """
|
||||
# Get Item
|
||||
return db.events.delete_all(session)
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_event(id: int, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
|
||||
""" Delete event from the Database """
|
||||
return db.events.delete(session, id)
|
|
@ -1,5 +1,6 @@
|
|||
import operator
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
||||
from mealie.core.config import app_dirs
|
||||
|
@ -10,6 +11,7 @@ from mealie.routes.deps import get_current_user
|
|||
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
||||
from mealie.services.backups import imports
|
||||
from mealie.services.backups.exports import backup_all
|
||||
from mealie.services.events import create_backup_event
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
|
||||
|
@ -45,6 +47,7 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session
|
|||
export_users=data.options.users,
|
||||
export_groups=data.options.groups,
|
||||
)
|
||||
create_backup_event("Manual Backup", f"Manual Backup Created '{Path(export_path).name}'", session)
|
||||
return {"export_path": export_path}
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
@ -75,7 +78,7 @@ async def download_backup_file(file_name: str):
|
|||
def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)):
|
||||
""" Import a database backup file generated from Mealie. """
|
||||
|
||||
return imports.import_database(
|
||||
db_import = imports.import_database(
|
||||
session=session,
|
||||
archive=import_data.name,
|
||||
import_recipes=import_data.recipes,
|
||||
|
@ -87,6 +90,8 @@ def import_database(file_name: str, import_data: ImportJob, session: Session = D
|
|||
force_import=import_data.force,
|
||||
rebase=import_data.rebase,
|
||||
)
|
||||
create_backup_event("Database Restore", f"Restored Database File {file_name}", session)
|
||||
return db_import
|
||||
|
||||
|
||||
@router.delete("/{file_name}/delete", status_code=status.HTTP_200_OK)
|
||||
|
|
|
@ -2,6 +2,7 @@ from pathlib import Path
|
|||
|
||||
from fastapi_camelcase import CamelModel
|
||||
|
||||
|
||||
class AppStatistics(CamelModel):
|
||||
total_recipes: int
|
||||
total_users: int
|
||||
|
|
31
mealie/schema/events.py
Normal file
31
mealie/schema/events.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from fastapi_camelcase import CamelModel
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class EventCategory(str, Enum):
|
||||
general = "general"
|
||||
recipe = "recipe"
|
||||
backup = "backup"
|
||||
scheduled = "scheduled"
|
||||
migration = "migration"
|
||||
sign_up = "signup"
|
||||
|
||||
|
||||
class Event(CamelModel):
|
||||
id: Optional[int]
|
||||
title: str
|
||||
text: str
|
||||
time_stamp: datetime = Field(default_factory=datetime.now)
|
||||
category: EventCategory = EventCategory.general
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class EventsOut(CamelModel):
|
||||
total: int
|
||||
events: list[Event]
|
|
@ -9,6 +9,7 @@ from mealie.core import root_logger
|
|||
from mealie.core.config import app_dirs
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.services.events import create_backup_event
|
||||
from pathvalidate import sanitize_filename
|
||||
from pydantic.main import BaseModel
|
||||
|
||||
|
@ -149,3 +150,5 @@ def auto_backup_job():
|
|||
session = create_session()
|
||||
backup_all(session=session, tag="Auto", templates=templates)
|
||||
logger.info("Auto Backup Called")
|
||||
create_backup_event("Automated Backup", "Automated backup created", session)
|
||||
session.close()
|
||||
|
|
40
mealie/services/events.py
Normal file
40
mealie/services/events.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.events import Event, EventCategory
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
|
||||
def save_event(title, text, category, session: Session):
|
||||
event = Event(title=title, text=text, category=category)
|
||||
session = session or create_session()
|
||||
db.events.create(session, event.dict())
|
||||
|
||||
|
||||
def create_general_event(title, text, session=None):
|
||||
category = EventCategory.general
|
||||
save_event(title=title, text=text, category=category, session=session)
|
||||
|
||||
|
||||
def create_recipe_event(title, text, session=None):
|
||||
category = EventCategory.recipe
|
||||
save_event(title=title, text=text, category=category, session=session)
|
||||
|
||||
|
||||
def create_backup_event(title, text, session=None):
|
||||
category = EventCategory.backup
|
||||
save_event(title=title, text=text, category=category, session=session)
|
||||
|
||||
|
||||
def create_scheduled_event(title, text, session=None):
|
||||
category = EventCategory.scheduled
|
||||
save_event(title=title, text=text, category=category, session=session)
|
||||
|
||||
|
||||
def create_migration_event(title, text, session=None):
|
||||
category = EventCategory.migration
|
||||
save_event(title=title, text=text, category=category, session=session)
|
||||
|
||||
|
||||
def create_sign_up_event(title, text, session=None):
|
||||
category = EventCategory.sign_up
|
||||
save_event(title=title, text=text, category=category, session=session)
|
|
@ -2,6 +2,7 @@ import requests
|
|||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.user import GroupInDB
|
||||
from mealie.services.events import create_scheduled_event
|
||||
from mealie.services.meal_services import get_todays_meal
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
|
@ -21,4 +22,6 @@ def post_webhooks(group: int, session: Session = None):
|
|||
for url in group_settings.webhook_urls:
|
||||
requests.post(url, json=todays_recipe.json())
|
||||
|
||||
create_scheduled_event("Meal Plan Webhook", f"Meal plan webhook executed for group '{group}'")
|
||||
|
||||
session.close()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue