Consolidate frontend types (#1245)

This commit is contained in:
Philipp Fischbeck 2022-05-21 21:22:02 +02:00 committed by GitHub
parent 6a88a59981
commit 479900e912
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 261 additions and 582 deletions

File diff suppressed because one or more lines are too long

View File

@ -18,33 +18,33 @@ export abstract class BaseAPI {
}
}
export abstract class BaseCRUDAPI<T, U> extends BaseAPI implements CrudAPIInterface {
export abstract class BaseCRUDAPI<CreateType, ReadType, UpdateType=CreateType> extends BaseAPI implements CrudAPIInterface {
abstract baseRoute: string;
abstract itemRoute(itemId: string | number): string;
async getAll(start = 0, limit = 9999, params = {} as any) {
return await this.requests.get<T[]>(this.baseRoute, {
return await this.requests.get<ReadType[]>(this.baseRoute, {
params: { start, limit, ...params },
});
}
async createOne(payload: U) {
return await this.requests.post<T>(this.baseRoute, payload);
async createOne(payload: CreateType) {
return await this.requests.post<ReadType>(this.baseRoute, payload);
}
async getOne(itemId: string | number) {
return await this.requests.get<T>(this.itemRoute(itemId));
return await this.requests.get<ReadType>(this.itemRoute(itemId));
}
async updateOne(itemId: string | number, payload: T) {
return await this.requests.put<T>(this.itemRoute(itemId), payload);
async updateOne(itemId: string | number, payload: UpdateType) {
return await this.requests.put<ReadType, UpdateType>(this.itemRoute(itemId), payload);
}
async patchOne(itemId: string, payload: Partial<T>) {
return await this.requests.patch<T>(this.itemRoute(itemId), payload);
async patchOne(itemId: string, payload: Partial<UpdateType>) {
return await this.requests.patch<ReadType, Partial<UpdateType>>(this.itemRoute(itemId), payload);
}
async deleteOne(itemId: string | number) {
return await this.requests.delete<T>(this.itemRoute(itemId));
return await this.requests.delete<ReadType>(this.itemRoute(itemId));
}
}

View File

@ -1,55 +1,19 @@
import { BaseCRUDAPI } from "../_base";
import { UserOut } from "~/types/api-types/user";
import { GroupBase, GroupInDB } from "~/types/api-types/user";
import { GroupAdminUpdate } from "~/types/api-types/group";
const prefix = "/api";
export interface Token {
name: string;
id: number;
createdAt: Date;
}
export interface Preferences {
privateGroup: boolean;
firstDayOfWeek: number;
recipePublic: boolean;
recipeShowNutrition: boolean;
recipeShowAssets: boolean;
recipeLandscapeView: boolean;
recipeDisableComments: boolean;
recipeDisableAmount: boolean;
groupId: number;
id: number;
}
export interface GroupCreate {
name: string;
}
export interface GroupRead extends GroupCreate {
id: number;
categories: any[];
webhooks: any[];
users: UserOut[];
preferences: Preferences;
}
export interface AdminGroupUpdate {
name: string;
id: number;
preferences: Preferences;
}
const routes = {
adminUsers: `${prefix}/admin/groups`,
adminUsersId: (id: number) => `${prefix}/admin/groups/${id}`,
adminUsersId: (id: string) => `${prefix}/admin/groups/${id}`,
};
export class AdminGroupsApi extends BaseCRUDAPI<GroupRead, GroupCreate> {
export class AdminGroupsApi extends BaseCRUDAPI<GroupBase, GroupInDB, GroupAdminUpdate> {
baseRoute: string = routes.adminUsers;
itemRoute = routes.adminUsersId;
async updateOne(id: number, payload: AdminGroupUpdate) {
async updateOne(id: string, payload: GroupAdminUpdate) {
// TODO: This should probably be a patch request, which isn't offered by the API currently
return await this.requests.put<GroupRead, AdminGroupUpdate>(this.itemRoute(id), payload);
return await this.requests.put<GroupInDB, GroupAdminUpdate>(this.itemRoute(id), payload);
}
}

View File

@ -1,5 +1,5 @@
import { BaseAPI } from "../_base";
import { ServerTask } from "~/api/types/server-task";
import { ServerTask } from "~/types/api-types/server";
const prefix = "/api";

View File

@ -8,7 +8,7 @@ const routes = {
adminUsersId: (tag: string) => `${prefix}/admin/users/${tag}`,
};
export class AdminUsersApi extends BaseCRUDAPI<UserOut, UserIn> {
export class AdminUsersApi extends BaseCRUDAPI<UserIn, UserOut, UserOut> {
baseRoute: string = routes.adminUsers;
itemRoute = routes.adminUsersId;
}

View File

@ -1,35 +1,5 @@
import { BaseAPI } from "../_base";
export interface BackupOptions {
recipes?: boolean;
settings?: boolean;
pages?: boolean;
themes?: boolean;
groups?: boolean;
users?: boolean;
notifications?: boolean;
}
export interface ImportBackup {
name: string;
options: BackupOptions;
}
export interface BackupJob {
tag?: string;
options: BackupOptions;
templates?: string[];
}
export interface BackupFile {
name: string;
date: string;
}
export interface AllBackups {
imports: BackupFile[];
templates: string[];
}
import { AllBackups, BackupOptions, CreateBackup } from "~/types/api-types/admin";
const prefix = "/api";
@ -52,7 +22,7 @@ export class BackupAPI extends BaseAPI {
/** Generates a backup of the recipe database in json format.
*/
async createOne(payload: BackupJob) {
async createOne(payload: CreateBackup) {
return await this.requests.post(routes.backupsExportDatabase, payload);
}

View File

@ -1,4 +1,7 @@
import { BaseAPI } from "../_base";
import { EmailInitationResponse, EmailInvitation } from "~/types/api-types/group";
import { ForgotPassword } from "~/types/api-types/user";
import { EmailTest } from "~/types/api-types/admin";
const routes = {
base: "/api/admin/email",
@ -7,30 +10,16 @@ const routes = {
invitation: "/api/groups/invitations/email",
};
export interface EmailResponse {
success: boolean;
error: string;
}
export interface EmailPayload {
email: string;
}
export interface InvitationEmail {
email: string;
token: string;
}
export class EmailAPI extends BaseAPI {
test(payload: EmailPayload) {
return this.requests.post<EmailResponse>(routes.base, payload);
test(payload: EmailTest) {
return this.requests.post<EmailInitationResponse>(routes.base, payload);
}
sendInvitation(payload: InvitationEmail) {
return this.requests.post<EmailResponse>(routes.invitation, payload);
sendInvitation(payload: EmailInvitation) {
return this.requests.post<EmailInitationResponse>(routes.invitation, payload);
}
sendForgotPassword(payload: EmailPayload) {
return this.requests.post<EmailResponse>(routes.forgotPassword, payload);
sendForgotPassword(payload: ForgotPassword) {
return this.requests.post<EmailInitationResponse>(routes.forgotPassword, payload);
}
}

View File

@ -8,7 +8,7 @@ const routes = {
cookbooksId: (id: number) => `${prefix}/groups/cookbooks/${id}`,
};
export class CookbookAPI extends BaseCRUDAPI<RecipeCookBook, CreateCookBook> {
export class CookbookAPI extends BaseCRUDAPI<CreateCookBook, RecipeCookBook, UpdateCookBook> {
baseRoute: string = routes.cookbooks;
itemRoute = routes.cookbooksId;

View File

@ -1,5 +1,5 @@
import { BaseCRUDAPI } from "../_base";
import { GroupEventNotifierCreate, GroupEventNotifierOut } from "~/types/api-types/group";
import { GroupEventNotifierCreate, GroupEventNotifierOut, GroupEventNotifierUpdate } from "~/types/api-types/group";
const prefix = "/api";
@ -8,7 +8,7 @@ const routes = {
eventNotifierId: (id: string | number) => `${prefix}/groups/events/notifications/${id}`,
};
export class GroupEventNotifierApi extends BaseCRUDAPI<GroupEventNotifierOut, GroupEventNotifierCreate> {
export class GroupEventNotifierApi extends BaseCRUDAPI<GroupEventNotifierCreate, GroupEventNotifierOut, GroupEventNotifierUpdate> {
baseRoute = routes.eventNotifier;
itemRoute = routes.eventNotifierId;

View File

@ -8,7 +8,7 @@ const routes = {
ruleId: (id: string | number) => `${prefix}/groups/mealplans/rules/${id}`,
};
export class MealPlanRulesApi extends BaseCRUDAPI<PlanRulesOut, PlanRulesCreate> {
export class MealPlanRulesApi extends BaseCRUDAPI<PlanRulesCreate, PlanRulesOut> {
baseRoute = routes.rule;
itemRoute = routes.ruleId;
}

View File

@ -1,5 +1,5 @@
import { BaseCRUDAPI } from "../_base";
import { CreatRandomEntry } from "~/types/api-types/meal-plan";
import { CreatePlanEntry, CreateRandomEntry, ReadPlanEntry, UpdatePlanEntry } from "~/types/api-types/meal-plan";
const prefix = "/api";
@ -9,31 +9,12 @@ const routes = {
mealplanId: (id: string | number) => `${prefix}/groups/mealplans/${id}`,
};
type PlanEntryType = "breakfast" | "lunch" | "dinner" | "side";
export interface CreateMealPlan {
date: string;
entryType: PlanEntryType;
title: string;
text: string;
recipeId?: string;
}
export interface UpdateMealPlan extends CreateMealPlan {
id: number;
groupId: number;
}
export interface MealPlan extends UpdateMealPlan {
recipe: any;
}
export class MealPlanAPI extends BaseCRUDAPI<MealPlan, CreateMealPlan> {
export class MealPlanAPI extends BaseCRUDAPI<CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry> {
baseRoute = routes.mealplan;
itemRoute = routes.mealplanId;
async setRandom(payload: CreatRandomEntry) {
async setRandom(payload: CreateRandomEntry) {
console.log(payload);
return await this.requests.post<MealPlan>(routes.random, payload);
return await this.requests.post<ReadPlanEntry>(routes.random, payload);
}
}

View File

@ -1,13 +1,11 @@
import { BaseAPI } from "../_base";
import { ReportSummary } from "./group-reports";
import { ReportSummary } from "~/types/api-types/reports";
import { SupportedMigrations } from "~/types/api-types/group";
const prefix = "/api";
export type SupportedMigration = "nextcloud" | "chowdown" | "mealie_alpha" | "paprika";
export interface MigrationPayload {
addMigrationTag: boolean;
migrationType: SupportedMigration;
migrationType: SupportedMigrations;
archive: File;
}

View File

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

View File

@ -1,33 +1,8 @@
import { BaseAPI } from "../_base";
import { ReportCategory, ReportOut, ReportSummary } from "~/types/api-types/reports";
const prefix = "/api";
export type ReportCategory = "backup" | "restore" | "migration";
export type SummaryStatus = "success" | "failure" | "partial" | "in-progress";
export interface ReportEntry {
id: string;
reportId: string;
timestamp: Date;
success: boolean;
message: string;
exception: string;
}
export interface ReportSummary {
id: string;
timestamp: Date;
category: ReportCategory;
groupId: number;
name: string;
status: SummaryStatus;
}
export interface Report extends ReportSummary {
entries: ReportEntry[];
}
const routes = {
base: `${prefix}/groups/reports`,
getOne: (id: string) => `${prefix}/groups/reports/${id}`,
@ -40,7 +15,7 @@ export class GroupReportsApi extends BaseAPI {
}
async getOne(id: string) {
return await this.requests.get<Report>(routes.getOne(id));
return await this.requests.get<ReportOut>(routes.getOne(id));
}
async deleteOne(id: string) {

View File

@ -4,7 +4,9 @@ import {
ShoppingListCreate,
ShoppingListItemCreate,
ShoppingListItemOut,
ShoppingListItemUpdate,
ShoppingListOut,
ShoppingListUpdate,
} from "~/types/api-types/group";
const prefix = "/api";
@ -18,7 +20,7 @@ const routes = {
shoppingListItemsId: (id: string) => `${prefix}/groups/shopping/items/${id}`,
};
export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListOut, ShoppingListCreate> {
export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListCreate, ShoppingListOut, ShoppingListUpdate> {
baseRoute = routes.shoppingLists;
itemRoute = routes.shoppingListsId;
@ -31,7 +33,7 @@ export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListOut, ShoppingListC
}
}
export class ShoppingListItemsApi extends BaseCRUDAPI<ShoppingListItemOut, ShoppingListItemCreate> {
export class ShoppingListItemsApi extends BaseCRUDAPI<ShoppingListItemCreate, ShoppingListItemOut, ShoppingListItemUpdate> {
baseRoute = routes.shoppingListItems;
itemRoute = routes.shoppingListItemsId;

View File

@ -1,5 +1,5 @@
import { BaseAPI } from "../_base";
import { ServerTask } from "~/api/types/server-task";
import { ServerTask } from "~/types/api-types/server";
const prefix = "/api";
const routes = {

View File

@ -1,4 +1,5 @@
import { BaseCRUDAPI } from "../_base";
import { CreateWebhook, ReadWebhook } from "~/types/api-types/group";
const prefix = "/api";
@ -7,19 +8,7 @@ const routes = {
webhooksId: (id: string | number) => `${prefix}/groups/webhooks/${id}`,
};
export interface CreateGroupWebhook {
enabled: boolean;
name: string;
url: string;
time: string;
}
export interface GroupWebhook extends CreateGroupWebhook {
id: string;
groupId: string;
}
export class WebhooksAPI extends BaseCRUDAPI<GroupWebhook, CreateGroupWebhook> {
export class WebhooksAPI extends BaseCRUDAPI<CreateWebhook, ReadWebhook> {
baseRoute = routes.webhooks;
itemRoute = routes.webhooksId;
}

View File

@ -1,6 +1,6 @@
import { BaseCRUDAPI } from "../_base";
import { GroupInDB, UserOut } from "~/types/api-types/user";
import { GroupStatistics, GroupStorage } from "~/types/api-types/group";
import { CategoryBase, GroupBase, GroupInDB, UserOut } from "~/types/api-types/user";
import { CreateInviteToken, GroupAdminUpdate, GroupStatistics, GroupStorage, ReadGroupPreferences, ReadInviteToken, SetPermissions, UpdateGroupPreferences } from "~/types/api-types/group";
const prefix = "/api";
@ -20,82 +20,34 @@ const routes = {
groupsId: (id: string | number) => `${prefix}/admin/groups/${id}`,
};
interface Category {
id: number;
name: string;
slug: string;
}
export interface CreateGroup {
name: string;
}
export interface UpdatePreferences {
privateGroup: boolean;
firstDayOfWeek: number;
recipePublic: boolean;
recipeShowNutrition: boolean;
recipeShowAssets: boolean;
recipeLandscapeView: boolean;
recipeDisableComments: boolean;
recipeDisableAmount: boolean;
}
export interface Preferences extends UpdatePreferences {
id: number;
group_id: number;
}
export interface Group extends CreateGroup {
id: number;
preferences: Preferences;
}
export interface CreateInvitation {
uses: number;
}
export interface Invitation {
group_id: number;
token: string;
uses_left: number;
}
export interface SetPermissions {
userId: string;
canInvite?: boolean;
canManage?: boolean;
canOrganize?: boolean;
}
export class GroupAPI extends BaseCRUDAPI<GroupInDB, CreateGroup> {
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<Group>(routes.groupsSelf);
return await this.requests.get<GroupInDB>(routes.groupsSelf);
}
async getCategories() {
return await this.requests.get<Category[]>(routes.categories);
return await this.requests.get<CategoryBase[]>(routes.categories);
}
async setCategories(payload: Category[]) {
return await this.requests.put<Category[]>(routes.categories, payload);
async setCategories(payload: CategoryBase[]) {
return await this.requests.put<CategoryBase[]>(routes.categories, payload);
}
async getPreferences() {
return await this.requests.get<Preferences>(routes.preferences);
return await this.requests.get<ReadGroupPreferences>(routes.preferences);
}
async setPreferences(payload: UpdatePreferences) {
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<Preferences, UpdatePreferences>(routes.preferences, payload);
return await this.requests.put<ReadGroupPreferences, UpdateGroupPreferences>(routes.preferences, payload);
}
async createInvitation(payload: CreateInvitation) {
return await this.requests.post<Invitation>(routes.invitation, payload);
async createInvitation(payload: CreateInviteToken) {
return await this.requests.post<ReadInviteToken>(routes.invitation, payload);
}
async fetchMembers() {
@ -104,7 +56,7 @@ export class GroupAPI extends BaseCRUDAPI<GroupInDB, CreateGroup> {
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<Permissions, SetPermissions>(routes.permissions, payload);
return await this.requests.put<UserOut, SetPermissions>(routes.permissions, payload);
}
async statistics() {

View File

@ -10,7 +10,7 @@ const routes = {
categoriesSlug: (category: string) => `${prefix}/categories/slug/${category}`,
};
export class CategoriesAPI extends BaseCRUDAPI<RecipeCategoryResponse, CategoryIn> {
export class CategoriesAPI extends BaseCRUDAPI<CategoryIn, RecipeCategoryResponse> {
baseRoute: string = routes.categories;
itemRoute = routes.categoriesId;

View File

@ -10,7 +10,7 @@ const routes = {
tagsSlug: (tag: string) => `${prefix}/tags/slug/${tag}`,
};
export class TagsAPI extends BaseCRUDAPI<RecipeTagResponse, TagIn> {
export class TagsAPI extends BaseCRUDAPI<TagIn, RecipeTagResponse> {
baseRoute: string = routes.tags;
itemRoute = routes.tagsId;

View File

@ -11,7 +11,7 @@ const routes = {
toolsSlug: (id: string) => `${prefix}/tools/slug/${id}`,
};
export class ToolsApi extends BaseCRUDAPI<RecipeTool, RecipeToolCreate> {
export class ToolsApi extends BaseCRUDAPI<RecipeToolCreate, RecipeTool> {
baseRoute: string = routes.tools;
itemRoute = routes.toolsId;

View File

@ -1,45 +1,10 @@
import { BaseAPI } from "../_base";
import { AssignCategories, AssignTags, DeleteRecipes, ExportRecipes } from "~/types/api-types/recipe";
import { GroupDataExport } from "~/types/api-types/group";
interface BasePayload {
recipes: string[];
}
type exportType = "json";
// Many bulk actions return nothing
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface RecipeBulkDelete extends BasePayload {}
interface RecipeBulkExport extends BasePayload {
exportType: exportType;
}
interface RecipeBulkCategorize extends BasePayload {
categories: string[];
}
interface RecipeBulkTag extends BasePayload {
tags: string[];
}
interface BulkActionError {
recipe: string;
error: string;
}
interface BulkActionResponse {
success: boolean;
message: string;
errors: BulkActionError[];
}
export interface GroupDataExport {
id: string;
groupId: string;
name: string;
filename: string;
path: string;
size: string;
expires: Date;
}
const prefix = "/api";
@ -53,19 +18,19 @@ const routes = {
};
export class BulkActionsAPI extends BaseAPI {
async bulkExport(payload: RecipeBulkExport) {
async bulkExport(payload: ExportRecipes) {
return await this.requests.post<BulkActionResponse>(routes.bulkExport, payload);
}
async bulkCategorize(payload: RecipeBulkCategorize) {
async bulkCategorize(payload: AssignCategories) {
return await this.requests.post<BulkActionResponse>(routes.bulkCategorize, payload);
}
async bulkTag(payload: RecipeBulkTag) {
async bulkTag(payload: AssignTags) {
return await this.requests.post<BulkActionResponse>(routes.bulkTag, payload);
}
async bulkDelete(payload: RecipeBulkDelete) {
async bulkDelete(payload: DeleteRecipes) {
return await this.requests.post<BulkActionResponse>(routes.bulkDelete, payload);
}
@ -74,6 +39,6 @@ export class BulkActionsAPI extends BaseAPI {
}
async purgeExports() {
return await this.requests.delete(routes.purgeExports);
return await this.requests.delete<BulkActionResponse>(routes.purgeExports);
}
}

View File

@ -9,7 +9,7 @@ const routes = {
merge: `${prefix}/foods/merge`,
};
export class FoodAPI extends BaseCRUDAPI<IngredientFood, CreateIngredientFood> {
export class FoodAPI extends BaseCRUDAPI<CreateIngredientFood, IngredientFood> {
baseRoute: string = routes.food;
itemRoute = routes.foodsFood;

View File

@ -9,7 +9,7 @@ const routes = {
merge: `${prefix}/units/merge`,
};
export class UnitAPI extends BaseCRUDAPI<IngredientUnit, CreateIngredientUnit> {
export class UnitAPI extends BaseCRUDAPI<CreateIngredientUnit, IngredientUnit> {
baseRoute: string = routes.unit;
itemRoute = routes.unitsUnit;

View File

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

View File

@ -1,4 +1,5 @@
import { BaseCRUDAPI } from "~/api/_base";
import { RecipeShareToken, RecipeShareTokenCreate } from "~/types/api-types/recipe";
const prefix = "/api";
@ -7,20 +8,7 @@ const routes = {
shareTokenId: (id: string) => `${prefix}/shared/recipes/${id}`,
};
export interface RecipeShareTokenCreate {
recipeId: string;
expiresAt?: Date;
}
export interface RecipeShareToken {
recipeId: string;
id: string;
groupId: number;
expiresAt: string;
createdAt: string;
}
export class RecipeShareApi extends BaseCRUDAPI<RecipeShareToken, RecipeShareTokenCreate> {
export class RecipeShareApi extends BaseCRUDAPI<RecipeShareTokenCreate, RecipeShareToken> {
baseRoute: string = routes.shareToken;
itemRoute = routes.shareTokenId;
}

View File

@ -1,11 +1,19 @@
import { CreateAsset, ParsedIngredient, Parser, RecipeZipToken, BulkCreatePayload } from "./types";
import { CommentsApi } from "./recipe-comments";
import { RecipeShareApi } from "./recipe-share";
import { BaseCRUDAPI } from "~/api/_base";
import { Recipe, CreateRecipe } from "~/types/api-types/recipe";
import { Recipe, CreateRecipe, RecipeAsset, CreateRecipeByUrlBulk, ParsedIngredient, UpdateImageResponse, RecipeZipTokenResponse } from "~/types/api-types/recipe";
import { ApiRequestInstance } from "~/types/api";
export type Parser = "nlp" | "brute";
export interface CreateAsset {
name: string;
icon: string;
extension: string;
file: File;
}
const prefix = "/api";
const routes = {
@ -31,7 +39,7 @@ const routes = {
recipeShareToken: (token: string) => `${prefix}/recipes/shared/${token}`,
};
export class RecipeAPI extends BaseCRUDAPI<Recipe, CreateRecipe> {
export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
baseRoute: string = routes.recipesBase;
itemRoute = routes.recipesRecipeSlug;
@ -58,7 +66,7 @@ export class RecipeAPI extends BaseCRUDAPI<Recipe, CreateRecipe> {
formData.append("extension", payload.extension);
formData.append("icon", payload.icon);
return await this.requests.post(routes.recipesRecipeSlugAssets(recipeSlug), formData);
return await this.requests.post<RecipeAsset>(routes.recipesRecipeSlugAssets(recipeSlug), formData);
}
updateImage(slug: string, fileObject: File) {
@ -66,11 +74,11 @@ export class RecipeAPI extends BaseCRUDAPI<Recipe, CreateRecipe> {
formData.append("image", fileObject);
formData.append("extension", fileObject.name.split(".").pop() ?? "");
return this.requests.put<any>(routes.recipesRecipeSlugImage(slug), formData);
return this.requests.put<UpdateImageResponse, FormData>(routes.recipesRecipeSlugImage(slug), formData);
}
updateImagebyURL(slug: string, url: string) {
return this.requests.post(routes.recipesRecipeSlugImage(slug), { url });
return this.requests.post<UpdateImageResponse>(routes.recipesRecipeSlugImage(slug), { url });
}
async testCreateOneUrl(url: string) {
@ -81,8 +89,8 @@ export class RecipeAPI extends BaseCRUDAPI<Recipe, CreateRecipe> {
return await this.requests.post<string>(routes.recipesCreateUrl, { url, includeTags });
}
async createManyByUrl(payload: BulkCreatePayload) {
return await this.requests.post(routes.recipesCreateUrlBulk, payload);
async createManyByUrl(payload: CreateRecipeByUrlBulk) {
return await this.requests.post<string>(routes.recipesCreateUrlBulk, payload);
}
async parseIngredients(parser: Parser, ingredients: Array<string>) {
@ -96,7 +104,7 @@ export class RecipeAPI extends BaseCRUDAPI<Recipe, CreateRecipe> {
}
async getZipToken(recipeSlug: string) {
return await this.requests.post<RecipeZipToken>(routes.recipesRecipeSlugExport(recipeSlug), {});
return await this.requests.post<RecipeZipTokenResponse>(routes.recipesRecipeSlugExport(recipeSlug), {});
}
getZipRedirectUrl(recipeSlug: string, token: string) {

View File

@ -1,72 +0,0 @@
import { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit } from "~/types/api-types/recipe";
import { RecipeCategory, RecipeTag } from "~/types/api-types/user";
export type Parser = "nlp" | "brute";
export interface Confidence {
average?: number;
comment?: number;
name?: number;
unit?: number;
quantity?: number;
food?: number;
}
export interface Ingredient {
title?: string;
note?: string;
unit?: IngredientUnit | CreateIngredientUnit;
food?: IngredientFood | CreateIngredientFood;
disableAmount?: boolean;
quantity?: number;
referenceId?: string;
}
export interface ParsedIngredient {
input: string
confidence: Confidence;
ingredient: Ingredient;
}
export interface BulkCreateRecipe {
url: string;
categories: RecipeCategory[];
tags: RecipeTag[];
}
export interface BulkCreatePayload {
imports: BulkCreateRecipe[];
}
export interface RecipeZipToken {
token: string;
}
export interface CreateAsset {
name: string;
icon: string;
extension: string;
file: File;
}
export interface RecipeCommentCreate {
recipeId: string;
text: string;
}
export interface RecipeCommentUpdate extends RecipeCommentCreate {
id: string;
}
interface RecipeCommentUser {
id: string;
username: string;
admin: boolean;
}
export interface RecipeComment extends RecipeCommentUpdate {
createdAt: any;
updatedAt: any;
userId: number;
user: RecipeCommentUser;
}

View File

@ -1,29 +1,5 @@
import { BaseCRUDAPI } from "../_base";
import { UserIn, UserOut } from "~/types/api-types/user";
// Interfaces
interface ChangePassword {
currentPassword: string;
newPassword: string;
}
interface CreateAPIToken {
name: string;
}
interface ResponseToken {
token: string;
}
interface PasswordResetPayload {
token: string;
email: string;
password: string;
passwordConfirm: string;
}
// Code
import { ChangePassword, DeleteTokenResponse, LongLiveTokenIn, LongLiveTokenOut, ResetPassword, UserBase, UserIn, UserOut } from "~/types/api-types/user";
const prefix = "/api";
@ -43,7 +19,7 @@ const routes = {
usersApiTokensTokenId: (token_id: string | number) => `${prefix}/users/api-tokens/${token_id}`,
};
export class UserApi extends BaseCRUDAPI<UserOut, UserIn> {
export class UserApi extends BaseCRUDAPI<UserIn, UserOut, UserBase> {
baseRoute: string = routes.users;
itemRoute = (itemid: string) => routes.usersId(itemid);
@ -63,12 +39,12 @@ export class UserApi extends BaseCRUDAPI<UserOut, UserIn> {
return await this.requests.put(routes.usersIdPassword(id), changePassword);
}
async createAPIToken(tokenName: CreateAPIToken) {
return await this.requests.post<ResponseToken>(routes.usersApiTokens, tokenName);
async createAPIToken(tokenName: LongLiveTokenIn) {
return await this.requests.post<LongLiveTokenOut>(routes.usersApiTokens, tokenName);
}
async deleteAPIToken(tokenId: string | number) {
return await this.requests.delete(routes.usersApiTokensTokenId(tokenId));
async deleteAPIToken(tokenId: number) {
return await this.requests.delete<DeleteTokenResponse>(routes.usersApiTokensTokenId(tokenId));
}
userProfileImage(id: string) {
@ -76,7 +52,7 @@ export class UserApi extends BaseCRUDAPI<UserOut, UserIn> {
return `/api/users/${id}/image`;
}
async resetPassword(payload: PasswordResetPayload) {
async resetPassword(payload: ResetPassword) {
return await this.requests.post(routes.passwordReset, payload);
}
}

View File

@ -1,14 +1,11 @@
import { BaseAPI } from "../_base";
import { FileTokenResponse } from "~/types/api-types/response";
const prefix = "/api";
interface DownloadData {
fileToken: string;
}
export class UtilsAPI extends BaseAPI {
async download(url: string) {
const { response } = await this.requests.get<DownloadData>(url);
const { response } = await this.requests.get<FileTokenResponse>(url);
if (!response) {
return;

View File

@ -1,8 +1,5 @@
import { BaseAPI } from "../_base";
export type Validation = {
valid: boolean;
};
import { ValidationResponse } from "~/types/api-types/response";
const prefix = "/api";
@ -15,18 +12,18 @@ const routes = {
export class ValidatorsApi extends BaseAPI {
async group(name: string) {
return await this.requests.get<Validation>(routes.group(name));
return await this.requests.get<ValidationResponse>(routes.group(name));
}
async username(name: string) {
return await this.requests.get<Validation>(routes.user(name));
return await this.requests.get<ValidationResponse>(routes.user(name));
}
async email(email: string) {
return await this.requests.get<Validation>(routes.email(email));
return await this.requests.get<ValidationResponse>(routes.email(email));
}
async recipe(groupId: string, name: string) {
return await this.requests.get<Validation>(routes.recipe(groupId, name));
return await this.requests.get<ValidationResponse>(routes.recipe(groupId, name));
}
}

View File

@ -1,8 +0,0 @@
export interface ServerTask {
id: number;
groupId: number;
callback: string;
createdAt: string;
status: string;
log: string;
}

View File

@ -20,7 +20,7 @@
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import { parseISO, formatDistanceToNow } from "date-fns";
import { GroupDataExport } from "~/api/class-interfaces/recipe-bulk-actions";
import { GroupDataExport } from "~/types/api-types/group";
export default defineComponent({
props: {
exports: {
@ -57,4 +57,3 @@ export default defineComponent({
},
});
</script>

View File

@ -56,7 +56,7 @@
<script lang="ts">
import { defineComponent, ref, toRefs, onMounted, reactive } from "@nuxtjs/composition-api";
import { useUserApi } from "~/composables/api";
import { RecipeComment } from "~/api/class-interfaces/recipes/types";
import { RecipeCommentOut } from "~/types/api-types/recipe";
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
export default defineComponent({
@ -76,7 +76,7 @@ export default defineComponent({
setup(props) {
const api = useUserApi();
const comments = ref<RecipeComment[]>([]);
const comments = ref<RecipeCommentOut[]>([]);
const state = reactive({
comment: "",

View File

@ -95,8 +95,9 @@ import { defineComponent, reactive, toRefs, useContext, useRouter, ref } from "@
import RecipeDialogShare from "./RecipeDialogShare.vue";
import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import { MealType, planTypeOptions } from "~/composables/use-group-mealplan";
import { planTypeOptions } from "~/composables/use-group-mealplan";
import { ShoppingListSummary } from "~/types/api-types/group";
import { PlanEntryType } from "~/types/api-types/meal-plan";
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
export interface ContextMenuIncludes {
@ -183,7 +184,7 @@ export default defineComponent({
loading: false,
menuItems: [] as ContextMenuItem[],
newMealdate: "",
newMealType: "dinner" as MealType,
newMealType: "dinner" as PlanEntryType,
pickerMenu: false,
});

View File

@ -59,7 +59,7 @@
import { defineComponent, computed, toRefs, reactive, useContext } from "@nuxtjs/composition-api";
import { whenever } from "@vueuse/shared";
import { useClipboard, useShare } from "@vueuse/core";
import { RecipeShareToken } from "~/api/class-interfaces/recipes/recipe-share";
import { RecipeShareToken } from "~/types/api-types/recipe";
import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
@ -118,7 +118,7 @@ export default defineComponent({
const { data } = await userApi.recipes.share.createOne({
recipeId: props.recipeId,
expiresAt: expirationDate,
expiresAt: expirationDate.toISOString(),
});
if (data) {

View File

@ -26,7 +26,7 @@
<script lang="ts">
import { defineComponent, useRouter } from "@nuxtjs/composition-api";
import { ReportSummary } from "~/api/class-interfaces/group-reports";
import { ReportSummary } from "~/types/api-types/reports";
export default defineComponent({
props: {

View File

@ -1,8 +1,13 @@
import { useAsync, ref, reactive } from "@nuxtjs/composition-api";
import { toastLoading, loader } from "./use-toast";
import { AllBackups, ImportBackup } from "~/api/class-interfaces/backups";
import { AllBackups, BackupOptions } from "~/types/api-types/admin";
import { useUserApi } from "~/composables/api";
interface ImportBackup {
name: string;
options: BackupOptions;
}
const backups = ref<AllBackups>({
imports: [],
templates: [],
@ -41,7 +46,6 @@ export const useBackups = function (fetch = true) {
options: {
recipes: true,
settings: true,
pages: true,
themes: true,
groups: true,
users: true,

View File

@ -1,7 +1,7 @@
import { useAsync, ref, Ref } from "@nuxtjs/composition-api";
import { useAsyncKey } from "./use-utils";
import { useUserApi } from "~/composables/api";
import { ReadCookBook, RecipeCookBook, UpdateCookBook } from "~/types/api-types/cookbook";
import { ReadCookBook, UpdateCookBook } from "~/types/api-types/cookbook";
let cookbookStore: Ref<ReadCookBook[] | null> | null = null;
@ -66,7 +66,7 @@ export const useCookbooks = function () {
}
loading.value = true;
const { data } = await api.cookbooks.updateOne(updateData.id, updateData as RecipeCookBook);
const { data } = await api.cookbooks.updateOne(updateData.id, updateData);
if (data && cookbookStore?.value) {
this.refreshAll();
}

View File

@ -2,9 +2,7 @@ import { useAsync, ref, Ref, watch } from "@nuxtjs/composition-api";
import { format } from "date-fns";
import { useAsyncKey } from "./use-utils";
import { useUserApi } from "~/composables/api";
import { CreateMealPlan, UpdateMealPlan } from "~/api/class-interfaces/group-mealplan";
export type MealType = "breakfast" | "lunch" | "dinner" | "side";
import { CreatePlanEntry, PlanEntryType, UpdatePlanEntry } from "~/types/api-types/meal-plan";
export const planTypeOptions = [
{ text: "Breakfast", value: "breakfast" },
@ -55,7 +53,7 @@ export const useMealplans = function (range: Ref<DateRange>) {
loading.value = false;
},
async createOne(payload: CreateMealPlan) {
async createOne(payload: CreatePlanEntry) {
loading.value = true;
const { data } = await api.mealplans.createOne(payload);
@ -65,13 +63,12 @@ export const useMealplans = function (range: Ref<DateRange>) {
loading.value = false;
},
async updateOne(updateData: UpdateMealPlan) {
async updateOne(updateData: UpdatePlanEntry) {
if (!updateData.id) {
return;
}
loading.value = true;
// @ts-ignore TODO Modify mealpan types to be from auto-generated files
const { data } = await api.mealplans.updateOne(updateData.id, updateData);
if (data) {
this.refreshAll();
@ -87,8 +84,8 @@ export const useMealplans = function (range: Ref<DateRange>) {
}
},
async setType(payload: UpdateMealPlan, typ: MealType) {
payload.entryType = typ;
async setType(payload: UpdatePlanEntry, type: PlanEntryType) {
payload.entryType = type;
await this.updateOne(payload);
},
};

View File

@ -1,7 +1,7 @@
import { useAsync, ref } from "@nuxtjs/composition-api";
import { useAsyncKey } from "./use-utils";
import { useUserApi } from "~/composables/api";
import { GroupWebhook } from "~/api/class-interfaces/group-webhooks";
import { ReadWebhook } from "~/types/api-types/group";
export const useGroupWebhooks = function () {
const api = useUserApi();
@ -47,7 +47,7 @@ export const useGroupWebhooks = function () {
loading.value = false;
},
async updateOne(updateData: GroupWebhook) {
async updateOne(updateData: ReadWebhook) {
if (!updateData.id) {
return;
}

View File

@ -1,7 +1,7 @@
import { useAsync, ref } from "@nuxtjs/composition-api";
import { useAsyncKey } from "./use-utils";
import { useUserApi } from "~/composables/api";
import { CreateGroup } from "~/api/class-interfaces/groups";
import { GroupBase } from "~/types/api-types/user";
export const useGroupSelf = function () {
const api = useUserApi();
@ -17,7 +17,7 @@ export const useGroupSelf = function () {
return group;
},
async updatePreferences() {
if (!group.value) {
if (!group.value?.preferences) {
return;
}
@ -65,7 +65,7 @@ export const useGroups = function () {
return data;
}
async function createGroup(payload: CreateGroup) {
async function createGroup(payload: GroupBase) {
loading.value = true;
const { data } = await api.groups.createOne(payload);

View File

@ -1,6 +1,6 @@
import { ref, Ref } from "@nuxtjs/composition-api";
import { RequestResponse } from "~/types/api";
import { Validation } from "~/api/public/validators";
import { ValidationResponse } from "~/types/api-types/response";
const EMAIL_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
@ -23,7 +23,7 @@ export const validators = {
*/
export const useAsyncValidator = (
value: Ref<string>,
validatorFunc: (v: string) => Promise<RequestResponse<Validation>>,
validatorFunc: (v: string) => Promise<RequestResponse<ValidationResponse>>,
validatorMessage: string,
errorMessages: Ref<string[]>
) => {

View File

@ -41,7 +41,7 @@
<script lang="ts">
import { defineComponent, onMounted, ref } from "@nuxtjs/composition-api";
import { ServerTask } from "~/api/types/server-task";
import { ServerTask } from "~/types/api-types/server";
import { useAdminApi } from "~/composables/api";
export default defineComponent({

View File

@ -28,8 +28,8 @@ import { defineComponent, useRoute, onMounted, ref } from "@nuxtjs/composition-a
import GroupPreferencesEditor from "~/components/Domain/Group/GroupPreferencesEditor.vue";
import { useAdminApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import { GroupInDB } from "~/types/api-types/user";
import { VForm } from "~/types/vuetify";
import { GroupRead } from "~/api/admin/admin-groups";
export default defineComponent({
components: {
@ -48,7 +48,7 @@ export default defineComponent({
const adminApi = useAdminApi();
const group = ref<GroupRead | null>(null);
const group = ref<GroupInDB | null>(null);
const userError = ref(false);

View File

@ -71,9 +71,9 @@
<script lang="ts">
import { defineComponent, reactive, toRefs, useContext, useRouter } from "@nuxtjs/composition-api";
import { Group } from "~/api/class-interfaces/groups";
import { fieldTypes } from "~/composables/forms";
import { useGroups } from "~/composables/use-groups";
import { GroupInDB } from "~/types/api-types/user";
export default defineComponent({
layout: "admin",
@ -121,7 +121,7 @@ export default defineComponent({
const router = useRouter();
function handleRowClick(item: Group) {
function handleRowClick(item: GroupInDB) {
router.push(`/admin/manage/groups/${item.id}`);
}

View File

@ -68,7 +68,8 @@
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from "@nuxtjs/composition-api";
import { Confidence, Parser } from "~/api/class-interfaces/recipes/types";
import { Parser } from "~/api/class-interfaces/recipes/recipe";
import { IngredientConfidence } from "~/types/api-types/recipe";
import { useUserApi } from "~/composables/api";
type ConfidenceAttribute = "average" | "comment" | "name" | "unit" | "quantity" | "food";
@ -85,7 +86,7 @@ export default defineComponent({
parser: "nlp" as Parser,
});
const confidence = ref<Confidence>({});
const confidence = ref<IngredientConfidence>({});
function getColor(attribute: ConfidenceAttribute) {
const percentage = getConfidence(attribute);
@ -141,7 +142,8 @@ export default defineComponent({
if (data) {
state.results = true;
confidence.value = data.confidence;
if (data.confidence)
confidence.value = data.confidence;
// TODO: Remove ts-ignore
// ts-ignore because data will likely change significantly once I figure out how to return results

View File

@ -259,7 +259,7 @@ export default defineComponent({
const { data } = await adminApi.about.checkApp();
if (data) {
appConfig.value = data;
appConfig.value = { ...data, isSiteSecure: false};
}
appConfig.value.isSiteSecure = isLocalHostOrHttps();
@ -323,7 +323,7 @@ export default defineComponent({
if (data.success) {
state.success = true;
} else {
state.error = data.error;
state.error = data.error ?? "";
state.success = false;
}
}

View File

@ -154,7 +154,7 @@ import { useUserApi } from "~/composables/api";
import { useRecipes, allRecipes } from "~/composables/recipes";
import { Recipe } from "~/types/api-types/recipe";
import GroupExportData from "~/components/Domain/Group/GroupExportData.vue";
import { GroupDataExport } from "~/api/class-interfaces/recipe-bulk-actions";
import { GroupDataExport } from "~/types/api-types/group";
import { MenuItem } from "~/components/global/BaseOverflowButton.vue";
const MODES = {

View File

@ -346,7 +346,7 @@ export default defineComponent({
date: "",
title: "",
text: "",
recipeId: undefined as number | undefined,
recipeId: undefined as string | undefined,
entryType: "dinner" as PlanEntryType,
});

View File

@ -66,10 +66,10 @@
<script lang="ts">
import { defineComponent, reactive, toRefs, useContext, computed, onMounted } from "@nuxtjs/composition-api";
import { SupportedMigration } from "~/api/class-interfaces/group-migrations";
import { ReportSummary } from "~/api/class-interfaces/group-reports";
import { ReportSummary } from "~/types/api-types/reports";
import { MenuItem } from "~/components/global/BaseOverflowButton.vue";
import { useUserApi } from "~/composables/api";
import { SupportedMigrations } from "~/types/api-types/group";
const MIGRATIONS = {
nextcloud: "nextcloud",
@ -88,7 +88,7 @@ export default defineComponent({
addMigrationTag: false,
loading: false,
treeState: true,
migrationType: MIGRATIONS.nextcloud as SupportedMigration,
migrationType: MIGRATIONS.nextcloud as SupportedMigrations,
fileObject: {} as File,
reports: [] as ReportSummary[],
});

View File

@ -702,8 +702,8 @@ export default defineComponent({
return;
}
const newVersion = await api.recipes.updateImage(recipe.value.slug, fileObject);
if (newVersion?.data?.version) {
recipe.value.image = newVersion.data.version;
if (newVersion?.data?.image) {
recipe.value.image = newVersion.data.image;
}
state.imageKey++;
}

View File

@ -83,8 +83,8 @@
<script lang="ts">
import { defineComponent, ref, useRoute, useRouter } from "@nuxtjs/composition-api";
import { invoke, until } from "@vueuse/core";
import { ParsedIngredient, Parser } from "~/api/class-interfaces/recipes/types";
import { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit } from "~/types/api-types/recipe";
import { Parser } from "~/api/class-interfaces/recipes/recipe";
import { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit, ParsedIngredient } from "~/types/api-types/recipe";
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
import { useUserApi } from "~/composables/api";
import { useFoods, useRecipe, useUnits } from "~/composables/recipes";

View File

@ -111,7 +111,7 @@ export default defineComponent({
}
}
async function deleteToken(id: string | number) {
async function deleteToken(id: number) {
const { data } = await api.users.deleteAPIToken(id);
nuxtContext.$auth.fetchUser();
return data;
@ -126,4 +126,3 @@ export default defineComponent({
},
});
</script>

View File

@ -50,10 +50,10 @@ export interface BackupOptions {
notifications?: boolean;
}
export interface CheckAppConfig {
emailReady?: boolean;
ldapReady?: boolean;
baseUrlSet?: boolean;
isUpToDate?: boolean;
emailReady: boolean;
ldapReady: boolean;
baseUrlSet: boolean;
isUpToDate: boolean;
}
export interface ChowdownURL {
url: string;
@ -174,6 +174,16 @@ export interface CustomPageOut {
export interface DockerVolumeText {
text: string;
}
export interface EmailReady {
ready: boolean;
}
export interface EmailSuccess {
success: boolean;
error?: string;
}
export interface EmailTest {
email: string;
}
export interface GroupImport {
name: string;
status: boolean;

View File

@ -41,7 +41,7 @@ export interface EmailInvitation {
export interface GroupAdminUpdate {
id: string;
name: string;
preferences: UpdateGroupPreferences;
preferences?: UpdateGroupPreferences;
}
export interface UpdateGroupPreferences {
privateGroup?: boolean;

View File

@ -14,10 +14,6 @@ export interface Category {
name: string;
slug: string;
}
export interface CreatRandomEntry {
date: string;
entryType?: PlanEntryType & string;
}
export interface CreatePlanEntry {
date: string;
entryType?: PlanEntryType & string;
@ -25,6 +21,10 @@ export interface CreatePlanEntry {
text?: string;
recipeId?: string;
}
export interface CreateRandomEntry {
date: string;
entryType?: PlanEntryType & string;
}
export interface ListItem {
title?: string;
text?: string;

View File

@ -26,15 +26,6 @@ export interface TagBase {
id: string;
slug: string;
}
export interface BulkActionError {
recipe: string;
error: string;
}
export interface BulkActionsResponse {
success: boolean;
message: string;
errors?: BulkActionError[];
}
export interface CategoryIn {
name: string;
}
@ -335,6 +326,9 @@ export interface RecipeToolSave {
onHand?: boolean;
groupId: string;
}
export interface RecipeZipTokenResponse {
token: string;
}
export interface SaveIngredientFood {
name: string;
description?: string;
@ -373,3 +367,6 @@ export interface UnitFoodBase {
name: string;
description?: string;
}
export interface UpdateImageResponse {
image: string;
}

View File

@ -18,5 +18,5 @@ export interface SuccessResponse {
error?: boolean;
}
export interface ValidationResponse {
valid?: boolean;
valid: boolean;
}

View File

@ -31,6 +31,9 @@ export interface CreateUserRegistration {
seedData?: boolean;
locale?: string;
}
export interface DeleteTokenResponse {
tokenDelete: string;
}
export interface ForgotPassword {
email: string;
}
@ -62,9 +65,7 @@ export interface UserOut {
cacheKey: string;
}
export interface LongLiveTokenOut {
name: string;
id: number;
createdAt: string;
token: string;
}
export interface ReadGroupPreferences {
privateGroup?: boolean;
@ -78,7 +79,7 @@ export interface ReadGroupPreferences {
groupId: string;
id: string;
}
export interface LoingLiveTokenIn {
export interface LongLiveTokenIn {
name: string;
}
export interface LongLiveTokenInDB {

View File

@ -1,25 +1,12 @@
from fastapi import APIRouter
from mealie.routes._base import BaseAdminController, controller
from mealie.schema._mealie import MealieModel
from mealie.schema.admin.email import EmailReady, EmailSuccess, EmailTest
from mealie.services.email import EmailService
router = APIRouter(prefix="/email")
class EmailReady(MealieModel):
ready: bool
class EmailSuccess(MealieModel):
success: bool
error: str = None
class EmailTest(MealieModel):
email: str
@controller(router)
class AdminEmailController(BaseAdminController):
@router.get("", response_model=EmailReady)

View File

@ -9,7 +9,7 @@ from mealie.routes._base import BaseUserController, controller
from mealie.routes._base.mixins import HttpRepo
from mealie.schema import mapper
from mealie.schema.meal_plan import CreatePlanEntry, ReadPlanEntry, SavePlanEntry, UpdatePlanEntry
from mealie.schema.meal_plan.new_meal import CreatRandomEntry
from mealie.schema.meal_plan.new_meal import CreateRandomEntry
from mealie.schema.meal_plan.plan_rules import PlanRulesDay
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.response.responses import ErrorResponse
@ -42,7 +42,7 @@ class GroupMealplanController(BaseUserController):
return self.repo.get_today(group_id=self.group_id)
@router.post("/random", response_model=ReadPlanEntry)
def create_random_meal(self, data: CreatRandomEntry):
def create_random_meal(self, data: CreateRandomEntry):
"""
create_random_meal is a route that provides the randomized funcitonality for mealplaners.
It operates by following the rules setout in the Groups mealplan settings. If not settings

View File

@ -7,13 +7,7 @@ from mealie.core.dependencies.dependencies import temporary_zip_path
from mealie.core.security import create_file_token
from mealie.routes._base import BaseUserController, controller
from mealie.schema.group.group_exports import GroupDataExport
from mealie.schema.recipe.recipe_bulk_actions import (
AssignCategories,
AssignTags,
BulkActionsResponse,
DeleteRecipes,
ExportRecipes,
)
from mealie.schema.recipe.recipe_bulk_actions import AssignCategories, AssignTags, DeleteRecipes, ExportRecipes
from mealie.schema.response.responses import SuccessResponse
from mealie.services.recipe.recipe_bulk_service import RecipeBulkActionsService
@ -26,15 +20,16 @@ class RecipeBulkActionsController(BaseUserController):
def service(self) -> RecipeBulkActionsService:
return RecipeBulkActionsService(self.repos, self.user, self.group)
@router.post("/tag", response_model=BulkActionsResponse)
# TODO Should these actions return some success response?
@router.post("/tag")
def bulk_tag_recipes(self, tag_data: AssignTags):
self.service.assign_tags(tag_data.recipes, tag_data.tags)
@router.post("/categorize", response_model=BulkActionsResponse)
@router.post("/categorize")
def bulk_categorize_recipes(self, assign_cats: AssignCategories):
self.service.assign_categories(assign_cats.recipes, assign_cats.categories)
@router.post("/delete", response_model=BulkActionsResponse)
@router.post("/delete")
def bulk_delete_recipes(self, delete_recipes: DeleteRecipes):
self.service.delete_recipes(delete_recipes.recipes)

View File

@ -27,6 +27,7 @@ from mealie.schema.recipe import Recipe, RecipeImageTypes, ScrapeRecipe
from mealie.schema.recipe.recipe import CreateRecipe, CreateRecipeByUrlBulk, RecipeSummary
from mealie.schema.recipe.recipe_asset import RecipeAsset
from mealie.schema.recipe.recipe_scraper import ScrapeRecipeTest
from mealie.schema.recipe.request_helpers import RecipeZipTokenResponse, UpdateImageResponse
from mealie.schema.response.responses import ErrorResponse
from mealie.schema.server.tasks import ServerTaskNames
from mealie.services import urls
@ -59,10 +60,6 @@ class RecipeGetAll(GetAll):
load_food: bool = False
class UpdateImageResponse(BaseModel):
image: str
class FormatResponse(BaseModel):
jjson: list[str] = Field(..., alias="json")
zip: list[str]
@ -81,10 +78,10 @@ class RecipeExportController(BaseRecipeController):
def get_recipe_formats_and_templates(self):
return TemplateService().templates
@router_exports.post("/{slug}/exports")
@router_exports.post("/{slug}/exports", response_model=RecipeZipTokenResponse)
def get_recipe_zip_token(self, slug: str):
"""Generates a recipe zip token to be used to download a recipe as a zip file"""
return {"token": create_recipe_slug_token(slug)}
return RecipeZipTokenResponse(token=create_recipe_slug_token(slug))
@router_exports.get("/{slug}/exports", response_class=FileResponse)
def get_recipe_as_format(self, slug: str, template_name: str, temp_dir=Depends(temporary_dir)):

View File

@ -5,17 +5,17 @@ from fastapi import HTTPException, status
from mealie.core.security import create_access_token
from mealie.routes._base import BaseUserController, controller
from mealie.routes._base.routers import UserAPIRouter
from mealie.schema.user import CreateToken, LoingLiveTokenIn, LongLiveTokenInDB
from mealie.schema.user import CreateToken, DeleteTokenResponse, LongLiveTokenIn, LongLiveTokenInDB, LongLiveTokenOut
router = UserAPIRouter(prefix="/users", tags=["Users: Tokens"])
@controller(router)
class UserApiTokensController(BaseUserController):
@router.post("/api-tokens", status_code=status.HTTP_201_CREATED)
@router.post("/api-tokens", status_code=status.HTTP_201_CREATED, response_model=LongLiveTokenOut)
def create_api_token(
self,
token_name: LoingLiveTokenIn,
token_name: LongLiveTokenIn,
):
"""Create api_token in the Database"""
@ -33,9 +33,9 @@ class UserApiTokensController(BaseUserController):
new_token_in_db = self.repos.api_tokens.create(token_model)
if new_token_in_db:
return {"token": token}
return LongLiveTokenOut(token=token)
@router.delete("/api-tokens/{token_id}")
@router.delete("/api-tokens/{token_id}", response_model=DeleteTokenResponse)
def delete_api_token(self, token_id: int):
"""Delete api_token from the Database"""
token: LongLiveTokenInDB = self.repos.api_tokens.get(token_id)
@ -45,6 +45,6 @@ class UserApiTokensController(BaseUserController):
if token.user.email == self.user.email:
deleted_token = self.repos.api_tokens.delete(token_id)
return {"token_delete": deleted_token.name}
return DeleteTokenResponse(token_delete=deleted_token.name)
else:
raise HTTPException(status.HTTP_403_FORBIDDEN)

View File

@ -1,6 +1,7 @@
# GENERATED CODE - DO NOT MODIFY BY HAND
from .about import *
from .backup import *
from .email import *
from .maintenance import *
from .migration import *
from .restore import *

View File

@ -28,10 +28,10 @@ class AdminAboutInfo(AppInfo):
class CheckAppConfig(MealieModel):
email_ready: bool = False
ldap_ready: bool = False
base_url_set: bool = False
is_up_to_date: bool = False
email_ready: bool
ldap_ready: bool
base_url_set: bool
is_up_to_date: bool
class DockerVolumeText(MealieModel):

View File

@ -0,0 +1,14 @@
from mealie.schema._mealie import MealieModel
class EmailReady(MealieModel):
ready: bool
class EmailSuccess(MealieModel):
success: bool
error: str = None
class EmailTest(MealieModel):
email: str

View File

@ -1,3 +1,5 @@
from typing import Optional
from pydantic import UUID4
from mealie.schema._mealie import MealieModel
@ -8,4 +10,4 @@ from .group_preferences import UpdateGroupPreferences
class GroupAdminUpdate(MealieModel):
id: UUID4
name: str
preferences: UpdateGroupPreferences
preferences: Optional[UpdateGroupPreferences] = None

View File

@ -16,7 +16,7 @@ class PlanEntryType(str, Enum):
side = "side"
class CreatRandomEntry(MealieModel):
class CreateRandomEntry(MealieModel):
date: date
entry_type: PlanEntryType = PlanEntryType.dinner

View File

@ -26,14 +26,3 @@ class AssignTags(ExportBase):
class DeleteRecipes(ExportBase):
pass
class BulkActionError(MealieModel):
recipe: str
error: str
class BulkActionsResponse(MealieModel):
success: bool
message: str
errors: list[BulkActionError] = []

View File

@ -12,3 +12,11 @@ class RecipeSlug(MealieModel):
class SlugResponse(BaseModel):
class Config:
schema_extra = {"example": "adult-mac-and-cheese"}
class UpdateImageResponse(BaseModel):
image: str
class RecipeZipTokenResponse(BaseModel):
token: str

View File

@ -2,4 +2,4 @@ from pydantic import BaseModel
class ValidationResponse(BaseModel):
valid: bool = False
valid: bool

View File

@ -1,4 +1,3 @@
from datetime import datetime
from pathlib import Path
from typing import Any, Optional
from uuid import UUID
@ -18,19 +17,18 @@ from ..recipe import CategoryBase
settings = get_app_settings()
class LoingLiveTokenIn(MealieModel):
class LongLiveTokenIn(MealieModel):
name: str
class LongLiveTokenOut(LoingLiveTokenIn):
id: int
created_at: datetime
class LongLiveTokenOut(MealieModel):
token: str
class Config:
orm_mode = True
class CreateToken(LoingLiveTokenIn):
class CreateToken(LongLiveTokenIn):
user_id: UUID4
token: str
@ -38,6 +36,13 @@ class CreateToken(LoingLiveTokenIn):
orm_mode = True
class DeleteTokenResponse(MealieModel):
token_delete: str
class Config:
orm_mode = True
class ChangePassword(MealieModel):
current_password: str
new_password: str