diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1d2bc8b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +dist +*.log +*.md +.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ff68e3b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +# This Dockerfile is for running the app in production. It must be connected to +# Redis with docker-compose.yml. + +# Use an Oracle Instant Client base image +FROM oraclelinux:8 AS oracle-client + +# Install Oracle Instant Client via Oracle's YUM repository +RUN dnf install -y oracle-instantclient-release-el8 && \ + dnf install -y oracle-instantclient-basic + +# Use the official Node.js image as the base image +FROM node:18 + +# Set the working directory inside the container +WORKDIR /app + +# Install dependencies for OracleDB +RUN apt-get update && apt-get install -y \ + libaio1 && \ + rm -rf /var/lib/apt/lists/* + +# Copy Oracle Instant Client from the previous stage +COPY --from=oracle-client /usr/lib/oracle /usr/lib/oracle +COPY --from=oracle-client /usr/share/oracle /usr/share/oracle + +# Set environment variables +ENV LD_LIBRARY_PATH=/usr/lib/oracle/21/client64/lib +ENV TNS_ADMIN=/usr/lib/oracle/21/client64/network/admin +ENV PATH="$PATH:/usr/lib/oracle/21/client64/bin" + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install the application dependencies +RUN npm ci + +# Copy the rest of the application files +COPY . . + +# Build the NestJS application +RUN npm run build + +# Expose the application port +EXPOSE 3004 + +# Command to run the application +CMD ["node", "dist/main"] diff --git a/Dockerfile.npm-run b/Dockerfile.npm-run new file mode 100644 index 0000000..8759649 --- /dev/null +++ b/Dockerfile.npm-run @@ -0,0 +1,47 @@ +# This file is for running any npm commands in docker. It is connected to redis +# with docker-compose.npm-run.yml. + +# Use an Oracle Instant Client base image +FROM oraclelinux:8 AS oracle-client + +# Install Oracle Instant Client via Oracle's YUM repository +RUN dnf install -y oracle-instantclient-release-el8 && \ + dnf install -y oracle-instantclient-basic + +# Use the official Node.js image as the base image +FROM node:18 + +# Set the working directory inside the container +WORKDIR /app + +# Install dependencies for OracleDB +RUN apt-get update && apt-get install -y \ + libaio1 && \ + rm -rf /var/lib/apt/lists/* + +# Copy Oracle Instant Client from the previous stage +COPY --from=oracle-client /usr/lib/oracle /usr/lib/oracle +COPY --from=oracle-client /usr/share/oracle /usr/share/oracle + +# Set environment variables +ENV LD_LIBRARY_PATH=/usr/lib/oracle/21/client64/lib +ENV TNS_ADMIN=/usr/lib/oracle/21/client64/network/admin +ENV PATH="$PATH:/usr/lib/oracle/21/client64/bin" + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install the application dependencies +RUN npm ci + +# Copy the rest of the application files +COPY . . + +# Build the NestJS application +RUN npm run build + +# Expose the application port +EXPOSE 3004 + +# Command to run the application +ENTRYPOINT ["npm"] diff --git a/README.md b/README.md index 953ab87..ffbc42c 100644 --- a/README.md +++ b/README.md @@ -12,32 +12,64 @@ Check the [wiki](https://github.com/luomus/laji-api/wiki) for a more technically [Nest](https://github.com/nestjs/nest) -### Installation +### Installation & running + +Fill in `.env` file, using `.env.example` as a template. + +The app can be ran inside a docker, or directly on the host machine. + +#### Docker (recommended) + +Use `docker` to build & start the app: ```bash -$ npm ci +npm run docker ``` -Fill in `.env` file, using `.env.example` as as template. +*Hint:* The docker container runs any npm script, `start:dev` being the default. You can run other scripts like so: + +```bash +npm run docker -- test:e2e-old +``` + +#### Without Docker + +Install the dependencies: + +```bash +$ npm ci +``` -#### Other requirements +Install the following: * [Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) >= 5 -* [Oracle Instantclient](https://node-oracledb.readthedocs.io/en/latest/user_guide/installation.html#instzip) +* [Oracle Instant Client](https://node-oracledb.readthedocs.io/en/latest/user_guide/installation.html#instzip) -### Run -Start Redis, and then run: +##### Running +1. Start Redis +2. Make sure the Oracle Instant Client is installed at `/opt/oracle/instantclient` or adjust the next step accordingly: +3. ```bash $ LD_LIBRARY_PATH=/opt/oracle/instantclient npm run start:dev ``` ### Test +Hint: The npm command works also with the aforementioned Docker container. These examples run `npm` directly for simplicity. + #### Unit tests -Unit tests coverage so far only some core logic. They act also as documentation for how they are supposed to work. +Unit tests cover so far only some core logic. They act also as documentation for how they are supposed to work. + +With Docker: + +```bash +npm run docker -- test +``` + +Without Docker: ```bash npm test @@ -47,10 +79,17 @@ npm test Currently we rely on the e2e tests from the old api. Fill in `integration-test/config.json` and then you can run the tests: +With Docker: + ```bash -$ LD_LIBRARY_PATH=/opt/oracle/instantclient npm run test:e2e-old +$ npm run docker -- test:e2e-old ``` +Without Docker: + +```bash +$ LD_LIBRARY_PATH=/opt/oracle/instantclient npm run test:e2e-old +``` ## Contact diff --git a/docker-compose.npm-run.yml b/docker-compose.npm-run.yml new file mode 100644 index 0000000..5afe599 --- /dev/null +++ b/docker-compose.npm-run.yml @@ -0,0 +1,34 @@ +# This file is for running any npm commands in docker. + +version: "3.8" + +services: + npm-run: + build: + context: . + dockerfile: Dockerfile.npm-run + ports: + - "3004:3004" # Default to 3004 if PORT is not set + #- "${PORT:-3004}:3004" # Default to 3004 if PORT is not set + environment: + - REDIS_URL=redis://redis:6379 + volumes: + - .:/usr/src/app + networks: + - backend + depends_on: + - redis + entrypoint: ["npm", "run"] # Always run `npm run` by default + command: ["start:dev"] # Default argument (overridden if custom command given) + + redis: + image: redis:latest + restart: always + ports: + - "6379:6379" + networks: + - backend + +networks: + backend: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..40d8afa --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +# This file is for running the app in production +version: "3.8" + +services: + laji-api: + build: . + ports: + - "0.0.0.0:${PORT:-3004}:3004" # Default to 3004 if PORT is not set + #- "0.0.0.0:3004:3004" # Default to 3004 if PORT is not set + environment: + - REDIS_URL=redis://redis:6379 + depends_on: + - redis + networks: + - backend + + redis: + image: redis:latest + restart: always + ports: + - "6379:6379" + networks: + - backend + +networks: + backend: + driver: bridge diff --git a/package.json b/package.json index 7828384..e844d52 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", "test:e2e-old": "mocha --timeout 6000 --require integration-test/setup.js ./integration-test/{form-permission,forms,collection,person,notifications,api-user,audio,image,warehouse,named-place,trait,document,annotation,area,information}/tests.js ./integration-test/document/validate-tests.js ./integration-test/document/batch-tests.js --exit", - "test:e2e-old:ci": "npm run test:e2e-old -- -R xunit --grep @no-ci --invert" + "test:e2e-old:ci": "npm run test:e2e-old -- -R xunit --grep @no-ci --invert", + "docker": "docker-compose -f docker-compose.npm-run.yml run npm-run" }, "dependencies": { "@luomus/laji-schema": "^2.0.101", diff --git a/src/main.ts b/src/main.ts index e8fbfa9..5bf8d92 100644 --- a/src/main.ts +++ b/src/main.ts @@ -70,6 +70,7 @@ export async function bootstrap() { .setTitle("Laji API") .setDescription(description) .setVersion("0") + .addServer(configService.get("SWAGGER_SERVER") as string) .addApiKey({ type: "apiKey", name: "access_token", in: "query" }, "access_token") .build() ); diff --git a/src/redis-cache/redis-cache.service.ts b/src/redis-cache/redis-cache.service.ts index 2e97053..40e06d8 100644 --- a/src/redis-cache/redis-cache.service.ts +++ b/src/redis-cache/redis-cache.service.ts @@ -1,4 +1,5 @@ import { Injectable, Logger, OnApplicationShutdown, OnModuleInit } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { createClient } from "redis"; @Injectable() @@ -7,8 +8,12 @@ export class RedisCacheService implements OnModuleInit, OnApplicationShutdown { private client: ReturnType; private logger = new Logger(RedisCacheService.name); + constructor(private config: ConfigService) {} + async onModuleInit() { - this.client = await createClient() + this.client = await createClient({ + url: this.config.get("REDIS_URL"), // Defaults to localhost on port 6379. + }) .on("error", err => { this.logger.error("Connecting to redis failed!"); throw new Error("Connecting to redis failed with message: " + err);