From 2b2bbcb1439baca9ae5b17746fbbf078777e42d4 Mon Sep 17 00:00:00 2001 From: jon r Date: Tue, 2 Jul 2024 23:27:11 +0200 Subject: [PATCH 1/6] feat(frontend): make application domain configurable with FRONTEND_URL --- frontend/src/app/[id]/page.tsx | 5 +++-- frontend/src/app/layout.tsx | 3 ++- .../CreateForm/components/EventInfo/EventInfo.tsx | 5 +++-- frontend/src/config/app.js | 7 +++++++ 4 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 frontend/src/config/app.js diff --git a/frontend/src/app/[id]/page.tsx b/frontend/src/app/[id]/page.tsx index deae4506..8c1346ea 100644 --- a/frontend/src/app/[id]/page.tsx +++ b/frontend/src/app/[id]/page.tsx @@ -7,6 +7,7 @@ import { Temporal } from '@js-temporal/polyfill' import Content from '/src/components/Content/Content' import Copyable from '/src/components/Copyable/Copyable' import { getEvent } from '/src/config/api' +import { AppBase } from '/src/config/app' import { useTranslation } from '/src/i18n/server' import { makeClass, relativeTimeFormat } from '/src/utils' @@ -49,10 +50,10 @@ const Page = async ({ params }: PageProps) => { >{t('common:created', { date: relativeTimeFormat(Temporal.Instant.fromEpochSeconds(event.created_at), i18n.language) })} - {`https://starbestfit.com/${event.id}`} + {`${AppBase}${event.id}`}

- ___ + ___

diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 8916beb0..930a7bc8 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -3,6 +3,7 @@ import { Karla } from 'next/font/google' import Egg from '/src/components/Egg/Egg' import Settings from '/src/components/Settings/Settings' +import { AppBase } from '/src/config/app' import TranslateDialog from '/src/components/TranslateDialog/TranslateDialog' import { fallbackLng } from '/src/i18n/options' import { useTranslation } from '/src/i18n/server' @@ -12,7 +13,7 @@ import './global.css' const karla = Karla({ subsets: ['latin'] }) export const metadata: Metadata = { - metadataBase: new URL('https://starbestfit.com'), + metadataBase: AppBase, title: { absolute: 'Star Fit', template: '%s - Star Fit', diff --git a/frontend/src/components/CreateForm/components/EventInfo/EventInfo.tsx b/frontend/src/components/CreateForm/components/EventInfo/EventInfo.tsx index 1eb202f4..bc754c1e 100644 --- a/frontend/src/components/CreateForm/components/EventInfo/EventInfo.tsx +++ b/frontend/src/components/CreateForm/components/EventInfo/EventInfo.tsx @@ -2,6 +2,7 @@ import { Trans } from 'react-i18next/TransWithoutContext' import Copyable from '/src/components/Copyable/Copyable' import { EventResponse } from '/src/config/api' +import { AppBase } from '/src/config/app' import { useTranslation } from '/src/i18n/client' import styles from './EventInfo.module.scss' @@ -16,10 +17,10 @@ const EventInfo = ({ event }: EventInfoProps) => { return

{event.name}

- {`https://starbestfit.com/${event.id}`} + {`${AppBase}${event.id}`}

- ___ + ___

} diff --git a/frontend/src/config/app.js b/frontend/src/config/app.js new file mode 100644 index 00000000..a476e617 --- /dev/null +++ b/frontend/src/config/app.js @@ -0,0 +1,7 @@ +if (typeof window === 'undefined') { + if (process.env.FRONTEND_URL === undefined) { + throw new Error('Expected Frontend URL environment variable') + } +} + +export const AppBase = new URL(process.env.FRONTEND_URL || window.location.protocol + "//" + window.location.host) From bc8cb6b7d1f7659719112d8b42fa6d5e379fa331 Mon Sep 17 00:00:00 2001 From: jon r Date: Tue, 2 Jul 2024 23:28:18 +0200 Subject: [PATCH 2/6] feat(frontend): add Next.js Docker container image build files --- frontend/.dockerignore | 3 +++ frontend/Dockerfile | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 00000000..2003cfba --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,3 @@ +node_modules +npm-debug.log +.env.local diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 00000000..5c77c961 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,33 @@ +FROM node:20-alpine AS base + +# Step 1. Rebuild the source code only when needed +FROM base AS builder + +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock ./ +# Omit --production flag for TypeScript devDependencies +RUN if [ -f yarn.lock ]; then yarn install --immutable; fi + +COPY src ./src +COPY public ./public +COPY next-env.d.ts . +COPY tsconfig.json . + +# Environment variables must be present at build time +# https://github.com/vercel/next.js/discussions/14030 +ARG NEXT_PUBLIC_API_URL +ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} +ARG FRONTEND_URL +ENV FRONTEND_URL=${FRONTEND_URL} + +# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry +# Uncomment the following line to disable telemetry at build time +ENV NEXT_TELEMETRY_DISABLED 1 + +# Build Next.js based on the preferred package manager +RUN if [ -f yarn.lock ]; then yarn build; fi + +ENTRYPOINT ["yarn"] +CMD ["start"] From 827149086dbd04fa02b79e453f8f55b599e5103b Mon Sep 17 00:00:00 2001 From: jon r Date: Tue, 2 Jul 2024 23:28:46 +0200 Subject: [PATCH 3/6] feat(compose): add Docker Compose example --- compose/.env.example | 12 +++++++ compose/.gitignore | 1 + compose/README.md | 32 +++++++++++++++++++ compose/compose.yml | 76 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 compose/.env.example create mode 100644 compose/.gitignore create mode 100644 compose/README.md create mode 100644 compose/compose.yml diff --git a/compose/.env.example b/compose/.env.example new file mode 100644 index 00000000..32aa2ee4 --- /dev/null +++ b/compose/.env.example @@ -0,0 +1,12 @@ +FQDN=localhost.localdomain +NEXT_PUBLIC_API_URL=http://api.${FQDN}:3000 +FRONTEND_URL=http://${FQDN}:3001 + +POSTGRES_HOST=database +POSTGRES_USER=localdomain-localhost +POSTGRES_DB=${POSTGRES_USER} +POSTGRES_PASSWORD= + +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DB} + +CRON_KEY= diff --git a/compose/.gitignore b/compose/.gitignore new file mode 100644 index 00000000..4c49bd78 --- /dev/null +++ b/compose/.gitignore @@ -0,0 +1 @@ +.env diff --git a/compose/README.md b/compose/README.md new file mode 100644 index 00000000..393c959c --- /dev/null +++ b/compose/README.md @@ -0,0 +1,32 @@ +# Compose + +This is an example development environment with Docker Compose. + +First copy the example environment and adapt the values to your needs. + +```sh +cp .env.example .env +``` + +You can then build the application: + +```sh +docker compose build api frontend +``` + +To start the application, pull preexisting images and run the containers. + +```sh +docker compose pull +docker compose up -d +``` + +This makes the application available at http://localhost.localdomain:3001/. + +The API will be listening on http://api.localhost.localdomain:3000. + +This configuration is prepared to be used for a production instance behind a Traefik reverse proxy. + +1. Remove the `ports:` declarations in `compose.yml`. +2. Uncomment the `web` network and the `labels:`. +3. Uncomment the `CRON_KEY` variable. diff --git a/compose/compose.yml b/compose/compose.yml new file mode 100644 index 00000000..85a6a139 --- /dev/null +++ b/compose/compose.yml @@ -0,0 +1,76 @@ +volumes: + postgres: + +networks: + internal: +# web: +# external: true + +services: + frontend: + image: ghcr.io/drinkablebreeze/starbestfit-frontend + ports: + - "3001:3000" + dns: "127.0.0.11" + dns_search: "*" + build: + context: ../frontend/ + args: + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} + FRONTEND_URL: ${FRONTEND_URL} + #NEXT_PUBLIC_GOOGLE_CLIENT_ID + #NEXT_PUBLIC_GOOGLE_API_KEY + environment: + - NEXT_PUBLIC_API_URL + - FRONTEND_URL + - NODE_ENV=production + #- NEXT_PUBLIC_GOOGLE_CLIENT_ID + #- NEXT_PUBLIC_GOOGLE_API_KEY + networks: + - internal +# - web +# traefik.enable: true +# traefik.http.routers.local-localhost.rule: Host(`${FQDN}`) +# traefik.http.routers.local-localhost.tls: true +# traefik.http.routers.local-localhost.tls.certresolver: letsencrypt + + api: + image: ghcr.io/drinkablebreeze/starbestfit-api + hostname: api.${FQDN} + ports: + - "3000:3000" + build: + context: ../api/ + init: true + depends_on: + database: + condition: service_healthy + environment: + - DATABASE_URL + - FRONTEND_URL +# - CRON_KEY + networks: + - internal +# - web +# labels: +# traefik.enable: true +# traefik.http.routers.local-localhost.rule: Host(`api.${FQDN}`) +# traefik.http.routers.local-localhost.tls: true +# traefik.http.routers.local-localhost.tls.certresolver: letsencrypt + + database: + image: postgres:15-alpine + environment: + - POSTGRES_USER + - POSTGRES_DB + - POSTGRES_PASSWORD + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + volumes: + - postgres:/var/lib/postgresql/data + networks: + - internal From 28cb5fe991582b815f8db454c8ba0909f4777e5e Mon Sep 17 00:00:00 2001 From: jon r Date: Wed, 3 Jul 2024 00:57:13 +0200 Subject: [PATCH 4/6] fix(compose): frontend labels --- compose/compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/compose/compose.yml b/compose/compose.yml index 85a6a139..fc941c46 100644 --- a/compose/compose.yml +++ b/compose/compose.yml @@ -29,6 +29,7 @@ services: networks: - internal # - web +# labels: # traefik.enable: true # traefik.http.routers.local-localhost.rule: Host(`${FQDN}`) # traefik.http.routers.local-localhost.tls: true From f561c1adaf84cfab94cdd1f726c7f846b052d8c8 Mon Sep 17 00:00:00 2001 From: jon r Date: Wed, 3 Jul 2024 00:57:47 +0200 Subject: [PATCH 5/6] feat(frontend/container): expose port 3000 --- frontend/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 5c77c961..acdec2aa 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -29,5 +29,6 @@ ENV NEXT_TELEMETRY_DISABLED 1 # Build Next.js based on the preferred package manager RUN if [ -f yarn.lock ]; then yarn build; fi +EXPOSE 3000 ENTRYPOINT ["yarn"] CMD ["start"] From cfea8428c72f557c1947576b51bc91e94d8ddafe Mon Sep 17 00:00:00 2001 From: jon r Date: Wed, 3 Jul 2024 01:02:20 +0200 Subject: [PATCH 6/6] fix(compose): labels using dash-delimited reverse domain --- compose/compose.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compose/compose.yml b/compose/compose.yml index fc941c46..395b1cd9 100644 --- a/compose/compose.yml +++ b/compose/compose.yml @@ -31,9 +31,9 @@ services: # - web # labels: # traefik.enable: true -# traefik.http.routers.local-localhost.rule: Host(`${FQDN}`) -# traefik.http.routers.local-localhost.tls: true -# traefik.http.routers.local-localhost.tls.certresolver: letsencrypt +# traefik.http.routers.localdomain-localhost.rule: Host(`${FQDN}`) +# traefik.http.routers.localdomain-localhost.tls: true +# traefik.http.routers.localdomain-localhost.tls.certresolver: letsencrypt api: image: ghcr.io/drinkablebreeze/starbestfit-api @@ -55,9 +55,9 @@ services: # - web # labels: # traefik.enable: true -# traefik.http.routers.local-localhost.rule: Host(`api.${FQDN}`) -# traefik.http.routers.local-localhost.tls: true -# traefik.http.routers.local-localhost.tls.certresolver: letsencrypt +# traefik.http.routers.localdomain-localhost-api.rule: Host(`api.${FQDN}`) +# traefik.http.routers.localdomain-localhost-api.tls: true +# traefik.http.routers.localdomain-localhost-api.tls.certresolver: letsencrypt database: image: postgres:15-alpine