From f7e24657f55722f2d9d704ac00df1846fbf4bb3c Mon Sep 17 00:00:00 2001 From: june Date: Fri, 15 Aug 2025 18:02:13 +1200 Subject: [PATCH 1/9] Initial schema creation --- src/data/schema.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/data/schema.ts diff --git a/src/data/schema.ts b/src/data/schema.ts new file mode 100644 index 0000000..97e3c6f --- /dev/null +++ b/src/data/schema.ts @@ -0,0 +1,40 @@ +// Base PB type +export interface BaseRecord { + id: string, + created: string, + updated: string +} + +export interface Ingredient extends BaseRecord { + quantity: string, + unit: string, + name: string +} + +export interface Step extends BaseRecord { + index: number, + instruction: string, + ingredients?: Ingredient[] +} + +export interface Tag extends BaseRecord { + name: string +} + +// not sure Image is the best type cos it might be quite heavy to get all the fields every time but +// it is here in case it is (a good idea) +export interface Image extends BaseRecord { + url: string +} + +export interface Recipe extends BaseRecord { + name: string, + description?: string, + servings?: number, + images?: string[], // image IDs + steps: Step[], + tags?: Tag[] +} + + + -- 2.47.2 From 2e54690ee426a18806808e9ad24e0998f5b6cb4a Mon Sep 17 00:00:00 2001 From: june Date: Fri, 15 Aug 2025 19:01:47 +1200 Subject: [PATCH 2/9] Starting basic API client --- src/data/pocketbase.ts | 34 +++++++++++++++++++++++++++++-- src/data/schema.ts | 9 ++++++-- src/pages/recipe/[recipeid].astro | 6 +++++- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/data/pocketbase.ts b/src/data/pocketbase.ts index bc56688..1bae413 100644 --- a/src/data/pocketbase.ts +++ b/src/data/pocketbase.ts @@ -1,4 +1,11 @@ -import Pocketbase from "pocketbase" +import Pocketbase, { type RecordListOptions } from "pocketbase" +import { + type Recipe, + type Ingredient, + type Step, + type Tag, + Collection +} from './schema' const client = new Pocketbase("http://localhost:4321") client.autoCancellation(false) @@ -9,4 +16,27 @@ client.files.getRelativeURL = (record: { [key: string]: any; }, filename: string return res.substring(21) } -export default client; \ No newline at end of file +export default client; + +class APIClient { + client: Pocketbase + + constructor() { + this.client = new Pocketbase("http://localhost:4321") + this.client.autoCancellation(false) + } + + async getRecipes(page: number, perPage: number = 20, options: RecordListOptions) { + return await this.client.collection(Collection.RECIPES).getList(page, perPage, options) + } + + async getAllRecipes() { + return await this.client.collection(Collection.RECIPES).getFullList({ expand: "steps,ingredients,tags" }) + } + + async getRecipe(id: string) { + return await this.client.collection(Collection.RECIPES).getOne(id, { expand: 'ingredients,tags,steps', }) + } +} + +export const client2 = new APIClient() \ No newline at end of file diff --git a/src/data/schema.ts b/src/data/schema.ts index 97e3c6f..88c76e5 100644 --- a/src/data/schema.ts +++ b/src/data/schema.ts @@ -36,5 +36,10 @@ export interface Recipe extends BaseRecord { tags?: Tag[] } - - +export const Collection = { + RECIPES: 'recipes', + STEPS: 'steps', + INGREDIENTS: 'ingredients', + TAGS: 'tags', + IMAGES: 'images' +} diff --git a/src/pages/recipe/[recipeid].astro b/src/pages/recipe/[recipeid].astro index d123a91..892ccf2 100644 --- a/src/pages/recipe/[recipeid].astro +++ b/src/pages/recipe/[recipeid].astro @@ -1,5 +1,6 @@ --- -import client from "@/data/pocketbase"; +import client, { client2 } from "@/data/pocketbase"; + import SiteLayout from "@/layouts/base"; import ImageCarousel from "@/components/Detail/ImageCarousel"; import IngredientTableView from "@/components/Detail/IngredientTableView"; @@ -8,6 +9,9 @@ import InfoView from "@/components/Detail/InfoView"; const { recipeid } = Astro.params; +const re2 = await client2.getRecipe(recipeid as string) +console.log(re2) + const re = await client.collection("recipes").getOne(recipeid ?? "0"); const stepIds = re.steps -- 2.47.2 From b3f92210faf252d8b2eabcd3d323087d8eaf5bea Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 11:01:56 +1200 Subject: [PATCH 3/9] Proxy forwards Query parameters too --- src/pages/api/[...proxy].ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/api/[...proxy].ts b/src/pages/api/[...proxy].ts index c831988..9574877 100644 --- a/src/pages/api/[...proxy].ts +++ b/src/pages/api/[...proxy].ts @@ -5,8 +5,7 @@ import type { APIRoute } from "astro"; const getProxyUrl = (request: Request) => { const proxyUrl = new URL(import.meta.env.PUBLIC_PB_URL); const requestUrl = new URL(request.url); - - return new URL(requestUrl.pathname, proxyUrl); + return new URL(requestUrl.pathname + requestUrl.search, proxyUrl); }; export const ALL: APIRoute = async ({ request }) => { -- 2.47.2 From b514655b5cb21e688bb084de45661e76991d9afe Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 12:22:10 +1200 Subject: [PATCH 4/9] progressively updating to use api --- src/data/pocketbase.ts | 4 ++-- src/data/schema.ts | 14 +++++++++++--- src/pages/recipe/[recipeid].astro | 25 +++---------------------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/data/pocketbase.ts b/src/data/pocketbase.ts index 1bae413..acb07d9 100644 --- a/src/data/pocketbase.ts +++ b/src/data/pocketbase.ts @@ -26,7 +26,7 @@ class APIClient { this.client.autoCancellation(false) } - async getRecipes(page: number, perPage: number = 20, options: RecordListOptions) { + async getRecipesPaginated(page: number, perPage: number = 30, options: RecordListOptions) { return await this.client.collection(Collection.RECIPES).getList(page, perPage, options) } @@ -35,7 +35,7 @@ class APIClient { } async getRecipe(id: string) { - return await this.client.collection(Collection.RECIPES).getOne(id, { expand: 'ingredients,tags,steps', }) + return await this.client.collection(Collection.RECIPES).getOne(id, { expand: 'ingredients,tags,steps' }) } } diff --git a/src/data/schema.ts b/src/data/schema.ts index 88c76e5..efaab1c 100644 --- a/src/data/schema.ts +++ b/src/data/schema.ts @@ -24,7 +24,7 @@ export interface Tag extends BaseRecord { // not sure Image is the best type cos it might be quite heavy to get all the fields every time but // it is here in case it is (a good idea) export interface Image extends BaseRecord { - url: string + id: string } export interface Recipe extends BaseRecord { @@ -32,8 +32,16 @@ export interface Recipe extends BaseRecord { description?: string, servings?: number, images?: string[], // image IDs - steps: Step[], - tags?: Tag[] + ingredients: string[] + steps: string[], + tags?: string[] + + expand: { + images?: Image[], // image IDs, + ingredients: Ingredient[] + steps: Step[], + tags?: Tag[] + } } export const Collection = { diff --git a/src/pages/recipe/[recipeid].astro b/src/pages/recipe/[recipeid].astro index 892ccf2..d422845 100644 --- a/src/pages/recipe/[recipeid].astro +++ b/src/pages/recipe/[recipeid].astro @@ -9,26 +9,7 @@ import InfoView from "@/components/Detail/InfoView"; const { recipeid } = Astro.params; -const re2 = await client2.getRecipe(recipeid as string) -console.log(re2) - -const re = await client.collection("recipes").getOne(recipeid ?? "0"); - -const stepIds = re.steps - -let steps = await Promise.all( - stepIds.map(async s => - await client.collection("steps").getOne(s) - ) -) - -steps = steps.sort((a, b) => a.index - b.index); - -const ingredients = await Promise.all( - re.ingredients.map(async s => - await client.collection("ingredients").getOne(s) - ) -) +const re = await client2.getRecipe(recipeid as string) --- @@ -41,12 +22,12 @@ const ingredients = await Promise.all(

Ingredients

- +
- +
-- 2.47.2 From cf44f19310295570ebcedf71e0ae4623786b734e Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 12:28:42 +1200 Subject: [PATCH 5/9] made new client the default export --- src/data/pocketbase.ts | 16 ++++++++-------- src/pages/recipe/[recipeid].astro | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/data/pocketbase.ts b/src/data/pocketbase.ts index acb07d9..7fe09de 100644 --- a/src/data/pocketbase.ts +++ b/src/data/pocketbase.ts @@ -7,16 +7,16 @@ import { Collection } from './schema' -const client = new Pocketbase("http://localhost:4321") -client.autoCancellation(false) +const raw_client = new Pocketbase("http://localhost:4321") +raw_client.autoCancellation(false) // Return a relative url for file instead of full path including localhost, which breaks external access -client.files.getRelativeURL = (record: { [key: string]: any; }, filename: string, queryParams?: FileOptions | undefined) => { - const res = client.files.getURL(record, filename) +raw_client.files.getRelativeURL = (record: { [key: string]: any; }, filename: string, queryParams?: FileOptions | undefined) => { + const res = raw_client.files.getURL(record, filename) return res.substring(21) } -export default client; +export { raw_client }; class APIClient { client: Pocketbase @@ -26,7 +26,7 @@ class APIClient { this.client.autoCancellation(false) } - async getRecipesPaginated(page: number, perPage: number = 30, options: RecordListOptions) { + async getRecipesPage(page: number, perPage: number = 30, options: RecordListOptions) { return await this.client.collection(Collection.RECIPES).getList(page, perPage, options) } @@ -38,5 +38,5 @@ class APIClient { return await this.client.collection(Collection.RECIPES).getOne(id, { expand: 'ingredients,tags,steps' }) } } - -export const client2 = new APIClient() \ No newline at end of file +const client = new APIClient() +export default client; \ No newline at end of file diff --git a/src/pages/recipe/[recipeid].astro b/src/pages/recipe/[recipeid].astro index d422845..ffe0c54 100644 --- a/src/pages/recipe/[recipeid].astro +++ b/src/pages/recipe/[recipeid].astro @@ -1,5 +1,5 @@ --- -import client, { client2 } from "@/data/pocketbase"; +import client from "@/data/pocketbase"; import SiteLayout from "@/layouts/base"; import ImageCarousel from "@/components/Detail/ImageCarousel"; @@ -9,7 +9,7 @@ import InfoView from "@/components/Detail/InfoView"; const { recipeid } = Astro.params; -const re = await client2.getRecipe(recipeid as string) +const re = await client.getRecipe(recipeid as string) --- -- 2.47.2 From b2c4b6e765bba8c34c65a7365e4288ddfbfb8452 Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 12:54:15 +1200 Subject: [PATCH 6/9] Got the detail view working again --- src/components/Card/TagRow.astro | 14 ++----------- src/components/Detail/ImageCarousel.astro | 12 +---------- src/components/Detail/InfoView.astro | 2 +- .../Detail/StepIngredientSideView.astro | 5 +---- src/components/Detail/StepView.astro | 2 +- src/data/pocketbase.ts | 20 ++++++++++++++++++- src/data/schema.ts | 2 ++ src/pages/recipe/[recipeid].astro | 2 +- 8 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/components/Card/TagRow.astro b/src/components/Card/TagRow.astro index af56aec..28ec731 100644 --- a/src/components/Card/TagRow.astro +++ b/src/components/Card/TagRow.astro @@ -2,21 +2,11 @@ import client from "../../data/pocketbase" interface Props { - tagIds: string[] + tags: string[] } -const { tagIds } = Astro.props +const { tags } = Astro.props -const tags = tagIds && tagIds.length > 0 - ? await Promise.all(tagIds.map(async (tagId: string) => { - try { - const tagData = await client.collection("tags").getOne(tagId) - return { name: tagData.name, id: tagId } - } catch (error) { - return null - } - })) - : [] ---
diff --git a/src/components/Detail/ImageCarousel.astro b/src/components/Detail/ImageCarousel.astro index bbdc8c6..3e5f90d 100644 --- a/src/components/Detail/ImageCarousel.astro +++ b/src/components/Detail/ImageCarousel.astro @@ -1,17 +1,7 @@ --- import client from "@/data/pocketbase"; const { class: className, recipe } = Astro.props - -async function getLink(img: string) { - const record = await client.collection("images").getOne(img) - const link = await client.files.getRelativeURL(record, record.image) - return link -} - -// Use Promise.all to wait for all async operations to complete -const links = await Promise.all( - recipe.images.map((img: string) => getLink(img)) -) +const links = await client.getRecipeImages(recipe as string) --- diff --git a/src/components/Detail/InfoView.astro b/src/components/Detail/InfoView.astro index 2516136..4bde49e 100644 --- a/src/components/Detail/InfoView.astro +++ b/src/components/Detail/InfoView.astro @@ -26,4 +26,4 @@ const waitTime = formatTime(re.waittime) {waitTime && (

{waitTime} Wait

)} {re.rating !== 0 && (

{re.rating}

/10

)}
- \ No newline at end of file + \ No newline at end of file diff --git a/src/components/Detail/StepIngredientSideView.astro b/src/components/Detail/StepIngredientSideView.astro index b4cdc49..325890d 100644 --- a/src/components/Detail/StepIngredientSideView.astro +++ b/src/components/Detail/StepIngredientSideView.astro @@ -3,13 +3,10 @@ import client from "@/data/pocketbase"; const { ingredients, class: className } = Astro.props; -const ings = await Promise.all( - ingredients.map(async i => await client.collection("ingredients").getOne(i)) -) ---
- {ings.map(i => ( + {ingredients.map(i => (

• {i.quantity} {i.unit || " "} {i.name}

diff --git a/src/components/Detail/StepView.astro b/src/components/Detail/StepView.astro index 1860721..ef41ea5 100644 --- a/src/components/Detail/StepView.astro +++ b/src/components/Detail/StepView.astro @@ -16,7 +16,7 @@ const { steps, class: className } = Astro.props

{s.instruction}

{s.ingredients && s.ingredients.length > 0 && (
- +
)}
diff --git a/src/data/pocketbase.ts b/src/data/pocketbase.ts index 7fe09de..d2b63ab 100644 --- a/src/data/pocketbase.ts +++ b/src/data/pocketbase.ts @@ -35,8 +35,26 @@ class APIClient { } async getRecipe(id: string) { - return await this.client.collection(Collection.RECIPES).getOne(id, { expand: 'ingredients,tags,steps' }) + return await this.client.collection(Collection.RECIPES).getOne(id, { expand: 'ingredients,tags,steps,images,steps.ingredients' }) } + + // IMAGE + async getImageURL(imgID: string, relative: boolean = true) { + const record = await this.client.collection("images").getOne(imgID) + const res = this.client.files.getURL(record, record.image) + return relative ? res.substring(21) : res + } + + async getRecipeImages(recipeID: string) { + const re = await this.getRecipe(recipeID) + const imgIDs = re.images ?? [] + + const urls = Promise.all( + imgIDs.map(img => this.getImageURL(img)) + ) + return urls + } + } const client = new APIClient() export default client; \ No newline at end of file diff --git a/src/data/schema.ts b/src/data/schema.ts index efaab1c..e2b49f3 100644 --- a/src/data/schema.ts +++ b/src/data/schema.ts @@ -25,6 +25,8 @@ export interface Tag extends BaseRecord { // it is here in case it is (a good idea) export interface Image extends BaseRecord { id: string + filename: string + url?: string } export interface Recipe extends BaseRecord { diff --git a/src/pages/recipe/[recipeid].astro b/src/pages/recipe/[recipeid].astro index ffe0c54..1b938d1 100644 --- a/src/pages/recipe/[recipeid].astro +++ b/src/pages/recipe/[recipeid].astro @@ -15,7 +15,7 @@ const re = await client.getRecipe(recipeid as string)
- +

{re.name}

-- 2.47.2 From ba237d645e92e095434bce46c8f71e6d80a63e53 Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 13:07:53 +1200 Subject: [PATCH 7/9] fixed tag row being a nuisance --- src/components/Card/OverviewCard.astro | 5 ++--- src/components/Card/TagRow.astro | 9 +-------- src/data/pocketbase.ts | 2 +- src/pages/index.astro | 4 ++-- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/components/Card/OverviewCard.astro b/src/components/Card/OverviewCard.astro index 8a73c9e..bd226c2 100644 --- a/src/components/Card/OverviewCard.astro +++ b/src/components/Card/OverviewCard.astro @@ -4,8 +4,7 @@ import TagRow from "./TagRow.astro" const { recipe } = Astro.props; -const headerImage = await client.collection("images").getOne(recipe.images[0]) -const image = await client.files.getRelativeURL(headerImage, headerImage.image) +const image = (await client.getRecipeImages(recipe.id))[0] ---
@@ -19,7 +18,7 @@ const image = await client.files.getRelativeURL(headerImage, headerImage.image)
- +
diff --git a/src/components/Card/TagRow.astro b/src/components/Card/TagRow.astro index 28ec731..e1a644e 100644 --- a/src/components/Card/TagRow.astro +++ b/src/components/Card/TagRow.astro @@ -1,17 +1,10 @@ --- -import client from "../../data/pocketbase" - -interface Props { - tags: string[] -} - const { tags } = Astro.props - ---
{ - tags.map(tag => ( + (tags ?? []).map(tag => ( (Collection.RECIPES).getFullList({ expand: "steps,ingredients,tags" }) + return await this.client.collection(Collection.RECIPES).getFullList({ expand: 'ingredients,tags,steps,images,steps.ingredients' }) } async getRecipe(id: string) { diff --git a/src/pages/index.astro b/src/pages/index.astro index bf1ef86..592c409 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -3,7 +3,7 @@ import PageLayout from "@/layouts/base" import client from "@/data/pocketbase" import OverviewCard from "@/components/Card/OverviewCard" -const recipies = await client.collection("recipes").getFullList() +const recipes = await client.getAllRecipes() --- @@ -11,7 +11,7 @@ const recipies = await client.collection("recipes").getFullList()
{ - recipies.map(r => ( + recipes.map(r => ( )) } -- 2.47.2 From 914633a79301e35aefd2e8ccaa63a4e1d890a9ac Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 13:09:13 +1200 Subject: [PATCH 8/9] fixed the rest of it too --- src/pages/recipe/[recipeid].astro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/recipe/[recipeid].astro b/src/pages/recipe/[recipeid].astro index 1b938d1..d5ce764 100644 --- a/src/pages/recipe/[recipeid].astro +++ b/src/pages/recipe/[recipeid].astro @@ -22,12 +22,12 @@ const re = await client.getRecipe(recipeid as string)

Ingredients

- +
- +
-- 2.47.2 From 592d55dcb6f6d4079dd37ddebb73c02df2d67529 Mon Sep 17 00:00:00 2001 From: june Date: Sat, 16 Aug 2025 13:10:25 +1200 Subject: [PATCH 9/9] remove old client --- src/data/pocketbase.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/data/pocketbase.ts b/src/data/pocketbase.ts index bab8b9c..ef6d8de 100644 --- a/src/data/pocketbase.ts +++ b/src/data/pocketbase.ts @@ -7,17 +7,6 @@ import { Collection } from './schema' -const raw_client = new Pocketbase("http://localhost:4321") -raw_client.autoCancellation(false) - -// Return a relative url for file instead of full path including localhost, which breaks external access -raw_client.files.getRelativeURL = (record: { [key: string]: any; }, filename: string, queryParams?: FileOptions | undefined) => { - const res = raw_client.files.getURL(record, filename) - return res.substring(21) -} - -export { raw_client }; - class APIClient { client: Pocketbase -- 2.47.2