diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bc94557 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,29 @@ +# EditorConfig is awesome: https://EditorConfig.org +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[Dockerfile] +indent_size = 4 + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +[*.pug,] +indent_style = tab +indent_size = 4 + +[*.sql] +indent_style = space +indent_size = 4 + +[*.ts] +quote_type = single diff --git a/DOCKER_SETUP.md b/DOCKER_SETUP.md new file mode 100644 index 0000000..8a995ad --- /dev/null +++ b/DOCKER_SETUP.md @@ -0,0 +1,82 @@ +# Run App on Docker + +Getting started with docker is a breeze! Follow these steps and you'll be contributing in no time. + +## Requirements + +- Docker v26 or newer - [Docker](https://www.docker.com/) + +## Installation + +**Clone the repository** + + ```bash + git clone https://github.com/Worklenz/worklenz.git + ``` + +### Use Docker Compose + +1. **Navigate to the project directory** + + ```bash + cd worklenz + ``` + +2. **Run Compose in detach mode** + + ```bash + docker compose up -d + ``` + +3. **Database Migration (Manual)** + + ```bash + docker compose exec backend bash + ``` + + You should have bash access to backend container. To test connection to db and check for current active db. + + ```bash + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db + SELECT current_database(); + ``` + + If you have an output from the db, you can start running the migrations (one command at a time). + + ```bash + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db -f 1_tables.sql + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db -f 2_triggers.sql + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db -f 3_system-data.sql + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db -f 4_views.sql + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db -f 5_functions.sql + PGPASSWORD=worklenz_password psql -h db -U worklenz_user -d worklenz_db -f 6_user-permission.sql + ``` + + and to verify + + ```bash + \dt + ``` + + Exit and you are good to go. + + ```bash + http://localhost:4200 + ``` + + for frontend access + +4. **Database Migration (Using PGAdmin)** + + ```bash + http://localhost:5050 + ``` + + for pgadmin access + + ```bash + username - admin@worklenz.com + password - worklenz_password + ``` + + Add a connection using db access details and run the *.sql files. diff --git a/docker-compose.yml b/docker-compose.yml index cafc4d1..a9af8b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,48 +1,68 @@ -version: '3.8' - -services: - frontend: - build: - context: ./worklenz-frontend - dockerfile: Dockerfile - container_name: worklenz_frontend - ports: - - "4200:4200" - volumes: - - ./worklenz-frontend:/app - networks: - - worklenz_network - - backend: - build: - context: ./worklenz-backend - dockerfile: Dockerfile - container_name: worklenz_backend - ports: - - "3000:3000" - depends_on: - - db - environment: - - DATABASE_URL=postgres://worklenz_user:worklenz_password@db:5432/worklenz_db - volumes: - - ./worklenz-backend:/app - networks: - - worklenz_network - - db: - image: postgres:15.6 - container_name: worklenz_db - environment: - POSTGRES_USER: worklenz_user - POSTGRES_PASSWORD: worklenz_password - POSTGRES_DB: worklenz_db - volumes: - - postgres_data:/var/lib/postgresql/data - networks: - - worklenz_network - -volumes: - postgres_data: - -networks: - worklenz_network: +services: + frontend: + build: + context: ./worklenz-frontend + dockerfile: Dockerfile + args: + USER: worklenz + APP: worklenz + container_name: worklenz_frontend + ports: + - "4200:4200" + networks: + - worklenz_network + restart: always + + backend: + build: + context: ./worklenz-backend + dockerfile: Dockerfile + args: + USER: worklenz + APP: worklenz + container_name: worklenz_backend + ports: + - "3000:3000" + depends_on: + - db + networks: + - worklenz_network + restart: always + + db: + image: postgres:16.3 + container_name: worklenz_db + environment: + POSTGRES_USER: worklenz_user + POSTGRES_PASSWORD: worklenz_password + POSTGRES_DB: worklenz_db + networks: + - worklenz_network + ports: + - "5432:5432" + restart: always + volumes: + - postgres_data:/var/lib/postgresql/data + + pgadmin: + image: dpage/pgadmin4 + container_name: worklenz_pgadmin + depends_on: + - db + environment: + PGADMIN_DEFAULT_EMAIL: admin@worklenz.com + PGADMIN_DEFAULT_PASSWORD: worklenz_password + networks: + - worklenz_network + ports: + - "5050:80" + restart: always + volumes: + - pgadmin_data:/var/lib/pgadmin + +volumes: + postgres_data: + pgadmin_data: + +networks: + worklenz_network: diff --git a/worklenz-backend/.editorconfig b/worklenz-backend/.editorconfig deleted file mode 100644 index 5f4cb0f..0000000 --- a/worklenz-backend/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -# This file is for unifying the coding style for different editors and IDEs -# editorconfig.org -root = true - -[*] -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -indent_size = 2 - -[*.pug,] -indent_style = tab -indent_size = 4 - -[*.sql] -indent_style = space -indent_size = 4 \ No newline at end of file diff --git a/worklenz-backend/.env.template b/worklenz-backend/.env.template deleted file mode 100644 index 85db9af..0000000 --- a/worklenz-backend/.env.template +++ /dev/null @@ -1,57 +0,0 @@ -# Server -NODE_ENV=development -PORT=3000 -SESSION_NAME=worklenz.sid -SESSION_SECRET="YOUR_SESSION_SECRET_HERE" -COOKIE_SECRET="YOUR_COOKIE_SECRET_HERE" - -# CORS -SOCKET_IO_CORS=http://localhost:4200 -SERVER_CORS=* - -# Database -DB_USER=DATABASE_USER_HERE # default : worklenz_backend (update "user-permission.sql" if needed) -DB_PASSWORD=DATABASE_PASSWORD_HERE -DB_NAME=DATABASE_NAME_HERE # default : worklenz_db -DB_HOST=DATABASE_HOST_HERE # default : localhost -DB_PORT=DATABASE_PORT_HERE # default : 5432 -DB_MAX_CLIENTS=50 - -# Google Login -GOOGLE_CLIENT_ID="GOOGLE_CLIENT_ID_HERE" -GOOGLE_CLIENT_SECRET="GOOGLE_CLIENT_SECRET_HERE" -GOOGLE_CALLBACK_URL="http://localhost:3000/secure/google/verify" -LOGIN_FAILURE_REDIRECT="/" -LOGIN_SUCCESS_REDIRECT="http://localhost:4200/auth/authenticate" - -# CLI -ANGULAR_DIST_DIR="/path/worklenz_frontend/dist/worklenz" -ANGULAR_SRC_DIR="/path/worklenz_frontend" -BACKEND_PUBLIC_DIR="/path/worklenz_backend/src/public" -BACKEND_VIEWS_DIR="/path/worklenz_backend/src/views/admin" -COMMIT_BUILD_IMMEDIATELY=true - -# HOST -HOSTNAME=localhost:4200 - -# SLACK -SLACK_WEBHOOK=SLACK_WEBHOOK_HERE -USE_PG_NATIVE=true - -# JWT SECRET -JWT_SECRET=JWT_SECRET_CODE_HERE - -# AWS -AWS_REGION="us-west-2" -AWS_ACCESS_KEY_ID="AWS_ACCESS_KEY_ID_HERE" -AWS_SECRET_ACCESS_KEY="AWS_SECRET_ACCESS_KEY_HERE" - -# S3 Credentials -REGION="us-west-2" -BUCKET="BUCKET_NAME_HERE" -S3_URL="S3_URL_HERE" -S3_ACCESS_KEY_ID="S3_ACCESS_KEY_ID_HERE" -S3_SECRET_ACCESS_KEY="S3_SECRET_ACCESS_KEY_HERE" - -# SES email -SOURCE_EMAIL="SOURCE_EMAIL_HERE" #Worklenz diff --git a/worklenz-backend/Dockerfile b/worklenz-backend/Dockerfile index c0ae538..7ebcd0f 100644 --- a/worklenz-backend/Dockerfile +++ b/worklenz-backend/Dockerfile @@ -1,26 +1,82 @@ -# Use the official Node.js 18 image as a base -FROM node:18 - -# Create and set the working directory -WORKDIR /usr/src/app - -# Install global dependencies -RUN npm install -g ts-node typescript grunt grunt-cli - -# Copy package.json and package-lock.json (if available) -COPY package*.json ./ - -# Install app dependencies -RUN npm ci - -# Copy the rest of the application code -COPY . . - -# Run the build script to compile TypeScript to JavaScript -RUN npm run build - -# Expose the port the app runs on -EXPOSE 3000 - -# Start the application -CMD ["npm", "start"] +# Use Ubuntu 23.04 as the base image +FROM ubuntu:23.04 + +# Docblock +LABEL author="@worklenz" +LABEL maintainers="" +LABEL description="Backend for Worklenz application" +LABEL website="https://worklenz.com" +LABEL origin="https://github.com/Worklenz/worklenz" +LABEL version="1.0.0" +LABEL license="AGPL-3.0" + +# Set non-root user and project name args. +ARG USER +ARG APP + +# Set non-interactive to avoid prompts during build. +ENV DEBIAN_FRONTEND=noninteractive + +# Set the timezone to EST (Eastern Standard Time). +ENV TZ=America/New_York + +# Update the package list and install necessary dependencies. +RUN apt-get update && apt-get install -y \ + sudo \ + locales \ + tzdata \ + curl \ + gnupg \ + build-essential \ + libpq-dev \ + postgresql-client && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Setup UTF-8 locale and set env. +RUN sed -i '/^#.* en_US.UTF-8 UTF-8/s/^#//' /etc/locale.gen && \ + locale-gen en_US.UTF-8 + +ENV LANG en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 + +# Set the timezone to EST (Eastern Standard Time). +RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \ + dpkg-reconfigure --frontend noninteractive tzdata + +# Install Node.js and npm. +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y nodejs + +# Install TypeScript, ts-node, Grunt, and Grunt CLI globally. +RUN npm install -g ts-node typescript grunt grunt-cli + +# Create a non-root user and group. +RUN groupadd ${USER} && \ + useradd -m -s /bin/bash -g ${USER} ${USER} && \ + echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USER} && \ + chmod 0440 /etc/sudoers.d/${USER} + +# Switch to non-root user. +USER ${USER} + +# Set the working directory. +WORKDIR /home/${USER}/${APP} + +# Copy the application code to the container. +COPY . . + +# Change the ownership of the working directory. +RUN sudo chown -R ${USER}:${USER} /home/${USER}/${APP} + +# Install project dependencies. +RUN npm ci + +# Run the build script to compile TypeScript to JavaScript. +RUN npm run build + +# Expose the port the app runs. +EXPOSE 3000 + +# Start the application. +CMD ["npm", "start"] diff --git a/worklenz-backend/database/6_user-permission.sql b/worklenz-backend/database/6_user-permission.sql index 2d90e11..b958215 100644 --- a/worklenz-backend/database/6_user-permission.sql +++ b/worklenz-backend/database/6_user-permission.sql @@ -1,35 +1,35 @@ --- Default ROLE : worklenz_client --- Default USER : worklenz_backend --- Change DATABASE_NAME, ROLE, PASSWORD and USER as needed. - -REVOKE CREATE ON SCHEMA public FROM PUBLIC; -CREATE ROLE worklenz_client; - -GRANT CONNECT ON DATABASE 'DATABASE_NAME' TO worklenz_client; -GRANT INSERT, SELECT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO worklenz_client; - -GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO worklenz_client; - -REVOKE ALL PRIVILEGES ON task_priorities FROM worklenz_client; -GRANT SELECT ON task_priorities TO worklenz_client; - -REVOKE ALL PRIVILEGES ON project_access_levels FROM worklenz_client; -GRANT SELECT ON project_access_levels TO worklenz_client; - -REVOKE ALL PRIVILEGES ON timezones FROM worklenz_client; -GRANT SELECT ON timezones TO worklenz_client; - -REVOKE ALL PRIVILEGES ON worklenz_alerts FROM worklenz_client; -GRANT SELECT ON worklenz_alerts TO worklenz_client; - -REVOKE ALL PRIVILEGES ON sys_task_status_categories FROM worklenz_client; -GRANT SELECT ON sys_task_status_categories TO worklenz_client; - -REVOKE ALL PRIVILEGES ON sys_project_statuses FROM worklenz_client; -GRANT SELECT ON sys_project_statuses TO worklenz_client; - -REVOKE ALL PRIVILEGES ON sys_project_healths FROM worklenz_client; -GRANT SELECT ON sys_project_healths TO worklenz_client; - -CREATE USER worklenz_backend WITH PASSWORD 'PASSWORD'; -GRANT worklenz_client TO worklenz_backend; +-- Default ROLE : worklenz_client +-- Default USER : worklenz_backend +-- Change DATABASE_NAME, ROLE, PASSWORD and USER as needed. + +REVOKE CREATE ON SCHEMA public FROM PUBLIC; +CREATE ROLE worklenz_client; + +GRANT CONNECT ON DATABASE 'worklenz_db' TO worklenz_client; +GRANT INSERT, SELECT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO worklenz_client; + +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO worklenz_client; + +REVOKE ALL PRIVILEGES ON task_priorities FROM worklenz_client; +GRANT SELECT ON task_priorities TO worklenz_client; + +REVOKE ALL PRIVILEGES ON project_access_levels FROM worklenz_client; +GRANT SELECT ON project_access_levels TO worklenz_client; + +REVOKE ALL PRIVILEGES ON timezones FROM worklenz_client; +GRANT SELECT ON timezones TO worklenz_client; + +REVOKE ALL PRIVILEGES ON worklenz_alerts FROM worklenz_client; +GRANT SELECT ON worklenz_alerts TO worklenz_client; + +REVOKE ALL PRIVILEGES ON sys_task_status_categories FROM worklenz_client; +GRANT SELECT ON sys_task_status_categories TO worklenz_client; + +REVOKE ALL PRIVILEGES ON sys_project_statuses FROM worklenz_client; +GRANT SELECT ON sys_project_statuses TO worklenz_client; + +REVOKE ALL PRIVILEGES ON sys_project_healths FROM worklenz_client; +GRANT SELECT ON sys_project_healths TO worklenz_client; + +CREATE USER worklenz_backend WITH PASSWORD 'worklenz_password'; +GRANT worklenz_client TO worklenz_user; diff --git a/worklenz-backend/package.json b/worklenz-backend/package.json index db238e1..e5979ec 100644 --- a/worklenz-backend/package.json +++ b/worklenz-backend/package.json @@ -1,147 +1,147 @@ -{ - "name": "worklenz-backend", - "version": "1.4.16", - "private": true, - "engines": { - "npm": ">=8.11.0", - "node": ">=16.13.0", - "yarn": "WARNING: Please use npm package manager instead of yarn" - }, - "main": "build/bin/www", - "repository": "GITHUB_REPO_HERE", - "author": "worklenz.com", - "scripts": { - "start": "node ./build/bin/www", - "tcs": "grunt build:tsc", - "build": "grunt build", - "watch": "grunt watch", - "es": "esbuild `find src -type f -name '*.ts'` --platform=node --minify=true --watch=true --target=esnext --format=cjs --tsconfig=tsconfig.prod.json --outdir=dist", - "copy": "grunt copy", - "sonar": "sonar-scanner -Dproject.settings=sonar-project-dev.properties", - "tsc": "tsc", - "test": "jest --setupFiles dotenv/config", - "test:watch": "jest --watch --setupFiles dotenv/config" - }, - "jestSonar": { - "reportPath": "coverage", - "reportFile": "test-reporter.xml", - "indent": 4 - }, - "dependencies": { - "@aws-sdk/client-s3": "^3.378.0", - "@aws-sdk/client-ses": "^3.378.0", - "@aws-sdk/s3-request-presigner": "^3.378.0", - "@aws-sdk/util-format-url": "^3.357.0", - "axios": "^1.6.0", - "bcrypt": "^5.1.0", - "bluebird": "^3.7.2", - "compression": "^1.7.4", - "connect-flash": "^0.1.1", - "connect-pg-simple": "^7.0.0", - "cookie-parser": "~1.4.4", - "cors": "^2.8.5", - "cron": "^2.4.0", - "csurf": "^1.11.0", - "debug": "^4.3.4", - "dotenv": "^16.3.1", - "exceljs": "^4.3.0", - "express": "^4.18.2", - "express-rate-limit": "^6.8.0", - "express-session": "^1.17.3", - "express-validator": "^6.15.0", - "helmet": "^6.2.0", - "hpp": "^0.2.3", - "http-errors": "^2.0.0", - "jsonschema": "^1.4.1", - "jsonwebtoken": "^9.0.1", - "lodash": "^4.17.21", - "mime-types": "^2.1.35", - "moment": "^2.29.4", - "moment-timezone": "^0.5.43", - "morgan": "^1.10.0", - "nanoid": "^3.3.6", - "passport": "^0.5.3", - "passport-google-oauth2": "^0.2.0", - "passport-google-oauth20": "^2.0.0", - "passport-local": "^1.0.0", - "path": "^0.12.7", - "pg": "^8.11.1", - "pg-native": "^3.0.1", - "pug": "^3.0.2", - "redis": "^4.6.7", - "sanitize-html": "^2.11.0", - "segfault-handler": "^1.3.0", - "sharp": "^0.32.6", - "slugify": "^1.6.6", - "socket.io": "^4.7.1", - "uglify-js": "^3.17.4", - "winston": "^3.10.0", - "xss-filters": "^1.2.7" - }, - "devDependencies": { - "@babel/preset-env": "^7.22.9", - "@babel/preset-typescript": "^7.22.5", - "@types/bcrypt": "^5.0.0", - "@types/bluebird": "^3.5.38", - "@types/compression": "^1.7.2", - "@types/connect-flash": "^0.0.37", - "@types/cookie-parser": "^1.4.3", - "@types/cron": "^2.0.1", - "@types/csurf": "^1.11.2", - "@types/express": "^4.17.17", - "@types/express-brute": "^1.0.2", - "@types/express-brute-redis": "^0.0.4", - "@types/express-rate-limit": "^6.0.0", - "@types/express-session": "^1.17.7", - "@types/express-validator": "^3.0.0", - "@types/fs-extra": "^9.0.13", - "@types/hpp": "^0.2.2", - "@types/http-errors": "^1.8.2", - "@types/jest": "^28.1.8", - "@types/jsonwebtoken": "^9.0.2", - "@types/lodash": "^4.14.196", - "@types/mime-types": "^2.1.1", - "@types/morgan": "^1.9.4", - "@types/node": "^18.17.1", - "@types/passport": "^1.0.12", - "@types/passport-google-oauth20": "^2.0.11", - "@types/passport-local": "^1.0.35", - "@types/pg": "^8.10.2", - "@types/pug": "^2.0.6", - "@types/redis": "^4.0.11", - "@types/sanitize-html": "^2.9.0", - "@types/sharp": "^0.31.1", - "@types/socket.io": "^3.0.2", - "@types/swagger-jsdoc": "^6.0.1", - "@types/toobusy-js": "^0.5.2", - "@types/uglify-js": "^3.17.1", - "@types/xss-filters": "^0.0.27", - "@typescript-eslint/eslint-plugin": "^5.62.0", - "@typescript-eslint/parser": "^5.62.0", - "chokidar": "^3.5.3", - "esbuild": "^0.17.19", - "esbuild-envfile-plugin": "^1.0.5", - "esbuild-node-externals": "^1.8.0", - "eslint": "^8.45.0", - "eslint-plugin-security": "^1.7.1", - "fs-extra": "^10.1.0", - "grunt": "^1.6.1", - "grunt-cli": "^1.4.3", - "grunt-contrib-clean": "^2.0.1", - "grunt-contrib-compress": "^2.0.0", - "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-uglify": "^5.2.2", - "grunt-contrib-watch": "^1.1.0", - "grunt-shell": "^4.0.0", - "grunt-sync": "^0.8.2", - "jest": "^28.1.3", - "jest-sonar-reporter": "^2.0.0", - "ncp": "^2.0.0", - "nodeman": "^1.1.2", - "swagger-jsdoc": "^6.2.8", - "ts-jest": "^28.0.8", - "ts-node": "^10.9.1", - "tslint": "^6.1.3", - "typescript": "^4.9.5" - } -} +{ + "name": "worklenz-backend", + "version": "1.4.16", + "private": true, + "engines": { + "npm": ">=8.11.0", + "node": ">=16.13.0", + "yarn": "WARNING: Please use npm package manager instead of yarn" + }, + "main": "build/bin/www", + "repository": "https://github.com/Worklenz/worklenz", + "author": "worklenz.com", + "scripts": { + "start": "node ./build/bin/www", + "tcs": "grunt build:tsc", + "build": "grunt build", + "watch": "grunt watch", + "es": "esbuild `find src -type f -name '*.ts'` --platform=node --minify=true --watch=true --target=esnext --format=cjs --tsconfig=tsconfig.prod.json --outdir=dist", + "copy": "grunt copy", + "sonar": "sonar-scanner -Dproject.settings=sonar-project-dev.properties", + "tsc": "tsc", + "test": "jest --setupFiles dotenv/config", + "test:watch": "jest --watch --setupFiles dotenv/config" + }, + "jestSonar": { + "reportPath": "coverage", + "reportFile": "test-reporter.xml", + "indent": 4 + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.378.0", + "@aws-sdk/client-ses": "^3.378.0", + "@aws-sdk/s3-request-presigner": "^3.378.0", + "@aws-sdk/util-format-url": "^3.357.0", + "axios": "^1.6.0", + "bcrypt": "^5.1.0", + "bluebird": "^3.7.2", + "compression": "^1.7.4", + "connect-flash": "^0.1.1", + "connect-pg-simple": "^7.0.0", + "cookie-parser": "~1.4.4", + "cors": "^2.8.5", + "cron": "^2.4.0", + "csurf": "^1.11.0", + "debug": "^4.3.4", + "dotenv": "^16.3.1", + "exceljs": "^4.3.0", + "express": "^4.18.2", + "express-rate-limit": "^6.8.0", + "express-session": "^1.17.3", + "express-validator": "^6.15.0", + "helmet": "^6.2.0", + "hpp": "^0.2.3", + "http-errors": "^2.0.0", + "jsonschema": "^1.4.1", + "jsonwebtoken": "^9.0.1", + "lodash": "^4.17.21", + "mime-types": "^2.1.35", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "morgan": "^1.10.0", + "nanoid": "^3.3.6", + "passport": "^0.5.3", + "passport-google-oauth2": "^0.2.0", + "passport-google-oauth20": "^2.0.0", + "passport-local": "^1.0.0", + "path": "^0.12.7", + "pg": "^8.11.1", + "pg-native": "^3.0.1", + "pug": "^3.0.2", + "redis": "^4.6.7", + "sanitize-html": "^2.11.0", + "segfault-handler": "^1.3.0", + "sharp": "^0.32.6", + "slugify": "^1.6.6", + "socket.io": "^4.7.1", + "uglify-js": "^3.17.4", + "winston": "^3.10.0", + "xss-filters": "^1.2.7" + }, + "devDependencies": { + "@babel/preset-env": "^7.22.9", + "@babel/preset-typescript": "^7.22.5", + "@types/bcrypt": "^5.0.0", + "@types/bluebird": "^3.5.38", + "@types/compression": "^1.7.2", + "@types/connect-flash": "^0.0.37", + "@types/cookie-parser": "^1.4.3", + "@types/cron": "^2.0.1", + "@types/csurf": "^1.11.2", + "@types/express": "^4.17.17", + "@types/express-brute": "^1.0.2", + "@types/express-brute-redis": "^0.0.4", + "@types/express-rate-limit": "^6.0.0", + "@types/express-session": "^1.17.7", + "@types/express-validator": "^3.0.0", + "@types/fs-extra": "^9.0.13", + "@types/hpp": "^0.2.2", + "@types/http-errors": "^1.8.2", + "@types/jest": "^28.1.8", + "@types/jsonwebtoken": "^9.0.2", + "@types/lodash": "^4.14.196", + "@types/mime-types": "^2.1.1", + "@types/morgan": "^1.9.4", + "@types/node": "^18.17.1", + "@types/passport": "^1.0.12", + "@types/passport-google-oauth20": "^2.0.11", + "@types/passport-local": "^1.0.35", + "@types/pg": "^8.10.2", + "@types/pug": "^2.0.6", + "@types/redis": "^4.0.11", + "@types/sanitize-html": "^2.9.0", + "@types/sharp": "^0.31.1", + "@types/socket.io": "^3.0.2", + "@types/swagger-jsdoc": "^6.0.1", + "@types/toobusy-js": "^0.5.2", + "@types/uglify-js": "^3.17.1", + "@types/xss-filters": "^0.0.27", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "chokidar": "^3.5.3", + "esbuild": "^0.17.19", + "esbuild-envfile-plugin": "^1.0.5", + "esbuild-node-externals": "^1.8.0", + "eslint": "^8.45.0", + "eslint-plugin-security": "^1.7.1", + "fs-extra": "^10.1.0", + "grunt": "^1.6.1", + "grunt-cli": "^1.4.3", + "grunt-contrib-clean": "^2.0.1", + "grunt-contrib-compress": "^2.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-uglify": "^5.2.2", + "grunt-contrib-watch": "^1.1.0", + "grunt-shell": "^4.0.0", + "grunt-sync": "^0.8.2", + "jest": "^28.1.3", + "jest-sonar-reporter": "^2.0.0", + "ncp": "^2.0.0", + "nodeman": "^1.1.2", + "swagger-jsdoc": "^6.2.8", + "ts-jest": "^28.0.8", + "ts-node": "^10.9.1", + "tslint": "^6.1.3", + "typescript": "^4.9.5" + } +} diff --git a/worklenz-frontend/.editorconfig b/worklenz-frontend/.editorconfig deleted file mode 100644 index 59d9a3a..0000000 --- a/worklenz-frontend/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# Editor configuration, see https://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.ts] -quote_type = single - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/worklenz-frontend/Dockerfile b/worklenz-frontend/Dockerfile index fb8f7d9..c1e89ed 100644 --- a/worklenz-frontend/Dockerfile +++ b/worklenz-frontend/Dockerfile @@ -1,11 +1,76 @@ -FROM node:alpine - -WORKDIR /usr/src/app - -COPY . /usr/src/app - -RUN npm install -g @angular/cli - -RUN npm install - -CMD ["npm", "start"] +# Use Ubuntu 23.04 as the base image +FROM ubuntu:23.04 + +# Docblock +LABEL author="@worklenz" +LABEL maintainers="" +LABEL description="Frontend for Worklenz application" +LABEL website="https://worklenz.com" +LABEL origin="https://github.com/Worklenz/worklenz" +LABEL version="1.0.0" +LABEL license="AGPL-3.0" + +# Set non-root user and project name args. +ARG USER +ARG APP + +# Set non-interactive to avoid prompts during build. +ENV DEBIAN_FRONTEND=noninteractive + +# Set the timezone to EST (Eastern Standard Time). +ENV TZ=America/New_York + +# Update the package list and install necessary dependencies. +RUN apt-get update && apt-get install -y \ + sudo \ + locales \ + tzdata \ + curl \ + gnupg && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Setup UTF-8 locale and set env. +RUN sed -i '/^#.* en_US.UTF-8 UTF-8/s/^#//' /etc/locale.gen && \ + locale-gen en_US.UTF-8 + +ENV LANG en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 + +# Set the timezone to EST (Eastern Standard Time). +RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \ + dpkg-reconfigure --frontend noninteractive tzdata + +# Install Node.js and npm. +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y nodejs + +# Install Angular CLI globally. +RUN npm install -g @angular/cli + +# Create a non-root user and group. +RUN groupadd ${USER} && \ + useradd -m -s /bin/bash -g ${USER} ${USER} && \ + echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USER} && \ + chmod 0440 /etc/sudoers.d/${USER} + +# Switch to non-root user. +USER ${USER} + +# Set the working directory. +WORKDIR /home/${USER}/${APP} + +# Copy the application code to the container. +COPY . . + +# Change the ownership of the working directory. +RUN sudo chown -R ${USER}:${USER} /home/${USER}/${APP} + +# Install project dependencies. +RUN npm ci + +# Expose the port the app runs. +EXPOSE 4200 + +# Start the application. +CMD ["npm", "start"] diff --git a/worklenz-frontend/package.json b/worklenz-frontend/package.json index 65432dc..d9b88f0 100644 --- a/worklenz-frontend/package.json +++ b/worklenz-frontend/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "scripts": { "ng": "ng", - "start": "ng serve --proxy-config proxy.config.json --disable-host-check", + "start": "ng serve --proxy-config proxy.config.json --disable-host-check --host 0.0.0.0", "build": "ng build --extract-licenses --common-chunk --delete-output-path --output-hashing=all", "watch": "ng build --watch --configuration development", "test": "ng test", diff --git a/worklenz-frontend/proxy.config.json b/worklenz-frontend/proxy.config.json index 6866fe1..a46fefc 100644 --- a/worklenz-frontend/proxy.config.json +++ b/worklenz-frontend/proxy.config.json @@ -1,24 +1,24 @@ -{ - "/api": { - "target": "http://127.0.0.1:3000/", - "headers": { - "language": "en", - "Accept": "application/json", - "Content-Type": "application/json; charset=utf-8" - }, - "secure": false, - "changeOrigin": true, - "logLevel": "debug" - }, - "/secure": { - "target": "http://127.0.0.1:3000/", - "headers": { - "language": "en", - "Accept": "application/json", - "Content-Type": "application/json; charset=utf-8" - }, - "secure": false, - "changeOrigin": true, - "logLevel": "debug" - } -} +{ + "/api": { + "target": "http://backend:3000/", + "headers": { + "language": "en", + "Accept": "application/json", + "Content-Type": "application/json; charset=utf-8" + }, + "secure": false, + "changeOrigin": true, + "logLevel": "debug" + }, + "/secure": { + "target": "http://backend:3000/", + "headers": { + "language": "en", + "Accept": "application/json", + "Content-Type": "application/json; charset=utf-8" + }, + "secure": false, + "changeOrigin": true, + "logLevel": "debug" + } +}