From beda08a10cc09c91961e398935e696eb3da6a890 Mon Sep 17 00:00:00 2001 From: Robson Ventura Rodrigues Date: Sat, 13 Apr 2024 11:32:43 -0300 Subject: [PATCH] feat: set up development environment using Docker Compose Implemented a Docker Compose setup to streamline the development environment. This includes defining services for the application server, client, and database, ensuring each component is isolated and easily manageable. Added Dockerfiles and docker-compose.yml with necessary configurations for development efficiency. --- charts/planka/Chart.yaml | 4 +- client/.env | 2 +- client/package-lock.json | 16 +++ client/package.json | 2 + client/src/components/Card/Tasks.jsx | 4 +- client/src/components/Card/Tasks.module.scss | 2 + .../src/components/CardModal/Tasks/Item.jsx | 3 +- client/src/components/Linkify.jsx | 68 ++++++++++++ client/src/containers/LoginContainer.js | 2 +- config/development/Dockerfile.client | 18 ++++ config/development/Dockerfile.server | 14 +++ config/development/nginx.conf | 47 ++++++++ docker-compose-dev.yml | 100 +++++++++++++----- package-lock.json | 4 +- package.json | 2 +- server/api/helpers/utils/send-email.js | 4 +- .../api/helpers/utils/send-slack-message.js | 6 +- 17 files changed, 257 insertions(+), 41 deletions(-) create mode 100644 client/src/components/Linkify.jsx create mode 100644 config/development/Dockerfile.client create mode 100644 config/development/Dockerfile.server create mode 100644 config/development/nginx.conf diff --git a/charts/planka/Chart.yaml b/charts/planka/Chart.yaml index 470f805..02158c7 100644 --- a/charts/planka/Chart.yaml +++ b/charts/planka/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.24 +version: 0.1.26 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.16.2" +appVersion: "1.16.4" dependencies: - alias: postgresql diff --git a/client/.env b/client/.env index e17aeb7..1b48387 100644 --- a/client/.env +++ b/client/.env @@ -1 +1 @@ -REACT_APP_VERSION=1.16.2 +REACT_APP_VERSION=1.16.4 diff --git a/client/package-lock.json b/client/package-lock.json index 62cebb9..b1e6907 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,6 +17,8 @@ "initials": "^3.1.2", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", + "linkify-react": "^4.1.3", + "linkifyjs": "^4.1.3", "lodash": "^4.17.21", "nanoid": "^5.0.3", "node-sass": "^9.0.0", @@ -11911,6 +11913,20 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linkify-react": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.1.3.tgz", + "integrity": "sha512-rhI3zM/fxn5BfRPHfi4r9N7zgac4vOIxub1wHIWXLA5ENTMs+BGaIaFO1D1PhmxgwhIKmJz3H7uCP0Dg5JwSlA==", + "peerDependencies": { + "linkifyjs": "^4.0.0", + "react": ">= 15.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz", + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", diff --git a/client/package.json b/client/package.json index 25cdce0..3f3c4e1 100755 --- a/client/package.json +++ b/client/package.json @@ -70,6 +70,8 @@ "initials": "^3.1.2", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", + "linkify-react": "^4.1.3", + "linkifyjs": "^4.1.3", "lodash": "^4.17.21", "nanoid": "^5.0.3", "node-sass": "^9.0.0", diff --git a/client/src/components/Card/Tasks.jsx b/client/src/components/Card/Tasks.jsx index d76340f..c530474 100644 --- a/client/src/components/Card/Tasks.jsx +++ b/client/src/components/Card/Tasks.jsx @@ -4,6 +4,8 @@ import classNames from 'classnames'; import { Progress } from 'semantic-ui-react'; import { useToggle } from '../../lib/hooks'; +import Linkify from '../Linkify'; + import styles from './Tasks.module.scss'; const Tasks = React.memo(({ items }) => { @@ -48,7 +50,7 @@ const Tasks = React.memo(({ items }) => { key={item.id} className={classNames(styles.task, item.isCompleted && styles.taskCompleted)} > - {item.name} + {item.name} ))} diff --git a/client/src/components/Card/Tasks.module.scss b/client/src/components/Card/Tasks.module.scss index 508ac89..8f8a7ec 100644 --- a/client/src/components/Card/Tasks.module.scss +++ b/client/src/components/Card/Tasks.module.scss @@ -55,8 +55,10 @@ display: block; font-size: 12px; line-height: 14px; + overflow: hidden; padding-bottom: 6px; padding-left: 14px; + text-overflow: ellipsis; &:before { content: "–"; diff --git a/client/src/components/CardModal/Tasks/Item.jsx b/client/src/components/CardModal/Tasks/Item.jsx index a34beb0..8bb3046 100755 --- a/client/src/components/CardModal/Tasks/Item.jsx +++ b/client/src/components/CardModal/Tasks/Item.jsx @@ -8,6 +8,7 @@ import { usePopup } from '../../../lib/popup'; import NameEdit from './NameEdit'; import ActionsStep from './ActionsStep'; +import Linkify from '../../Linkify'; import styles from './Item.module.scss'; @@ -65,7 +66,7 @@ const Item = React.memo( onClick={handleClick} > - {name} + {name} {isPersisted && canEdit && ( diff --git a/client/src/components/Linkify.jsx b/client/src/components/Linkify.jsx new file mode 100644 index 0000000..71b01fd --- /dev/null +++ b/client/src/components/Linkify.jsx @@ -0,0 +1,68 @@ +import React, { useCallback } from 'react'; +import PropTypes from 'prop-types'; +import LinkifyReact from 'linkify-react'; + +import history from '../history'; + +const Linkify = React.memo(({ children, linkStopPropagation, ...props }) => { + const handleLinkClick = useCallback( + (event) => { + if (linkStopPropagation) { + event.stopPropagation(); + } + + if (!event.target.getAttribute('target')) { + event.preventDefault(); + history.push(event.target.href); + } + }, + [linkStopPropagation], + ); + + const linkRenderer = useCallback( + ({ attributes: { href, ...linkProps }, content }) => { + let url; + try { + url = new URL(href, window.location); + } catch (error) {} // eslint-disable-line no-empty + + const isSameSite = !!url && url.origin === window.location.origin; + + return ( + + {isSameSite ? url.pathname : content} + + ); + }, + [handleLinkClick], + ); + + return ( + + {children} + + ); +}); + +Linkify.propTypes = { + children: PropTypes.string.isRequired, + linkStopPropagation: PropTypes.bool, +}; + +Linkify.defaultProps = { + linkStopPropagation: false, +}; + +export default Linkify; diff --git a/client/src/containers/LoginContainer.js b/client/src/containers/LoginContainer.js index 881b65b..1a16788 100755 --- a/client/src/containers/LoginContainer.js +++ b/client/src/containers/LoginContainer.js @@ -20,7 +20,7 @@ const mapStateToProps = (state) => { isSubmittingUsingOidc, error, withOidc: !!oidcConfig, - isOidcEnforced: oidcConfig && oidcConfig.isEnforced, + isOidcEnforced: !!oidcConfig && oidcConfig.isEnforced, }; }; diff --git a/config/development/Dockerfile.client b/config/development/Dockerfile.client new file mode 100644 index 0000000..8a5ed92 --- /dev/null +++ b/config/development/Dockerfile.client @@ -0,0 +1,18 @@ +FROM node:18-alpine as server-dependencies + +RUN apk -U upgrade \ + && apk add build-base python3 \ + --no-cache + +WORKDIR /app/client +COPY package.json package-lock.json /app/client/ +RUN npm install npm@latest --global \ + && npm install pnpm --global \ + && pnpm import \ + && pnpm install + + +WORKDIR /app/ +COPY ../../package.json ../../package-lock.json /app/ +RUN pnpm import \ + && pnpm install diff --git a/config/development/Dockerfile.server b/config/development/Dockerfile.server new file mode 100644 index 0000000..0f51abe --- /dev/null +++ b/config/development/Dockerfile.server @@ -0,0 +1,14 @@ +FROM node:18-alpine as server-dependencies + +RUN apk -U upgrade \ + && apk add build-base python3 \ + --no-cache + +WORKDIR /app + +COPY package.json package-lock.json ./ + +RUN npm install npm@latest --global \ + && npm install pnpm --global \ + && pnpm import \ + && pnpm install diff --git a/config/development/nginx.conf b/config/development/nginx.conf new file mode 100644 index 0000000..94fc7d7 --- /dev/null +++ b/config/development/nginx.conf @@ -0,0 +1,47 @@ +user nginx; +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + server { + listen 80; + + location /api/ { + proxy_pass http://server:1337; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /socket.io { + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://server:1337/socket.io; + } + + location / { + proxy_pass http://client:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + } +} diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 9fe7085..b44da1b 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -1,20 +1,18 @@ -version: '3' +version: '3.8' services: - planka: - image: ghcr.io/plankanban/planka:master - restart: on-failure + + server: + build: + context: ./server + dockerfile: ../config/development/Dockerfile.server volumes: - - user-avatars:/app/public/user-avatars - - project-background-images:/app/public/project-background-images - - attachments:/app/private/attachments - ports: - - 3000:1337 + - ./server:/app + - /app/node_modules environment: - - BASE_URL=http://localhost:3000 - - DATABASE_URL=postgresql://postgres@postgres/planka + - NODE_ENV=development + - DATABASE_URL=postgresql://user:password@postgres:5432/planka_db - SECRET_KEY=notsecretkey - # - TRUST_PROXY=0 # - TOKEN_EXPIRES_IN=365 # In days @@ -31,14 +29,6 @@ services: # - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_USERNAME=demo - # Email Notifications (https://nodemailer.com/smtp/) - # - SMTP_HOST= - # - SMTP_PORT=587 - # - SMTP_SECURE=true - # - SMTP_USER= - # - SMTP_PASSWORD= - # - SMTP_FROM="Demo Demo" - # - OIDC_ISSUER= # - OIDC_CLIENT_ID= # - OIDC_CLIENT_SECRET= @@ -51,26 +41,82 @@ services: # - OIDC_IGNORE_USERNAME=true # - OIDC_IGNORE_ROLES=true # - OIDC_ENFORCED=true + + # Email Notifications (https://nodemailer.com/smtp/) + # - SMTP_HOST= + # - SMTP_PORT=587 + # - SMTP_SECURE=true + # - SMTP_USER= + # - SMTP_PASSWORD= + # - SMTP_FROM="Demo Demo" + + # - SLACK_BOT_TOKEN= + # - SLACK_CHANNEL_ID= + + working_dir: /app + command: ["sh", "-c", "npm run start"] + depends_on: + - init-db + + client: + build: + context: ./client + dockerfile: ../config/development/Dockerfile.client + volumes: + - ./client:/app/client + - /app/client/node_modules + - /app/node_modules + environment: + - NODE_ENV=development + - CHOKIDAR_USEPOLLING=true + - BASE_URL=http://localhost:3000 + - REACT_APP_SERVER_BASE_URL=http://localhost:3000 + working_dir: /app/client + command: npm start + + init-db: + build: + context: ./server + dockerfile: ../config/development/Dockerfile.server + environment: + - DATABASE_URL=postgresql://user:password@postgres:5432/planka_db + + working_dir: /app + command: ["sh", "-c", "npm run db:init"] + volumes: + - ./server:/app + - /app/node_modules depends_on: postgres: condition: service_healthy postgres: - image: postgres:14-alpine - restart: on-failure + image: postgres:latest volumes: - db-data:/var/lib/postgresql/data environment: - - POSTGRES_DB=planka - - POSTGRES_HOST_AUTH_METHOD=trust + POSTGRES_DB: planka_db + POSTGRES_USER: user + POSTGRES_PASSWORD: password + ports: + - "5432:5432" + restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d planka"] interval: 10s timeout: 5s retries: 5 + proxy: + image: nginx:alpine + ports: + - "3000:80" + volumes: + - ./config/development/nginx.conf:/etc/nginx/nginx.conf + depends_on: + - server + - client + + volumes: - user-avatars: - project-background-images: - attachments: db-data: diff --git a/package-lock.json b/package-lock.json index eee91c9..9c2bff2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "planka", - "version": "1.16.2", + "version": "1.16.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "planka", - "version": "1.16.2", + "version": "1.16.4", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 7e06e77..dab2865 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "planka", - "version": "1.16.2", + "version": "1.16.4", "private": true, "homepage": "https://plankanban.github.io/planka", "repository": { diff --git a/server/api/helpers/utils/send-email.js b/server/api/helpers/utils/send-email.js index 7b8d08b..02593f8 100644 --- a/server/api/helpers/utils/send-email.js +++ b/server/api/helpers/utils/send-email.js @@ -23,9 +23,9 @@ module.exports = { from: sails.config.custom.smtpFrom, }); - sails.log.info('Email sent: %s', info.messageId); + sails.log.info(`Email sent: ${info.messageId}`); } catch (error) { - sails.log.error(error); // TODO: provide description text? + sails.log.error(`Error sending email: ${error}`); } }, }; diff --git a/server/api/helpers/utils/send-slack-message.js b/server/api/helpers/utils/send-slack-message.js index 573fcf8..510ae1b 100644 --- a/server/api/helpers/utils/send-slack-message.js +++ b/server/api/helpers/utils/send-slack-message.js @@ -35,19 +35,19 @@ module.exports = { body: JSON.stringify(body), }); } catch (error) { - sails.log.error(error); // TODO: provide description text? + sails.log.error(`Error sending to Slack: ${error}`); return; } if (!response.ok) { - sails.log.error('Error sending to Slack: %s', response.error); + sails.log.error(`Error sending to Slack: ${response.error}`); return; } const responseJson = await response.json(); if (!responseJson.ok) { - sails.log.error('Error sending to Slack: %s', responseJson.error); + sails.log.error(`Error sending to Slack: ${responseJson.error}`); } }, };