mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -04:00
chore: Remove Server Tasks (#3592)
This commit is contained in:
parent
78d2a3b8aa
commit
61becdbec7
File diff suppressed because one or more lines are too long
@ -85,12 +85,6 @@ export default defineComponent({
|
||||
title: i18n.tc("sidebar.maintenance"),
|
||||
restricted: true,
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.check,
|
||||
to: "/admin/background-tasks",
|
||||
title: i18n.tc("sidebar.background-tasks"),
|
||||
restricted: true,
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.slotMachine,
|
||||
to: "/admin/parser",
|
||||
|
@ -1,19 +0,0 @@
|
||||
import { BaseAPI } from "../base/base-clients";
|
||||
import { ServerTask } from "~/lib/api/types/server";
|
||||
import { PaginationData } from "~/lib/api/types/non-generated";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
base: `${prefix}/admin/server-tasks`,
|
||||
};
|
||||
|
||||
export class AdminTaskAPI extends BaseAPI {
|
||||
async testTask() {
|
||||
return await this.requests.post<ServerTask>(`${routes.base}`, {});
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
return await this.requests.get<PaginationData<ServerTask>>(routes.base);
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { AdminAboutAPI } from "./admin/admin-about";
|
||||
import { AdminTaskAPI } from "./admin/admin-tasks";
|
||||
import { AdminUsersApi } from "./admin/admin-users";
|
||||
import { AdminGroupsApi } from "./admin/admin-groups";
|
||||
import { AdminBackupsApi } from "./admin/admin-backups";
|
||||
@ -9,7 +8,6 @@ import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||
|
||||
export class AdminAPI {
|
||||
public about: AdminAboutAPI;
|
||||
public serverTasks: AdminTaskAPI;
|
||||
public users: AdminUsersApi;
|
||||
public groups: AdminGroupsApi;
|
||||
public backups: AdminBackupsApi;
|
||||
@ -18,7 +16,6 @@ export class AdminAPI {
|
||||
|
||||
constructor(requests: ApiRequestInstance) {
|
||||
this.about = new AdminAboutAPI(requests);
|
||||
this.serverTasks = new AdminTaskAPI(requests);
|
||||
this.users = new AdminUsersApi(requests);
|
||||
this.groups = new AdminGroupsApi(requests);
|
||||
this.backups = new AdminBackupsApi(requests);
|
||||
|
@ -15,7 +15,6 @@ import { RegisterAPI } from "./user/user-registration";
|
||||
import { MealPlanAPI } from "./user/group-mealplan";
|
||||
import { EmailAPI } from "./user/email";
|
||||
import { BulkActionsAPI } from "./user/recipe-bulk-actions";
|
||||
import { GroupServerTaskAPI } from "./user/group-tasks";
|
||||
import { ToolsApi } from "./user/organizer-tools";
|
||||
import { GroupMigrationApi } from "./user/group-migrations";
|
||||
import { GroupReportsApi } from "./user/group-reports";
|
||||
@ -46,7 +45,6 @@ export class UserApiClient {
|
||||
public bulk: BulkActionsAPI;
|
||||
public groupMigration: GroupMigrationApi;
|
||||
public groupReports: GroupReportsApi;
|
||||
public grouperServerTasks: GroupServerTaskAPI;
|
||||
public tools: ToolsApi;
|
||||
public shopping: ShoppingApi;
|
||||
public multiPurposeLabels: MultiPurposeLabelsApi;
|
||||
@ -72,7 +70,6 @@ export class UserApiClient {
|
||||
this.register = new RegisterAPI(requests);
|
||||
this.mealplans = new MealPlanAPI(requests);
|
||||
this.mealplanRules = new MealPlanRulesApi(requests);
|
||||
this.grouperServerTasks = new GroupServerTaskAPI(requests);
|
||||
|
||||
// Group
|
||||
this.groupMigration = new GroupMigrationApi(requests);
|
||||
|
@ -1,25 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
*/
|
||||
|
||||
export type ServerTaskNames = "Background Task" | "Database Backup" | "Bulk Recipe Import";
|
||||
export type ServerTaskStatus = "running" | "finished" | "failed";
|
||||
|
||||
export interface ServerTask {
|
||||
groupId: string;
|
||||
name?: ServerTaskNames & string;
|
||||
createdAt?: string;
|
||||
status?: ServerTaskStatus & string;
|
||||
log?: string;
|
||||
id: number;
|
||||
}
|
||||
export interface ServerTaskCreate {
|
||||
groupId: string;
|
||||
name?: ServerTaskNames & string;
|
||||
createdAt?: string;
|
||||
status?: ServerTaskStatus & string;
|
||||
log?: string;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { BaseAPI } from "../base/base-clients";
|
||||
import { ServerTask } from "~/lib/api/types/server";
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
base: `${prefix}/groups/server-tasks`,
|
||||
};
|
||||
|
||||
export class GroupServerTaskAPI extends BaseAPI {
|
||||
async getAll() {
|
||||
return await this.requests.get<ServerTask[]>(routes.base);
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
<template>
|
||||
<v-container class="narrow-container">
|
||||
<BasePageTitle divider>
|
||||
<template #header>
|
||||
<v-img max-height="125" max-width="125" :src="require('~/static/svgs/manage-tasks.svg')"></v-img>
|
||||
</template>
|
||||
<template #title> {{ $t('admin.background-tasks') }} </template>
|
||||
{{ $t('admin.background-tasks-description') }}
|
||||
</BasePageTitle>
|
||||
<v-card-actions>
|
||||
<BaseButton color="info" :loading="loading" @click="refreshTasks">
|
||||
<template #icon> {{ $globals.icons.refresh }} </template>
|
||||
{{ $t('general.refresh') }}
|
||||
</BaseButton>
|
||||
<BaseButton color="info" @click="testTask">
|
||||
<template #icon> {{ $globals.icons.testTube }} </template>
|
||||
{{ $t('general.test') }}
|
||||
</BaseButton>
|
||||
</v-card-actions>
|
||||
<v-expansion-panels class="mt-2">
|
||||
<v-expansion-panel v-for="(task, i) in tasks" :key="i">
|
||||
<v-expansion-panel-header>
|
||||
<span>
|
||||
<v-progress-circular v-if="task.status === 'running'" indeterminate color="info"></v-progress-circular>
|
||||
<v-icon v-else-if="task.status === 'finished'" large color="success"> {{ $globals.icons.check }}</v-icon>
|
||||
<v-icon v-else-if="task.status === 'failed'" large color="error"> {{ $globals.icons.close }}</v-icon>
|
||||
<v-icon v-else-if="task.status === 'pending'" large color="gray"> {{ $globals.icons.pending }}</v-icon>
|
||||
<span class="ml-2">
|
||||
{{ task.name }}
|
||||
</span>
|
||||
</span>
|
||||
{{ $d(Date.parse(task.createdAt), "short") }}
|
||||
</v-expansion-panel-header>
|
||||
<v-expansion-panel-content style="white-space: pre-line">
|
||||
{{ task.log === "" ? $t('admin.no-logs-found') : task.log }}
|
||||
</v-expansion-panel-content>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from "@nuxtjs/composition-api";
|
||||
import { ServerTask } from "~/lib/api/types/server";
|
||||
import { useAdminApi } from "~/composables/api";
|
||||
|
||||
export default defineComponent({
|
||||
layout: "admin",
|
||||
setup() {
|
||||
const api = useAdminApi();
|
||||
|
||||
const tasks = ref<ServerTask[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
async function refreshTasks() {
|
||||
loading.value = true;
|
||||
const { data } = await api.serverTasks.getAll();
|
||||
|
||||
if (data) {
|
||||
tasks.value = data.items;
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
async function testTask() {
|
||||
await api.serverTasks.testTask();
|
||||
refreshTasks();
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await refreshTasks();
|
||||
});
|
||||
|
||||
return {
|
||||
loading,
|
||||
refreshTasks,
|
||||
testTask,
|
||||
tasks,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("admin.tasks"),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@ -15,13 +15,19 @@ from .._model_utils import GUID, auto_init
|
||||
from ..group.invite_tokens import GroupInviteToken
|
||||
from ..group.webhooks import GroupWebhooksModel
|
||||
from ..recipe.category import Category, group_to_categories
|
||||
from ..server.task import ServerTaskModel
|
||||
from .cookbook import CookBook
|
||||
from .mealplan import GroupMealPlan
|
||||
from .preferences import GroupPreferencesModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..recipe import IngredientFoodModel, IngredientUnitModel, RecipeModel, Tag, Tool
|
||||
from ..recipe import (
|
||||
IngredientFoodModel,
|
||||
IngredientUnitModel,
|
||||
RecipeModel,
|
||||
Tag,
|
||||
Tool,
|
||||
)
|
||||
from ..server.task import ServerTaskModel
|
||||
from ..users import User
|
||||
from .events import GroupEventNotifierModel
|
||||
from .exports import GroupDataExportsModel
|
||||
@ -67,7 +73,7 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
webhooks: Mapped[list[GroupWebhooksModel]] = orm.relationship(GroupWebhooksModel, **common_args)
|
||||
recipe_actions: Mapped[list["GroupRecipeAction"]] = orm.relationship("GroupRecipeAction", **common_args)
|
||||
cookbooks: Mapped[list[CookBook]] = orm.relationship(CookBook, **common_args)
|
||||
server_tasks: Mapped[list[ServerTaskModel]] = orm.relationship(ServerTaskModel, **common_args)
|
||||
server_tasks: Mapped[list["ServerTaskModel"]] = orm.relationship("ServerTaskModel", **common_args)
|
||||
data_exports: Mapped[list["GroupDataExportsModel"]] = orm.relationship("GroupDataExportsModel", **common_args)
|
||||
shopping_lists: Mapped[list["ShoppingList"]] = orm.relationship("ShoppingList", **common_args)
|
||||
group_reports: Mapped[list["ReportModel"]] = orm.relationship("ReportModel", **common_args)
|
||||
|
@ -14,6 +14,8 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class ServerTaskModel(SqlAlchemyBase, BaseMixins):
|
||||
# Server Tasks are deprecated, but the table still exists in the database
|
||||
|
||||
__tablename__ = "server_tasks"
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
completed_date: Mapped[datetime] = mapped_column(DateTime, nullable=True)
|
||||
|
@ -29,7 +29,6 @@ from mealie.db.models.recipe.recipe_timeline import RecipeTimelineEvent
|
||||
from mealie.db.models.recipe.shared import RecipeShareTokenModel
|
||||
from mealie.db.models.recipe.tag import Tag
|
||||
from mealie.db.models.recipe.tool import Tool
|
||||
from mealie.db.models.server.task import ServerTaskModel
|
||||
from mealie.db.models.users import LongLiveToken, User
|
||||
from mealie.db.models.users.password_reset import PasswordResetModel
|
||||
from mealie.db.models.users.user_to_recipe import UserToRecipe
|
||||
@ -59,7 +58,6 @@ from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUni
|
||||
from mealie.schema.recipe.recipe_share_token import RecipeShareToken
|
||||
from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut
|
||||
from mealie.schema.reports.reports import ReportEntryOut, ReportOut
|
||||
from mealie.schema.server import ServerTask
|
||||
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser
|
||||
from mealie.schema.user.user import UserRatingOut
|
||||
from mealie.schema.user.user_passwords import PrivatePasswordResetToken
|
||||
@ -162,10 +160,6 @@ class AllRepositories:
|
||||
# ================================================================
|
||||
# Group
|
||||
|
||||
@cached_property
|
||||
def server_tasks(self) -> RepositoryGeneric[ServerTask, ServerTaskModel]:
|
||||
return RepositoryGeneric(self.session, PK_ID, ServerTaskModel, ServerTask)
|
||||
|
||||
@cached_property
|
||||
def groups(self) -> RepositoryGroup:
|
||||
return RepositoryGroup(self.session, PK_ID, Group, GroupInDB)
|
||||
|
@ -8,7 +8,6 @@ from . import (
|
||||
admin_maintenance,
|
||||
admin_management_groups,
|
||||
admin_management_users,
|
||||
admin_server_tasks,
|
||||
)
|
||||
|
||||
router = AdminAPIRouter(prefix="/admin")
|
||||
@ -17,7 +16,6 @@ router.include_router(admin_about.router, tags=["Admin: About"])
|
||||
router.include_router(admin_management_users.router, tags=["Admin: Manage Users"])
|
||||
router.include_router(admin_management_groups.router, tags=["Admin: Manage Groups"])
|
||||
router.include_router(admin_email.router, tags=["Admin: Email"])
|
||||
router.include_router(admin_server_tasks.router, tags=["Admin: Server Tasks"])
|
||||
router.include_router(admin_backups.router, tags=["Admin: Backups"])
|
||||
router.include_router(admin_maintenance.router, tags=["Admin: Maintenance"])
|
||||
router.include_router(admin_analytics.router, tags=["Admin: Analytics"])
|
||||
|
@ -1,27 +0,0 @@
|
||||
from fastapi import BackgroundTasks, Depends
|
||||
|
||||
from mealie.routes._base import BaseAdminController, controller
|
||||
from mealie.routes._base.routers import UserAPIRouter
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.server.tasks import ServerTask, ServerTaskNames, ServerTaskPagination
|
||||
from mealie.services.server_tasks import BackgroundExecutor, test_executor_func
|
||||
|
||||
router = UserAPIRouter()
|
||||
|
||||
|
||||
@controller(router)
|
||||
class AdminServerTasksController(BaseAdminController):
|
||||
@router.get("/server-tasks", response_model=ServerTaskPagination)
|
||||
def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
|
||||
response = self.repos.server_tasks.page_all(
|
||||
pagination=q,
|
||||
override=ServerTask,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(router.url_path_for("get_all"), q.model_dump())
|
||||
return response
|
||||
|
||||
@router.post("/server-tasks", response_model=ServerTask, status_code=201)
|
||||
def create_test_tasks(self, bg_tasks: BackgroundTasks):
|
||||
bg_executor = BackgroundExecutor(self.group.id, self.repos, bg_tasks)
|
||||
return bg_executor.dispatch(ServerTaskNames.default, test_executor_func)
|
@ -1,10 +0,0 @@
|
||||
# This file is auto-generated by gen_schema_exports.py
|
||||
from .tasks import ServerTask, ServerTaskCreate, ServerTaskNames, ServerTaskPagination, ServerTaskStatus
|
||||
|
||||
__all__ = [
|
||||
"ServerTask",
|
||||
"ServerTaskCreate",
|
||||
"ServerTaskNames",
|
||||
"ServerTaskPagination",
|
||||
"ServerTaskStatus",
|
||||
]
|
@ -1,50 +0,0 @@
|
||||
import datetime
|
||||
import enum
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import ConfigDict, Field
|
||||
|
||||
from mealie.schema._mealie import MealieModel
|
||||
from mealie.schema.response.pagination import PaginationBase
|
||||
|
||||
|
||||
class ServerTaskNames(str, enum.Enum):
|
||||
default = "Background Task"
|
||||
backup_task = "Database Backup"
|
||||
bulk_recipe_import = "Bulk Recipe Import"
|
||||
|
||||
|
||||
class ServerTaskStatus(str, enum.Enum):
|
||||
running = "running"
|
||||
finished = "finished"
|
||||
failed = "failed"
|
||||
|
||||
|
||||
class ServerTaskCreate(MealieModel):
|
||||
group_id: UUID
|
||||
name: ServerTaskNames = ServerTaskNames.default
|
||||
created_at: datetime.datetime = Field(default_factory=datetime.datetime.now)
|
||||
status: ServerTaskStatus = ServerTaskStatus.running
|
||||
log: str = ""
|
||||
|
||||
def set_running(self) -> None:
|
||||
self.status = ServerTaskStatus.running
|
||||
|
||||
def set_finished(self) -> None:
|
||||
self.status = ServerTaskStatus.finished
|
||||
|
||||
def set_failed(self) -> None:
|
||||
self.status = ServerTaskStatus.failed
|
||||
|
||||
def append_log(self, message: str) -> None:
|
||||
# Prefix with Timestamp and append new line and join to log
|
||||
self.log += f"{datetime.datetime.now()}: {message}\n"
|
||||
|
||||
|
||||
class ServerTask(ServerTaskCreate):
|
||||
id: int
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ServerTaskPagination(PaginationBase):
|
||||
items: list[ServerTask]
|
@ -1 +0,0 @@
|
||||
from .background_executory import *
|
@ -1,62 +0,0 @@
|
||||
from collections.abc import Callable
|
||||
from random import getrandbits
|
||||
from time import sleep
|
||||
from typing import Any
|
||||
|
||||
from fastapi import BackgroundTasks
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.schema.server.tasks import ServerTask, ServerTaskCreate, ServerTaskNames
|
||||
|
||||
|
||||
class BackgroundExecutor:
|
||||
sleep_time = 60
|
||||
|
||||
def __init__(self, group_id: UUID4, repos: AllRepositories, bg: BackgroundTasks) -> None:
|
||||
self.group_id = group_id
|
||||
self.repos = repos
|
||||
self.background_tasks = bg
|
||||
|
||||
def dispatch(self, task_name: ServerTaskNames, func: Callable, *args: Any, **kwargs: Any) -> ServerTask:
|
||||
"""The dispatch function is a wrapper around the BackgroundTasks class in Starlett. It directly calls
|
||||
the add_task function and your task will be run in the background. This function all passes the id required
|
||||
to check on the server tasks in the database and provide updates.
|
||||
|
||||
Tasks that are dispachd by the Background executor should be designed to accept this key word argument
|
||||
and update the item in the database accordingly.
|
||||
"""
|
||||
|
||||
server_task = ServerTaskCreate(group_id=self.group_id, name=task_name)
|
||||
server_task = self.repos.server_tasks.create(server_task)
|
||||
|
||||
self.background_tasks.add_task(func, *args, **kwargs, task_id=server_task.id, session=self.repos.session)
|
||||
|
||||
return server_task
|
||||
|
||||
|
||||
def test_executor_func(task_id: int, session: Session) -> None:
|
||||
database = get_repositories(session)
|
||||
task = database.server_tasks.get_one(task_id)
|
||||
|
||||
task.append_log("test task has started")
|
||||
task.append_log("test task sleeping for 60 seconds")
|
||||
|
||||
sleep(BackgroundExecutor.sleep_time)
|
||||
|
||||
task.append_log("test task has finished sleep")
|
||||
|
||||
# Randomly Decide to set to failed or not
|
||||
|
||||
is_fail = bool(getrandbits(1))
|
||||
|
||||
if is_fail:
|
||||
task.append_log("test task has failed")
|
||||
task.set_failed()
|
||||
else:
|
||||
task.append_log("test task has succeeded")
|
||||
task.set_finished()
|
||||
|
||||
database.server_tasks.update(task.id, task)
|
@ -1,21 +0,0 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from mealie.services.server_tasks.background_executory import BackgroundExecutor
|
||||
from tests.utils import api_routes
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
||||
def test_admin_server_tasks_test_and_get(api_client: TestClient, admin_user: TestUser):
|
||||
# Bootstrap Timer
|
||||
BackgroundExecutor.sleep_time = 1
|
||||
|
||||
response = api_client.post(api_routes.admin_server_tasks, headers=admin_user.token)
|
||||
assert response.status_code == 201
|
||||
|
||||
response = api_client.get(api_routes.admin_server_tasks, headers=admin_user.token)
|
||||
as_dict = response.json()["items"]
|
||||
|
||||
assert len(as_dict) == 1
|
||||
|
||||
# Reset Timer
|
||||
BackgroundExecutor.sleep_time = 60
|
@ -33,8 +33,6 @@ admin_maintenance_logs = "/api/admin/maintenance/logs"
|
||||
"""`/api/admin/maintenance/logs`"""
|
||||
admin_maintenance_storage = "/api/admin/maintenance/storage"
|
||||
"""`/api/admin/maintenance/storage`"""
|
||||
admin_server_tasks = "/api/admin/server-tasks"
|
||||
"""`/api/admin/server-tasks`"""
|
||||
admin_users = "/api/admin/users"
|
||||
"""`/api/admin/users`"""
|
||||
admin_users_password_reset_token = "/api/admin/users/password-reset-token"
|
||||
|
Loading…
x
Reference in New Issue
Block a user