From c056a31d2f79f7e5e49a2cde75ad527efe251cd3 Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Tue, 20 Dec 2022 20:28:53 +0900 Subject: [PATCH] Ensuring the website can be built and deployed fully in docker. This includes an end to end docker-compose configuration as a simple demonstration. --- docker/Dockerfile.website | 58 ++++++++++++ scripts/endtoend-demo/README.md | 18 ++++ scripts/endtoend-demo/docker-compose.yaml | 89 +++++++++++++++++++ .../frontend-development/docker-compose.yaml | 5 +- website/next.config.js | 1 + website/package.json | 4 - website/src/components/CallToAction.tsx | 4 +- website/src/components/Header.tsx | 22 ++--- website/src/pages/api/auth/[...nextauth].ts | 3 +- website/src/pages/api/new_task/[task_type].ts | 5 +- website/src/pages/api/update_task.ts | 5 +- website/src/pages/grading/grade-output.tsx | 2 +- website/tsconfig.json | 6 +- 13 files changed, 194 insertions(+), 28 deletions(-) create mode 100644 docker/Dockerfile.website create mode 100644 scripts/endtoend-demo/README.md create mode 100644 scripts/endtoend-demo/docker-compose.yaml diff --git a/docker/Dockerfile.website b/docker/Dockerfile.website new file mode 100644 index 00000000..c43d44e0 --- /dev/null +++ b/docker/Dockerfile.website @@ -0,0 +1,58 @@ +# Install dependencies only when needed +FROM node:16.19 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 /app + +# Install dependencies based on the preferred package manager +COPY ./website/package.json ./website/package-lock.json ./ +RUN \ + if [ -f package-lock.json ]; then npm ci; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Rebuild the source code only when needed +FROM node:16.19 AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY ./website/ . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED 1 + +# RUN yarn build +RUN npx prisma generate +RUN npm run build + +# Production image, copy all the files and run next +FROM node:16.19 AS runner +WORKDIR /app + +ENV NODE_ENV production +# Uncomment the following line in case you want to disable telemetry during runtime. +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Copy over the schema file so we can do a db push on startup. +# TODO: Delete when used for real production. +COPY ./website/prisma/schema.prisma ./ +COPY ./website/wait-for-it.sh ./ + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 + +CMD ["node", "server.js"] diff --git a/scripts/endtoend-demo/README.md b/scripts/endtoend-demo/README.md new file mode 100644 index 00000000..49b193a5 --- /dev/null +++ b/scripts/endtoend-demo/README.md @@ -0,0 +1,18 @@ +# End to End Demo + +This sets up an entire stack needed to run Open Assistant, including the website, backend, and associated dependent services. + +To start the service, do the following: + +```sh +docker compose up --build +cd ../../website +npx prisma db push +``` + +NOTE: we're working on making the prisma step integrated into the docker +initialization process. + +Then, navigate to `http://localhost:3000` and interact with the website. When +logging in, navigate to `http://localhost:1080` to get the magic email login +link. diff --git a/scripts/endtoend-demo/docker-compose.yaml b/scripts/endtoend-demo/docker-compose.yaml new file mode 100644 index 00000000..b17a7b74 --- /dev/null +++ b/scripts/endtoend-demo/docker-compose.yaml @@ -0,0 +1,89 @@ +version: "3.7" + +services: + # This DB is for the FastAPI Backend. + db: + image: postgres + restart: always + ports: + - 5432:5432 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + interval: 2s + timeout: 2s + retries: 10 + + # This DB is for Web Authentication and data caching. + webdb: + image: postgres + restart: always + ports: + - 5433:5432 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + interval: 2s + timeout: 2s + retries: 10 + + # This lets you manually inspect the web and backend databases. + adminer: + image: adminer + restart: always + ports: + - 8089:8080 + + # This fakes an SMTP email server used by website authentication. + # User registration emails can be found by going to localhost:1080 and + # opening the emails listed. + maildev: + image: maildev/maildev + restart: always + environment: + - MAILDEV_WEB_PORT=1080 + - MAILDEV_SMTP_PORT=1025 + ports: + - "1080:1080" + - "1025:1025" + + # The oassist backend service. + backend: + build: + dockerfile: docker/Dockerfile.backend + context: ../../ + image: oasst-backend + environment: + - POSTGRES_HOST=db + - ALLOW_ANY_API_KEY=True + - MAX_WORKERS=1 + depends_on: + db: + condition: service_healthy + ports: + - "8080:8080" + + # The oassist web service. + web: + build: + dockerfile: docker/Dockerfile.website + context: ../../ + image: oasst-web + environment: + - DATABASE_URL=postgres://postgres:postgres@webdb/ocgpt_website + - FASTAPI_URL=http://backend:8080 + - FASTAPI_KEY=1234 + - NEXTAUTH_SECRET=O/M2uIbGj+lDD2oyNa8ax4jEOJqCPJzO53UbWShmq98= + - EMAIL_SERVER_HOST=maildev + - EMAIL_SERVER_PORT=1025 + - EMAIL_FROM=info@example.com + - NEXTAUTH_URL=http://localhost:3000 + depends_on: + webdb: + condition: service_healthy + ports: + - "3000:3000" diff --git a/scripts/frontend-development/docker-compose.yaml b/scripts/frontend-development/docker-compose.yaml index 6f9c01f4..99f30f62 100644 --- a/scripts/frontend-development/docker-compose.yaml +++ b/scripts/frontend-development/docker-compose.yaml @@ -47,8 +47,9 @@ services: ports: - "8080:8080" - # This fakes and SMTP email server. User registration emails can be found by going to - # localhost:1080 and opening the emails listed. + # This fakes an SMTP email server used by website authentication. + # User registration emails can be found by going to localhost:1080 and + # opening the emails listed. maildev: image: maildev/maildev restart: always diff --git a/website/next.config.js b/website/next.config.js index c1c1519d..1cbdb0e0 100644 --- a/website/next.config.js +++ b/website/next.config.js @@ -1,5 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: "standalone", reactStrictMode: true, experimental: { scrollRestoration: true, diff --git a/website/package.json b/website/package.json index b7680c79..10708782 100644 --- a/website/package.json +++ b/website/package.json @@ -17,10 +17,6 @@ "@heroicons/react": "^2.0.13", "@next-auth/prisma-adapter": "^1.0.5", "@prisma/client": "^4.7.1", - "@supabase/auth-helpers-nextjs": "^0.5.2", - "@supabase/auth-helpers-react": "^0.3.1", - "@supabase/auth-ui-react": "^0.2.6", - "@supabase/supabase-js": "^2.1.4", "@tailwindcss/forms": "^0.5.3", "autoprefixer": "^10.4.13", "axios": "^1.2.1", diff --git a/website/src/components/CallToAction.tsx b/website/src/components/CallToAction.tsx index 61f11540..35949645 100644 --- a/website/src/components/CallToAction.tsx +++ b/website/src/components/CallToAction.tsx @@ -16,7 +16,7 @@ export function CallToAction() { here:

- + + ); } return ( - + ); } diff --git a/website/src/pages/api/auth/[...nextauth].ts b/website/src/pages/api/auth/[...nextauth].ts index 6b2c0ae7..86d8bdc4 100644 --- a/website/src/pages/api/auth/[...nextauth].ts +++ b/website/src/pages/api/auth/[...nextauth].ts @@ -1,3 +1,4 @@ +import type { AuthOptions } from "next-auth"; import NextAuth from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; import EmailProvider from "next-auth/providers/email"; @@ -31,7 +32,7 @@ if (process.env.DISCORD_CLIENT_ID) { ); } -export const authOptions = { +export const authOptions: AuthOptions = { // Ensure we can store user data in a database. adapter: PrismaAdapter(prisma), providers, diff --git a/website/src/pages/api/new_task/[task_type].ts b/website/src/pages/api/new_task/[task_type].ts index 0d45c5f3..3943536c 100644 --- a/website/src/pages/api/new_task/[task_type].ts +++ b/website/src/pages/api/new_task/[task_type].ts @@ -1,5 +1,6 @@ import { getToken } from "next-auth/jwt"; +import prisma from "src/lib/prismadb"; import { authOptions } from "src/pages/api/auth/[...nextauth]"; /** @@ -10,7 +11,7 @@ import { authOptions } from "src/pages/api/auth/[...nextauth]"; * 3) Send and Ack to the Task Backend with our local id for the task. * 4) Return everything to the client. */ -export default async (req, res) => { +const handler = async (req, res) => { const { task_type } = req.query; const token = await getToken({ req }); @@ -69,3 +70,5 @@ export default async (req, res) => { // Send the results to the client. res.status(200).json(registeredTask); }; + +export default handler; diff --git a/website/src/pages/api/update_task.ts b/website/src/pages/api/update_task.ts index b7919c5a..6760623c 100644 --- a/website/src/pages/api/update_task.ts +++ b/website/src/pages/api/update_task.ts @@ -1,5 +1,6 @@ import { getToken } from "next-auth/jwt"; +import prisma from "src/lib/prismadb"; import { authOptions } from "src/pages/api/auth/[...nextauth]"; /** @@ -11,7 +12,7 @@ import { authOptions } from "src/pages/api/auth/[...nextauth]"; * 3) (TODO) Acks the new task with our local task ID to the Task Backend. * 4) Returns the newly created task to the client. */ -export default async (req, res) => { +const handler = async (req, res) => { const token = await getToken({ req }); // Return nothing if the user isn't registered. @@ -76,3 +77,5 @@ export default async (req, res) => { // Send the next task in the sequence to the client. res.status(200).json(newRegisteredTask); }; + +export default handler; diff --git a/website/src/pages/grading/grade-output.tsx b/website/src/pages/grading/grade-output.tsx index 5fabe7ba..c04a99c3 100644 --- a/website/src/pages/grading/grade-output.tsx +++ b/website/src/pages/grading/grade-output.tsx @@ -134,7 +134,7 @@ const GradeOutput = () => { diff --git a/website/tsconfig.json b/website/tsconfig.json index 1563f3e8..8a5ec4fe 100644 --- a/website/tsconfig.json +++ b/website/tsconfig.json @@ -13,7 +13,11 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve" + "jsx": "preserve", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"]