diff --git a/.gitignore b/deployment/.gitignore similarity index 100% rename from .gitignore rename to deployment/.gitignore diff --git a/aio-autostart.sh b/deployment/aio-autostart.sh similarity index 51% rename from aio-autostart.sh rename to deployment/aio-autostart.sh index 2352e03..1a627f1 100755 --- a/aio-autostart.sh +++ b/deployment/aio-autostart.sh @@ -1,6 +1,11 @@ #!/bin/bash set -e +NODE_PORT=8080 +FRONTEND_PORT=3000 +SOURCE_TOKEN="CHANGEME" +SCRIBEAR_URL="http://192.168.10.160:${FRONTEND_PORT}" + SCRIPT_DIR=$(dirname $0) cd $SCRIPT_DIR BASE_DIR=$(pwd) @@ -21,18 +26,23 @@ mkdir -p $BASE_DIR/logs echo "Starting Whisper Service" -cd $BASE_DIR/whisper-service; +cd $BASE_DIR/../whisper-service; source .venv/bin/activate; python index.py 2>> $BASE_DIR/logs/whisper-service.log & PYTHON_PID=$! echo "Starting Node Server" -cd $BASE_DIR/node-server; +cd $BASE_DIR/../node-server; node ./build/src/index.js >> $BASE_DIR/logs/node-server.log & NODE_PID=$! sleep 15 +until [ "$(curl --max-time 1 -s -w '%{http_code}' -o /dev/null "${SCRIBEAR_URL}")" -eq 200 ] +do + echo "Can't reach frontend at ${SCRIBEAR_URL}. Waiting for frontend to be ready..." + sleep 1 +done echo "Launching Chrome" -google-chrome "https://scribear.illinois.edu/v/latest/?mode=kiosk&serverAddress=127.0.0.1:8080" --start-fullscreen +google-chrome "http://localhost:${FRONTEND_PORT}/?mode=kiosk&kioskServerAddress=localhost:${NODE_PORT}&sourceToken=${SOURCE_TOKEN}&scribearURL=${SCRIBEAR_URL}" --start-fullscreen diff --git a/compose_cpu.yaml b/deployment/compose_cpu.yaml similarity index 67% rename from compose_cpu.yaml rename to deployment/compose_cpu.yaml index 600b6b3..ddfc0b9 100644 --- a/compose_cpu.yaml +++ b/deployment/compose_cpu.yaml @@ -1,18 +1,23 @@ name: 'scribear-server' services: + whisper-service: + image: scribear/whisper-service-cpu:main + environment: + - LOG_LEVEL=${LOG_LEVEL} + - API_KEY=${API_KEY} + expose: + - 80 + restart: unless-stopped + node-server: - build: - context: ./node-server - dockerfile: Dockerfile + image: scribear/node-server:main environment: - NODE_ENV=${NODE_ENV} - LOG_LEVEL=${LOG_LEVEL} - - HOST=0.0.0.0 - - PORT=8080 - USE_HTTPS=${USE_HTTPS} - CORS_ORIGIN=${CORS_ORIGIN} - SERVER_ADDRESS=${SERVER_ADDRESS} - - WHISPER_SERVICE_ENDPOINT=ws://whisper-service:8000/whisper?api_key=${API_KEY}&model_key=${MODEL_KEY} + - WHISPER_SERVICE_ENDPOINT=ws://whisper-service:80/whisper?api_key=${API_KEY}&model_key=${MODEL_KEY} - WHISPER_RECONNECT_INTERVAL_SEC=${WHISPER_RECONNECT_INTERVAL_SEC} - REQUIRE_AUTH=${REQUIRE_AUTH} - SOURCE_TOKEN=${SOURCE_TOKEN} @@ -22,21 +27,18 @@ services: - SESSION_TOKEN_BYTES=${SESSION_TOKEN_BYTES} - SESSION_LENGTH_SEC=${SESSION_LENGTH_SEC} ports: - - ${PORT}:8080 + - ${NODE_PORT}:80 volumes: - ${KEY_FILEPATH:-/dev/null}:/app/cert/key.pem - ${CERTIFICATE_FILEPATH:-/dev/null}:/app/cert/key.pem restart: unless-stopped - whisper-service: - build: - context: ./whisper-service - dockerfile: Dockerfile_CPU - environment: - - LOG_LEVEL=${LOG_LEVEL} - - API_KEY=${API_KEY} - - HOST=0.0.0.0 - - PORT=8000 - expose: - - 8000 - restart: unless-stopped \ No newline at end of file + frontend: + depends_on: + whisper-service: + condition: service_healthy + node-server: + condition: service_healthy + ports: + - ${FRONTEND_PORT}:80 + image: scribear/frontend:master \ No newline at end of file diff --git a/compose_cuda.yaml b/deployment/compose_cuda.yaml similarity index 68% rename from compose_cuda.yaml rename to deployment/compose_cuda.yaml index 3864003..0b6a749 100644 --- a/compose_cuda.yaml +++ b/deployment/compose_cuda.yaml @@ -1,18 +1,26 @@ name: 'scribear-server' services: + whisper-service: + image: scribear/whisper-service-cuda:main + environment: + - LOG_LEVEL=${LOG_LEVEL} + - API_KEY=${API_KEY} + expose: + - 80 + restart: unless-stopped + node-server: + image: scribear/node-server:main build: context: ./node-server dockerfile: Dockerfile environment: - NODE_ENV=${NODE_ENV} - LOG_LEVEL=${LOG_LEVEL} - - HOST=0.0.0.0 - - PORT=8080 - USE_HTTPS=${USE_HTTPS} - CORS_ORIGIN=${CORS_ORIGIN} - SERVER_ADDRESS=${SERVER_ADDRESS} - - WHISPER_SERVICE_ENDPOINT=ws://whisper-service:8000/whisper?api_key=${API_KEY}&model_key=${MODEL_KEY} + - WHISPER_SERVICE_ENDPOINT=ws://whisper-service:80/whisper?api_key=${API_KEY}&model_key=${MODEL_KEY} - WHISPER_RECONNECT_INTERVAL_SEC=${WHISPER_RECONNECT_INTERVAL_SEC} - REQUIRE_AUTH=${REQUIRE_AUTH} - SOURCE_TOKEN=${SOURCE_TOKEN} @@ -22,21 +30,18 @@ services: - SESSION_TOKEN_BYTES=${SESSION_TOKEN_BYTES} - SESSION_LENGTH_SEC=${SESSION_LENGTH_SEC} ports: - - ${PORT}:8080 + - ${NODE_PORT}:80 volumes: - ${KEY_FILEPATH:-/dev/null}:/app/cert/key.pem - ${CERTIFICATE_FILEPATH:-/dev/null}:/app/cert/key.pem restart: unless-stopped - whisper-service: - build: - context: ./whisper-service - dockerfile: Dockerfile_CUDA - environment: - - LOG_LEVEL=${LOG_LEVEL} - - API_KEY=${API_KEY} - - HOST=0.0.0.0 - - PORT=8000 - expose: - - 8000 - restart: unless-stopped \ No newline at end of file + frontend: + depends_on: + whisper-service: + condition: service_healthy + node-server: + condition: service_healthy + ports: + - ${FRONTEND_PORT}:80 + image: scribear/frontend:master \ No newline at end of file diff --git a/deployment/docker-autostart.sh b/deployment/docker-autostart.sh new file mode 100755 index 0000000..1e4825e --- /dev/null +++ b/deployment/docker-autostart.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +source .env +if [ $WHISPER_SERVICE_CUDA = 'true' ]; then + echo Starting services with CUDA enabled for whisper-service + docker compose -f ./compose_cuda.yaml up -d +else + echo Starting services without CUDA + docker compose -f ./compose_cpu.yaml up -d +fi + +until [ "$(curl --max-time 1 -s -w '%{http_code}' -o /dev/null "${SCRIBEAR_URL}")" -eq 200 ] +do + echo "Can't reach frontend at ${SCRIBEAR_URL}. Waiting for frontend to be ready..." + sleep 1 +done + +echo "Launching Chrome" +google-chrome "http://localhost:${FRONTEND_PORT}/?mode=kiosk&kioskServerAddress=localhost:${NODE_PORT}&sourceToken=${SOURCE_TOKEN}&scribearURL=${SCRIBEAR_URL}" --start-fullscreen diff --git a/template.env b/deployment/template.env similarity index 88% rename from template.env rename to deployment/template.env index bf606d3..f3ea5cb 100644 --- a/template.env +++ b/deployment/template.env @@ -2,8 +2,7 @@ NODE_ENV="production" LOG_LEVEL="info" #### Host and port API webserver should listen on -HOST="0.0.0.0" -PORT=8080 +NODE_PORT=8080 CORS_ORIGIN='*' SERVER_ADDRESS="192.168.10.160:8080" # Enable or disable HTTPS @@ -29,5 +28,8 @@ SESSION_TOKEN_BYTES=32 # How long a single session token is valid for after generation in seconds SESSION_LENGTH_SEC=5400 +WHISPER_SERVICE_CUDA=false MODEL_KEY="faster-whisper:cpu-tiny-en" -API_KEY="CHANGEME" \ No newline at end of file +API_KEY="CHANGEME" +FRONTEND_PORT=3000 +SCRIBEAR_URL="http://192.168.10.160:3000" diff --git a/node-server/Dockerfile b/node-server/Dockerfile index 42fed2b..0324eb7 100644 --- a/node-server/Dockerfile +++ b/node-server/Dockerfile @@ -12,7 +12,11 @@ RUN npm run build WORKDIR /app/build/src +ENV HOST=0.0.0.0 +ENV PORT=80 ENV KEY_FILEPATH="/app/cert/key.pem" ENV CERTIFICATE_FILEPATH="/app/cert/cert.pem" +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --start-interval=1s --retries=3 CMD curl -f http://localhost:80/healthcheck || exit 1 + CMD ["node", "index.js"] \ No newline at end of file diff --git a/node-server/src/index.ts b/node-server/src/index.ts index aafdd92..efcf12b 100644 --- a/node-server/src/index.ts +++ b/node-server/src/index.ts @@ -42,25 +42,3 @@ async function init() { } await init(); - -// import {Type} from '@sinclair/typebox'; -// import {Value} from '@sinclair/typebox/value'; - -// const TEST = Type.Intersect([ -// Type.Union([ -// Type.Object({ -// flag: Type.Literal(false), -// }), -// Type.Object({ -// flag: Type.Literal(true), -// key: Type.String({minLength: 1}), -// }), -// ]), -// Type.Object({ -// other: Type.String(), -// }), -// ]); - -// const env = Value.Convert(TEST, {flag: false, key: '90', other: 'asdf'}); - -// console.log(env); diff --git a/node-server/src/server/routes/healthcheck_handler.ts b/node-server/src/server/routes/healthcheck_handler.ts new file mode 100644 index 0000000..0e4bd8e --- /dev/null +++ b/node-server/src/server/routes/healthcheck_handler.ts @@ -0,0 +1,11 @@ +import {FastifyInstance} from 'fastify'; + +/** + * Registers endpoint for checking if server is alive + * @param fastify fastify webserver instance + */ +export default function healthcheckHandler(fastify: FastifyInstance) { + fastify.get('/healthcheck', (req, reply) => { + return reply.code(200).send('ok'); + }); +} diff --git a/node-server/src/server/services/token_service.ts b/node-server/src/server/services/token_service.ts index 34b9400..78142e5 100644 --- a/node-server/src/server/services/token_service.ts +++ b/node-server/src/server/services/token_service.ts @@ -7,7 +7,7 @@ const MAX_TIMESTAMP = 8640000000000000; export default class TokenService { private _validAccessTokens: {[key: string]: Date} = {}; private _validSessionTokens: {[key: string]: Date} = {}; - private _currentAccessToken = ''; + private _currentAccessToken = ' '; constructor( private _config: ConfigType, @@ -134,7 +134,7 @@ export default class TokenService { */ createSessionToken() { if (!this._config.auth.required) { - return {sessionToken: '', expires: this._computeNewSessionExpiry()}; + return {sessionToken: ' ', expires: this._computeNewSessionExpiry()}; } let sessionToken = crypto.randomBytes(this._config.auth.sessionTokenBytes).toString('base64url'); while (sessionToken in this._validSessionTokens) { diff --git a/node-server/src/server/start_server.ts b/node-server/src/server/start_server.ts index 8c4308b..c9482f9 100644 --- a/node-server/src/server/start_server.ts +++ b/node-server/src/server/start_server.ts @@ -10,6 +10,7 @@ import fastifyHelmet from '@fastify/helmet'; import fastifySensible from '@fastify/sensible'; import TokenService from './services/token_service.js'; import accessTokenHandler from './routes/session_auth_handler.js'; +import healthcheckHandler from './routes/healthcheck_handler.js'; declare module 'fastify' { export interface FastifyInstance { @@ -60,6 +61,7 @@ export default function createServer(config: ConfigType, logger: Logger) { // Register routes fastify.register(websocketHandler); fastify.register(accessTokenHandler); + fastify.register(healthcheckHandler); return fastify; } diff --git a/whisper-service/Dockerfile_CPU b/whisper-service/Dockerfile_CPU index 4277ece..054250f 100644 --- a/whisper-service/Dockerfile_CPU +++ b/whisper-service/Dockerfile_CPU @@ -8,4 +8,9 @@ RUN pip install -r requirements.txt COPY . . +ENV HOST=0.0.0.0 +ENV PORT=80 + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --start-interval=1s --retries=3 CMD curl -f http://localhost:80/healthcheck || exit 1 + CMD ["python", "index.py"] \ No newline at end of file diff --git a/whisper-service/Dockerfile_CUDA b/whisper-service/Dockerfile_CUDA index 31ac24b..09e7a46 100644 --- a/whisper-service/Dockerfile_CUDA +++ b/whisper-service/Dockerfile_CUDA @@ -9,4 +9,9 @@ COPY . . RUN pip install -r requirements.txt +ENV HOST=0.0.0.0 +ENV PORT=80 + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --start-interval=1s --retries=3 CMD curl -f http://localhost:80/healthcheck || exit 1 + CMD ["python3", "index.py"] \ No newline at end of file diff --git a/whisper-service/create_server.py b/whisper-service/create_server.py index 444490e..17318a2 100644 --- a/whisper-service/create_server.py +++ b/whisper-service/create_server.py @@ -31,6 +31,10 @@ def create_server( ''' fastapi_app = FastAPI() + @fastapi_app.get("/healthcheck") + def healthcheck(): + return 'ok' + @fastapi_app.websocket("/whisper") async def whisper( websocket: WebSocket,