Add arbitrary domain support + website hosting to onebox setup

This commit is contained in:
Weves 2023-05-05 16:48:36 -07:00 committed by Chris Weaver
parent e20179048d
commit 5aabc5abe6
11 changed files with 135 additions and 38 deletions

View File

@ -1,5 +0,0 @@
This serves as an example for how to deploy everything in a single box. This is far
from optimal, but can get you started easily and cheaply. To run:
1. Set up a `.env` file in this directory with relevant environment variables (TODO: document)
2. `docker compose up -d --build`

1
deployment/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.env*

7
deployment/README.md Normal file
View File

@ -0,0 +1,7 @@
This serves as an example for how to deploy everything on a single machine. This is
not optimal, but can get you started easily and cheaply. To run:
1. Set up a `.env` + `.env.nginx` file in this directory with relevant environment variables
a. TODO: add description of required variables
2. `chmod +x init-letsencrypt.sh` + `./init-letsencrypt.sh` to setup https certificate
2. `docker compose up -d --build` to start nginx, postgres, web/api servers, and the background indexing job

View File

@ -6,14 +6,20 @@ upstream app_server {
#server unix:/tmp/gunicorn.sock fail_timeout=0;
# for a TCP configuration
# TODO: use gunicorn to manage multiple processes
server api:8080 fail_timeout=0;
}
upstream web_server {
server web:3000 fail_timeout=0;
}
server {
listen 80;
server_name api.danswer.dev;
server_name ${DOMAIN};
location / {
location ~ ^/api(.*)$ {
rewrite ^/api(/.*)$ $1 break;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
@ -23,6 +29,14 @@ server {
proxy_pass http://app_server;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://web_server;
}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
@ -30,14 +44,14 @@ server {
server {
listen 443 ssl;
server_name api.danswer.dev;
server_name ${DOMAIN};
location / {
proxy_pass http://api.danswer.dev;
proxy_pass http://${DOMAIN};
}
ssl_certificate /etc/letsencrypt/live/api.danswer.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.danswer.dev/privkey.pem;
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

View File

@ -3,13 +3,13 @@ version: '3'
services:
api:
build:
context: ..
context: ../backend
dockerfile: Dockerfile
depends_on:
- db
# just for local testing
ports:
- "8080:8080"
# uncomment for local testing
# ports:
# - "8080:8080"
env_file:
- .env
environment:
@ -18,7 +18,7 @@ services:
- local_dynamic_storage:/home/storage
background:
build:
context: ..
context: ../backend
dockerfile: Dockerfile.background
depends_on:
- db
@ -28,6 +28,14 @@ services:
- POSTGRES_HOST=db
volumes:
- local_dynamic_storage:/home/storage
web:
build:
context: ../web
dockerfile: Dockerfile
args:
NEXT_PUBLIC_API_URL: /api
depends_on:
- api
db:
image: postgres:15.2-alpine
restart: always
@ -47,7 +55,11 @@ services:
- ./data/nginx:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
command: >
/bin/sh -c "envsubst '$$\{DOMAIN\}' < /etc/nginx/conf.d/app.conf.template > /etc/nginx/conf.d/app.conf
&& while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\""
env_file:
- .env.nginx
depends_on:
- api
certbot:

View File

@ -1,14 +1,20 @@
#!/bin/bash
# .env.nginx file must be present in the same directory as this script and
# must set DOMAIN (and optionally EMAIL)
set -o allexport
source .env.nginx
set +o allexport
if ! [ -x "$(command -v docker compose)" ]; then
echo 'Error: docker compose is not installed.' >&2
exit 1
fi
domains=(api.danswer.dev www.api.danswer.dev)
domains=("$DOMAIN" "www.$DOMAIN")
rsa_key_size=4096
data_path="./data/certbot"
email="" # Adding a valid address is strongly recommended
email="$EMAIL" # Adding a valid address is strongly recommended
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
if [ -d "$data_path" ]; then

55
web/Dockerfile Normal file
View File

@ -0,0 +1,55 @@
FROM node:18-alpine AS base
# Step 1. 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 /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Step 2. Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Disable automatic telemetry collection
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# Step 3. Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Disable automatic telemetry collection
ENV NEXT_TELEMETRY_DISABLED 1
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
# Add back in if we add anything to `public`
# COPY --from=builder /app/public ./public
# 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
# Note: Don't expose ports here, Compose will handle that for us if necessary.
# If you want to run this without compose, specify the ports to
# expose via cli
CMD ["node", "server.js"]

View File

@ -3,6 +3,23 @@ const nextConfig = {
experimental: {
appDir: true,
},
output: "standalone",
redirects: async () => {
// In production, something else (nginx in the one box setup) takes care
// of this redirect
// NOTE: this may get adjusted later if we want to support hosting of the
// API server on a different domain without requring some kind of proxy
if (process.env.NODE_ENV === "development") {
return [
{
source: "/api/:path*",
destination: "http://localhost:8080/:path*", // Proxy to Backend
permanent: true,
},
];
}
return [];
},
};
module.exports = nextConfig;

View File

@ -6,11 +6,8 @@ import "tailwindcss/tailwind.css";
import { SearchResultsDisplay } from "./SearchResultsDisplay";
import { SearchResponse } from "./types";
const BACKEND_URL =
process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8080"; // "http://servi-lb8a1-jhqpsz92kbm2-1605938866.us-east-2.elb.amazonaws.com/direct-qa";
const searchRequest = async (query: string): Promise<SearchResponse> => {
const response = await fetch(BACKEND_URL + "/direct-qa", {
const response = await fetch("/api/direct-qa", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({

View File

@ -1,7 +1,6 @@
import React, { useEffect, useState } from "react";
import { Formik, Form, Field, ErrorMessage, FormikHelpers } from "formik";
import * as Yup from "yup";
import { BACKEND_URL } from "@/lib/constants";
import { Popup } from "./Popup";
interface FormData {
@ -18,7 +17,7 @@ const validationSchema = Yup.object().shape({
});
const getConfig = async (): Promise<FormData> => {
const response = await fetch(BACKEND_URL + "/admin/slack_connector_config");
const response = await fetch("/api/admin/slack_connector_config");
return response.json();
};
@ -31,17 +30,13 @@ const handleSubmit = async (
) => {
setSubmitting(true);
try {
// Replace this with your actual API call
const response = await fetch(
BACKEND_URL + "/admin/slack_connector_config",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
}
);
const response = await fetch("/api/admin/slack_connector_config", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});
if (response.ok) {
setPopup({ message: "Success!", type: "success" });

View File

@ -1,2 +0,0 @@
export const BACKEND_URL =
process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8080"; // "http://servi-lb8a1-jhqpsz92kbm2-1605938866.us-east-2.elb.amazonaws.com/direct-qa";