diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67b9059..d3d5a78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
All notable changes to Epicure are documented here. This file is mirrored in-app at `/changelog` (and in the admin dashboard) via `apps/web/lib/changelog.ts` — update both together.
+## 0.31.0 — 2026-07-14 16:35
+
+### Added
+- **Recipes can now be marked as a Drink/cocktail** instead of a dish — the recipe form hides irrelevant food-only fields (prep/cook time, difficulty, batch cooking) for drinks, and you can filter your recipe list by type. Drink recipes also no longer show the food/drink pairing suggestion buttons on their own page.
+
## 0.30.1 — 2026-07-14 16:20
Internal: fixed test mocks left stale by the earlier API-key auth-widening pass (recipes/meal-plans/shopping-lists route tests) — no user-facing change.
diff --git a/apps/web/app/(app)/recipes/[id]/edit/page.tsx b/apps/web/app/(app)/recipes/[id]/edit/page.tsx
index 98e5e5e..5af41dc 100644
--- a/apps/web/app/(app)/recipes/[id]/edit/page.tsx
+++ b/apps/web/app/(app)/recipes/[id]/edit/page.tsx
@@ -35,6 +35,7 @@ export default async function EditRecipePage({ params }: Params) {
title: recipe.title,
description: recipe.description ?? undefined,
baseServings: recipe.baseServings,
+ recipeType: recipe.recipeType,
visibility: recipe.visibility,
difficulty: recipe.difficulty,
prepMins: recipe.prepMins,
diff --git a/apps/web/app/(app)/recipes/[id]/page.tsx b/apps/web/app/(app)/recipes/[id]/page.tsx
index ccd0f28..efc8af0 100644
--- a/apps/web/app/(app)/recipes/[id]/page.tsx
+++ b/apps/web/app/(app)/recipes/[id]/page.tsx
@@ -166,7 +166,7 @@ export default async function RecipePage({ params }: Params) {
)}
- {!recipe.isBatchCook && (
+ {!recipe.isBatchCook && recipe.recipeType !== "drink" && (
<>
diff --git a/apps/web/app/(app)/recipes/page.tsx b/apps/web/app/(app)/recipes/page.tsx
index e546ed5..677c95d 100644
--- a/apps/web/app/(app)/recipes/page.tsx
+++ b/apps/web/app/(app)/recipes/page.tsx
@@ -22,6 +22,7 @@ type SearchParams = Promise<{
tag?: string;
page?: string;
batchCook?: string;
+ recipeType?: string;
}>;
const SORT_MAP = {
@@ -40,7 +41,7 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
if (!session) return null;
const m = getMessages((session.user as { locale?: string }).locale);
- const { q, sort, visibility, difficulty, tag, page: pageParam, batchCook } = await searchParams;
+ const { q, sort, visibility, difficulty, tag, page: pageParam, batchCook, recipeType } = await searchParams;
const query = (q ?? "").trim().slice(0, 200);
const sortKey: SortKey = (sort && sort in SORT_MAP ? sort : "updated_desc") as SortKey;
const tagFilter = tag?.trim().slice(0, 50) || undefined;
@@ -54,6 +55,7 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
? (difficulty as "easy" | "medium" | "hard")
: undefined;
const batchCookFilter = batchCook === "1" || batchCook === "0" ? batchCook : undefined;
+ const recipeTypeFilter = recipeType === "dish" || recipeType === "drink" ? recipeType : undefined;
// Escape ilike wildcard chars (% and _) so a search like "100%" matches literally.
const escapedQuery = query.replace(/[\\%_]/g, (c) => `\\${c}`);
@@ -78,6 +80,7 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
visibilityFilter ? eq(recipes.visibility, visibilityFilter) : undefined,
difficultyFilter ? eq(recipes.difficulty, difficultyFilter) : undefined,
tagFilter ? sql`${recipes.tags} @> ARRAY[${tagFilter}]::text[]` : undefined,
+ recipeTypeFilter ? eq(recipes.recipeType, recipeTypeFilter) : undefined,
);
const [userRecipes, totalRow] = await Promise.all([
@@ -112,6 +115,7 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
if (difficultyFilter) params.set("difficulty", difficultyFilter);
if (tagFilter) params.set("tag", tagFilter);
if (batchCookFilter) params.set("batchCook", batchCookFilter);
+ if (recipeTypeFilter) params.set("recipeType", recipeTypeFilter);
if (p > 1) params.set("page", String(p));
const qs = params.toString();
return qs ? `/recipes?${qs}` : "/recipes";
@@ -133,9 +137,10 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
initialDifficulty={difficultyFilter ?? ""}
initialTag={tagFilter ?? ""}
initialBatchCook={batchCookFilter ?? ""}
+ initialRecipeType={recipeTypeFilter ?? ""}
/>
-
+
{totalPages > 1 && (
diff --git a/apps/web/app/api/v1/recipes/[id]/route.ts b/apps/web/app/api/v1/recipes/[id]/route.ts
index 72b63fd..761dbf6 100644
--- a/apps/web/app/api/v1/recipes/[id]/route.ts
+++ b/apps/web/app/api/v1/recipes/[id]/route.ts
@@ -12,6 +12,7 @@ const UpdateRecipeSchema = z.object({
title: z.string().min(1).max(200).optional(),
description: z.string().max(2000).optional(),
baseServings: z.number().int().min(1).max(100).optional(),
+ recipeType: z.enum(["dish", "drink"]).optional(),
visibility: z.enum(["private", "unlisted", "public"]).optional(),
difficulty: z.enum(["easy", "medium", "hard"]).nullable().optional(),
prepMins: z.number().int().min(0).max(1440).nullable().optional(),
@@ -150,6 +151,7 @@ export async function PUT(req: NextRequest, { params }: Params) {
if (data.title !== undefined) updates.title = data.title;
if (data.description !== undefined) updates.description = data.description;
if (data.baseServings !== undefined) updates.baseServings = data.baseServings;
+ if (data.recipeType !== undefined) updates.recipeType = data.recipeType;
if (data.visibility !== undefined) updates.visibility = data.visibility;
if (data.difficulty !== undefined) updates.difficulty = data.difficulty ?? undefined;
if (data.prepMins !== undefined) updates.prepMins = data.prepMins ?? undefined;
diff --git a/apps/web/app/api/v1/recipes/route.ts b/apps/web/app/api/v1/recipes/route.ts
index 415d196..0081e2a 100644
--- a/apps/web/app/api/v1/recipes/route.ts
+++ b/apps/web/app/api/v1/recipes/route.ts
@@ -13,6 +13,7 @@ const CreateRecipeSchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(2000).optional(),
baseServings: z.number().int().min(1).max(100).default(4),
+ recipeType: z.enum(["dish", "drink"]).default("dish"),
visibility: z.enum(["private", "unlisted", "public"]).default("private"),
difficulty: z.enum(["easy", "medium", "hard"]).optional(),
prepMins: z.number().int().min(0).max(1440).optional(),
@@ -69,9 +70,11 @@ export async function GET(req: NextRequest) {
const limit = Math.min(parseInt(searchParams.get("limit") ?? "20"), 100);
const offset = parseInt(searchParams.get("offset") ?? "0");
const visibility = searchParams.get("visibility") as "private" | "unlisted" | "public" | null;
+ const recipeType = searchParams.get("recipeType") as "dish" | "drink" | null;
const conditions = [eq(recipes.authorId, session!.user.id)];
if (visibility) conditions.push(eq(recipes.visibility, visibility));
+ if (recipeType) conditions.push(eq(recipes.recipeType, recipeType));
const rows = await db
.select()
@@ -118,6 +121,7 @@ export async function POST(req: NextRequest) {
title: data.title,
description: data.description,
baseServings: data.baseServings,
+ recipeType: data.recipeType,
visibility: data.visibility,
difficulty: data.difficulty,
prepMins: data.prepMins,
diff --git a/apps/web/components/recipe/recipe-form.tsx b/apps/web/components/recipe/recipe-form.tsx
index c535246..ca36fee 100644
--- a/apps/web/components/recipe/recipe-form.tsx
+++ b/apps/web/components/recipe/recipe-form.tsx
@@ -66,6 +66,7 @@ type RecipeFormProps = {
title?: string;
description?: string;
baseServings?: number;
+ recipeType?: "dish" | "drink";
visibility?: "private" | "unlisted" | "public";
difficulty?: "easy" | "medium" | "hard" | null;
prepMins?: number | null;
@@ -103,6 +104,7 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
const [title, setTitle] = useState(defaultValues?.title ?? "");
const [description, setDescription] = useState(defaultValues?.description ?? "");
const [baseServings, setBaseServings] = useState(String(defaultValues?.baseServings ?? 4));
+ const [recipeType, setRecipeType] = useState<"dish" | "drink">(defaultValues?.recipeType ?? "dish");
const [visibility, setVisibility] = useState<"private" | "unlisted" | "public">(defaultValues?.visibility ?? "private");
const [difficulty, setDifficulty] = useState<"easy" | "medium" | "hard" | "">(defaultValues?.difficulty ?? "");
const [prepMins, setPrepMins] = useState(String(defaultValues?.prepMins ?? ""));
@@ -150,6 +152,7 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
photos,
isBatchCook,
dishes,
+ recipeType,
]);
// Browser-level guard: warn on tab close / reload / external navigation while dirty.
@@ -301,6 +304,7 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
title: title.trim(),
description: description.trim() || undefined,
baseServings: parseInt(baseServings) || 4,
+ recipeType,
visibility,
difficulty: difficulty || undefined,
prepMins: prepMins ? parseInt(prepMins) : undefined,
@@ -365,9 +369,22 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
/>
-
+
+
+
+
+
+
@@ -477,16 +498,20 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
-
+ {recipeType === "dish" && (
+ <>
+
- {/* Batch cooking */}
-
-
-
-
-
-
{t("batchCookToggleHelp")}
-
+ {/* Batch cooking */}
+
+
+
+
+
+
{t("batchCookToggleHelp")}
+
+ >
+ )}
diff --git a/apps/web/components/recipe/recipes-header.tsx b/apps/web/components/recipe/recipes-header.tsx
index 7dff83f..fa685b5 100644
--- a/apps/web/components/recipe/recipes-header.tsx
+++ b/apps/web/components/recipe/recipes-header.tsx
@@ -71,6 +71,12 @@ const BATCH_COOK_KEYS: Record
= {
"0": "batchCookExclude",
};
+const RECIPE_TYPE_KEYS: Record = {
+ "": "all",
+ dish: "dish",
+ drink: "drink",
+};
+
export function RecipesHeader({
count,
initialQuery = "",
@@ -79,6 +85,7 @@ export function RecipesHeader({
initialDifficulty = "",
initialTag = "",
initialBatchCook = "",
+ initialRecipeType = "",
}: {
count: number;
initialQuery?: string;
@@ -87,6 +94,7 @@ export function RecipesHeader({
initialDifficulty?: string;
initialTag?: string;
initialBatchCook?: string;
+ initialRecipeType?: string;
}) {
const router = useRouter();
const pathname = usePathname();
@@ -106,6 +114,7 @@ export function RecipesHeader({
difficulty: initialDifficulty,
tag: initialTag,
batchCook: initialBatchCook,
+ recipeType: initialRecipeType,
...overrides,
};
if (current.q?.trim()) params.set("q", current.q.trim());
@@ -114,6 +123,7 @@ export function RecipesHeader({
if (current.difficulty) params.set("difficulty", current.difficulty);
if (current.tag?.trim()) params.set("tag", current.tag.trim());
if (current.batchCook) params.set("batchCook", current.batchCook);
+ if (current.recipeType) params.set("recipeType", current.recipeType);
startTransition(() => router.push(`${pathname}?${params.toString()}`));
}
@@ -122,7 +132,7 @@ export function RecipesHeader({
pushParams({ q: value });
}
- const activeFilterCount = [initialVisibility, initialDifficulty, initialTag, initialBatchCook].filter(Boolean).length;
+ const activeFilterCount = [initialVisibility, initialDifficulty, initialTag, initialBatchCook, initialRecipeType].filter(Boolean).length;
const sortChanged = initialSort !== "updated_desc";
return (
@@ -270,12 +280,26 @@ export function RecipesHeader({
))}
+
+
+ {t("recipeTypeLabel")}
+ {Object.entries(RECIPE_TYPE_KEYS).map(([value, key]) => (
+ pushParams({ recipeType: value })}
+ className={cn(initialRecipeType === value && "font-medium text-primary")}
+ >
+ {value === "" ? t(key) : tRecipe(`recipeType.${key}`)}
+
+ ))}
+
{activeFilterCount > 0 && (
<>
pushParams({ visibility: "", difficulty: "", tag: "", batchCook: "" })}
+ onClick={() => pushParams({ visibility: "", difficulty: "", tag: "", batchCook: "", recipeType: "" })}
className="text-muted-foreground"
>
{t("clearFilters")}
diff --git a/apps/web/lib/changelog.ts b/apps/web/lib/changelog.ts
index db31273..ffd2eb8 100644
--- a/apps/web/lib/changelog.ts
+++ b/apps/web/lib/changelog.ts
@@ -1,5 +1,5 @@
// Mirrors CHANGELOG.md at the repo root — update both together.
-export const APP_VERSION = "0.30.1";
+export const APP_VERSION = "0.31.0";
export type ChangelogEntry = {
version: string;
@@ -11,6 +11,13 @@ export type ChangelogEntry = {
};
export const CHANGELOG: ChangelogEntry[] = [
+ {
+ version: "0.31.0",
+ date: "2026-07-14 16:35",
+ added: [
+ "**Recipes can now be marked as a Drink/cocktail** instead of a dish — the recipe form hides irrelevant food-only fields (prep/cook time, difficulty, batch cooking) for drinks, and you can filter your recipe list by type. Drink recipes also no longer show the food/drink pairing suggestion buttons on their own page.",
+ ],
+ },
{
version: "0.30.1",
date: "2026-07-14 16:20",
diff --git a/apps/web/lib/openapi.ts b/apps/web/lib/openapi.ts
index 187b7ba..2b202dc 100644
--- a/apps/web/lib/openapi.ts
+++ b/apps/web/lib/openapi.ts
@@ -49,7 +49,7 @@ export function generateOpenApiSpec(): object {
const RecipeRef = registry.register("Recipe", z.object({
id: z.string(), authorId: z.string(), title: z.string(), description: z.string().nullable(),
- baseServings: z.number().int(), visibility: z.enum(["private", "unlisted", "public"]),
+ baseServings: z.number().int(), recipeType: z.enum(["dish", "drink"]), visibility: z.enum(["private", "unlisted", "public"]),
difficulty: z.enum(["easy", "medium", "hard"]).nullable(),
prepMins: z.number().int().nullable(), cookMins: z.number().int().nullable(),
tags: z.array(z.string()), sourceUrl: z.string().nullable(), language: z.string().nullable(),
@@ -62,7 +62,7 @@ export function generateOpenApiSpec(): object {
const RecipeSummaryRef = registry.register("RecipeSummary", z.object({
id: z.string(), authorId: z.string(), title: z.string(), description: z.string().nullable(),
- baseServings: z.number().int(), visibility: z.enum(["private", "unlisted", "public"]),
+ baseServings: z.number().int(), recipeType: z.enum(["dish", "drink"]), visibility: z.enum(["private", "unlisted", "public"]),
difficulty: z.enum(["easy", "medium", "hard"]).nullable(),
prepMins: z.number().int().nullable(), cookMins: z.number().int().nullable(),
tags: z.array(z.string()), dietaryTags: DietaryTagsRef, aiGenerated: z.boolean(),
@@ -75,6 +75,7 @@ export function generateOpenApiSpec(): object {
title: z.string().min(1).max(200),
description: z.string().max(2000).optional(),
baseServings: z.number().int().min(1).max(100).default(4),
+ recipeType: z.enum(["dish", "drink"]).default("dish"),
visibility: z.enum(["private", "unlisted", "public"]).default("private"),
difficulty: z.enum(["easy", "medium", "hard"]).optional(),
prepMins: z.number().int().min(0).max(1440).optional(),
@@ -116,6 +117,7 @@ export function generateOpenApiSpec(): object {
title: z.string().min(1).max(200).optional(),
description: z.string().max(2000).nullable().optional(),
baseServings: z.number().int().min(1).max(100).optional(),
+ recipeType: z.enum(["dish", "drink"]).optional(),
visibility: z.enum(["private", "unlisted", "public"]).optional(),
difficulty: z.enum(["easy", "medium", "hard"]).nullable().optional(),
prepMins: z.number().int().min(0).max(1440).nullable().optional(),
@@ -232,7 +234,7 @@ export function generateOpenApiSpec(): object {
const idParam = z.object({ id: z.string() });
const weekStartParam = z.object({ weekStart: z.string().describe("ISO date YYYY-MM-DD (Monday)") });
- registry.registerPath({ method: "get", path: "/api/v1/recipes", summary: "List your own recipes", description: "Returns a lean summary per recipe — no ingredients/steps/photos/batchDishes (use GET /recipes/{id} for full detail).", security, request: { query: z.object({ limit: z.coerce.number().max(100).default(20), offset: z.coerce.number().default(0), visibility: z.enum(["private", "unlisted", "public"]).optional() }) }, responses: { 200: { description: "Paginated", content: { "application/json": { schema: PaginatedRecipes } } }, 401: { description: "Unauthorized", content: { "application/json": { schema: ApiErrorRef } } } } });
+ registry.registerPath({ method: "get", path: "/api/v1/recipes", summary: "List your own recipes", description: "Returns a lean summary per recipe — no ingredients/steps/photos/batchDishes (use GET /recipes/{id} for full detail).", security, request: { query: z.object({ limit: z.coerce.number().max(100).default(20), offset: z.coerce.number().default(0), visibility: z.enum(["private", "unlisted", "public"]).optional(), recipeType: z.enum(["dish", "drink"]).optional() }) }, responses: { 200: { description: "Paginated", content: { "application/json": { schema: PaginatedRecipes } } }, 401: { description: "Unauthorized", content: { "application/json": { schema: ApiErrorRef } } } } });
registry.registerPath({ method: "post", path: "/api/v1/recipes", summary: "Create recipe", security, request: { body: { content: { "application/json": { schema: CreateRecipeRef } }, required: true } }, responses: { 201: { description: "Created", content: { "application/json": { schema: RecipeRef } } }, 400: { description: "Bad request", content: { "application/json": { schema: ApiErrorRef } } }, 401: { description: "Unauthorized", content: { "application/json": { schema: ApiErrorRef } } } } });
registry.registerPath({ method: "get", path: "/api/v1/recipes/{id}", summary: "Get recipe", security, request: { params: idParam }, responses: { 200: { description: "Recipe", content: { "application/json": { schema: RecipeRef } } }, 404: { description: "Not found", content: { "application/json": { schema: ApiErrorRef } } } } });
registry.registerPath({ method: "put", path: "/api/v1/recipes/{id}", summary: "Update recipe", security, request: { params: idParam, body: { content: { "application/json": { schema: UpdateRecipeRef } }, required: true } }, responses: { 200: { description: "Updated", content: { "application/json": { schema: RecipeRef } } }, 400: { description: "Bad request", content: { "application/json": { schema: ApiErrorRef } } }, 401: { description: "Unauthorized", content: { "application/json": { schema: ApiErrorRef } } }, 404: { description: "Not found", content: { "application/json": { schema: ApiErrorRef } } } } });
diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index 48a1daf..4ae4b33 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -232,6 +232,10 @@
"medium": "Medium",
"hard": "Hard"
},
+ "recipeType": {
+ "dish": "Dish",
+ "drink": "Drink"
+ },
"dietary": {
"vegan": "Vegan",
"vegetarian": "Vegetarian",
@@ -664,6 +668,7 @@
"batchCookLabel": "Batch cooking",
"batchCookOnly": "Batch cooking only",
"batchCookExclude": "Hide batch cooking",
+ "recipeTypeLabel": "Type",
"clearFilters": "Clear filters",
"tabMine": "My Recipes",
"tabFavorites": "Favorites",
@@ -689,7 +694,11 @@
"titlePlaceholder": "My amazing recipe",
"descriptionLabel": "Description",
"descriptionPlaceholder": "What makes this recipe special…",
+ "recipeType": "Type",
+ "recipeTypeDish": "Dish",
+ "recipeTypeDrink": "Drink / cocktail",
"servings": "Servings",
+ "servingsDrink": "Makes (servings)",
"prepMins": "Prep (min)",
"cookMins": "Cook (min)",
"difficulty": "Difficulty",
diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json
index b993ec1..563a44e 100644
--- a/apps/web/messages/fr.json
+++ b/apps/web/messages/fr.json
@@ -232,6 +232,10 @@
"medium": "Moyen",
"hard": "Difficile"
},
+ "recipeType": {
+ "dish": "Plat",
+ "drink": "Boisson"
+ },
"dietary": {
"vegan": "Végétalien",
"vegetarian": "Végétarien",
@@ -655,6 +659,7 @@
"batchCookLabel": "Batch cooking",
"batchCookOnly": "Batch cooking uniquement",
"batchCookExclude": "Masquer le batch cooking",
+ "recipeTypeLabel": "Type",
"clearFilters": "Effacer les filtres",
"tabMine": "Mes recettes",
"tabFavorites": "Favoris",
@@ -680,7 +685,11 @@
"titlePlaceholder": "Ma super recette",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Ce qui rend cette recette spéciale…",
+ "recipeType": "Type",
+ "recipeTypeDish": "Plat",
+ "recipeTypeDrink": "Boisson / cocktail",
"servings": "Portions",
+ "servingsDrink": "Quantité (portions)",
"prepMins": "Prép. (min)",
"cookMins": "Cuisson (min)",
"difficulty": "Difficulté",
diff --git a/apps/web/package.json b/apps/web/package.json
index 122afe1..abe7a77 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "@epicure/web",
- "version": "0.30.1",
+ "version": "0.31.0",
"private": true,
"scripts": {
"dev": "next dev",
diff --git a/package.json b/package.json
index 7c9fbe5..30342cf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "epicure",
- "version": "0.30.1",
+ "version": "0.31.0",
"private": true,
"scripts": {
"dev": "pnpm --filter web dev",
diff --git a/packages/db/src/migrations/0039_sudden_franklin_storm.sql b/packages/db/src/migrations/0039_sudden_franklin_storm.sql
new file mode 100644
index 0000000..d04b999
--- /dev/null
+++ b/packages/db/src/migrations/0039_sudden_franklin_storm.sql
@@ -0,0 +1,3 @@
+CREATE TYPE "public"."recipe_type" AS ENUM('dish', 'drink');--> statement-breakpoint
+ALTER TABLE "recipes" ADD COLUMN "recipe_type" "recipe_type" DEFAULT 'dish' NOT NULL;--> statement-breakpoint
+CREATE INDEX "recipes_type_idx" ON "recipes" USING btree ("recipe_type");
\ No newline at end of file
diff --git a/packages/db/src/migrations/meta/0039_snapshot.json b/packages/db/src/migrations/meta/0039_snapshot.json
new file mode 100644
index 0000000..5207a99
--- /dev/null
+++ b/packages/db/src/migrations/meta/0039_snapshot.json
@@ -0,0 +1,5051 @@
+{
+ "id": "51fbc864-e45b-4521-aea9-e2aae95adc73",
+ "prevId": "3386741b-7100-413f-b011-f4b50eeb22aa",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.accounts": {
+ "name": "accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "accounts_user_id_users_id_fk": {
+ "name": "accounts_user_id_users_id_fk",
+ "tableFrom": "accounts",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_keys": {
+ "name": "api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "api_key_scope",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'full'"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "api_keys_user_id_users_id_fk": {
+ "name": "api_keys_user_id_users_id_fk",
+ "tableFrom": "api_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.push_subscriptions": {
+ "name": "push_subscriptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "endpoint": {
+ "name": "endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "p256dh": {
+ "name": "p256dh",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "auth": {
+ "name": "auth",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "push_subscriptions_user_id_users_id_fk": {
+ "name": "push_subscriptions_user_id_users_id_fk",
+ "tableFrom": "push_subscriptions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "push_subscriptions_endpoint_unique": {
+ "name": "push_subscriptions_endpoint_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "endpoint"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sessions": {
+ "name": "sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sessions_token_unique": {
+ "name": "sessions_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.two_factors": {
+ "name": "two_factors",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "secret": {
+ "name": "secret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backup_codes": {
+ "name": "backup_codes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "two_factors_user_id_users_id_fk": {
+ "name": "two_factors_user_id_users_id_fk",
+ "tableFrom": "two_factors",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_ai_keys": {
+ "name": "user_ai_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_key": {
+ "name": "encrypted_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_ai_keys_user_id_users_id_fk": {
+ "name": "user_ai_keys_user_id_users_id_fk",
+ "tableFrom": "user_ai_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_blocks": {
+ "name": "user_blocks",
+ "schema": "",
+ "columns": {
+ "blocker_id": {
+ "name": "blocker_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "blocked_id": {
+ "name": "blocked_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_blocks_blocker_id_users_id_fk": {
+ "name": "user_blocks_blocker_id_users_id_fk",
+ "tableFrom": "user_blocks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "blocker_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_blocks_blocked_id_users_id_fk": {
+ "name": "user_blocks_blocked_id_users_id_fk",
+ "tableFrom": "user_blocks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "blocked_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_blocks_blocker_id_blocked_id_pk": {
+ "name": "user_blocks_blocker_id_blocked_id_pk",
+ "columns": [
+ "blocker_id",
+ "blocked_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_follows": {
+ "name": "user_follows",
+ "schema": "",
+ "columns": {
+ "follower_id": {
+ "name": "follower_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "following_id": {
+ "name": "following_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_follows_follower_id_users_id_fk": {
+ "name": "user_follows_follower_id_users_id_fk",
+ "tableFrom": "user_follows",
+ "tableTo": "users",
+ "columnsFrom": [
+ "follower_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_follows_following_id_users_id_fk": {
+ "name": "user_follows_following_id_users_id_fk",
+ "tableFrom": "user_follows",
+ "tableTo": "users",
+ "columnsFrom": [
+ "following_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_follows_follower_id_following_id_pk": {
+ "name": "user_follows_follower_id_following_id_pk",
+ "columns": [
+ "follower_id",
+ "following_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_model_prefs": {
+ "name": "user_model_prefs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text_provider": {
+ "name": "text_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "text_model": {
+ "name": "text_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vision_provider": {
+ "name": "vision_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vision_model": {
+ "name": "vision_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "meal_plan_provider": {
+ "name": "meal_plan_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "meal_plan_model": {
+ "name": "meal_plan_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_model_prefs_user_id_users_id_fk": {
+ "name": "user_model_prefs_user_id_users_id_fk",
+ "tableFrom": "user_model_prefs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_model_prefs_user_id_unique": {
+ "name": "user_model_prefs_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_notification_prefs": {
+ "name": "user_notification_prefs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "follow": {
+ "name": "follow",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "comment": {
+ "name": "comment",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "reply": {
+ "name": "reply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "reaction": {
+ "name": "reaction",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "rating": {
+ "name": "rating",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "mention": {
+ "name": "mention",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "leftover_expiring": {
+ "name": "leftover_expiring",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "shopping_list": {
+ "name": "shopping_list",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_notification_prefs_user_id_users_id_fk": {
+ "name": "user_notification_prefs_user_id_users_id_fk",
+ "tableFrom": "user_notification_prefs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_notification_prefs_user_id_unique": {
+ "name": "user_notification_prefs_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_nutrition_goals": {
+ "name": "user_nutrition_goals",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "calories_kcal": {
+ "name": "calories_kcal",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "protein_g": {
+ "name": "protein_g",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "carbs_g": {
+ "name": "carbs_g",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fat_g": {
+ "name": "fat_g",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_nutrition_goals_user_id_users_id_fk": {
+ "name": "user_nutrition_goals_user_id_users_id_fk",
+ "tableFrom": "user_nutrition_goals",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_nutrition_goals_user_id_unique": {
+ "name": "user_nutrition_goals_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_custom_avatar": {
+ "name": "has_custom_avatar",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "use_gravatar": {
+ "name": "use_gravatar",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "private_bio": {
+ "name": "private_bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_private": {
+ "name": "is_private",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "user_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'user'"
+ },
+ "tier": {
+ "name": "tier",
+ "type": "tier",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'free'"
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unit_pref": {
+ "name": "unit_pref",
+ "type": "unit_pref",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'metric'"
+ },
+ "locale": {
+ "name": "locale",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'en'"
+ },
+ "two_factor_enabled": {
+ "name": "two_factor_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ },
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ },
+ "users_stripe_customer_id_unique": {
+ "name": "users_stripe_customer_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "stripe_customer_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verifications": {
+ "name": "verifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ingredients": {
+ "name": "ingredients",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "aliases": {
+ "name": "aliases",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "category": {
+ "name": "category",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "known_allergens": {
+ "name": "known_allergens",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ingredients_name_unique": {
+ "name": "ingredients_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_batch_dishes": {
+ "name": "recipe_batch_dishes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "fridge_days": {
+ "name": "fridge_days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "freezer_friendly": {
+ "name": "freezer_friendly",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "freezer_note": {
+ "name": "freezer_note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "day_of_instructions": {
+ "name": "day_of_instructions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "recipe_batch_dishes_recipe_idx": {
+ "name": "recipe_batch_dishes_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recipe_batch_dishes_recipe_id_recipes_id_fk": {
+ "name": "recipe_batch_dishes_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_batch_dishes",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_ingredients": {
+ "name": "recipe_ingredients",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ingredient_id": {
+ "name": "ingredient_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_name": {
+ "name": "raw_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unit": {
+ "name": "unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "note": {
+ "name": "note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {
+ "recipe_ingredients_recipe_idx": {
+ "name": "recipe_ingredients_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recipe_ingredients_recipe_id_recipes_id_fk": {
+ "name": "recipe_ingredients_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_ingredients",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "recipe_ingredients_ingredient_id_ingredients_id_fk": {
+ "name": "recipe_ingredients_ingredient_id_ingredients_id_fk",
+ "tableFrom": "recipe_ingredients",
+ "tableTo": "ingredients",
+ "columnsFrom": [
+ "ingredient_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_notes": {
+ "name": "recipe_notes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "recipe_notes_recipe_user_idx": {
+ "name": "recipe_notes_recipe_user_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recipe_notes_recipe_id_recipes_id_fk": {
+ "name": "recipe_notes_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_notes",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "recipe_notes_user_id_users_id_fk": {
+ "name": "recipe_notes_user_id_users_id_fk",
+ "tableFrom": "recipe_notes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_photos": {
+ "name": "recipe_photos",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_cover": {
+ "name": "is_cover",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "recipe_photos_recipe_id_recipes_id_fk": {
+ "name": "recipe_photos_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_photos",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_snapshots": {
+ "name": "recipe_snapshots",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "snapshot_data": {
+ "name": "snapshot_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "recipe_snapshots_recipe_idx": {
+ "name": "recipe_snapshots_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recipe_snapshots_recipe_id_recipes_id_fk": {
+ "name": "recipe_snapshots_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_snapshots",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "recipe_snapshots_author_id_users_id_fk": {
+ "name": "recipe_snapshots_author_id_users_id_fk",
+ "tableFrom": "recipe_snapshots",
+ "tableTo": "users",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_steps": {
+ "name": "recipe_steps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instruction": {
+ "name": "instruction",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timer_seconds": {
+ "name": "timer_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "photo_url": {
+ "name": "photo_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "applies_to": {
+ "name": "applies_to",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ }
+ },
+ "indexes": {
+ "recipe_steps_recipe_idx": {
+ "name": "recipe_steps_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recipe_steps_recipe_id_recipes_id_fk": {
+ "name": "recipe_steps_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_steps",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipe_variations": {
+ "name": "recipe_variations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "parent_recipe_id": {
+ "name": "parent_recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "child_recipe_id": {
+ "name": "child_recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ai_generated": {
+ "name": "ai_generated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "recipe_variations_parent_recipe_id_recipes_id_fk": {
+ "name": "recipe_variations_parent_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_variations",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "parent_recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "recipe_variations_child_recipe_id_recipes_id_fk": {
+ "name": "recipe_variations_child_recipe_id_recipes_id_fk",
+ "tableFrom": "recipe_variations",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "child_recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recipes": {
+ "name": "recipes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "base_servings": {
+ "name": "base_servings",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4
+ },
+ "recipe_type": {
+ "name": "recipe_type",
+ "type": "recipe_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'dish'"
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'private'"
+ },
+ "source_url": {
+ "name": "source_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ai_generated": {
+ "name": "ai_generated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "ai_model": {
+ "name": "ai_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ai_prompt": {
+ "name": "ai_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "language": {
+ "name": "language",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dietary_tags": {
+ "name": "dietary_tags",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'::jsonb"
+ },
+ "dietary_verified": {
+ "name": "dietary_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "nutrition_data": {
+ "name": "nutrition_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "difficulty": {
+ "name": "difficulty",
+ "type": "difficulty",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prep_mins": {
+ "name": "prep_mins",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cook_mins": {
+ "name": "cook_mins",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "is_batch_cook": {
+ "name": "is_batch_cook",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "recipes_author_idx": {
+ "name": "recipes_author_idx",
+ "columns": [
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "recipes_visibility_idx": {
+ "name": "recipes_visibility_idx",
+ "columns": [
+ {
+ "expression": "visibility",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "recipes_dietary_tags_gin": {
+ "name": "recipes_dietary_tags_gin",
+ "columns": [
+ {
+ "expression": "dietary_tags",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "gin",
+ "with": {}
+ },
+ "recipes_batch_cook_idx": {
+ "name": "recipes_batch_cook_idx",
+ "columns": [
+ {
+ "expression": "is_batch_cook",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "recipes_type_idx": {
+ "name": "recipes_type_idx",
+ "columns": [
+ {
+ "expression": "recipe_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recipes_author_id_users_id_fk": {
+ "name": "recipes_author_id_users_id_fk",
+ "tableFrom": "recipes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_allergens": {
+ "name": "user_allergens",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allergen_tag": {
+ "name": "allergen_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_allergens_user_id_users_id_fk": {
+ "name": "user_allergens_user_id_users_id_fk",
+ "tableFrom": "user_allergens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.collection_favorites": {
+ "name": "collection_favorites",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "collection_id": {
+ "name": "collection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "collection_favorites_collection_idx": {
+ "name": "collection_favorites_collection_idx",
+ "columns": [
+ {
+ "expression": "collection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "collection_favorites_user_collection_idx": {
+ "name": "collection_favorites_user_collection_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "collection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "collection_favorites_user_id_users_id_fk": {
+ "name": "collection_favorites_user_id_users_id_fk",
+ "tableFrom": "collection_favorites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "collection_favorites_collection_id_collections_id_fk": {
+ "name": "collection_favorites_collection_id_collections_id_fk",
+ "tableFrom": "collection_favorites",
+ "tableTo": "collections",
+ "columnsFrom": [
+ "collection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.collection_members": {
+ "name": "collection_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "collection_id": {
+ "name": "collection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "collection_member_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'viewer'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "collection_members_user_idx": {
+ "name": "collection_members_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "collection_members_collection_id_collections_id_fk": {
+ "name": "collection_members_collection_id_collections_id_fk",
+ "tableFrom": "collection_members",
+ "tableTo": "collections",
+ "columnsFrom": [
+ "collection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "collection_members_user_id_users_id_fk": {
+ "name": "collection_members_user_id_users_id_fk",
+ "tableFrom": "collection_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.collection_recipes": {
+ "name": "collection_recipes",
+ "schema": "",
+ "columns": {
+ "collection_id": {
+ "name": "collection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "added_at": {
+ "name": "added_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "collection_recipes_collection_id_collections_id_fk": {
+ "name": "collection_recipes_collection_id_collections_id_fk",
+ "tableFrom": "collection_recipes",
+ "tableTo": "collections",
+ "columnsFrom": [
+ "collection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "collection_recipes_recipe_id_recipes_id_fk": {
+ "name": "collection_recipes_recipe_id_recipes_id_fk",
+ "tableFrom": "collection_recipes",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.collections": {
+ "name": "collections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "collections_user_idx": {
+ "name": "collections_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "collections_user_id_users_id_fk": {
+ "name": "collections_user_id_users_id_fk",
+ "tableFrom": "collections",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comment_reactions": {
+ "name": "comment_reactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "comment_reaction_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comment_reactions_comment_idx": {
+ "name": "comment_reactions_comment_idx",
+ "columns": [
+ {
+ "expression": "comment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_reactions_comment_id_comments_id_fk": {
+ "name": "comment_reactions_comment_id_comments_id_fk",
+ "tableFrom": "comment_reactions",
+ "tableTo": "comments",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_reactions_user_id_users_id_fk": {
+ "name": "comment_reactions_user_id_users_id_fk",
+ "tableFrom": "comment_reactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comments": {
+ "name": "comments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comments_recipe_idx": {
+ "name": "comments_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comments_recipe_id_recipes_id_fk": {
+ "name": "comments_recipe_id_recipes_id_fk",
+ "tableFrom": "comments",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comments_user_id_users_id_fk": {
+ "name": "comments_user_id_users_id_fk",
+ "tableFrom": "comments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comments_parent_id_comments_id_fk": {
+ "name": "comments_parent_id_comments_id_fk",
+ "tableFrom": "comments",
+ "tableTo": "comments",
+ "columnsFrom": [
+ "parent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.cooking_history": {
+ "name": "cooking_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "batch_dish_id": {
+ "name": "batch_dish_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cooked_at": {
+ "name": "cooked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "servings": {
+ "name": "servings",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expiry_reminder_sent_at": {
+ "name": "expiry_reminder_sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "cooking_history_user_idx": {
+ "name": "cooking_history_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cooking_history_batch_dish_idx": {
+ "name": "cooking_history_batch_dish_idx",
+ "columns": [
+ {
+ "expression": "batch_dish_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cooking_history_user_id_users_id_fk": {
+ "name": "cooking_history_user_id_users_id_fk",
+ "tableFrom": "cooking_history",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "cooking_history_recipe_id_recipes_id_fk": {
+ "name": "cooking_history_recipe_id_recipes_id_fk",
+ "tableFrom": "cooking_history",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "cooking_history_batch_dish_id_recipe_batch_dishes_id_fk": {
+ "name": "cooking_history_batch_dish_id_recipe_batch_dishes_id_fk",
+ "tableFrom": "cooking_history",
+ "tableTo": "recipe_batch_dishes",
+ "columnsFrom": [
+ "batch_dish_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.favorites": {
+ "name": "favorites",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "favorites_user_idx": {
+ "name": "favorites_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "favorites_user_id_users_id_fk": {
+ "name": "favorites_user_id_users_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "favorites_recipe_id_recipes_id_fk": {
+ "name": "favorites_recipe_id_recipes_id_fk",
+ "tableFrom": "favorites",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notifications": {
+ "name": "notifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "read": {
+ "name": "read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notifications_user_idx": {
+ "name": "notifications_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notifications_user_unread_idx": {
+ "name": "notifications_user_unread_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "read",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notifications_user_id_users_id_fk": {
+ "name": "notifications_user_id_users_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notifications_actor_id_users_id_fk": {
+ "name": "notifications_actor_id_users_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notifications_recipe_id_recipes_id_fk": {
+ "name": "notifications_recipe_id_recipes_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notifications_comment_id_comments_id_fk": {
+ "name": "notifications_comment_id_comments_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "comments",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ratings": {
+ "name": "ratings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "score": {
+ "name": "score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "review_text": {
+ "name": "review_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "photo_key": {
+ "name": "photo_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ratings_user_idx": {
+ "name": "ratings_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ratings_recipe_idx": {
+ "name": "ratings_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ratings_recipe_id_recipes_id_fk": {
+ "name": "ratings_recipe_id_recipes_id_fk",
+ "tableFrom": "ratings",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ratings_user_id_users_id_fk": {
+ "name": "ratings_user_id_users_id_fk",
+ "tableFrom": "ratings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.meal_plan_entries": {
+ "name": "meal_plan_entries",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "meal_plan_id": {
+ "name": "meal_plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "day": {
+ "name": "day",
+ "type": "weekday",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "meal_type": {
+ "name": "meal_type",
+ "type": "meal_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "batch_dish_id": {
+ "name": "batch_dish_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "servings": {
+ "name": "servings",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 2
+ },
+ "note": {
+ "name": "note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "meal_plan_entries_plan_idx": {
+ "name": "meal_plan_entries_plan_idx",
+ "columns": [
+ {
+ "expression": "meal_plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "meal_plan_entries_plan_day_meal_idx": {
+ "name": "meal_plan_entries_plan_day_meal_idx",
+ "columns": [
+ {
+ "expression": "meal_plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "day",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "meal_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "meal_plan_entries_meal_plan_id_meal_plans_id_fk": {
+ "name": "meal_plan_entries_meal_plan_id_meal_plans_id_fk",
+ "tableFrom": "meal_plan_entries",
+ "tableTo": "meal_plans",
+ "columnsFrom": [
+ "meal_plan_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "meal_plan_entries_recipe_id_recipes_id_fk": {
+ "name": "meal_plan_entries_recipe_id_recipes_id_fk",
+ "tableFrom": "meal_plan_entries",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "meal_plan_entries_batch_dish_id_recipe_batch_dishes_id_fk": {
+ "name": "meal_plan_entries_batch_dish_id_recipe_batch_dishes_id_fk",
+ "tableFrom": "meal_plan_entries",
+ "tableTo": "recipe_batch_dishes",
+ "columnsFrom": [
+ "batch_dish_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.meal_plan_members": {
+ "name": "meal_plan_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "meal_plan_id": {
+ "name": "meal_plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "collection_member_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'viewer'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "meal_plan_members_plan_idx": {
+ "name": "meal_plan_members_plan_idx",
+ "columns": [
+ {
+ "expression": "meal_plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "meal_plan_members_user_idx": {
+ "name": "meal_plan_members_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "meal_plan_members_meal_plan_id_meal_plans_id_fk": {
+ "name": "meal_plan_members_meal_plan_id_meal_plans_id_fk",
+ "tableFrom": "meal_plan_members",
+ "tableTo": "meal_plans",
+ "columnsFrom": [
+ "meal_plan_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "meal_plan_members_user_id_users_id_fk": {
+ "name": "meal_plan_members_user_id_users_id_fk",
+ "tableFrom": "meal_plan_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.meal_plans": {
+ "name": "meal_plans",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "week_start": {
+ "name": "week_start",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "meal_plans_user_idx": {
+ "name": "meal_plans_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "meal_plans_user_week_idx": {
+ "name": "meal_plans_user_week_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "week_start",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "meal_plans_user_id_users_id_fk": {
+ "name": "meal_plans_user_id_users_id_fk",
+ "tableFrom": "meal_plans",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pantry_items": {
+ "name": "pantry_items",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ingredient_id": {
+ "name": "ingredient_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_name": {
+ "name": "raw_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unit": {
+ "name": "unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "pantry_items_user_idx": {
+ "name": "pantry_items_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "pantry_items_user_id_users_id_fk": {
+ "name": "pantry_items_user_id_users_id_fk",
+ "tableFrom": "pantry_items",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "pantry_items_ingredient_id_ingredients_id_fk": {
+ "name": "pantry_items_ingredient_id_ingredients_id_fk",
+ "tableFrom": "pantry_items",
+ "tableTo": "ingredients",
+ "columnsFrom": [
+ "ingredient_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.shopping_list_items": {
+ "name": "shopping_list_items",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "list_id": {
+ "name": "list_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ingredient_id": {
+ "name": "ingredient_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_name": {
+ "name": "raw_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unit": {
+ "name": "unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aisle": {
+ "name": "aisle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked": {
+ "name": "checked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "in_pantry": {
+ "name": "in_pantry",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "shopping_list_items_list_id_shopping_lists_id_fk": {
+ "name": "shopping_list_items_list_id_shopping_lists_id_fk",
+ "tableFrom": "shopping_list_items",
+ "tableTo": "shopping_lists",
+ "columnsFrom": [
+ "list_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "shopping_list_items_ingredient_id_ingredients_id_fk": {
+ "name": "shopping_list_items_ingredient_id_ingredients_id_fk",
+ "tableFrom": "shopping_list_items",
+ "tableTo": "ingredients",
+ "columnsFrom": [
+ "ingredient_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.shopping_list_members": {
+ "name": "shopping_list_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "list_id": {
+ "name": "list_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "collection_member_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'viewer'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "shopping_list_members_list_idx": {
+ "name": "shopping_list_members_list_idx",
+ "columns": [
+ {
+ "expression": "list_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "shopping_list_members_user_idx": {
+ "name": "shopping_list_members_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "shopping_list_members_list_id_shopping_lists_id_fk": {
+ "name": "shopping_list_members_list_id_shopping_lists_id_fk",
+ "tableFrom": "shopping_list_members",
+ "tableTo": "shopping_lists",
+ "columnsFrom": [
+ "list_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "shopping_list_members_user_id_users_id_fk": {
+ "name": "shopping_list_members_user_id_users_id_fk",
+ "tableFrom": "shopping_list_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.shopping_lists": {
+ "name": "shopping_lists",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "public_editable": {
+ "name": "public_editable",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "generated_at": {
+ "name": "generated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "shopping_lists_user_id_users_id_fk": {
+ "name": "shopping_lists_user_id_users_id_fk",
+ "tableFrom": "shopping_lists",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.audit_logs": {
+ "name": "audit_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "audit_logs_created_idx": {
+ "name": "audit_logs_created_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "audit_logs_user_id_users_id_fk": {
+ "name": "audit_logs_user_id_users_id_fk",
+ "tableFrom": "audit_logs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invites": {
+ "name": "invites",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "user_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'user'"
+ },
+ "tier": {
+ "name": "tier",
+ "type": "tier",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'free'"
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "used_at": {
+ "name": "used_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "used_by_id": {
+ "name": "used_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "invites_created_by_id_users_id_fk": {
+ "name": "invites_created_by_id_users_id_fk",
+ "tableFrom": "invites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invites_used_by_id_users_id_fk": {
+ "name": "invites_used_by_id_users_id_fk",
+ "tableFrom": "invites",
+ "tableTo": "users",
+ "columnsFrom": [
+ "used_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "invites_token_uniq": {
+ "name": "invites_token_uniq",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.reports": {
+ "name": "reports",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "reporter_id": {
+ "name": "reporter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "report_target_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "report_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "reviewed_by_id": {
+ "name": "reviewed_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reviewed_at": {
+ "name": "reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "reports_status_idx": {
+ "name": "reports_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "reports_reporter_id_users_id_fk": {
+ "name": "reports_reporter_id_users_id_fk",
+ "tableFrom": "reports",
+ "tableTo": "users",
+ "columnsFrom": [
+ "reporter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "reports_reviewed_by_id_users_id_fk": {
+ "name": "reports_reviewed_by_id_users_id_fk",
+ "tableFrom": "reports",
+ "tableTo": "users",
+ "columnsFrom": [
+ "reviewed_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.site_settings": {
+ "name": "site_settings",
+ "schema": "",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_secret": {
+ "name": "is_secret",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_by_id": {
+ "name": "updated_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "site_settings_updated_by_id_users_id_fk": {
+ "name": "site_settings_updated_by_id_users_id_fk",
+ "tableFrom": "site_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tier_definitions": {
+ "name": "tier_definitions",
+ "schema": "",
+ "columns": {
+ "tier": {
+ "name": "tier",
+ "type": "tier",
+ "typeSchema": "public",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "max_recipes": {
+ "name": "max_recipes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ai_calls_per_month": {
+ "name": "ai_calls_per_month",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_mb": {
+ "name": "storage_mb",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_public_recipes": {
+ "name": "max_public_recipes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_usage": {
+ "name": "user_usage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "month": {
+ "name": "month",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ai_calls_used": {
+ "name": "ai_calls_used",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "recipe_count": {
+ "name": "recipe_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_used_mb": {
+ "name": "storage_used_mb",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {
+ "user_usage_user_month_idx": {
+ "name": "user_usage_user_month_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "month",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_usage_user_id_users_id_fk": {
+ "name": "user_usage_user_id_users_id_fk",
+ "tableFrom": "user_usage",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_usage_user_month_uniq": {
+ "name": "user_usage_user_month_uniq",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id",
+ "month"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.webhook_deliveries": {
+ "name": "webhook_deliveries",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "webhook_id": {
+ "name": "webhook_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event": {
+ "name": "event",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status_code": {
+ "name": "status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "success": {
+ "name": "success",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "webhook_deliveries_webhook_id_webhooks_id_fk": {
+ "name": "webhook_deliveries_webhook_id_webhooks_id_fk",
+ "tableFrom": "webhook_deliveries",
+ "tableTo": "webhooks",
+ "columnsFrom": [
+ "webhook_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.webhooks": {
+ "name": "webhooks",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "events": {
+ "name": "events",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "secret": {
+ "name": "secret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "webhooks_user_id_users_id_fk": {
+ "name": "webhooks_user_id_users_id_fk",
+ "tableFrom": "webhooks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.conversation_reads": {
+ "name": "conversation_reads",
+ "schema": "",
+ "columns": {
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_read_at": {
+ "name": "last_read_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "conversation_reads_conversation_id_conversations_id_fk": {
+ "name": "conversation_reads_conversation_id_conversations_id_fk",
+ "tableFrom": "conversation_reads",
+ "tableTo": "conversations",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "conversation_reads_user_id_users_id_fk": {
+ "name": "conversation_reads_user_id_users_id_fk",
+ "tableFrom": "conversation_reads",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "conversation_reads_conversation_id_user_id_pk": {
+ "name": "conversation_reads_conversation_id_user_id_pk",
+ "columns": [
+ "conversation_id",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.conversations": {
+ "name": "conversations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_a_id": {
+ "name": "user_a_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_b_id": {
+ "name": "user_b_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "conversations_last_message_idx": {
+ "name": "conversations_last_message_idx",
+ "columns": [
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "conversations_user_a_id_users_id_fk": {
+ "name": "conversations_user_a_id_users_id_fk",
+ "tableFrom": "conversations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_a_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "conversations_user_b_id_users_id_fk": {
+ "name": "conversations_user_b_id_users_id_fk",
+ "tableFrom": "conversations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_b_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "conversations_pair_uniq": {
+ "name": "conversations_pair_uniq",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_a_id",
+ "user_b_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.messages": {
+ "name": "messages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sender_id": {
+ "name": "sender_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "messages_conversation_idx": {
+ "name": "messages_conversation_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "messages_conversation_id_conversations_id_fk": {
+ "name": "messages_conversation_id_conversations_id_fk",
+ "tableFrom": "messages",
+ "tableTo": "conversations",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "messages_sender_id_users_id_fk": {
+ "name": "messages_sender_id_users_id_fk",
+ "tableFrom": "messages",
+ "tableTo": "users",
+ "columnsFrom": [
+ "sender_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.processed_stripe_events": {
+ "name": "processed_stripe_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chat_messages": {
+ "name": "chat_messages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipe_id": {
+ "name": "recipe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "chat_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chat_messages_user_idx": {
+ "name": "chat_messages_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chat_messages_recipe_idx": {
+ "name": "chat_messages_recipe_idx",
+ "columns": [
+ {
+ "expression": "recipe_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chat_messages_user_id_users_id_fk": {
+ "name": "chat_messages_user_id_users_id_fk",
+ "tableFrom": "chat_messages",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chat_messages_recipe_id_recipes_id_fk": {
+ "name": "chat_messages_recipe_id_recipes_id_fk",
+ "tableFrom": "chat_messages",
+ "tableTo": "recipes",
+ "columnsFrom": [
+ "recipe_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.api_key_scope": {
+ "name": "api_key_scope",
+ "schema": "public",
+ "values": [
+ "full",
+ "read"
+ ]
+ },
+ "public.tier": {
+ "name": "tier",
+ "schema": "public",
+ "values": [
+ "free",
+ "pro"
+ ]
+ },
+ "public.unit_pref": {
+ "name": "unit_pref",
+ "schema": "public",
+ "values": [
+ "metric",
+ "imperial"
+ ]
+ },
+ "public.user_role": {
+ "name": "user_role",
+ "schema": "public",
+ "values": [
+ "user",
+ "moderator",
+ "admin"
+ ]
+ },
+ "public.difficulty": {
+ "name": "difficulty",
+ "schema": "public",
+ "values": [
+ "easy",
+ "medium",
+ "hard"
+ ]
+ },
+ "public.recipe_type": {
+ "name": "recipe_type",
+ "schema": "public",
+ "values": [
+ "dish",
+ "drink"
+ ]
+ },
+ "public.visibility": {
+ "name": "visibility",
+ "schema": "public",
+ "values": [
+ "private",
+ "unlisted",
+ "public"
+ ]
+ },
+ "public.collection_member_role": {
+ "name": "collection_member_role",
+ "schema": "public",
+ "values": [
+ "viewer",
+ "editor"
+ ]
+ },
+ "public.comment_reaction_type": {
+ "name": "comment_reaction_type",
+ "schema": "public",
+ "values": [
+ "like",
+ "love",
+ "laugh",
+ "wow",
+ "sad",
+ "fire"
+ ]
+ },
+ "public.notification_type": {
+ "name": "notification_type",
+ "schema": "public",
+ "values": [
+ "follow",
+ "comment",
+ "reply",
+ "reaction",
+ "rating",
+ "mention"
+ ]
+ },
+ "public.meal_type": {
+ "name": "meal_type",
+ "schema": "public",
+ "values": [
+ "breakfast",
+ "lunch",
+ "dinner",
+ "snack"
+ ]
+ },
+ "public.weekday": {
+ "name": "weekday",
+ "schema": "public",
+ "values": [
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+ "sun"
+ ]
+ },
+ "public.report_status": {
+ "name": "report_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "reviewed",
+ "dismissed"
+ ]
+ },
+ "public.report_target_type": {
+ "name": "report_target_type",
+ "schema": "public",
+ "values": [
+ "recipe",
+ "comment",
+ "user"
+ ]
+ },
+ "public.chat_role": {
+ "name": "chat_role",
+ "schema": "public",
+ "values": [
+ "user",
+ "assistant"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json
index b7a75b2..c17688a 100644
--- a/packages/db/src/migrations/meta/_journal.json
+++ b/packages/db/src/migrations/meta/_journal.json
@@ -274,6 +274,13 @@
"when": 1784014063004,
"tag": "0038_peaceful_norrin_radd",
"breakpoints": true
+ },
+ {
+ "idx": 39,
+ "version": "7",
+ "when": 1784030221865,
+ "tag": "0039_sudden_franklin_storm",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/db/src/schema/recipes.ts b/packages/db/src/schema/recipes.ts
index b385f1c..db09873 100644
--- a/packages/db/src/schema/recipes.ts
+++ b/packages/db/src/schema/recipes.ts
@@ -15,6 +15,7 @@ import { users } from "./users";
export const visibilityEnum = pgEnum("visibility", ["private", "unlisted", "public"]);
export const difficultyEnum = pgEnum("difficulty", ["easy", "medium", "hard"]);
+export const recipeTypeEnum = pgEnum("recipe_type", ["dish", "drink"]);
export type DietaryTags = {
vegan?: boolean;
@@ -32,6 +33,7 @@ export const recipes = pgTable("recipes", {
title: text("title").notNull(),
description: text("description"),
baseServings: integer("base_servings").notNull().default(4),
+ recipeType: recipeTypeEnum("recipe_type").notNull().default("dish"),
visibility: visibilityEnum("visibility").notNull().default("private"),
sourceUrl: text("source_url"),
aiGenerated: boolean("ai_generated").notNull().default(false),
@@ -53,6 +55,7 @@ export const recipes = pgTable("recipes", {
index("recipes_visibility_idx").on(t.visibility),
index("recipes_dietary_tags_gin").using("gin", t.dietaryTags),
index("recipes_batch_cook_idx").on(t.isBatchCook),
+ index("recipes_type_idx").on(t.recipeType),
]);
export const ingredients = pgTable("ingredients", {