mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 10:13:32 -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:
		| @@ -8,7 +8,9 @@ | ||||
|       :loading="loading" | ||||
|     > | ||||
|       <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> | ||||
|       <v-card-text class="mt-6"> | ||||
|         <v-text-field dense :label="$t('settings.backup.backup-tag')" v-model="tag"></v-text-field> | ||||
|   | ||||
| @@ -1,7 +1,12 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <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-app-bar dark :color="color" class="mt-n1 mb-0"> | ||||
|           <v-icon large left> | ||||
| @@ -24,6 +29,7 @@ | ||||
|             <v-btn color="error" text @click="deleteEvent" v-if="$listeners.delete"> | ||||
|               {{ $t("general.delete") }} | ||||
|             </v-btn> | ||||
|             <slot name="extra-buttons"> </slot> | ||||
|             <v-btn color="success" type="submit" @click="submitEvent"> | ||||
|               {{ submitText }} | ||||
|             </v-btn> | ||||
|   | ||||
| @@ -1,21 +1,14 @@ | ||||
| <template> | ||||
|   <div class="text-center"> | ||||
|     <v-dialog v-model="dialog" width="500" :fullscreen="$vuetify.breakpoint.xsOnly"> | ||||
|       <v-card> | ||||
|         <v-toolbar dark color="primary" v-show="$vuetify.breakpoint.xsOnly"> | ||||
|           <v-btn icon dark @click="dialog = false"> | ||||
|             <v-icon>mdi-close</v-icon> | ||||
|           </v-btn> | ||||
|           <v-toolbar-title></v-toolbar-title> | ||||
|           <v-spacer></v-spacer> | ||||
|           <v-toolbar-items> | ||||
|             <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> | ||||
|     <BaseDialog | ||||
|       :title="name" | ||||
|       titleIcon="mdi-database" | ||||
|       :submit-text="$t('general.import')" | ||||
|       :loading="loading" | ||||
|       ref="baseDialog" | ||||
|       @submit="raiseEvent" | ||||
|     > | ||||
|       <v-card-subtitle class="mb-n3 mt-3" v-if="date"> {{ $d(new Date(date), "medium") }} </v-card-subtitle> | ||||
|       <v-divider></v-divider> | ||||
|  | ||||
|       <v-card-text> | ||||
| @@ -31,28 +24,29 @@ | ||||
|       </v-card-text> | ||||
|  | ||||
|       <v-divider></v-divider> | ||||
|  | ||||
|         <v-card-actions> | ||||
|           <TheDownloadBtn :download-url="downloadUrl" /> | ||||
|           <v-spacer></v-spacer> | ||||
|           <v-btn color="error" text @click="raiseEvent('delete')"> | ||||
|             {{ $t("general.delete") }} | ||||
|       <template v-slot:extra-buttons> | ||||
|         <TheDownloadBtn :download-url="downloadUrl"> | ||||
|           <template v-slot:default="{ downloadFile }"> | ||||
|             <v-btn class="mr-1" color="info" @click="downloadFile"> | ||||
|               <v-icon left> mdi-download </v-icon> | ||||
|               {{ $t("general.download") }} | ||||
|             </v-btn> | ||||
|           <v-btn color="success" outlined @click="raiseEvent('import')" v-show="$vuetify.breakpoint.smAndUp"> | ||||
|             {{ $t("general.import") }} | ||||
|           </v-btn> | ||||
|         </v-card-actions> | ||||
|       </v-card> | ||||
|     </v-dialog> | ||||
|           </template> | ||||
|         </TheDownloadBtn> | ||||
|       </template> | ||||
|     </BaseDialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| const IMPORT_EVENT = "import"; | ||||
| import { api } from "@/api"; | ||||
| import BaseDialog from "./BaseDialog"; | ||||
| import ImportOptions from "@/components/FormHelpers/ImportOptions"; | ||||
| import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn.vue"; | ||||
| import { backupURLs } from "@/api/backup"; | ||||
| export default { | ||||
|   components: { ImportOptions, TheDownloadBtn }, | ||||
|   components: { ImportOptions, TheDownloadBtn, BaseDialog }, | ||||
|   props: { | ||||
|     name: { | ||||
|       default: "Backup Name", | ||||
| @@ -63,6 +57,7 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       loading: false, | ||||
|       options: { | ||||
|         recipes: true, | ||||
|         settings: true, | ||||
| @@ -87,12 +82,13 @@ export default { | ||||
|     }, | ||||
|     open() { | ||||
|       this.dialog = true; | ||||
|       this.$refs.baseDialog.open(); | ||||
|     }, | ||||
|     close() { | ||||
|       this.dialog = false; | ||||
|     }, | ||||
|     raiseEvent(event) { | ||||
|       let eventData = { | ||||
|     async raiseEvent() { | ||||
|       const eventData = { | ||||
|         name: this.name, | ||||
|         force: this.forceImport, | ||||
|         rebase: this.rebaseImport, | ||||
| @@ -102,8 +98,18 @@ export default { | ||||
|         users: this.options.users, | ||||
|         groups: this.options.groups, | ||||
|       }; | ||||
|       this.close(); | ||||
|       this.$emit(event, eventData); | ||||
|       this.loading = true; | ||||
|       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-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> | ||||
|       </template> | ||||
| @@ -77,6 +77,11 @@ export default { | ||||
|     this.getVersion(); | ||||
|     this.resetView(); | ||||
|   }, | ||||
|   watch: { | ||||
|     user() { | ||||
|       this.hideImage = false; | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     isMain() { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| export const initials = { | ||||
|   computed: { | ||||
|     initials() { | ||||
|       if (!this.user.fullName) return "00" | ||||
|       const allNames = this.user.fullName.trim().split(" "); | ||||
|       const initials = allNames.reduce( | ||||
|         (acc, curr, index) => { | ||||
|   | ||||
| @@ -112,13 +112,7 @@ export default { | ||||
|     }, | ||||
|  | ||||
|     async importBackup(data) { | ||||
|       this.loading = true; | ||||
|       const response = await api.backups.import(data.name, data); | ||||
|       if (response) { | ||||
|         const importData = response.data; | ||||
|         this.$refs.report.open(importData); | ||||
|       } | ||||
|       this.loading = false; | ||||
|       this.$refs.report.open(data); | ||||
|     }, | ||||
|  | ||||
|     async createBackup() { | ||||
|   | ||||
| @@ -123,19 +123,12 @@ export default { | ||||
|       }, | ||||
|       showPassword: false, | ||||
|       loading: false, | ||||
|       user: { | ||||
|         fullName: "", | ||||
|         email: "", | ||||
|         group: "", | ||||
|         admin: false, | ||||
|         id: 0, | ||||
|       }, | ||||
|       user: {}, | ||||
|     }; | ||||
|   }, | ||||
|  | ||||
|   computed: { | ||||
|     userProfileImage() { | ||||
|       this.resetImage(); | ||||
|       return `api/users/${this.user.id}/image`; | ||||
|     }, | ||||
|   }, | ||||
| @@ -144,10 +137,13 @@ export default { | ||||
|     this.refreshProfile(); | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     resetImage() { | ||||
|   watch: { | ||||
|     user() { | ||||
|       this.hideImage = false; | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     async refreshProfile() { | ||||
|       this.user = await api.users.self(); | ||||
|     }, | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class _Recipes(BaseDocument): | ||||
|         return f"{slug}.{extension}" | ||||
|  | ||||
|     def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int: | ||||
|         return self._countr_attribute( | ||||
|         return self._count_attribute( | ||||
|             session, | ||||
|             attribute_name=RecipeModel.recipe_category, | ||||
|             attr_match=None, | ||||
| @@ -47,7 +47,7 @@ class _Recipes(BaseDocument): | ||||
|         ) | ||||
|  | ||||
|     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 | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -178,7 +178,7 @@ class BaseDocument: | ||||
|         else: | ||||
|             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 | ||||
|     ) -> Union[int, BaseModel]: | ||||
|         eff_schema = override_schema or self.schema | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| import datetime | ||||
|  | ||||
| from apscheduler.schedulers.background import BackgroundScheduler | ||||
| from mealie.core import root_logger | ||||
| from mealie.db.database import db | ||||
| from mealie.db.db_setup import create_session | ||||
| from mealie.db.models.event import Event | ||||
| from mealie.schema.user import GroupInDB | ||||
| from mealie.services.backups.exports import auto_backup_job | ||||
| from mealie.services.scheduler.global_scheduler import scheduler | ||||
| @@ -13,6 +16,21 @@ logger = root_logger.get_logger() | ||||
| # 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) | ||||
| def update_webhook_schedule(): | ||||
|     """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user