Skip to content

Commit

Permalink
Merge pull request #38 from briefercloud/single-port
Browse files Browse the repository at this point in the history
Simplify deployment by removing the need for any env vars by default
  • Loading branch information
vieiralucas authored Sep 16, 2024
2 parents d9e513d + 8194528 commit f45f784
Show file tree
Hide file tree
Showing 21 changed files with 210 additions and 122 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
data
nginx
node_modules
.env.*
.turbo
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,18 @@ Alternatively, you can also run Briefer using Docker directly:
# Run Briefer using Docker
docker run -d \
-p 3000:3000 \
-p 8080:8080 \
-v briefer_psql_data:/var/lib/postgresql/data \
-v briefer_jupyter_data:/home/jupyteruser \
-v briefer_briefer_data:/home/briefer \
briefercloud/briefer
```

When running on Windows' PowerShell, it might be necessary to add an extra ``` ` ``` to the end of each line instead of the `\`, like this:
When running on Windows' PowerShell, it might be necessary to add an extra `` ` `` to the end of each line instead of the `\`, like this:

```bash
# Run Briefer using Docker
docker run -d `
-p 3000:3000 `
-p 8080:8080 `
-v briefer_psql_data:/var/lib/postgresql/data `
-v briefer_jupyter_data:/home/jupyteruser `
-v briefer_briefer_data:/home/briefer `
Expand Down
31 changes: 31 additions & 0 deletions apps/api/src/websocket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type Server = {
export function createSocketServer(server: http.Server): Server {
const io: IOServer = new BaseServer(server, {
cors: { credentials: true, origin: config().FRONTEND_URL },
transports: ['websocket'],
})

io.use(async (socket: Socket, next) => {
Expand All @@ -68,15 +69,31 @@ export function createSocketServer(server: http.Server): Server {
next(new Error('Unauthorized'))
}
} catch (err) {
logger.error(
{
err,
socketId: socket.id,
},
'Error authenticating socket connection'
)
next(new Error('Internal Server Error'))
}
})

let workInProgress: Map<string, Promise<void>> = new Map()

io.on('connection', (socket: Socket) => {
logger.info({ socketId: socket.id }, 'Client connected to socket server')

const session = socket.session
if (!session) {
logger.error(
{
socketId: socket.id,
},
'Socket connection did not have a session'
)

socket.disconnect(true)
return
}
Expand Down Expand Up @@ -104,6 +121,20 @@ export function createSocketServer(server: http.Server): Server {
trackWork(handleRestartEnvironment(socket, session))
)
socket.on('complete-python', trackWork(completePython(io, socket, session)))

socket.on('disconnect', (reason) => {
logger.info(
{ socketId: socket.id, reason },
'Client disconnected from socket server'
)
})

socket.on('error', (error) => {
logger.error(
{ socketId: socket.id, error },
'Socket server error occurred'
)
})
})

return {
Expand Down
3 changes: 1 addition & 2 deletions apps/api/src/yjs/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ async function getRequestData(req: http.IncomingMessage): Promise<{
} | null> {
const cookiesHeader = req.headers.cookie
const cookies = cookie.parse(cookiesHeader ?? '')
const url = new URL(req.url ?? '', config().API_URL)
const query = qs.parse(url.search.slice(1))
const query = qs.parse(req.url?.split('?')[1] ?? '')
const docId = query['documentId']
const clock = parseInt((query['clock'] ?? '').toString())
const isDataApp = query['isDataApp'] === 'true'
Expand Down
2 changes: 1 addition & 1 deletion apps/web/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/we
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/start.sh ./apps/web/start.sh

EXPOSE 3000
EXPOSE 4000

CMD ./apps/web/start.sh
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.8",
"private": true,
"scripts": {
"dev": "next dev --experimental-https && local-ssl-proxy --key",
"dev": "next dev -p 4000 --experimental-https && local-ssl-proxy --key",
"build": "next build",
"start": "node dist/server.js",
"lint": "next lint",
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/hooks/useWebsocket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ export function WebsocketProvider({ children }: Props) {
const workspaceId = useStringQuery('workspaceId')
useEffect(() => {
if (session.data) {
const socket = io(NEXT_PUBLIC_API_URL(), {
const url = new URL(NEXT_PUBLIC_API_URL())
const withoutPathname = url.origin
const socket = io(withoutPathname, {
withCredentials: true,
path: url.pathname + '/socket.io',
transports: ['websocket'],
})
setSocket(socket)

Expand Down
16 changes: 13 additions & 3 deletions apps/web/src/utils/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@ function getFromWindow(key: string, or?: string): string {
}
}

function currentUrl() {
return `${window.location.protocol}//${window.location.host}`
}

export const NEXT_PUBLIC_API_URL = () =>
process.env.NEXT_PUBLIC_API_URL || getFromWindow('NEXT_PUBLIC_API_URL')
process.env.NEXT_PUBLIC_API_URL ||
getFromWindow('NEXT_PUBLIC_API_URL') ||
`${currentUrl()}/api`

export const NEXT_PUBLIC_API_WS_URL = () =>
process.env.NEXT_PUBLIC_API_WS_URL || getFromWindow('NEXT_PUBLIC_API_WS_URL')
process.env.NEXT_PUBLIC_API_WS_URL ||
getFromWindow('NEXT_PUBLIC_API_WS_URL') ||
`${currentUrl().replace('http', 'ws')}/api`

export const NEXT_PUBLIC_PUBLIC_URL = () =>
process.env.NEXT_PUBLIC_PUBLIC_URL || getFromWindow('NEXT_PUBLIC_PUBLIC_URL')
process.env.NEXT_PUBLIC_PUBLIC_URL ||
getFromWindow('NEXT_PUBLIC_PUBLIC_URL') ||
currentUrl()

export const NEXT_PUBLIC_GATEWAY_IP = () =>
process.env.NEXT_PUBLIC_GATEWAY_IP ||
Expand Down
2 changes: 1 addition & 1 deletion apps/web/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ echo "window.env.NEXT_PUBLIC_API_URL = '$NEXT_PUBLIC_API_URL'" >> $SCRIPT_DIR/pu
echo "window.env.NEXT_PUBLIC_API_WS_URL = '$NEXT_PUBLIC_API_WS_URL'" >> $SCRIPT_DIR/public/env.js
echo "window.env.NEXT_PUBLIC_PUBLIC_URL = '$NEXT_PUBLIC_PUBLIC_URL'" >> $SCRIPT_DIR/public/env.js

node $SCRIPT_DIR/server.js
HOSTNAME=0.0.0.0 PORT=4000 node $SCRIPT_DIR/server.js
30 changes: 14 additions & 16 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,8 @@ services:
build:
context: '.'
dockerfile: 'apps/web/Dockerfile'
args:
NODE_ENV: 'production'
NEXT_PUBLIC_API_URL: 'https://api.${TLD:?error}'
NEXT_PUBLIC_API_WS_URL: 'wss://api.${TLD:?error}'
NEXT_PUBLIC_PUBLIC_URL: 'https://app.${TLD:?error}'
environment:
NODE_ENV: 'production'
NEXT_PUBLIC_API_URL: 'https://api.${TLD:?error}'
NEXT_PUBLIC_API_WS_URL: 'wss://api.${TLD:?error}'
NEXT_PUBLIC_PUBLIC_URL: 'https://app.${TLD:?error}'
ports:
- '3000:3000'
depends_on:
api:
condition: service_healthy
Expand All @@ -97,14 +87,11 @@ services:
build:
context: '.'
dockerfile: 'apps/api/Dockerfile'
ports:
- '8080:8080'
environment:
NODE_ENV: 'production'
LOG_LEVEL: 'info'
API_URL: 'https://api.${TLD:?error}'
FRONTEND_URL: 'https://app.${TLD:?error}'
TLD: ${TLD:?error}
LOG_LEVEL: 'debug'
API_URL: '/api'
FRONTEND_URL: '/'
LOGIN_JWT_SECRET: ${LOGIN_JWT_SECRET:?error}
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET:?error}
AI_API_URL: 'http://ai:8000'
Expand Down Expand Up @@ -138,6 +125,17 @@ services:
ai:
condition: service_healthy

nginx:
image: nginx:1.27
depends_on:
- web
- api
ports:
- '3000:3000'
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
restart: always

volumes:
jupyter:
postgres_data:
7 changes: 7 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ COPY --from=web-installer --chown=briefer:briefer /app/apps/web/public ./apps/we
COPY --from=web-installer --chown=briefer:briefer /app/apps/web/start.sh ./apps/web/start.sh
##### END OF COPY API AND WEB BUILDS ####

# NGINX
RUN mkdir -p /var/log/nginx /etc/nginx /etc/nginx/conf.d
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./docker/start-nginx.sh /app/start-nginx.sh
RUN chmod +x /app/start-nginx.sh


USER postgres
RUN mkdir -p /var/lib/postgresql/data \
&& chown -R postgres:postgres /var/lib/postgresql \
Expand Down
5 changes: 2 additions & 3 deletions docker/setup/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ def run_api(cfg):
default_env = {
"NODE_ENV": "production",
"LOG_LEVEL": "info",
"API_URL": cfg["API_URL"],
"FRONTEND_URL": cfg["FRONTEND_URL"],
"TLD": cfg["TLD"],
"API_URL": "/api",
"FRONTEND_URL": "/",
"LOGIN_JWT_SECRET": cfg["LOGIN_JWT_SECRET"],
"AUTH_JWT_SECRET": cfg["AUTH_JWT_SECRET"],
"AI_API_URL": "http://localhost:8000",
Expand Down
4 changes: 0 additions & 4 deletions docker/setup/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,9 @@ def get_config(dir):

def generate_apps_config():
fpath = get_config_path(APPS_CONFIG_DIR)
tld = os.getenv("TLD", "localhost")
cfg = {
"NODE_ENV": "production",
"LOG_LEVEL": "info",
"TLD": tld,
"API_URL": f"https://api.{tld}" if tld != "localhost" else "http://localhost:8080",
"FRONTEND_URL": f"https://app.{tld}" if tld != "localhost" else "http://localhost:3000",
"POSTGRES_USERNAME": "briefer",
"POSTGRES_PASSWORD": get_random_secret(8),
"JUPYTER_TOKEN": get_random_secret(32),
Expand Down
26 changes: 2 additions & 24 deletions docker/setup/web.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
import time
import subprocess
from pathlib import Path
import json
import logging
import os

CONFIG_DIR = Path(Path.home(), ".config", "briefer")
CONFIG_FILE_PATH = Path(CONFIG_DIR, "briefer.json")
SETUP_FILE_PATH = Path(CONFIG_DIR, "setup")

def wait_setup():
while SETUP_FILE_PATH.exists():
logging.info("Waiting for setup to finish")
time.sleep(0.3)

def get_config():
while not CONFIG_FILE_PATH.exists():
logging.info("Waiting for config file")
time.sleep(0.3)

logging.info("Reading config file")
with open(CONFIG_FILE_PATH, "r") as f:
cfg = json.load(f)

# override config with env vars
for k, _ in cfg.items():
if k in os.environ:
cfg[k] = os.environ[k]

return cfg


def run_web(cfg):
def run_web():
logging.info("Running Web")

default_env = {
"NODE_ENV": "production",
"NEXT_PUBLIC_API_URL": cfg["API_URL"],
"NEXT_PUBLIC_API_WS_URL": cfg["API_URL"].replace('http', 'ws'),
"NEXT_PUBLIC_PUBLIC_URL": cfg["FRONTEND_URL"],
}
env = os.environ.copy()
for k, v in default_env.items():
Expand All @@ -51,8 +30,7 @@ def run_web(cfg):

def main():
wait_setup()
cfg = get_config()
run_web(cfg)
run_web()


if __name__ == '__main__':
Expand Down
28 changes: 28 additions & 0 deletions docker/start-nginx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

# Function to ensure web and api are in /etc/hosts
update_hosts() {
if ! grep -q "127.0.0.1 web" /etc/hosts; then
echo "127.0.0.1 web" >> /etc/hosts
fi
if ! grep -q "127.0.0.1 api" /etc/hosts; then
echo "127.0.0.1 api" >> /etc/hosts
fi
}

# Loop until both web and api services are reachable
while true; do
update_hosts

# Check if the web and api services are reachable
if curl --output /dev/null --silent --head --fail http://web:4000 && curl --output /dev/null --silent --head --fail http://api:8080/readyz; then
echo "Web and API services are reachable, starting Nginx..."
break
else
echo "Waiting for Web and API services to be reachable..."
sleep 2
fi
done

# Start Nginx
nginx -g 'daemon off;'
10 changes: 10 additions & 0 deletions docker/supervisord.conf
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,13 @@ stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr
stdout_logfile_maxbytes=0
stderr_logfile_maxbytes=0

[program:nginx]
command=/app/start-nginx.sh
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stderr_logfile=/dev/stderr
stdout_logfile_maxbytes=0
stderr_logfile_maxbytes=0
priority=7
Loading

0 comments on commit f45f784

Please sign in to comment.