Skip to content

Commit

Permalink
Add frontend prod dockerfile
Browse files Browse the repository at this point in the history
- Make various changes to solve issues at build time
  • Loading branch information
dmtrek14 committed Feb 8, 2024
1 parent 18c6262 commit 8dbf479
Show file tree
Hide file tree
Showing 18 changed files with 256 additions and 141 deletions.
46 changes: 46 additions & 0 deletions docker/docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
version: "3"
services:
db:
image: postgres:13.9
container_name: phpreport-db
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: phpreport
POSTGRES_USER: phpreport
POSTGRES_DB: phpreport
phpreport-app:
build:
context: ../
dockerfile: docker/dev.app.Dockerfile
container_name: phpreport-app
ports:
- "8000:8000"
depends_on:
- db
api:
build:
context: ../
dockerfile: docker/prod.api.Dockerfile
container_name: phpreport-api
env_file:
- ../.env
ports:
- "8555:8555"
depends_on:
- db
frontend:
build:
context: ../
dockerfile: docker/prod.frontend.Dockerfile
container_name: phpreport-frontend
env_file:
- ../frontend/.env.local
ports:
- "5173:3000"
depends_on:
- api
volumes:
pgdata:
47 changes: 47 additions & 0 deletions docker/prod.frontend.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
FROM node:lts-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat

WORKDIR /frontend

COPY frontend/package*.json /frontend

RUN npm ci

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /frontend

COPY --from=deps /frontend/node_modules ./node_modules
COPY frontend /frontend

ENV NEXT_TELEMETRY_DISABLED 1

RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /frontend

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

COPY --from=builder --chown=nextjs:nodejs /frontend/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /frontend/.next/static ./.next/static

USER nextjs

EXPOSE 5173

CMD ["node", "server.js"]

1 change: 1 addition & 0 deletions frontend/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
async redirects() {
return [
{
Expand Down
35 changes: 18 additions & 17 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
"@mui/joy": "^5.0.0-beta.20",
"@tanstack/react-query": "^4.35.3",
"@testing-library/user-event": "^14.5.1",
"@types/node": "20.6.0",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@types/react": "18.2.23",
"@types/react-dom": "18.2.8",
"date-fns": "^2.30.0",
"eslint": "8.49.0",
"eslint-config-next": "^14.0.4",
Expand Down
101 changes: 101 additions & 0 deletions frontend/src/app/api/auth/[...nextauth]/authOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { NextAuthOptions } from 'next-auth'
import KeycloakProvider from 'next-auth/providers/keycloak'
import { fetchFactory } from '@/infra/lib/apiClient'
import { makeGetCurrentUser } from '@/infra/user/getCurrentUser'
import { JWT } from 'next-auth/jwt'

/**
* Takes a token, and returns a new token with updated
* `accessToken` and `accessTokenExpires`. If an error occurs,
* returns the old token and an error property
*/
async function refreshAccessToken(token: JWT) {
try {
const url = `${process.env.OIDC_TOKEN_ENDPOINT}`

const params = {
grant_type: 'refresh_token',
client_id: process.env.OIDC_CLIENT_ID!,
client_secret: process.env.OIDC_CLIENT_SECRET!,
refresh_token: token.refreshToken!
}

const response = await fetch(url, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams(params),
method: 'POST'
})

const refreshedTokens = await response.json()

if (!response.ok) {
throw refreshedTokens
}

return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken // Fall back to old refresh token
}
} catch (error) {
return {
...token,
error: 'RefreshAccessTokenError'
}
}
}

export const authOptions: NextAuthOptions = {
providers: [
KeycloakProvider({
clientId: process.env.OIDC_CLIENT_ID!,
clientSecret: process.env.OIDC_CLIENT_SECRET!,
issuer: process.env.OIDC_AUTHORITY
})
],
pages: {
error: '/auth/error'
},
callbacks: {
async redirect({ baseUrl }) {
return baseUrl
},
async session({ session, token }) {
session.accessToken = token.accessToken
session.user = { ...session.user, ...token.user }
session.accessTokenExpires = token.accessTokenExpires
session.refreshToken = token.refreshToken

return session
},
async jwt({ token, account, profile, trigger }) {
if (trigger === 'update' && Date.now() > token.accessTokenExpires!) {
const newToken = await refreshAccessToken(token)

return newToken
}

if (account && profile) {
token.accessToken = account.access_token
token.accessTokenExpires = account.expires_at * 1000
token.refreshToken = account.refresh_token
token.id = profile.id

const apiClient = fetchFactory({ baseURL: process.env.API_BASE!, token: token.accessToken })
const getCurrentUser = makeGetCurrentUser(apiClient)


const user = await getCurrentUser()

token.user = user
}

return token
}
}
}


Loading

0 comments on commit 8dbf479

Please sign in to comment.