diff --git a/client/Dockerfile b/client/Dockerfile index 47e0746a..d622caf0 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,19 +1,26 @@ -FROM node:18-alpine +FROM node:lts AS base +WORKDIR /app -# Set working directory -WORKDIR /opt/app +# By copying only the package.json and package-lock.json here, we ensure that the following `-deps` steps are independent of the source code. +# Therefore, the `-deps` steps will be skipped if only the source code changes. +COPY package.json package-lock.json ./ -# Copy package files -COPY package*.json ./ +FROM base AS prod-deps +RUN npm install --omit=dev -# Install dependencies -RUN npm ci +FROM base AS build-deps +RUN npm install -# Copy source code +FROM build-deps AS build COPY . . +RUN npm run build + +FROM base AS runtime +COPY --from=prod-deps /app/node_modules ./node_modules +COPY --from=build /app/dist ./dist + +ENV HOST=0.0.0.0 +ENV PORT=4321 -# Expose port EXPOSE 4321 - -# Start the development server -CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] \ No newline at end of file +CMD node ./dist/server/entry.mjs \ No newline at end of file diff --git a/client/astro.config.mjs b/client/astro.config.mjs index 508cbece..a49f256b 100644 --- a/client/astro.config.mjs +++ b/client/astro.config.mjs @@ -3,9 +3,16 @@ import { defineConfig } from 'astro/config'; import tailwindcss from '@tailwindcss/vite'; +import node from '@astrojs/node'; + // https://astro.build/config export default defineConfig({ + output: 'server', vite: { plugins: [tailwindcss()] - } + }, + + adapter: node({ + mode: 'standalone' + }) }); \ No newline at end of file diff --git a/client/docker-compose.yml b/client/docker-compose.yml new file mode 100644 index 00000000..2784cb24 --- /dev/null +++ b/client/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3.8' + +services: + client: + build: + context: . + dockerfile: Dockerfile + container_name: astro-client + restart: unless-stopped + ports: + - "4321:4321" + env_file: + - .env + environment: + NODE_ENV: ${NODE_ENV} + STRAPI_URL: ${STRAPI_URL} + volumes: + - ./src:/opt/app/src + - ./public:/opt/app/public + - ./astro.config.mjs:/opt/app/astro.config.mjs + - ./tsconfig.json:/opt/app/tsconfig.json + networks: + - strapi-network + +networks: + strapi-network: + external: true \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 12fc959d..cd6bedb4 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,6 +8,7 @@ "name": "client", "version": "0.0.1", "dependencies": { + "@astrojs/node": "^9.2.2", "@tailwindcss/vite": "^4.1.10", "@types/qs": "^6.14.0", "astro": "^5.7.12", @@ -70,6 +71,20 @@ "vfile": "^6.0.3" } }, + "node_modules/@astrojs/node": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-9.2.2.tgz", + "integrity": "sha512-PtLPuuojmcl9O3CEvXqL/D+wB4x5DlbrGOvP0MeTAh/VfKFprYAzgw1+45xsnTO+QvPWb26l1cT+ZQvvohmvMw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.6.1", + "send": "^1.2.0", + "server-destroy": "^1.0.1" + }, + "peerDependencies": { + "astro": "^5.3.0" + } + }, "node_modules/@astrojs/prism": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.2.0.tgz", @@ -1433,6 +1448,15 @@ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "license": "MIT" }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1532,12 +1556,27 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -1639,6 +1678,12 @@ "@esbuild/win32-x64": "0.25.4" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -1660,6 +1705,15 @@ "@types/estree": "^1.0.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -1728,6 +1782,15 @@ "unicode-trie": "^2.0.0" } }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2060,6 +2123,31 @@ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "license": "BSD-2-Clause" }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -2070,6 +2158,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/iron-webcrypto": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", @@ -3274,6 +3368,27 @@ ], "license": "MIT" }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -3435,6 +3550,18 @@ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", "license": "MIT" }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/oniguruma-parser": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", @@ -3645,6 +3772,15 @@ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", "license": "MIT" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -3942,6 +4078,40 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "license": "ISC" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/sharp": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", @@ -4117,6 +4287,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -4223,6 +4402,15 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", diff --git a/client/package.json b/client/package.json index 93ca0d6a..ddb9c089 100644 --- a/client/package.json +++ b/client/package.json @@ -9,6 +9,7 @@ "astro": "astro" }, "dependencies": { + "@astrojs/node": "^9.2.2", "@tailwindcss/vite": "^4.1.10", "@types/qs": "^6.14.0", "astro": "^5.7.12", diff --git a/client/src/collections/strapiPoleElementsLoader.mjs b/client/src/collections/strapiPoleElementsLoader.mjs deleted file mode 100644 index 44fac7e5..00000000 --- a/client/src/collections/strapiPoleElementsLoader.mjs +++ /dev/null @@ -1,87 +0,0 @@ -import { defineCollection, z } from "astro:content"; -import qs from "qs"; - -// Define a custom content collection that loads data from Strapi -const strapiPoleElementsLoader = defineCollection({ - // Async loader function that fetches data from Strapi API - loader: async () => { - // Get Strapi URL from environment variables or fallback to localhost - const BASE_URL = import.meta.env.STRAPI_URL || "http://localhost:1337"; - const path = "/api/elements"; - const url = new URL(path, BASE_URL); - - // Build query parameters using qs to populate cover image data - url.search = qs.stringify({ - populate: { - mainImage: { - fields: ["url", "alternativeText"], - }, - }, - }); - - try { - // Fetch articles from Strapi - const poleElementsData = await fetch(url.href); - - if (!poleElementsData.ok) { - throw new Error(`Failed to fetch data from Strapi: ${poleElementsData.status} ${poleElementsData.statusText}`); - } - - const response = await poleElementsData.json(); - const { data } = response; - - // Check if data is null or undefined - if (!data) { - throw new Error("No data received from Strapi API - the response was null or undefined"); - } - - // Ensure data is an array - const dataArray = Array.isArray(data) ? data : [data]; - - // Transform the API response into the desired data structure - return dataArray - .filter(item => item !== null && item !== undefined) // Filter out null/undefined items - .map((item) => ({ - id: item.id?.toString() || "", - name: item.name || "", - title: item.name || "", - description: item.description || "", - createdAt: item.createdAt || "", - updatedAt: item.updatedAt || "", - publishedAt: item.publishedAt || "", - mainImage: item.mainImage ? { - id: Number(item.mainImage.id) || 0, - documentId: item.mainImage.documentId || "", - url: item.mainImage.url || "", - alternativeText: item.mainImage.alternativeText || "", - } : { - id: 0, - documentId: "", - url: "", - alternativeText: "", - } - })); - } catch (error) { - console.error("Error loading pole elements from Strapi:", error); - return []; - } - }, - // Define the schema for type validation using Zod - schema: z.object({ - id: z.string(), - name: z.string(), - title: z.string(), - description: z.string(), - createdAt: z.string(), - updatedAt: z.string(), - publishedAt: z.string(), - mainImage: z.object({ - id: z.number(), - documentId: z.string(), - url: z.string(), - alternativeText: z.string(), - }), - }), -}); - -export default strapiPoleElementsLoader; \ No newline at end of file diff --git a/client/src/components/PoleElementsList.astro b/client/src/components/PoleElementsList.astro index ed37698a..10a77a76 100644 --- a/client/src/components/PoleElementsList.astro +++ b/client/src/components/PoleElementsList.astro @@ -1,7 +1,7 @@ --- // Import necessary components and utilities import MarkdownComponent from "./MardownContent.astro"; -import { getStrapiMedia } from "../utils/strapi"; +import { getStrapiMedia } from "../lib/strapi"; import { getStrapiBaseUrl } from "../config/strapi"; import type { HTMLAttributes } from "astro/types"; @@ -18,19 +18,20 @@ const BASE_URL = getStrapiBaseUrl();
{ elements.map((poleElement) => ( + console.log(poleElement),
{poleElement.data.mainImage.alternativeText}

- {poleElement.data.name} + {poleElement.name}

diff --git a/client/src/content.config.mjs b/client/src/content.config.mjs deleted file mode 100644 index 93c2d8dc..00000000 --- a/client/src/content.config.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import strapiPoleElementsLoader from "./collections/strapiPoleElementsLoader.mjs"; - -// Export the collection for use in Astro pages -export const collections = { - poleElements: strapiPoleElementsLoader, -}; \ No newline at end of file diff --git a/client/src/env.d.ts b/client/src/env.d.ts new file mode 100644 index 00000000..7d4b2669 --- /dev/null +++ b/client/src/env.d.ts @@ -0,0 +1,3 @@ +interface ImportMetaEnv { + readonly STRAPI_URL: string; + } \ No newline at end of file diff --git a/client/src/interfaces/poleElement.ts b/client/src/interfaces/poleElement.ts new file mode 100644 index 00000000..c86fd27b --- /dev/null +++ b/client/src/interfaces/poleElement.ts @@ -0,0 +1,15 @@ +export default interface PoleElement { + id: string; + name: string; + title: string; + description: string; + createdAt: string; + updatedAt: string; + publishedAt: string; + mainImage: { + id: number; + documentId: string; + url: string; + alternativeText: string; + }; +} \ No newline at end of file diff --git a/client/src/lib/strapi.ts b/client/src/lib/strapi.ts new file mode 100644 index 00000000..e5526eda --- /dev/null +++ b/client/src/lib/strapi.ts @@ -0,0 +1,68 @@ +interface Props { + endpoint: string; + query?: Record; + wrappedByKey?: string; + wrappedByList?: boolean; + } + + /** + * Fetches data from the Strapi API + * @param endpoint - The endpoint to fetch from + * @param query - The query parameters to add to the url + * @param wrappedByKey - The key to unwrap the response from + * @param wrappedByList - If the response is a list, unwrap it + * @returns + */ + export default async function fetchApi({ + endpoint, + query, + wrappedByKey, + wrappedByList, + }: Props): Promise { + if (endpoint.startsWith('/')) { + endpoint = endpoint.slice(1); + } + const strapiUrl = import.meta.env.STRAPI_URL || process.env.STRAPI_URL || "http://localhost:1337"; + console.log(strapiUrl); + const url = new URL(`${strapiUrl}/api/${endpoint}`); + + if (query) { + Object.entries(query).forEach(([key, value]) => { + url.searchParams.append(key, value); + }); + } + + try { + const res = await fetch(url.toString()); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + let data = await res.json(); + + if (wrappedByKey) { + data = data[wrappedByKey]; + } + + if (wrappedByList) { + data = data[0]; + } + + return data as T; + } catch (error) { + console.error('Error fetching from Strapi API:', error); + throw error; + } + } + +// Helper function to handle media URLs from Strapi +export function getStrapiMedia(url: string | null, baseUrl: string) { + if (url == null) return null; + // Return as-is if it's a data URL (base64) + if (url.startsWith("data:")) return url; + // Return as-is if it's an absolute URL + if (url.startsWith("http") || url.startsWith("//")) return url; + // Prepend baseUrl for relative URLs + return `${baseUrl}${url}`; +} \ No newline at end of file diff --git a/client/src/pages/elements.astro b/client/src/pages/elements.astro index b5fd05aa..47a20df9 100644 --- a/client/src/pages/elements.astro +++ b/client/src/pages/elements.astro @@ -1,14 +1,15 @@ --- // Import necessary components and utilities import Layout from "../layouts/Layout.astro"; -import { getCollection } from "astro:content"; import PoleElementsList from "../components/PoleElementsList.astro"; -import { getStrapiBaseUrl } from "../config/strapi"; -// Fetch all posts from Strapi using Astro's content collection -const strapiPoleElements = await getCollection("poleElements"); -// Get Strapi URL from global config -const BASE_URL = getStrapiBaseUrl(); +import fetchApi from '../lib/strapi'; +import type PoleElement from "../interfaces/poleElement"; + +const strapiPoleElements = await fetchApi({ + endpoint: 'elements?populate=*', // the content type to fetch + wrappedByKey: 'data', // the key to unwrap the response +}); --- diff --git a/client/src/pages/elements/[...id].astro b/client/src/pages/elements/[...id].astro index 5679c471..c4ba6ca9 100644 --- a/client/src/pages/elements/[...id].astro +++ b/client/src/pages/elements/[...id].astro @@ -1,20 +1,30 @@ --- -import { getCollection } from 'astro:content'; import MardownContent from '../../components/MardownContent.astro'; import Layout from '../../layouts/Layout.astro'; -// 1. Genera una nueva ruta para cada entrada de colección -export async function getStaticPaths() { - const poleElements = await getCollection('poleElements'); - return poleElements.map(entry => ({ - params: { id: entry.id }, props: { entry }, - })); -} -// 2. Para tu plantilla, puedes obtener la entrada directamente de la prop -const { entry } = Astro.props; ---- +import fetchApi from '../../lib/strapi'; +import type PoleElement from '../../interfaces/poleElement'; - -

{entry.data.title}

- +const { id } = Astro.params; + +let poleElement: PoleElement; + +try { + poleElement = await fetchApi({ + endpoint: 'elements', + wrappedByKey: 'data', + wrappedByList: true, + query: { + 'populate': '*', + 'filters[id][$eq]': id || '', + }, + }); +} catch (error) { + return Astro.redirect('/404'); +} + +--- + +

{poleElement.name}

+ {poleElement.description && }
\ No newline at end of file diff --git a/client/src/utils/strapi.ts b/client/src/utils/strapi.ts deleted file mode 100644 index 31e67f67..00000000 --- a/client/src/utils/strapi.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Helper function to handle media URLs from Strapi -export function getStrapiMedia(url: string | null, baseUrl: string) { - if (url == null) return null; - // Return as-is if it's a data URL (base64) - if (url.startsWith("data:")) return url; - // Return as-is if it's an absolute URL - if (url.startsWith("http") || url.startsWith("//")) return url; - // Prepend baseUrl for relative URLs - return `${baseUrl}${url}`; -} \ No newline at end of file diff --git a/docker-compose.env b/docker-compose.env index 37e72d77..2b34b194 100644 --- a/docker-compose.env +++ b/docker-compose.env @@ -27,4 +27,4 @@ PORT=1337 # Client Configuration CLIENT_NODE_ENV=development -STRAPI_URL=http://strapi:1337 \ No newline at end of file +STRAPI_URL=http://localhost:1337 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e511c545..ce29ac98 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: ports: - "1337:1337" env_file: - - docker-compose.env + - .env environment: DATABASE_CLIENT: ${DATABASE_CLIENT} DATABASE_HOST: ${DATABASE_HOST} @@ -29,31 +29,14 @@ services: - ./server/public/uploads:/opt/app/public/uploads - ./server/.tmp:/opt/app/.tmp depends_on: - - postgres - networks: - - strapi-network - - client: - build: - context: ./client - dockerfile: Dockerfile - container_name: astro-client - restart: unless-stopped - ports: - - "4321:4321" - env_file: - - docker-compose.env - environment: - NODE_ENV: ${CLIENT_NODE_ENV} - STRAPI_URL: ${STRAPI_URL} - volumes: - - ./client/src:/opt/app/src - - ./client/public:/opt/app/public - - ./client/astro.config.mjs:/opt/app/astro.config.mjs - - ./client/tsconfig.json:/opt/app/tsconfig.json - depends_on: - - strapi - - postgres + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "nc", "-z", "localhost", "1337"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s networks: - strapi-network @@ -69,8 +52,12 @@ services: POSTGRES_PASSWORD: ${DATABASE_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data - ports: - - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s networks: - strapi-network