04b1fc3024
Test Suite / test (24.15.0) (push) Has been cancelled
- Added a new API path for rematching recipe ingredients in `api_paths.dart`. - Implemented a manual product creation dialog in `inventory_screen.dart` to allow users to create new products directly. - Integrated the rematch functionality in `recipe_repository.dart` to handle rematching of recipe ingredients. - Updated the recipe detail screen to include a button for triggering the rematch process. - Introduced a new `RecipeMatchingService` in the backend to handle ingredient matching logic. - Added database migration to include `aiEngineEnabled` column in the User table. Co-authored-by: Copilot <copilot@github.com>
731 lines
23 KiB
TypeScript
731 lines
23 KiB
TypeScript
import { RecipesService } from './recipes.service';
|
|
import { CreateRecipeDto } from './dto/create-recipe.dto';
|
|
import { CreateIngredientDto } from './dto/create-ingredient.dto';
|
|
import { ParseMarkdownDto } from './dto/parse-markdown.dto';
|
|
import { ShareRecipeDto } from './dto/share-recipe.dto';
|
|
import { SetRecipeVisibilityDto } from './dto/set-recipe-visibility.dto';
|
|
import { RecipeAnalysisService } from './recipe-analysis.service';
|
|
declare class UpdateImageDto {
|
|
sourceUrl: string;
|
|
}
|
|
export declare class RecipesController {
|
|
private readonly recipesService;
|
|
private readonly recipeAnalysisService;
|
|
constructor(recipesService: RecipesService, recipeAnalysisService: RecipeAnalysisService);
|
|
parseMarkdown(dto: ParseMarkdownDto): Promise<{
|
|
name: string;
|
|
description: string;
|
|
instructions: string;
|
|
ingredients: {
|
|
rawName: string;
|
|
rawLine: string;
|
|
alternatives: string[];
|
|
quantity: number;
|
|
unit: string;
|
|
note: string | null;
|
|
suggestions: import("./recipe-matching.service").IngredientSuggestion[];
|
|
}[];
|
|
}>;
|
|
getAiSuggestions(user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
suggestions: import("./recipes.service").AiRecipeSuggestion[];
|
|
}>;
|
|
findAll(user: {
|
|
userId: number;
|
|
}): Promise<({
|
|
owner: {
|
|
id: number;
|
|
username: string;
|
|
} | null;
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
shares: {
|
|
userId: number;
|
|
}[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
})[]>;
|
|
getInventoryPreview(id: number, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
recipe: {
|
|
id: number;
|
|
name: string;
|
|
description: string | null;
|
|
};
|
|
ingredients: {
|
|
ingredientId: any;
|
|
productId: any;
|
|
productName: any;
|
|
requiredQuantity: number;
|
|
requiredUnit: any;
|
|
note: any;
|
|
availableQuantity: number;
|
|
availableUnit: any;
|
|
matchingInventoryItems: {
|
|
id: any;
|
|
quantity: any;
|
|
unit: any;
|
|
location: any;
|
|
brand: any;
|
|
bestBeforeDate: any;
|
|
}[];
|
|
otherInventoryItems: {
|
|
id: any;
|
|
quantity: any;
|
|
unit: any;
|
|
location: any;
|
|
convertedQuantity: number;
|
|
canConvert: boolean;
|
|
}[];
|
|
status: "missing" | "enough" | "unit_mismatch";
|
|
fromPantry: boolean;
|
|
missingQuantity: number;
|
|
}[];
|
|
summary: {
|
|
totalIngredients: number;
|
|
enoughCount: number;
|
|
missingCount: number;
|
|
unitMismatchCount: number;
|
|
canCookExactly: boolean;
|
|
pantryCount: number;
|
|
};
|
|
}>;
|
|
getRecipeAnalysis(id: number, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
recipeId: number;
|
|
ingredients: ({
|
|
ingredientId: any;
|
|
rawName: any;
|
|
quantity: number;
|
|
unit: any;
|
|
note: any;
|
|
status: "missing" | "substitutable" | "exact_match" | "covered_by_pantry";
|
|
matchedProductId: any;
|
|
matchedProductName: any;
|
|
source: string;
|
|
availableQuantity: number;
|
|
missingQuantity: number;
|
|
} | {
|
|
ingredientId: any;
|
|
rawName: any;
|
|
quantity: number;
|
|
unit: any;
|
|
note: any;
|
|
status: "missing" | "substitutable" | "exact_match" | "covered_by_pantry";
|
|
matchedProductId: any;
|
|
matchedProductName: any;
|
|
source: null;
|
|
availableQuantity: number;
|
|
missingQuantity: number;
|
|
})[];
|
|
summary: {
|
|
exactCount: number;
|
|
pantryCount: number;
|
|
substituteCount: number;
|
|
missingCount: number;
|
|
};
|
|
shoppingListCandidates: {
|
|
ingredientId: any;
|
|
rawName: any;
|
|
quantity: number;
|
|
unit: any;
|
|
missingQuantity: number;
|
|
}[];
|
|
}>;
|
|
rematchRecipeIngredients(id: number, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
recipeId: number;
|
|
ingredients: ({
|
|
ingredientId: any;
|
|
rawName: any;
|
|
quantity: number;
|
|
unit: any;
|
|
note: any;
|
|
status: "missing" | "substitutable" | "exact_match" | "covered_by_pantry";
|
|
matchedProductId: any;
|
|
matchedProductName: any;
|
|
source: string;
|
|
availableQuantity: number;
|
|
missingQuantity: number;
|
|
} | {
|
|
ingredientId: any;
|
|
rawName: any;
|
|
quantity: number;
|
|
unit: any;
|
|
note: any;
|
|
status: "missing" | "substitutable" | "exact_match" | "covered_by_pantry";
|
|
matchedProductId: any;
|
|
matchedProductName: any;
|
|
source: null;
|
|
availableQuantity: number;
|
|
missingQuantity: number;
|
|
})[];
|
|
summary: {
|
|
exactCount: number;
|
|
pantryCount: number;
|
|
substituteCount: number;
|
|
missingCount: number;
|
|
};
|
|
shoppingListCandidates: {
|
|
ingredientId: any;
|
|
rawName: any;
|
|
quantity: number;
|
|
unit: any;
|
|
missingQuantity: number;
|
|
}[];
|
|
}>;
|
|
findOne(id: number, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
owner: {
|
|
id: number;
|
|
username: string;
|
|
} | null;
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
shares: {
|
|
userId: number;
|
|
}[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
create(createRecipeDto: CreateRecipeDto, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
update(id: number, createRecipeDto: CreateRecipeDto, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
remove(id: number, user: {
|
|
userId: number;
|
|
}): Promise<void>;
|
|
updateImage(id: number, dto: UpdateImageDto, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
owner: {
|
|
id: number;
|
|
username: string;
|
|
} | null;
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
shares: {
|
|
userId: number;
|
|
}[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
addIngredient(id: number, ingredient: CreateIngredientDto, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
}>;
|
|
setVisibility(id: number, dto: SetRecipeVisibilityDto, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
owner: {
|
|
id: number;
|
|
username: string;
|
|
} | null;
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
shares: {
|
|
userId: number;
|
|
}[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
shareRecipe(id: number, dto: ShareRecipeDto, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
owner: {
|
|
id: number;
|
|
username: string;
|
|
} | null;
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
shares: {
|
|
userId: number;
|
|
}[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
unshareRecipe(id: number, username: string, user: {
|
|
userId: number;
|
|
}): Promise<{
|
|
owner: {
|
|
id: number;
|
|
username: string;
|
|
} | null;
|
|
ingredients: ({
|
|
product: ({
|
|
nutrition: {
|
|
id: number;
|
|
productId: number;
|
|
calories: number | null;
|
|
protein: number | null;
|
|
fat: number | null;
|
|
carbohydrates: number | null;
|
|
salt: number | null;
|
|
sugar: number | null;
|
|
fiber: number | null;
|
|
} | null;
|
|
} & {
|
|
name: string;
|
|
category: string | null;
|
|
status: string;
|
|
id: number;
|
|
categoryId: number | null;
|
|
normalizedName: string;
|
|
canonicalName: string | null;
|
|
isActive: boolean;
|
|
deletedAt: Date | null;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number;
|
|
isPrivate: boolean;
|
|
}) | null;
|
|
} & {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
productId: number | null;
|
|
quantity: import("@prisma/client/runtime/library").Decimal | null;
|
|
unit: string | null;
|
|
recipeId: number;
|
|
rawName: string;
|
|
rawLine: string | null;
|
|
note: string | null;
|
|
alternativeProductIds: import("@prisma/client/runtime/library").JsonValue | null;
|
|
matchConfidence: number | null;
|
|
matchSource: string | null;
|
|
analysisStatus: string | null;
|
|
})[];
|
|
shares: {
|
|
userId: number;
|
|
}[];
|
|
} & {
|
|
name: string;
|
|
isPublic: boolean;
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
ownerId: number | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
imageUrl: string | null;
|
|
servings: number | null;
|
|
}>;
|
|
}
|
|
export {};
|