mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	Consolidate frontend types (#1245)
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							6a88a59981
						
					
				
				
					commit
					479900e912
				
			
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -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)); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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"; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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 = { | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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)); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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)); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| export interface ServerTask { | ||||
|   id: number; | ||||
|   groupId: number; | ||||
|   callback: string; | ||||
|   createdAt: string; | ||||
|   status: string; | ||||
|   log: string; | ||||
| } | ||||
| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -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: "", | ||||
|   | ||||
| @@ -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, | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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: { | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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(); | ||||
|       } | ||||
|   | ||||
| @@ -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); | ||||
|     }, | ||||
|   }; | ||||
|   | ||||
| @@ -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; | ||||
|       } | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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[]> | ||||
| ) => { | ||||
|   | ||||
| @@ -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({ | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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}`); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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,6 +142,7 @@ export default defineComponent({ | ||||
|       if (data) { | ||||
|         state.results = true; | ||||
|  | ||||
|         if (data.confidence) | ||||
|           confidence.value = data.confidence; | ||||
|  | ||||
|         // TODO: Remove ts-ignore | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|       } | ||||
|   | ||||
| @@ -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 = { | ||||
|   | ||||
| @@ -346,7 +346,7 @@ export default defineComponent({ | ||||
|       date: "", | ||||
|       title: "", | ||||
|       text: "", | ||||
|       recipeId: undefined as number | undefined, | ||||
|       recipeId: undefined as string | undefined, | ||||
|       entryType: "dinner" as PlanEntryType, | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -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[], | ||||
|     }); | ||||
|   | ||||
| @@ -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++; | ||||
|     } | ||||
|   | ||||
| @@ -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"; | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -41,7 +41,7 @@ export interface EmailInvitation { | ||||
| export interface GroupAdminUpdate { | ||||
|   id: string; | ||||
|   name: string; | ||||
|   preferences: UpdateGroupPreferences; | ||||
|   preferences?: UpdateGroupPreferences; | ||||
| } | ||||
| export interface UpdateGroupPreferences { | ||||
|   privateGroup?: boolean; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -18,5 +18,5 @@ export interface SuccessResponse { | ||||
|   error?: boolean; | ||||
| } | ||||
| export interface ValidationResponse { | ||||
|   valid?: boolean; | ||||
|   valid: boolean; | ||||
| } | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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)): | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 * | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
							
								
								
									
										14
									
								
								mealie/schema/admin/email.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								mealie/schema/admin/email.py
									
									
									
									
									
										Normal 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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class PlanEntryType(str, Enum): | ||||
|     side = "side" | ||||
|  | ||||
|  | ||||
| class CreatRandomEntry(MealieModel): | ||||
| class CreateRandomEntry(MealieModel): | ||||
|     date: date | ||||
|     entry_type: PlanEntryType = PlanEntryType.dinner | ||||
|  | ||||
|   | ||||
| @@ -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] = [] | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -2,4 +2,4 @@ from pydantic import BaseModel | ||||
|  | ||||
|  | ||||
| class ValidationResponse(BaseModel): | ||||
|     valid: bool = False | ||||
|     valid: bool | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user