chore: Nuxt 4 upgrade (#7426)

This commit is contained in:
Kuchenpirat
2026-04-08 17:25:41 +02:00
committed by GitHub
parent 70a251a331
commit d3e41582ae
561 changed files with 1840 additions and 2750 deletions

View File

@@ -1,40 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { AllBackups, BackupOptions, CreateBackup } from "~/lib/api/types/admin";
const prefix = "/api";
const routes = {
backupsAvailable: `${prefix}/backups/available`,
backupsExportDatabase: `${prefix}/backups/export/database`,
backupsUpload: `${prefix}/backups/upload`,
backupsFileNameDownload: (fileName: string) => `${prefix}/backups/${fileName}/download`,
backupsFileNameImport: (fileName: string) => `${prefix}/backups/${fileName}/import`,
backupsFileNameDelete: (fileName: string) => `${prefix}/backups/${fileName}/delete`,
};
export class BackupAPI extends BaseAPI {
/** Returns a list of available .zip files for import into Mealie.
*/
async getAll() {
return await this.requests.get<AllBackups>(routes.backupsAvailable);
}
/** Generates a backup of the recipe database in json format.
*/
async createOne(payload: CreateBackup) {
return await this.requests.post(routes.backupsExportDatabase, payload);
}
/** Import a database backup file generated from Mealie.
*/
async restoreDatabase(fileName: string, payload: BackupOptions) {
return await this.requests.post(routes.backupsFileNameImport(fileName), { name: fileName, ...payload });
}
/** Removes a database backup from the file system
*/
async deleteOne(fileName: string) {
return await this.requests.delete(routes.backupsFileNameDelete(fileName));
}
}

View File

@@ -1,25 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { EmailInitationResponse, EmailInvitation } from "~/lib/api/types/household";
import type { ForgotPassword } from "~/lib/api/types/user";
import type { EmailTest } from "~/lib/api/types/admin";
const routes = {
base: "/api/admin/email",
forgotPassword: "/api/users/forgot-password",
invitation: "/api/households/invitations/email",
};
export class EmailAPI extends BaseAPI {
test(payload: EmailTest) {
return this.requests.post<EmailInitationResponse>(routes.base, payload);
}
sendInvitation(payload: EmailInvitation) {
return this.requests.post<EmailInitationResponse>(routes.invitation, payload);
}
sendForgotPassword(payload: ForgotPassword) {
return this.requests.post<EmailInitationResponse>(routes.forgotPassword, payload);
}
}

View File

@@ -1,18 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { CreateCookBook, ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
const prefix = "/api";
const routes = {
cookbooks: `${prefix}/households/cookbooks`,
cookbooksId: (id: number) => `${prefix}/households/cookbooks/${id}`,
};
export class CookbookAPI extends BaseCRUDAPI<CreateCookBook, ReadCookBook, UpdateCookBook> {
baseRoute: string = routes.cookbooks;
itemRoute = routes.cookbooksId;
async updateAll(payload: UpdateCookBook[]) {
return await this.requests.put(this.baseRoute, payload);
}
}

View File

@@ -1,22 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { GroupEventNotifierCreate, GroupEventNotifierOut, GroupEventNotifierUpdate } from "~/lib/api/types/household";
const prefix = "/api";
const routes = {
eventNotifier: `${prefix}/households/events/notifications`,
eventNotifierId: (id: string | number) => `${prefix}/households/events/notifications/${id}`,
};
export class GroupEventNotifierApi extends BaseCRUDAPI<
GroupEventNotifierCreate,
GroupEventNotifierOut,
GroupEventNotifierUpdate
> {
baseRoute = routes.eventNotifier;
itemRoute = routes.eventNotifierId;
async test(itemId: string) {
return await this.requests.post(`${this.baseRoute}/${itemId}/test`, {});
}
}

View File

@@ -1,14 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { PlanRulesCreate, PlanRulesOut } from "~/lib/api/types/meal-plan";
const prefix = "/api";
const routes = {
rule: `${prefix}/households/mealplans/rules`,
ruleId: (id: string | number) => `${prefix}/households/mealplans/rules/${id}`,
};
export class MealPlanRulesApi extends BaseCRUDAPI<PlanRulesCreate, PlanRulesOut> {
baseRoute = routes.rule;
itemRoute = routes.ruleId;
}

View File

@@ -1,19 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { CreatePlanEntry, CreateRandomEntry, ReadPlanEntry, UpdatePlanEntry } from "~/lib/api/types/meal-plan";
const prefix = "/api";
const routes = {
mealplan: `${prefix}/households/mealplans`,
random: `${prefix}/households/mealplans/random`,
mealplanId: (id: string | number) => `${prefix}/households/mealplans/${id}`,
};
export class MealPlanAPI extends BaseCRUDAPI<CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry> {
baseRoute = routes.mealplan;
itemRoute = routes.mealplanId;
async setRandom(payload: CreateRandomEntry) {
return await this.requests.post<ReadPlanEntry>(routes.random, payload);
}
}

View File

@@ -1,27 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { ReportSummary } from "~/lib/api/types/reports";
import type { SupportedMigrations } from "~/lib/api/types/group";
const prefix = "/api";
export interface MigrationPayload {
addMigrationTag: boolean;
migrationType: SupportedMigrations;
archive: File;
}
const routes = {
base: `${prefix}/groups/migrations`,
};
export class GroupMigrationApi extends BaseAPI {
async startMigration(payload: MigrationPayload) {
const form = new FormData();
form.append("add_migration_tag", String(payload.addMigrationTag));
form.append("migration_type", payload.migrationType);
form.append("archive", payload.archive);
console.log(form);
return await this.requests.post<ReportSummary>(routes.base, form);
}
}

View File

@@ -1,18 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { MultiPurposeLabelCreate, MultiPurposeLabelOut, MultiPurposeLabelUpdate } from "~/lib/api/types/labels";
const prefix = "/api";
const routes = {
labels: `${prefix}/groups/labels`,
labelsId: (id: string | number) => `${prefix}/groups/labels/${id}`,
};
export class MultiPurposeLabelsApi extends BaseCRUDAPI<
MultiPurposeLabelCreate,
MultiPurposeLabelOut,
MultiPurposeLabelUpdate
> {
baseRoute = routes.labels;
itemRoute = routes.labelsId;
}

View File

@@ -1,19 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { CreateGroupRecipeAction, GroupRecipeActionOut } from "~/lib/api/types/household";
const prefix = "/api";
const routes = {
groupRecipeActions: `${prefix}/households/recipe-actions`,
groupRecipeActionsId: (id: string | number) => `${prefix}/households/recipe-actions/${id}`,
groupRecipeActionsIdTriggerRecipeSlug: (id: string | number, recipeSlug: string) => `${prefix}/households/recipe-actions/${id}/trigger/${recipeSlug}`,
};
export class GroupRecipeActionsAPI extends BaseCRUDAPI<CreateGroupRecipeAction, GroupRecipeActionOut> {
baseRoute = routes.groupRecipeActions;
itemRoute = routes.groupRecipeActionsId;
async triggerAction(id: string | number, recipeSlug: string, recipeScale: number) {
return await this.requests.post(routes.groupRecipeActionsIdTriggerRecipeSlug(id, recipeSlug), { recipe_scale: recipeScale });
}
}

View File

@@ -1,24 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { ReportCategory, ReportOut, ReportSummary } from "~/lib/api/types/reports";
const prefix = "/api";
const routes = {
base: `${prefix}/groups/reports`,
getOne: (id: string) => `${prefix}/groups/reports/${id}`,
};
export class GroupReportsApi extends BaseAPI {
async getAll(category: ReportCategory | null) {
const query = category ? `?report_type=${category}` : "";
return await this.requests.get<ReportSummary[]>(routes.base + query);
}
async getOne(id: string) {
return await this.requests.get<ReportOut>(routes.getOne(id));
}
async deleteOne(id: string) {
return await this.requests.delete(routes.getOne(id));
}
}

View File

@@ -1,26 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { SuccessResponse } from "~/lib/api/types/response";
import type { SeederConfig } from "~/lib/api/types/group";
const prefix = "/api";
const routes = {
base: `${prefix}/groups/seeders`,
foods: `${prefix}/groups/seeders/foods`,
units: `${prefix}/groups/seeders/units`,
labels: `${prefix}/groups/seeders/labels`,
};
export class GroupDataSeederApi extends BaseAPI {
foods(payload: SeederConfig) {
return this.requests.post<SuccessResponse>(routes.foods, payload);
}
units(payload: SeederConfig) {
return this.requests.post<SuccessResponse>(routes.units, payload);
}
labels(payload: SeederConfig) {
return this.requests.post<SuccessResponse>(routes.labels, payload);
}
}

View File

@@ -1,80 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { ApiRequestInstance } from "~/lib/api/types/non-generated";
import type {
ShoppingListAddRecipeParamsBulk,
ShoppingListCreate,
ShoppingListItemCreate,
ShoppingListItemOut,
ShoppingListItemUpdateBulk,
ShoppingListMultiPurposeLabelUpdate,
ShoppingListOut,
ShoppingListUpdate,
} from "~/lib/api/types/household";
const prefix = "/api";
const routes = {
shoppingLists: `${prefix}/households/shopping/lists`,
shoppingListsId: (id: string) => `${prefix}/households/shopping/lists/${id}`,
shoppingListIdAddRecipe: (id: string) => `${prefix}/households/shopping/lists/${id}/recipe`,
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}/delete`,
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/households/shopping/lists/${id}/label-settings`,
shoppingListItems: `${prefix}/households/shopping/items`,
shoppingListItemsCreateBulk: `${prefix}/households/shopping/items/create-bulk`,
shoppingListItemsId: (id: string) => `${prefix}/households/shopping/items/${id}`,
};
export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListCreate, ShoppingListOut, ShoppingListUpdate> {
baseRoute = routes.shoppingLists;
itemRoute = routes.shoppingListsId;
async addRecipes(itemId: string, data: ShoppingListAddRecipeParamsBulk[]) {
return await this.requests.post(routes.shoppingListIdAddRecipe(itemId), data);
}
async removeRecipe(itemId: string, recipeId: string, recipeDecrementQuantity = 1) {
return await this.requests.post(routes.shoppingListIdRemoveRecipe(itemId, recipeId), { recipeDecrementQuantity });
}
async updateLabelSettings(itemId: string, listSettings: ShoppingListMultiPurposeLabelUpdate[]) {
return await this.requests.put(routes.shoppingListIdUpdateLabelSettings(itemId), listSettings);
}
}
export class ShoppingListItemsApi extends BaseCRUDAPI<
ShoppingListItemCreate,
ShoppingListItemOut,
ShoppingListItemUpdateBulk
> {
baseRoute = routes.shoppingListItems;
itemRoute = routes.shoppingListItemsId;
async createMany(items: ShoppingListItemCreate[]) {
return await this.requests.post(routes.shoppingListItemsCreateBulk, items);
}
async updateMany(items: ShoppingListItemOut[]) {
return await this.requests.put(routes.shoppingListItems, items);
}
async deleteMany(items: ShoppingListItemOut[]) {
let query = "?";
items.forEach((item) => {
query += `ids=${item.id}&`;
});
return await this.requests.delete(routes.shoppingListItems + query);
}
}
export class ShoppingApi {
public lists: ShoppingListsApi;
public items: ShoppingListItemsApi;
constructor(requests: ApiRequestInstance) {
this.lists = new ShoppingListsApi(requests);
this.items = new ShoppingListItemsApi(requests);
}
}

View File

@@ -1,20 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { CreateWebhook, ReadWebhook } from "~/lib/api/types/household";
const prefix = "/api";
const routes = {
webhooks: `${prefix}/households/webhooks`,
webhooksId: (id: string | number) => `${prefix}/households/webhooks/${id}`,
webhooksIdTest: (id: string | number) => `${prefix}/households/webhooks/${id}/test`,
};
export class WebhooksAPI extends BaseCRUDAPI<CreateWebhook, ReadWebhook> {
baseRoute = routes.webhooks;
itemRoute = routes.webhooksId;
itemTestRoute = routes.webhooksIdTest;
async testOne(itemId: string | number) {
return await this.requests.post<null>(`${this.itemTestRoute(itemId)}`, {});
}
}

View File

@@ -1,48 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { PaginationData } from "../types/non-generated";
import type { QueryValue } from "../base/route";
import type { GroupBase, GroupInDB, GroupSummary, UserSummary } from "~/lib/api/types/user";
import type {
GroupAdminUpdate,
GroupStorage,
ReadGroupPreferences,
UpdateGroupPreferences,
} from "~/lib/api/types/group";
const prefix = "/api";
const routes = {
groups: `${prefix}/admin/groups`,
groupsSelf: `${prefix}/groups/self`,
preferences: `${prefix}/groups/preferences`,
storage: `${prefix}/groups/storage`,
members: `${prefix}/groups/members`,
groupsId: (id: string | number) => `${prefix}/admin/groups/${id}`,
};
export class GroupAPI extends BaseCRUDAPI<GroupBase, GroupInDB, GroupAdminUpdate> {
baseRoute = routes.groups;
itemRoute = routes.groupsId;
/** Returns the Group Data for the Current User
*/
async getCurrentUserGroup() {
return await this.requests.get<GroupSummary>(routes.groupsSelf);
}
async getPreferences() {
return await this.requests.get<ReadGroupPreferences>(routes.preferences);
}
async setPreferences(payload: UpdateGroupPreferences) {
// TODO: This should probably be a patch request, which isn't offered by the API currently
return await this.requests.put<ReadGroupPreferences, UpdateGroupPreferences>(routes.preferences, payload);
}
async fetchMembers(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
return await this.requests.get<PaginationData<UserSummary>>(routes.members, { page, perPage, ...params });
}
async storage() {
return await this.requests.get<GroupStorage>(routes.storage);
}
}

View File

@@ -1,71 +0,0 @@
import { BaseCRUDAPIReadOnly } from "../base/base-clients";
import type { PaginationData } from "../types/non-generated";
import type { QueryValue } from "../base/route";
import type { UserOut } from "~/lib/api/types/user";
import type {
HouseholdInDB,
HouseholdStatistics,
ReadHouseholdPreferences,
SetPermissions,
UpdateHouseholdPreferences,
CreateInviteToken,
ReadInviteToken,
HouseholdSummary,
HouseholdRecipeSummary,
} from "~/lib/api/types/household";
const prefix = "/api";
const routes = {
households: `${prefix}/groups/households`,
householdsSelf: `${prefix}/households/self`,
members: `${prefix}/households/members`,
permissions: `${prefix}/households/permissions`,
preferences: `${prefix}/households/preferences`,
statistics: `${prefix}/households/statistics`,
invitation: `${prefix}/households/invitations`,
householdsId: (id: string | number) => `${prefix}/groups/households/${id}`,
householdsSelfRecipesSlug: (recipeSlug: string) => `${prefix}/households/self/recipes/${recipeSlug}`,
};
export class HouseholdAPI extends BaseCRUDAPIReadOnly<HouseholdSummary> {
baseRoute = routes.households;
itemRoute = routes.householdsId;
/** Returns the Household Data for the Current User
*/
async getCurrentUserHousehold() {
return await this.requests.get<HouseholdInDB>(routes.householdsSelf);
}
async getCurrentUserHouseholdRecipe(recipeSlug: string) {
return await this.requests.get<HouseholdRecipeSummary>(routes.householdsSelfRecipesSlug(recipeSlug));
}
async getPreferences() {
return await this.requests.get<ReadHouseholdPreferences>(routes.preferences);
}
async setPreferences(payload: UpdateHouseholdPreferences) {
// TODO: This should probably be a patch request, which isn't offered by the API currently
return await this.requests.put<ReadHouseholdPreferences, UpdateHouseholdPreferences>(routes.preferences, payload);
}
async createInvitation(payload: CreateInviteToken) {
return await this.requests.post<ReadInviteToken>(routes.invitation, payload);
}
async fetchMembers(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
return await this.requests.get<PaginationData<UserOut>>(routes.members, { page, perPage, ...params });
}
async setMemberPermissions(payload: SetPermissions) {
// TODO: This should probably be a patch request, which isn't offered by the API currently
return await this.requests.put<UserOut, SetPermissions>(routes.permissions, payload);
}
async statistics() {
return await this.requests.get<HouseholdStatistics>(routes.statistics);
}
}

View File

@@ -1,20 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import { config } from "../config";
import type { CategoryIn, RecipeCategoryResponse } from "~/lib/api/types/recipe";
const prefix = config.PREFIX + "/organizers";
const routes = {
categories: `${prefix}/categories`,
categoriesId: (category: string) => `${prefix}/categories/${category}`,
categoriesSlug: (category: string) => `${prefix}/categories/slug/${category}`,
};
export class CategoriesAPI extends BaseCRUDAPI<CategoryIn, RecipeCategoryResponse> {
baseRoute: string = routes.categories;
itemRoute = routes.categoriesId;
async bySlug(slug: string) {
return await this.requests.get<RecipeCategoryResponse>(routes.categoriesSlug(slug));
}
}

View File

@@ -1,20 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import { config } from "../config";
import type { RecipeTagResponse, TagIn } from "~/lib/api/types/recipe";
const prefix = config.PREFIX + "/organizers";
const routes = {
tags: `${prefix}/tags`,
tagsId: (tag: string) => `${prefix}/tags/${tag}`,
tagsSlug: (tag: string) => `${prefix}/tags/slug/${tag}`,
};
export class TagsAPI extends BaseCRUDAPI<TagIn, RecipeTagResponse> {
baseRoute: string = routes.tags;
itemRoute = routes.tagsId;
async bySlug(slug: string) {
return await this.requests.get<RecipeTagResponse>(routes.tagsSlug(slug));
}
}

View File

@@ -1,20 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import { config } from "../config";
import type { RecipeTool, RecipeToolCreate, RecipeToolResponse } from "~/lib/api/types/recipe";
const prefix = config.PREFIX + "/organizers";
const routes = {
tools: `${prefix}/tools`,
toolsId: (id: string) => `${prefix}/tools/${id}`,
toolsSlug: (id: string) => `${prefix}/tools/slug/${id}`,
};
export class ToolsApi extends BaseCRUDAPI<RecipeToolCreate, RecipeTool> {
baseRoute: string = routes.tools;
itemRoute = routes.toolsId;
async bySlug(slug: string) {
return await this.requests.get<RecipeToolResponse>(routes.toolsSlug(slug));
}
}

View File

@@ -1,48 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { AssignCategories, AssignSettings, AssignTags, DeleteRecipes, ExportRecipes } from "~/lib/api/types/recipe";
import type { GroupDataExport } from "~/lib/api/types/group";
// Many bulk actions return nothing
type BulkActionResponse = object;
const prefix = "/api";
const routes = {
bulkExport: prefix + "/recipes/bulk-actions/export",
purgeExports: prefix + "/recipes/bulk-actions/export/purge",
bulkCategorize: prefix + "/recipes/bulk-actions/categorize",
bulkTag: prefix + "/recipes/bulk-actions/tag",
bulkDelete: prefix + "/recipes/bulk-actions/delete",
bulkSettings: prefix + "/recipes/bulk-actions/settings",
};
export class BulkActionsAPI extends BaseAPI {
async bulkExport(payload: ExportRecipes) {
return await this.requests.post<BulkActionResponse>(routes.bulkExport, payload);
}
async bulkCategorize(payload: AssignCategories) {
return await this.requests.post<BulkActionResponse>(routes.bulkCategorize, payload);
}
async bulkSetSettings(payload: AssignSettings) {
return await this.requests.post<BulkActionResponse>(routes.bulkSettings, payload);
}
async bulkTag(payload: AssignTags) {
return await this.requests.post<BulkActionResponse>(routes.bulkTag, payload);
}
async bulkDelete(payload: DeleteRecipes) {
return await this.requests.post<BulkActionResponse>(routes.bulkDelete, payload);
}
async fetchExports() {
return await this.requests.get<GroupDataExport[]>(routes.bulkExport);
}
async purgeExports() {
return await this.requests.delete<BulkActionResponse>(routes.purgeExports);
}
}

View File

@@ -1,19 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { CreateIngredientFood, IngredientFood } from "~/lib/api/types/recipe";
const prefix = "/api";
const routes = {
food: `${prefix}/foods`,
foodsFood: (tag: string) => `${prefix}/foods/${tag}`,
merge: `${prefix}/foods/merge`,
};
export class FoodAPI extends BaseCRUDAPI<CreateIngredientFood, IngredientFood> {
baseRoute: string = routes.food;
itemRoute = routes.foodsFood;
merge(fromId: string, toId: string) {
return this.requests.put<IngredientFood>(routes.merge, { fromFood: fromId, toFood: toId });
}
}

View File

@@ -1,19 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type { CreateIngredientUnit, IngredientUnit } from "~/lib/api/types/recipe";
const prefix = "/api";
const routes = {
unit: `${prefix}/units`,
unitsUnit: (tag: string) => `${prefix}/units/${tag}`,
merge: `${prefix}/units/merge`,
};
export class UnitAPI extends BaseCRUDAPI<CreateIngredientUnit, IngredientUnit> {
baseRoute: string = routes.unit;
itemRoute = routes.unitsUnit;
merge(fromId: string, toId: string) {
return this.requests.put<IngredientUnit>(routes.merge, { fromUnit: fromId, toUnit: toId });
}
}

View File

@@ -1 +0,0 @@
export { RecipeAPI } from "./recipe";

View File

@@ -1,19 +0,0 @@
import { BaseCRUDAPI } from "../../base/base-clients";
import type { RecipeCommentCreate, RecipeCommentOut, RecipeCommentUpdate } from "~/lib/api/types/recipe";
const prefix = "/api";
const routes = {
comment: `${prefix}/comments`,
byRecipe: (id: string) => `${prefix}/recipes/${id}/comments`,
commentsId: (id: string) => `${prefix}/comments/${id}`,
};
export class CommentsApi extends BaseCRUDAPI<RecipeCommentCreate, RecipeCommentOut, RecipeCommentUpdate> {
baseRoute: string = routes.comment;
itemRoute = routes.commentsId;
async byRecipe(slug: string) {
return await this.requests.get<RecipeCommentOut[]>(routes.byRecipe(slug));
}
}

View File

@@ -1,19 +0,0 @@
import { BaseCRUDAPI } from "../../base/base-clients";
import type { RecipeShareToken, RecipeShareTokenCreate } from "~/lib/api/types/recipe";
const prefix = "/api";
const routes = {
shareToken: `${prefix}/shared/recipes`,
shareTokenId: (id: string) => `${prefix}/shared/recipes/${id}`,
shareTokenIdZip: (id: string) => `${prefix}/recipes/shared/${id}/zip`,
};
export class RecipeShareApi extends BaseCRUDAPI<RecipeShareTokenCreate, RecipeShareToken> {
baseRoute: string = routes.shareToken;
itemRoute = routes.shareTokenId;
getZipRedirectUrl(tokenId: string) {
return routes.shareTokenIdZip(tokenId);
}
}

View File

@@ -1,283 +0,0 @@
import { SSE } from "sse.js";
import type { SSEvent } from "sse.js";
import { BaseCRUDAPI } from "../../base/base-clients";
import { route } from "../../base";
import { CommentsApi } from "./recipe-comments";
import { RecipeShareApi } from "./recipe-share";
import type {
Recipe,
CreateRecipe,
RecipeAsset,
CreateRecipeByUrlBulk,
ParsedIngredient,
UpdateImageResponse,
RecipeLastMade,
RecipeSuggestionQuery,
RecipeSuggestionResponse,
RecipeTimelineEventIn,
RecipeTimelineEventOut,
RecipeTimelineEventUpdate,
} from "~/lib/api/types/recipe";
import type { SSEDataEventDone, SSEDataEventMessage } from "~/lib/api/types/response";
import type { ApiRequestInstance, PaginationData, RequestResponse } from "~/lib/api/types/non-generated";
import { SSEDataEventStatus } from "~/lib/api/types/non-generated";
export type Parser = "nlp" | "brute" | "openai";
export interface CreateAsset {
name: string;
icon: string;
extension: string;
file: File;
}
const prefix = "/api";
const routes = {
recipesCreate: `${prefix}/recipes/create`,
recipesBase: `${prefix}/recipes`,
recipesSuggestions: `${prefix}/recipes/suggestions`,
recipesTestScrapeUrl: `${prefix}/recipes/test-scrape-url`,
recipesCreateUrl: `${prefix}/recipes/create/url/stream`,
recipesCreateUrlBulk: `${prefix}/recipes/create/url/bulk`,
recipesCreateFromZip: `${prefix}/recipes/create/zip`,
recipesCreateFromImage: `${prefix}/recipes/create/image`,
recipesCreateFromHtmlOrJson: `${prefix}/recipes/create/html-or-json/stream`,
recipesCategory: `${prefix}/recipes/category`,
recipesParseIngredient: `${prefix}/parser/ingredient`,
recipesParseIngredients: `${prefix}/parser/ingredients`,
recipesTimelineEvent: `${prefix}/recipes/timeline/events`,
recipesRecipeSlug: (recipe_slug: string) => `${prefix}/recipes/${recipe_slug}`,
recipesRecipeSlugImage: (recipe_slug: string) => `${prefix}/recipes/${recipe_slug}/image`,
recipesRecipeSlugAssets: (recipe_slug: string) => `${prefix}/recipes/${recipe_slug}/assets`,
recipesSlugComments: (slug: string) => `${prefix}/recipes/${slug}/comments`,
recipesSlugCommentsId: (slug: string, id: number) => `${prefix}/recipes/${slug}/comments/${id}`,
recipesSlugLastMade: (slug: string) => `${prefix}/recipes/${slug}/last-made`,
recipesTimelineEventId: (id: string) => `${prefix}/recipes/timeline/events/${id}`,
recipesTimelineEventIdImage: (id: string) => `${prefix}/recipes/timeline/events/${id}/image`,
};
export type RecipeSearchQuery = {
search?: string;
orderDirection?: "asc" | "desc";
groupId?: string;
queryFilter?: string;
cookbook?: string;
households?: string[];
categories?: string[];
requireAllCategories?: boolean;
tags?: string[];
requireAllTags?: boolean;
tools?: string[];
requireAllTools?: boolean;
foods?: string[];
requireAllFoods?: boolean;
page?: number;
perPage?: number;
orderBy?: string;
orderByNullPosition?: "first" | "last";
_searchSeed?: string;
};
export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
baseRoute: string = routes.recipesBase;
itemRoute = routes.recipesRecipeSlug;
comments: CommentsApi;
share: RecipeShareApi;
constructor(requests: ApiRequestInstance) {
super(requests);
this.comments = new CommentsApi(requests);
this.share = new RecipeShareApi(requests);
}
async search(rsq: RecipeSearchQuery) {
return await this.requests.get<PaginationData<Recipe>>(route(routes.recipesBase, rsq));
}
async getAllByCategory(categories: string[]) {
return await this.requests.get<Recipe[]>(routes.recipesCategory, {
categories,
});
}
async getSuggestions(q: RecipeSuggestionQuery, foods: string[] | null = null, tools: string[] | null = null) {
return await this.requests.get<RecipeSuggestionResponse>(
route(routes.recipesSuggestions, { ...q, foods, tools }),
);
}
async createAsset(recipeSlug: string, payload: CreateAsset) {
const formData = new FormData();
formData.append("file", payload.file);
formData.append("name", payload.name);
formData.append("extension", payload.extension);
formData.append("icon", payload.icon);
return await this.requests.post<RecipeAsset>(routes.recipesRecipeSlugAssets(recipeSlug), formData);
}
updateImage(slug: string, fileObject: File) {
const formData = new FormData();
formData.append("image", fileObject);
formData.append("extension", fileObject.name.split(".").pop() ?? "");
return this.requests.put<UpdateImageResponse, FormData>(routes.recipesRecipeSlugImage(slug), formData);
}
updateImagebyURL(slug: string, url: string) {
return this.requests.post<UpdateImageResponse>(routes.recipesRecipeSlugImage(slug), { url });
}
deleteImage(slug: string) {
return this.requests.delete<string>(routes.recipesRecipeSlugImage(slug));
}
async testCreateOneUrl(url: string, useOpenAI = false) {
return await this.requests.post<Recipe | null>(routes.recipesTestScrapeUrl, { url, useOpenAI });
}
private streamRecipeCreate(streamRoute: string, payload: object, onProgress?: (message: string) => void): Promise<RequestResponse<string>> {
return new Promise((resolve) => {
const { token } = useMealieAuth();
const sse = new SSE(streamRoute, {
headers: {
"Content-Type": "application/json",
...(token.value ? { Authorization: `Bearer ${token.value}` } : {}),
},
payload: JSON.stringify(payload),
withCredentials: true,
autoReconnect: false,
});
if (onProgress) {
sse.addEventListener(SSEDataEventStatus.Progress, (e: SSEvent) => {
const { message } = JSON.parse(e.data) as SSEDataEventMessage;
onProgress(message);
});
}
sse.addEventListener(SSEDataEventStatus.Done, (e: SSEvent) => {
const { slug } = JSON.parse(e.data) as SSEDataEventDone;
sse.close();
resolve({ response: { status: 201, data: slug } as any, data: slug, error: null });
});
sse.addEventListener(SSEDataEventStatus.Error, (e: SSEvent) => {
try {
const { message } = JSON.parse(e.data) as SSEDataEventMessage;
sse.close();
resolve({ response: null, data: null, error: new Error(message) });
}
catch {
// Not a backend error payload (e.g. XHR connection-close event); ignore
}
});
sse.stream();
});
}
async createOneByHtmlOrJson(
data: string,
includeTags: boolean,
includeCategories: boolean,
url: string | null = null,
onProgress?: (message: string) => void,
): Promise<RequestResponse<string>> {
return this.streamRecipeCreate(routes.recipesCreateFromHtmlOrJson, { data, includeTags, includeCategories, url }, onProgress);
}
async createOneByUrl(
url: string,
includeTags: boolean,
includeCategories: boolean,
onProgress?: (message: string) => void,
): Promise<RequestResponse<string>> {
return this.streamRecipeCreate(routes.recipesCreateUrl, { url, includeTags, includeCategories }, onProgress);
}
async createManyByUrl(payload: CreateRecipeByUrlBulk) {
return await this.requests.post<string>(routes.recipesCreateUrlBulk, payload);
}
async createOneFromImages(fileObjects: (Blob | File)[], translateLanguage: string | null = null) {
const formData = new FormData();
fileObjects.forEach((file) => {
formData.append("images", file);
});
let apiRoute = routes.recipesCreateFromImage;
if (translateLanguage) {
apiRoute = `${apiRoute}?translateLanguage=${translateLanguage}`;
}
return await this.requests.post<string>(apiRoute, formData);
}
async parseIngredients(parser: Parser, ingredients: Array<string>) {
parser = parser || "nlp";
return await this.requests.post<ParsedIngredient[]>(routes.recipesParseIngredients, { parser, ingredients });
}
async parseIngredient(parser: Parser, ingredient: string) {
parser = parser || "nlp";
return await this.requests.post<ParsedIngredient>(routes.recipesParseIngredient, { parser, ingredient });
}
async updateMany(payload: Recipe[]) {
return await this.requests.put<Recipe[]>(routes.recipesBase, payload);
}
async patchMany(payload: Recipe[]) {
return await this.requests.patch<Recipe[]>(routes.recipesBase, payload);
}
async updateLastMade(recipeSlug: string, timestamp: string) {
return await this.requests.patch<Recipe, RecipeLastMade>(routes.recipesSlugLastMade(recipeSlug), { timestamp });
}
async createTimelineEvent(payload: RecipeTimelineEventIn) {
return await this.requests.post<RecipeTimelineEventOut>(routes.recipesTimelineEvent, payload);
}
async updateTimelineEvent(eventId: string, payload: RecipeTimelineEventUpdate) {
return await this.requests.put<RecipeTimelineEventOut, RecipeTimelineEventUpdate>(
routes.recipesTimelineEventId(eventId),
payload,
);
}
async deleteTimelineEvent(eventId: string) {
return await this.requests.delete<RecipeTimelineEventOut>(routes.recipesTimelineEventId(eventId));
}
async getAllTimelineEvents(page = 1, perPage = -1, params = {} as any) {
return await this.requests.get<PaginationData<RecipeTimelineEventOut>>(
routes.recipesTimelineEvent, { page, perPage, ...params },
);
}
async updateTimelineEventImage(eventId: string, fileObject: Blob | File, fileName: string) {
const formData = new FormData();
formData.append("image", fileObject);
formData.append("extension", fileName.split(".").pop() ?? "");
return await this.requests.put<UpdateImageResponse, FormData>(routes.recipesTimelineEventIdImage(eventId), formData);
}
}

View File

@@ -1,9 +0,0 @@
import { BaseAPI } from "../base/base-clients";
export class UploadFile extends BaseAPI {
file(url: string, fileObject: any) {
const { $axios } = useNuxtApp();
return $axios.post<string>(url, fileObject, { headers: { "Content-Type": "multipart/form-data" } });
// return this.requests.post<string>(url, fileObject);
}
}

View File

@@ -1,14 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { CreateUserRegistration } from "~/lib/api/types/user";
const prefix = "/api";
const routes = {
register: `${prefix}/users/register`,
};
export class RegisterAPI extends BaseAPI {
async register(payload: CreateUserRegistration) {
return await this.requests.post<any>(routes.register, payload);
}
}

View File

@@ -1,98 +0,0 @@
import { BaseCRUDAPI } from "../base/base-clients";
import type {
ChangePassword,
DeleteTokenResponse,
LongLiveTokenIn,
LongLiveTokenOut,
ResetPassword,
UserBase,
UserIn,
UserOut,
UserRatingOut,
UserRatingSummary,
} from "~/lib/api/types/user";
export interface UserRatingsSummaries {
ratings: UserRatingSummary[];
}
export interface UserRatingsOut {
ratings: UserRatingOut[];
}
const prefix = "/api";
const routes = {
usersSelf: `${prefix}/users/self`,
ratingsSelf: `${prefix}/users/self/ratings`,
passwordReset: `${prefix}/users/reset-password`,
passwordChange: `${prefix}/users/password`,
users: `${prefix}/users`,
usersIdImage: (id: string) => `${prefix}/users/${id}/image`,
usersIdResetPassword: (id: string) => `${prefix}/users/${id}/reset-password`,
usersId: (id: string) => `${prefix}/users/${id}`,
usersIdFavorites: (id: string) => `${prefix}/users/${id}/favorites`,
usersIdFavoritesSlug: (id: string, slug: string) => `${prefix}/users/${id}/favorites/${slug}`,
usersIdRatings: (id: string) => `${prefix}/users/${id}/ratings`,
usersIdRatingsSlug: (id: string, slug: string) => `${prefix}/users/${id}/ratings/${slug}`,
usersSelfFavoritesId: (id: string) => `${prefix}/users/self/favorites/${id}`,
usersSelfRatingsId: (id: string) => `${prefix}/users/self/ratings/${id}`,
usersApiTokens: `${prefix}/users/api-tokens`,
usersApiTokensTokenId: (token_id: string | number) => `${prefix}/users/api-tokens/${token_id}`,
};
export class UserApi extends BaseCRUDAPI<UserIn, UserOut, UserBase> {
baseRoute: string = routes.users;
itemRoute = (itemid: string) => routes.usersId(itemid);
async addFavorite(id: string, slug: string) {
return await this.requests.post(routes.usersIdFavoritesSlug(id, slug), {});
}
async removeFavorite(id: string, slug: string) {
return await this.requests.delete(routes.usersIdFavoritesSlug(id, slug));
}
async getFavorites(id: string) {
return await this.requests.get<UserRatingsOut>(routes.usersIdFavorites(id));
}
async getSelfFavorites() {
return await this.requests.get<UserRatingsSummaries>(routes.ratingsSelf);
}
async getRatings(id: string) {
return await this.requests.get<UserRatingsOut>(routes.usersIdRatings(id));
}
async setRating(id: string, slug: string, rating: number | null, isFavorite: boolean | null) {
return await this.requests.post(routes.usersIdRatingsSlug(id, slug), { rating, isFavorite });
}
async getSelfRatings() {
return await this.requests.get<UserRatingsSummaries>(routes.ratingsSelf);
}
async changePassword(changePassword: ChangePassword) {
return await this.requests.put(routes.passwordChange, changePassword);
}
async createAPIToken(tokenName: LongLiveTokenIn) {
return await this.requests.post<LongLiveTokenOut>(routes.usersApiTokens, tokenName);
}
async deleteAPIToken(tokenId: number) {
return await this.requests.delete<DeleteTokenResponse>(routes.usersApiTokensTokenId(tokenId));
}
userProfileImage(id: string) {
if (!id || id === undefined) return;
return `/api/users/${id}/image`;
}
async resetPassword(payload: ResetPassword) {
return await this.requests.post(routes.passwordReset, payload);
}
}

View File

@@ -1,20 +0,0 @@
import { BaseAPI } from "../base/base-clients";
import type { FileTokenResponse } from "~/lib/api/types/response";
const prefix = "/api";
export class UtilsAPI extends BaseAPI {
async download(url: string) {
const { response } = await this.requests.get<FileTokenResponse>(url);
if (!response) {
return;
}
const token: string = response.data.fileToken;
const tokenURL = prefix + "/utils/download?token=" + token;
window.open(tokenURL, "_blank");
return response;
}
}