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 (
-
- Log in
-
+ Log in
);
}
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 = () => {
submitResponse(tasks[0])}
- className="nline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
+ className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Submit
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"]