feat: init
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
chuan
2025-11-11 01:56:44 +08:00
commit bba4bb40c8
4638 changed files with 447437 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
ARG PLANE_VERSION=v0.27.1
FROM --platform=$BUILDPLATFORM tonistiigi/binfmt AS binfmt
# **************************************************
# STAGE 0: Image Loading
# **************************************************
FROM node:22-alpine AS node
FROM artifacts.plane.so/makeplane/plane-frontend:${PLANE_VERSION} AS web-img
FROM artifacts.plane.so/makeplane/plane-backend:${PLANE_VERSION} AS backend-img
FROM artifacts.plane.so/makeplane/plane-space:${PLANE_VERSION} AS space-img
FROM artifacts.plane.so/makeplane/plane-admin:${PLANE_VERSION} AS admin-img
FROM artifacts.plane.so/makeplane/plane-live:${PLANE_VERSION} AS live-img
FROM artifacts.plane.so/makeplane/plane-proxy:${PLANE_VERSION} AS proxy-img
# **************************************************
# STAGE 1: Runner
# **************************************************
FROM python:3.12.10-alpine AS runner
WORKDIR /app
RUN apk add --no-cache \
"libpq" \
"libxslt" \
"xmlsec"
COPY --from=node /usr/lib /usr/lib
COPY --from=node /usr/local/lib /usr/local/lib
COPY --from=node /usr/local/include /usr/local/include
COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=web-img /app /app/web
COPY --from=space-img /app /app/space
COPY --from=admin-img /app /app/admin
COPY --from=live-img /app /app/live
RUN rm -rf /app/web/apps/web/.next/cache && \
rm -rf /app/space/apps/space/.next/cache && \
rm -rf /app/admin/apps/admin/.next/cache
COPY --from=proxy-img /usr/bin/caddy /usr/bin/caddy
COPY dist/Caddyfile /app/proxy/Caddyfile
COPY --from=backend-img /code /app/backend
COPY --from=backend-img /usr/local/lib/python3.12/site-packages/ /usr/local/lib/python3.12/site-packages/
COPY --from=backend-img /usr/local/bin/ /usr/local/bin/
RUN apk add --no-cache nss-tools bash curl uuidgen ncdu vim
RUN pip install supervisor
RUN mkdir -p /etc/supervisor/conf.d
COPY start.sh /app/start.sh
COPY dist/plane.env /app/plane.env
COPY supervisor.conf /etc/supervisor/conf.d/supervisor.conf
RUN mkdir -p /app/logs/access && \
mkdir -p /app/logs/error && \
mkdir -p /app/data && \
chmod +x /app/start.sh
VOLUME ['/app/data', '/app/logs']
EXPOSE 80 443
CMD ["/app/start.sh"]

View File

@@ -0,0 +1,174 @@
# Plane Community All-In-One (AIO) Docker Image
The Plane Community All-In-One Docker image packages all Plane services into a single container for easy deployment and testing. This image includes web interface, API server, background workers, live server, and more.
## What's Included
The AIO image contains the following services:
- **Web App** (Port 3001): Main Plane web interface
- **Space** (Port 3002): Public project spaces
- **Admin** (Port 3003): Administrative interface
- **API Server** (Port 3004): Backend API
- **Live Server** (Port 3005): Real-time collaboration
- **Proxy** (Port 80, 443): Caddy reverse proxy
- **Worker & Beat**: Background task processing
## Prerequisites
### Required External Services
The AIO image requires these external services to be running:
- **PostgreSQL Database**: For data storage
- **Redis**: For caching and session management
- **RabbitMQ**: For message queuing
- **S3-Compatible Storage**: For file uploads (AWS S3 or MinIO)
### Required Environment Variables
You must provide these environment variables:
#### Core Configuration
- `DOMAIN_NAME`: Your domain name or IP address
- `DATABASE_URL`: PostgreSQL connection string
- `REDIS_URL`: Redis connection string
- `AMQP_URL`: RabbitMQ connection string
#### Storage Configuration
- `AWS_REGION`: AWS region (e.g., us-east-1)
- `AWS_ACCESS_KEY_ID`: S3 access key
- `AWS_SECRET_ACCESS_KEY`: S3 secret key
- `AWS_S3_BUCKET_NAME`: S3 bucket name
- `AWS_S3_ENDPOINT_URL`: S3 endpoint (optional, defaults to AWS)
## Quick Start
### Basic Usage
```bash
docker run --name plane-aio --rm -it \
-p 80:80 \
-e DOMAIN_NAME=your-domain.com \
-e DATABASE_URL=postgresql://user:pass@host:port/database \
-e REDIS_URL=redis://host:port \
-e AMQP_URL=amqp://user:pass@host:port/vhost \
-e AWS_REGION=us-east-1 \
-e AWS_ACCESS_KEY_ID=your-access-key \
-e AWS_SECRET_ACCESS_KEY=your-secret-key \
-e AWS_S3_BUCKET_NAME=your-bucket \
artifacts.plane.so/makeplane/plane-aio-community:latest
```
### Example with IP Address
```bash
MYIP=192.168.68.169
docker run --name myaio --rm -it \
-p 80:80 \
-e DOMAIN_NAME=${MYIP} \
-e DATABASE_URL=postgresql://plane:plane@${MYIP}:15432/plane \
-e REDIS_URL=redis://${MYIP}:16379 \
-e AMQP_URL=amqp://plane:plane@${MYIP}:15673/plane \
-e AWS_REGION=us-east-1 \
-e AWS_ACCESS_KEY_ID=5MV45J9NF5TEFZWYCRAX \
-e AWS_SECRET_ACCESS_KEY=7xMqAiAHsf2UUjMH+EwICXlyJL9TO30m8leEaDsL \
-e AWS_S3_BUCKET_NAME=plane-app \
-e AWS_S3_ENDPOINT_URL=http://${MYIP}:19000 \
-e FILE_SIZE_LIMIT=10485760 \
artifacts.plane.so/makeplane/plane-aio-community:latest
```
## Configuration Options
### Optional Environment Variables
#### Network & Protocol
- `SITE_ADDRESS`: Server bind address (default: `:80`)
#### Security & Secrets
- `SECRET_KEY`: Django secret key (default provided)
- `LIVE_SERVER_SECRET_KEY`: Live server secret (default provided)
#### File Handling
- `FILE_SIZE_LIMIT`: Maximum file upload size in bytes (default: `5242880` = 5MB)
#### API Configuration
- `API_KEY_RATE_LIMIT`: API key rate limit (default: `60/minute`)
## Port Mapping
The following ports are exposed:
- `80`: Main web interface (HTTP)
- `443`: HTTPS (if SSL configured)
## Volume Mounts
### Recommended Persistent Volumes
```bash
-v /path/to/logs:/app/logs \
-v /path/to/data:/app/data
```
## Building the Image
To build the AIO image yourself:
```bash
cd deployments/aio/community
IMAGE_NAME=myplane-aio ./build.sh --release=v0.27.1 [--platform=linux/amd64]
```
Available build options:
- `--release`: Plane version to build (required)
- `--image-name`: Custom image name (default: `plane-aio-community`)
## Troubleshooting
### Logs
All service logs are available in `/app/logs/`:
- Access logs: `/app/logs/access/`
- Error logs: `/app/logs/error/`
### Health Checks
The container runs multiple services managed by Supervisor. Check service status:
```bash
docker exec -it <container-name> supervisorctl status
```
### Common Issues
1. **Database Connection Failed**: Ensure PostgreSQL is accessible and credentials are correct
2. **Redis Connection Failed**: Verify Redis server is running and URL is correct
3. **File Upload Issues**: Check S3 credentials and bucket permissions
### Environment Validation
The container will validate required environment variables on startup and display helpful error messages if any are missing.
## Production Considerations
- Use proper SSL certificates for HTTPS
- Configure proper backup strategies for data
- Monitor resource usage and scale accordingly
- Use external load balancer for high availability
- Regularly update to latest versions
- Secure your environment variables and secrets
## Support
For issues and support, please refer to the official Plane documentation.

View File

@@ -0,0 +1,155 @@
#!/bin/bash
set -e
DIST_DIR=${DIST_DIR:-./dist}
CPU_ARCH=$(uname -m)
IMAGE_NAME=${IMAGE_NAME:-makeplane/plane-aio-community}
# loop though all flags and set the variables
for arg in "$@"; do
case $arg in
--release)
APP_RELEASE_VERSION="$2"
shift
shift
;;
--release=*)
APP_RELEASE_VERSION="${arg#*=}"
shift
;;
--image-name)
IMAGE_NAME="$2"
shift
shift
;;
--image-name=*)
IMAGE_NAME="${arg#*=}"
shift
;;
esac
done
if [ -z "$APP_RELEASE_VERSION" ]; then
echo ""
echo "Usage: "
echo " ./build.sh [flags]"
echo ""
echo "Flags:"
echo " --release=<APP_RELEASE_VERSION> required (e.g. v0.27.1)"
echo ""
echo "Example: ./build.sh --release=v0.27.1 --platform=linux/amd64"
exit 1
fi
# Install yq if not present
if ! command -v yq &> /dev/null; then
echo "Installing yq..."
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${CPU_ARCH}
sudo chmod +x /usr/local/bin/yq
fi
cd $(dirname "$0")
string_replace(){
local file="$1"
local search="$2"
local replace="$3"
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s|$search|$replace|g" "$file"
else
sed -i "s|$search|$replace|g" "$file"
fi
}
remove_line(){
local file="$1"
local line="$2"
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' '/'$line'/d' "$file"
else
sed -i '/'$line'/d' "$file"
fi
}
update_env_file(){
local file="$1"
local key="$2"
local value="$3"
# if key is in file, replace it
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's|^'$key'=.*|'$key'='$value'|' "$file"
else
sed -i 's|^'$key'=.*|'$key'='$value'|' "$file"
fi
# if key not in file, add it
if ! grep -q "^$key=" "$file"; then
echo "$key=$value" >> "$file"
fi
}
build_dist_files(){
cp ./variables.env $DIST_DIR/plane.env
cp ../../../apps/proxy/Caddyfile.ce $DIST_DIR/Caddyfile
echo "" >> $DIST_DIR/plane.env
echo "" >> $DIST_DIR/plane.env
# update the plane.env file with the APP_RELEASE_VERSION
update_env_file $DIST_DIR/plane.env "APP_RELEASE_VERSION" "$APP_RELEASE_VERSION"
update_env_file $DIST_DIR/plane.env "APP_RELEASE" "$APP_RELEASE_VERSION"
update_env_file $DIST_DIR/plane.env "APP_VERSION" "$APP_RELEASE_VERSION"
update_env_file $DIST_DIR/plane.env "API_BASE_URL" "http://localhost:3004"
update_env_file $DIST_DIR/plane.env "SITE_ADDRESS" ":80"
# remove this line containing `plane-minio:9000`
remove_line $DIST_DIR/Caddyfile "plane-minio:9000" ""
# in caddyfile, update `reverse_proxy /spaces/* space:3000` to `reverse_proxy /spaces/* space:3002`
string_replace $DIST_DIR/Caddyfile "web:3000" "localhost:3001"
string_replace $DIST_DIR/Caddyfile "space:3000" "localhost:3002"
string_replace $DIST_DIR/Caddyfile "admin:3000" "localhost:3003"
string_replace $DIST_DIR/Caddyfile "api:8000" "localhost:3004"
string_replace $DIST_DIR/Caddyfile "live:3000" "localhost:3005"
# print docker build command
echo "------------------------------------------------"
echo "Run the following command to build the image:"
echo "------------------------------------------------"
echo ""
echo "docker build -t $IMAGE_NAME \\"
echo " -f $(pwd)/Dockerfile \\"
echo " --build-arg PLANE_VERSION=$APP_RELEASE_VERSION \\"
echo " $(pwd)"
echo ""
echo "------------------------------------------------"
}
main(){
# check if the dist directory exists
echo ""
if [ -d "$DIST_DIR" ]; then
echo "Cleaning existing dist directory..."
rm -rf $DIST_DIR
fi
echo "Creating dist directory..."
mkdir -p $DIST_DIR
echo ""
build_dist_files
if [ $? -ne 0 ]; then
echo "Error: Failed to build docker image"
exit 1
fi
}
main "$@"

View File

@@ -0,0 +1,169 @@
#!/bin/bash -e
print_header(){
clear
echo "------------------------------------------------"
echo "Plane Community (All-In-One)"
echo "------------------------------------------------"
echo ""
echo "You are required to pass below environment variables to the script"
echo " DOMAIN_NAME, DATABASE_URL, REDIS_URL, AMQP_URL"
echo " AWS_REGION, AWS_ACCESS_KEY_ID"
echo " AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET_NAME"
echo ""
echo "Other optional environment variables: "
echo " SITE_ADDRESS (default: ':80')"
echo " FILE_SIZE_LIMIT (default: 5242880)"
echo " APP_PROTOCOL (http or https)"
echo " SECRET_KEY (default: 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5)"
echo " LIVE_SERVER_SECRET_KEY (default: htbqvBJAgpm9bzvf3r4urJer0ENReatceh)"
echo ""
echo ""
}
check_required_env(){
echo "Checking required environment variables..."
local keys=("DOMAIN_NAME" "DATABASE_URL" "REDIS_URL" "AMQP_URL"
"AWS_REGION" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "AWS_S3_BUCKET_NAME")
local missing_keys=()
# Check if the environment variable is set and not empty
for key in "${keys[@]}"; do
if [ -z "${!key}" ]; then
echo " ❌ '$key' is not set or is empty"
missing_keys+=("$key")
fi
done
if [ ${#missing_keys[@]} -gt 0 ]; then
echo ""
exit 1
fi
# add checkmark
echo "✅ Required environment variables are available"
echo ""
}
update_env_value(){
local key="$1"
local value="$2"
# check if the file exists
if [ ! -f "plane.env" ]; then
echo "plane.env file not found"
exit 1
fi
# check if the key exists and add it if it doesn't
if ! grep -q "^$key=.*" plane.env; then
echo "${key}=${value}" >> plane.env
return 0
fi
# if key and value are not empty, update the value
if [ -n "$key" ] && [ -n "$value" ]; then
sed -i "s|^$key=.*|$key=$value|" plane.env
return 0
fi
}
check_pre_requisites(){
check_required_env
# check if the file exists
if [ ! -f "plane.env" ]; then
echo "plane.env file not found"
exit 1
fi
# add a new line to the end of the file
echo "" >> plane.env
echo "" >> plane.env
echo "✅ Pre-requisites checked"
echo ""
}
validate_domain_name() {
local domain="$1"
# Check if it's an IP address first
if [[ "$domain" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "IP"
return 0
fi
# FQDN validation regex
local fqdn_regex='^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.?$'
if [[ "$domain" =~ $fqdn_regex ]]; then
# Additional checks
if [[ ${#domain} -le 253 ]] && [[ ! "$domain" =~ \.\. ]] && [[ ! "$domain" =~ ^- ]] && [[ ! "$domain" =~ -\. ]]; then
echo "FQDN"
return 0
fi
fi
echo "INVALID"
return 1
}
update_env_file(){
echo "Updating environment file..."
# check if DOMAIN_NAME is valid IP address
local domain_type=$(validate_domain_name "$DOMAIN_NAME")
if [ "$domain_type" == "INVALID" ]; then
echo "DOMAIN_NAME is not a valid FQDN or IP address"
exit 1
fi
local app_protocol=${APP_PROTOCOL:-http}
update_env_value "APP_PROTOCOL" "$app_protocol"
update_env_value "DOMAIN_NAME" "$DOMAIN_NAME"
update_env_value "APP_DOMAIN" "$DOMAIN_NAME"
if [ -n "$SITE_ADDRESS" ]; then
update_env_value "SITE_ADDRESS" "$SITE_ADDRESS"
else
update_env_value "SITE_ADDRESS" ":80"
fi
update_env_value "WEB_URL" "$app_protocol://$DOMAIN_NAME"
update_env_value "CORS_ALLOWED_ORIGINS" "http://$DOMAIN_NAME,https://$DOMAIN_NAME"
# update database url
update_env_value "DATABASE_URL" "$DATABASE_URL"
update_env_value "REDIS_URL" "$REDIS_URL"
update_env_value "AMQP_URL" "$AMQP_URL"
# update aws credentials
update_env_value "AWS_REGION" "$AWS_REGION"
update_env_value "AWS_ACCESS_KEY_ID" "$AWS_ACCESS_KEY_ID"
update_env_value "AWS_SECRET_ACCESS_KEY" "$AWS_SECRET_ACCESS_KEY"
update_env_value "AWS_S3_BUCKET_NAME" "$AWS_S3_BUCKET_NAME"
update_env_value "AWS_S3_ENDPOINT_URL" "${AWS_S3_ENDPOINT_URL:-https://s3.${AWS_REGION}.amazonaws.com}"
update_env_value "BUCKET_NAME" "$AWS_S3_BUCKET_NAME"
update_env_value "USE_MINIO" "0"
# Optional environment variables
update_env_value "SECRET_KEY" "${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5}"
update_env_value "FILE_SIZE_LIMIT" "${FILE_SIZE_LIMIT:-5242880}"
update_env_value "LIVE_SERVER_SECRET_KEY" "${LIVE_SERVER_SECRET_KEY:-htbqvBJAgpm9bzvf3r4urJer0ENReatceh}"
update_env_value "API_KEY_RATE_LIMIT" "${API_KEY_RATE_LIMIT:-60/minute}"
echo "✅ Environment file updated"
echo ""
}
main(){
print_header
check_pre_requisites
update_env_file
# load plane.env as exported variables
export $(grep -v '^#' plane.env | xargs)
/usr/local/bin/supervisord -c /etc/supervisor/conf.d/supervisor.conf
}
main "$@"

View File

@@ -0,0 +1,123 @@
[supervisord]
user=root
nodaemon=true
stderr_logfile=/app/logs/error/supervisor.err.log
stdout_logfile=/app/logs/access/supervisor.log
[program:migrator]
directory=/app/backend
command=sh -c "./bin/docker-entrypoint-migrator.sh"
autostart=true
autorestart=unexpected
stdout_logfile=/app/logs/access/migrator.log
stderr_logfile=/app/logs/error/migrator.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
priority=10
[program:web]
command=sh -c "node /app/web/apps/web/server.js"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/web.log
stderr_logfile=/app/logs/error/web.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
environment=PORT=3001,HOSTNAME=0.0.0.0
priority=15
[program:space]
command=sh -c "node /app/space/apps/space/server.js"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/space.log
stderr_logfile=/app/logs/error/space.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
environment=PORT=3002,HOSTNAME=0.0.0.0
priority=15
[program:admin]
command=sh -c "node /app/admin/apps/admin/server.js"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/admin.log
stderr_logfile=/app/logs/error/admin.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
environment=PORT=3003,HOSTNAME=0.0.0.0
priority=15
[program:api]
directory=/app/backend
command=sh -c "./bin/docker-entrypoint-api.sh"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/api.log
stdout_logfile_maxbytes=0
stderr_logfile=/app/logs/error/api.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
environment=PORT=3004,HOSTNAME=0.0.0.0
priority=15
[program:worker]
directory=/app/backend
command=sh -c "./bin/docker-entrypoint-worker.sh"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/worker.log
stdout_logfile_maxbytes=0
stderr_logfile=/app/logs/error/worker.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
priority=20
[program:beat]
directory=/app/backend
command=sh -c "./bin/docker-entrypoint-beat.sh"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/beat.log
stdout_logfile_maxbytes=0
stderr_logfile=/app/logs/error/beat.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
priority=20
[program:live]
command=sh -c "node /app/live/apps/live/dist/start.js"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/live.log
stdout_logfile_maxbytes=0
stderr_logfile=/app/logs/error/live.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
environment=PORT=3005,HOSTNAME=0.0.0.0
priority=20
[program:proxy]
directory=/app/proxy
command=sh -c "caddy run --config /app/proxy/Caddyfile"
autostart=true
autorestart=true
stdout_logfile=/app/logs/access/proxy.log
stdout_logfile_maxbytes=0
stderr_logfile=/app/logs/error/proxy.err.log
# stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=5
priority=20

View File

@@ -0,0 +1,53 @@
APP_DOMAIN=localhost
APP_RELEASE=stable
# If SSL Cert to be generated, set CERT_EMAIl="email <EMAIL_ADDRESS>"
CERT_EMAIL=
CERT_ACME_CA=https://acme-v02.api.letsencrypt.org/directory
SITE_ADDRESS=:80
# For DNS Challenge based certificate generation, set the CERT_ACME_DNS, CERT_EMAIL
# CERT_ACME_DNS="acme_dns <CERT_DNS_PROVIDER> <CERT_DNS_PROVIDER_API_KEY>"
CERT_ACME_DNS=
WEB_URL=http://localhost
DEBUG=0
CORS_ALLOWED_ORIGINS=http://localhost
API_BASE_URL=http://localhost:3004
#DB SETTINGS
DATABASE_URL=
# REDIS SETTINGS
REDIS_URL=
# RabbitMQ Settings
AMQP_URL=
# Secret Key
SECRET_KEY=60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5
# DATA STORE SETTINGS
USE_MINIO=0
AWS_REGION=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_S3_ENDPOINT_URL=https://s3.amazonaws.com
AWS_S3_BUCKET_NAME=
BUCKET_NAME=
FILE_SIZE_LIMIT=5242880
# Gunicorn Workers
GUNICORN_WORKERS=1
# Force HTTPS for handling SSL Termination
MINIO_ENDPOINT_SSL=0
# API key rate limit
API_KEY_RATE_LIMIT=60/minute
# Live Server Secret Key
LIVE_SERVER_SECRET_KEY=htbqvBJAgpm9bzvf3r4urJer0ENReatceh