mirror of
https://github.com/plankanban/planka.git
synced 2025-08-10 16:05:35 +02:00
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.
This commit is contained in:
parent
ea1b3b7f92
commit
beda08a10c
17 changed files with 259 additions and 43 deletions
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
REACT_APP_VERSION=1.16.2
|
||||
REACT_APP_VERSION=1.16.4
|
||||
|
|
16
client/package-lock.json
generated
16
client/package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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}
|
||||
<Linkify linkStopPropagation>{item.name}</Linkify>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -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: "–";
|
||||
|
|
|
@ -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}
|
||||
>
|
||||
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
|
||||
{name}
|
||||
<Linkify linkStopPropagation>{name}</Linkify>
|
||||
</span>
|
||||
</span>
|
||||
{isPersisted && canEdit && (
|
||||
|
|
68
client/src/components/Linkify.jsx
Normal file
68
client/src/components/Linkify.jsx
Normal file
|
@ -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 (
|
||||
<a
|
||||
{...linkProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||
href={href}
|
||||
target={isSameSite ? undefined : '_blank'}
|
||||
rel={isSameSite ? undefined : 'noreferrer'}
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
{isSameSite ? url.pathname : content}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
[handleLinkClick],
|
||||
);
|
||||
|
||||
return (
|
||||
<LinkifyReact
|
||||
{...props} // eslint-disable-line react/jsx-props-no-spreading
|
||||
options={{
|
||||
defaultProtocol: 'https',
|
||||
render: linkRenderer,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</LinkifyReact>
|
||||
);
|
||||
});
|
||||
|
||||
Linkify.propTypes = {
|
||||
children: PropTypes.string.isRequired,
|
||||
linkStopPropagation: PropTypes.bool,
|
||||
};
|
||||
|
||||
Linkify.defaultProps = {
|
||||
linkStopPropagation: false,
|
||||
};
|
||||
|
||||
export default Linkify;
|
|
@ -20,7 +20,7 @@ const mapStateToProps = (state) => {
|
|||
isSubmittingUsingOidc,
|
||||
error,
|
||||
withOidc: !!oidcConfig,
|
||||
isOidcEnforced: oidcConfig && oidcConfig.isEnforced,
|
||||
isOidcEnforced: !!oidcConfig && oidcConfig.isEnforced,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
18
config/development/Dockerfile.client
Normal file
18
config/development/Dockerfile.client
Normal file
|
@ -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
|
14
config/development/Dockerfile.server
Normal file
14
config/development/Dockerfile.server
Normal file
|
@ -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
|
47
config/development/nginx.conf
Normal file
47
config/development/nginx.conf
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,18 @@
|
|||
version: '3'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
planka:
|
||||
image: ghcr.io/plankanban/planka:master
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- user-avatars:/app/public/user-avatars
|
||||
- project-background-images:/app/public/project-background-images
|
||||
- attachments:/app/private/attachments
|
||||
ports:
|
||||
- 3000:1337
|
||||
environment:
|
||||
- BASE_URL=http://localhost:3000
|
||||
- DATABASE_URL=postgresql://postgres@postgres/planka
|
||||
- SECRET_KEY=notsecretkey
|
||||
|
||||
server:
|
||||
build:
|
||||
context: ./server
|
||||
dockerfile: ../config/development/Dockerfile.server
|
||||
volumes:
|
||||
- ./server:/app
|
||||
- /app/node_modules
|
||||
environment:
|
||||
- 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" <demo@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" <demo@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:
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -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": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "planka",
|
||||
"version": "1.16.2",
|
||||
"version": "1.16.4",
|
||||
"private": true,
|
||||
"homepage": "https://plankanban.github.io/planka",
|
||||
"repository": {
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue