mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
Bug/misc bug fixes (#400)
* potentiall fix #329 * typo * auto purge events * image error * update import dialog * fix scheduler interval time * adjust icon position * check for property Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
145eb9f1ee
commit
a1dd6c941b
@ -8,7 +8,9 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template v-slot:open="{ open }">
|
<template v-slot:open="{ open }">
|
||||||
<v-btn @click="open" class="mx-2" small :color="color"> <v-icon left> mdi-plus </v-icon> {{$t('general.custom')}} </v-btn>
|
<v-btn @click="open" class="mx-2" small :color="color">
|
||||||
|
<v-icon left> mdi-plus </v-icon> {{ $t("general.custom") }}
|
||||||
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card-text class="mt-6">
|
<v-card-text class="mt-6">
|
||||||
<v-text-field dense :label="$t('settings.backup.backup-tag')" v-model="tag"></v-text-field>
|
<v-text-field dense :label="$t('settings.backup.backup-tag')" v-model="tag"></v-text-field>
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<slot name="open" v-bind="{ open }"> </slot>
|
<slot name="open" v-bind="{ open }"> </slot>
|
||||||
<v-dialog v-model="dialog" :width="modalWidth + 'px'" :content-class="top ? 'top-dialog' : undefined">
|
<v-dialog
|
||||||
|
v-model="dialog"
|
||||||
|
:width="modalWidth + 'px'"
|
||||||
|
:content-class="top ? 'top-dialog' : undefined"
|
||||||
|
:fullscreen="$vuetify.breakpoint.xsOnly"
|
||||||
|
>
|
||||||
<v-card height="100%">
|
<v-card height="100%">
|
||||||
<v-app-bar dark :color="color" class="mt-n1 mb-0">
|
<v-app-bar dark :color="color" class="mt-n1 mb-0">
|
||||||
<v-icon large left>
|
<v-icon large left>
|
||||||
@ -24,6 +29,7 @@
|
|||||||
<v-btn color="error" text @click="deleteEvent" v-if="$listeners.delete">
|
<v-btn color="error" text @click="deleteEvent" v-if="$listeners.delete">
|
||||||
{{ $t("general.delete") }}
|
{{ $t("general.delete") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
<slot name="extra-buttons"> </slot>
|
||||||
<v-btn color="success" type="submit" @click="submitEvent">
|
<v-btn color="success" type="submit" @click="submitEvent">
|
||||||
{{ submitText }}
|
{{ submitText }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<v-dialog v-model="dialog" width="500" :fullscreen="$vuetify.breakpoint.xsOnly">
|
<BaseDialog
|
||||||
<v-card>
|
:title="name"
|
||||||
<v-toolbar dark color="primary" v-show="$vuetify.breakpoint.xsOnly">
|
titleIcon="mdi-database"
|
||||||
<v-btn icon dark @click="dialog = false">
|
:submit-text="$t('general.import')"
|
||||||
<v-icon>mdi-close</v-icon>
|
:loading="loading"
|
||||||
</v-btn>
|
ref="baseDialog"
|
||||||
<v-toolbar-title></v-toolbar-title>
|
@submit="raiseEvent"
|
||||||
<v-spacer></v-spacer>
|
>
|
||||||
<v-toolbar-items>
|
<v-card-subtitle class="mb-n3 mt-3" v-if="date"> {{ $d(new Date(date), "medium") }} </v-card-subtitle>
|
||||||
<v-btn dark text @click="raiseEvent('import')">
|
|
||||||
{{ $t("general.import") }}
|
|
||||||
</v-btn>
|
|
||||||
</v-toolbar-items>
|
|
||||||
</v-toolbar>
|
|
||||||
<v-card-title> {{ name }} </v-card-title>
|
|
||||||
<v-card-subtitle class="mb-n3" v-if="date"> {{ $d(new Date(date), "medium") }} </v-card-subtitle>
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
@ -31,28 +24,29 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
<template v-slot:extra-buttons>
|
||||||
<v-card-actions>
|
<TheDownloadBtn :download-url="downloadUrl">
|
||||||
<TheDownloadBtn :download-url="downloadUrl" />
|
<template v-slot:default="{ downloadFile }">
|
||||||
<v-spacer></v-spacer>
|
<v-btn class="mr-1" color="info" @click="downloadFile">
|
||||||
<v-btn color="error" text @click="raiseEvent('delete')">
|
<v-icon left> mdi-download </v-icon>
|
||||||
{{ $t("general.delete") }}
|
{{ $t("general.download") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="success" outlined @click="raiseEvent('import')" v-show="$vuetify.breakpoint.smAndUp">
|
</template>
|
||||||
{{ $t("general.import") }}
|
</TheDownloadBtn>
|
||||||
</v-btn>
|
</template>
|
||||||
</v-card-actions>
|
</BaseDialog>
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const IMPORT_EVENT = "import";
|
||||||
|
import { api } from "@/api";
|
||||||
|
import BaseDialog from "./BaseDialog";
|
||||||
import ImportOptions from "@/components/FormHelpers/ImportOptions";
|
import ImportOptions from "@/components/FormHelpers/ImportOptions";
|
||||||
import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn.vue";
|
import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn.vue";
|
||||||
import { backupURLs } from "@/api/backup";
|
import { backupURLs } from "@/api/backup";
|
||||||
export default {
|
export default {
|
||||||
components: { ImportOptions, TheDownloadBtn },
|
components: { ImportOptions, TheDownloadBtn, BaseDialog },
|
||||||
props: {
|
props: {
|
||||||
name: {
|
name: {
|
||||||
default: "Backup Name",
|
default: "Backup Name",
|
||||||
@ -63,6 +57,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
loading: false,
|
||||||
options: {
|
options: {
|
||||||
recipes: true,
|
recipes: true,
|
||||||
settings: true,
|
settings: true,
|
||||||
@ -87,12 +82,13 @@ export default {
|
|||||||
},
|
},
|
||||||
open() {
|
open() {
|
||||||
this.dialog = true;
|
this.dialog = true;
|
||||||
|
this.$refs.baseDialog.open();
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.dialog = false;
|
this.dialog = false;
|
||||||
},
|
},
|
||||||
raiseEvent(event) {
|
async raiseEvent() {
|
||||||
let eventData = {
|
const eventData = {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
force: this.forceImport,
|
force: this.forceImport,
|
||||||
rebase: this.rebaseImport,
|
rebase: this.rebaseImport,
|
||||||
@ -102,8 +98,18 @@ export default {
|
|||||||
users: this.options.users,
|
users: this.options.users,
|
||||||
groups: this.options.groups,
|
groups: this.options.groups,
|
||||||
};
|
};
|
||||||
this.close();
|
this.loading = true;
|
||||||
this.$emit(event, eventData);
|
const importData = await this.importBackup(eventData);
|
||||||
|
|
||||||
|
this.$emit(IMPORT_EVENT, importData);
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
async importBackup(data) {
|
||||||
|
this.loading = true;
|
||||||
|
const response = await api.backups.import(data.name, data);
|
||||||
|
if (response) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title> {{ user.fullName }}</v-list-item-title>
|
<v-list-item-title> {{ user.fullName }}</v-list-item-title>
|
||||||
<v-list-item-subtitle> {{ user.admin ? $t('user.admin') : $t('user.user') }}</v-list-item-subtitle>
|
<v-list-item-subtitle> {{ user.admin ? $t("user.admin") : $t("user.user") }}</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
@ -77,6 +77,11 @@ export default {
|
|||||||
this.getVersion();
|
this.getVersion();
|
||||||
this.resetView();
|
this.resetView();
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
user() {
|
||||||
|
this.hideImage = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isMain() {
|
isMain() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export const initials = {
|
export const initials = {
|
||||||
computed: {
|
computed: {
|
||||||
initials() {
|
initials() {
|
||||||
|
if (!this.user.fullName) return "00"
|
||||||
const allNames = this.user.fullName.trim().split(" ");
|
const allNames = this.user.fullName.trim().split(" ");
|
||||||
const initials = allNames.reduce(
|
const initials = allNames.reduce(
|
||||||
(acc, curr, index) => {
|
(acc, curr, index) => {
|
||||||
|
@ -112,13 +112,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async importBackup(data) {
|
async importBackup(data) {
|
||||||
this.loading = true;
|
this.$refs.report.open(data);
|
||||||
const response = await api.backups.import(data.name, data);
|
|
||||||
if (response) {
|
|
||||||
const importData = response.data;
|
|
||||||
this.$refs.report.open(importData);
|
|
||||||
}
|
|
||||||
this.loading = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async createBackup() {
|
async createBackup() {
|
||||||
|
@ -123,19 +123,12 @@ export default {
|
|||||||
},
|
},
|
||||||
showPassword: false,
|
showPassword: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
user: {
|
user: {},
|
||||||
fullName: "",
|
|
||||||
email: "",
|
|
||||||
group: "",
|
|
||||||
admin: false,
|
|
||||||
id: 0,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
userProfileImage() {
|
userProfileImage() {
|
||||||
this.resetImage();
|
|
||||||
return `api/users/${this.user.id}/image`;
|
return `api/users/${this.user.id}/image`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -144,10 +137,13 @@ export default {
|
|||||||
this.refreshProfile();
|
this.refreshProfile();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
watch: {
|
||||||
resetImage() {
|
user() {
|
||||||
this.hideImage = false;
|
this.hideImage = false;
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
async refreshProfile() {
|
async refreshProfile() {
|
||||||
this.user = await api.users.self();
|
this.user = await api.users.self();
|
||||||
},
|
},
|
||||||
|
@ -38,7 +38,7 @@ class _Recipes(BaseDocument):
|
|||||||
return f"{slug}.{extension}"
|
return f"{slug}.{extension}"
|
||||||
|
|
||||||
def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int:
|
def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int:
|
||||||
return self._countr_attribute(
|
return self._count_attribute(
|
||||||
session,
|
session,
|
||||||
attribute_name=RecipeModel.recipe_category,
|
attribute_name=RecipeModel.recipe_category,
|
||||||
attr_match=None,
|
attr_match=None,
|
||||||
@ -47,7 +47,7 @@ class _Recipes(BaseDocument):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def count_untagged(self, session: Session, count=True, override_schema=None) -> int:
|
def count_untagged(self, session: Session, count=True, override_schema=None) -> int:
|
||||||
return self._countr_attribute(
|
return self._count_attribute(
|
||||||
session, attribute_name=RecipeModel.tags, attr_match=None, count=count, override_schema=override_schema
|
session, attribute_name=RecipeModel.tags, attr_match=None, count=count, override_schema=override_schema
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ class BaseDocument:
|
|||||||
else:
|
else:
|
||||||
return session.query(self.sql_model).filter_by(**{match_key: match_value}).count()
|
return session.query(self.sql_model).filter_by(**{match_key: match_value}).count()
|
||||||
|
|
||||||
def _countr_attribute(
|
def _count_attribute(
|
||||||
self, session: Session, attribute_name: str, attr_match: str = None, count=True, override_schema=None
|
self, session: Session, attribute_name: str, attr_match: str = None, count=True, override_schema=None
|
||||||
) -> Union[int, BaseModel]:
|
) -> Union[int, BaseModel]:
|
||||||
eff_schema = override_schema or self.schema
|
eff_schema = override_schema or self.schema
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from mealie.core import root_logger
|
from mealie.core import root_logger
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import create_session
|
from mealie.db.db_setup import create_session
|
||||||
|
from mealie.db.models.event import Event
|
||||||
from mealie.schema.user import GroupInDB
|
from mealie.schema.user import GroupInDB
|
||||||
from mealie.services.backups.exports import auto_backup_job
|
from mealie.services.backups.exports import auto_backup_job
|
||||||
from mealie.services.scheduler.global_scheduler import scheduler
|
from mealie.services.scheduler.global_scheduler import scheduler
|
||||||
@ -13,6 +16,21 @@ logger = root_logger.get_logger()
|
|||||||
# TODO Fix Scheduler
|
# TODO Fix Scheduler
|
||||||
|
|
||||||
|
|
||||||
|
@scheduler.scheduled_job(trigger="interval", minutes=1440)
|
||||||
|
def purge_events_database():
|
||||||
|
"""
|
||||||
|
Ran daily. Purges all events after 100
|
||||||
|
"""
|
||||||
|
logger.info("Purging Events in Database")
|
||||||
|
expiration_days = 7
|
||||||
|
limit = datetime.datetime.now() - datetime.timedelta(days=expiration_days)
|
||||||
|
session = create_session()
|
||||||
|
session.query(Event).filter(Event.time_stamp <= limit).delete()
|
||||||
|
session.commit()
|
||||||
|
session.close()
|
||||||
|
logger.info("Events Purges")
|
||||||
|
|
||||||
|
|
||||||
@scheduler.scheduled_job(trigger="interval", minutes=30)
|
@scheduler.scheduled_job(trigger="interval", minutes=30)
|
||||||
def update_webhook_schedule():
|
def update_webhook_schedule():
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user