diff --git a/apps/documenso/2.19.0/.env.sample b/apps/documenso/2.19.0/.env.sample new file mode 100644 index 000000000..4b0f4bf3b --- /dev/null +++ b/apps/documenso/2.19.0/.env.sample @@ -0,0 +1,23 @@ +CONTAINER_NAME=documenso-compose-check +PANEL_APP_BIND_ADDRESS=127.0.0.1 +PANEL_APP_PORT_HTTP=3000 +NEXT_PUBLIC_WEBAPP_URL=http://127.0.0.1:3000 +PANEL_DB_TYPE=postgresql +PANEL_DB_HOST=postgresql +PANEL_DB_PORT=5432 +PANEL_DB_NAME=documenso +PANEL_DB_USER=documenso +PANEL_DB_USER_PASSWORD=change-me +SMTP_HOST=smtp.example.com +SMTP_PORT=587 +SMTP_SECURE=false +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_FROM_NAME=Documenso +SMTP_FROM_ADDRESS=documenso@example.com +DISABLE_SIGNUP=false +NEXTAUTH_SECRET=generate +NEXT_PRIVATE_ENCRYPTION_KEY=generate +NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=generate +SIGNING_PASSPHRASE=generate +NEXT_PRIVATE_DATABASE_URL=generate diff --git a/apps/documenso/2.19.0/data.yml b/apps/documenso/2.19.0/data.yml new file mode 100644 index 000000000..a630418fe --- /dev/null +++ b/apps/documenso/2.19.0/data.yml @@ -0,0 +1,377 @@ +additionalProperties: + formFields: + - default: 127.0.0.1 + edit: true + envKey: PANEL_APP_BIND_ADDRESS + labelEn: Bind Address + labelZh: 绑定地址 + label: + en: Bind Address + zh: 绑定地址 + zh-Hant: 綁定位址 + ja: バインドアドレス + ko: 바인드 주소 + ru: Адрес привязки + ms: Alamat ikatan + pt-br: Endereco de vinculacao + required: true + type: text + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http://127.0.0.1:3000 + edit: true + envKey: NEXT_PUBLIC_WEBAPP_URL + labelEn: Public URL + labelZh: 公开访问地址 + label: + en: Public URL + zh: 公开访问地址 + zh-Hant: 公開存取網址 + ja: 公開 URL + ko: 공개 URL + ru: Публичный URL + ms: URL awam + pt-br: URL publica + required: true + type: text + - default: postgresql + envKey: PANEL_DB_TYPE + labelEn: Database Runtime + labelZh: 数据库运行环境 + label: + en: Database Runtime + zh: 数据库运行环境 + zh-Hant: 資料庫執行環境 + ja: データベース実行環境 + ko: 데이터베이스 런타임 + ru: Среда базы данных + ms: Masa jalan pangkalan data + pt-br: Ambiente de banco de dados + required: true + type: apps + values: + - label: PostgreSQL + value: postgresql + child: + default: '' + envKey: PANEL_DB_HOST + labelEn: PostgreSQL Service + labelZh: PostgreSQL 服务 + label: + en: PostgreSQL Service + zh: PostgreSQL 服务 + zh-Hant: PostgreSQL 服務 + ja: PostgreSQL サービス + ko: PostgreSQL 서비스 + ru: Сервис PostgreSQL + ms: Perkhidmatan PostgreSQL + pt-br: Servico PostgreSQL + required: true + type: service + - default: 5432 + edit: false + envKey: PANEL_DB_PORT + labelEn: Database Port + labelZh: 数据库端口 + label: + en: Database Port + zh: 数据库端口 + zh-Hant: 資料庫連接埠 + ja: データベースポート + ko: 데이터베이스 포트 + ru: Порт базы данных + ms: Port pangkalan data + pt-br: Porta do banco de dados + required: true + type: number + - default: documenso + edit: false + envKey: PANEL_DB_NAME + labelEn: Database Name + labelZh: 数据库名称 + label: + en: Database Name + zh: 数据库名称 + zh-Hant: 資料庫名稱 + ja: データベース名 + ko: 데이터베이스 이름 + ru: Имя базы данных + ms: Nama pangkalan data + pt-br: Nome do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: documenso + edit: false + envKey: PANEL_DB_USER + labelEn: Database User + labelZh: 数据库用户 + label: + en: Database User + zh: 数据库用户 + zh-Hant: 資料庫使用者 + ja: データベースユーザー + ko: 데이터베이스 사용자 + ru: Пользователь базы данных + ms: Pengguna pangkalan data + pt-br: Usuario do banco de dados + random: true + required: true + rule: paramCommon + type: text + - default: change-me + edit: false + envKey: PANEL_DB_USER_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + label: + en: Database Password + zh: 数据库密码 + zh-Hant: 資料庫密碼 + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ru: Пароль базы данных + ms: Kata laluan pangkalan data + pt-br: Senha do banco de dados + required: true + rule: paramComplexity + type: password + - default: smtp.example.com + edit: true + envKey: SMTP_HOST + labelEn: SMTP Host + labelZh: SMTP 主机 + label: + en: SMTP Host + zh: SMTP 主机 + zh-Hant: SMTP 主機 + ja: SMTP ホスト + ko: SMTP 호스트 + ru: SMTP-сервер + ms: Hos SMTP + pt-br: Servidor SMTP + required: true + type: text + - default: 587 + edit: true + envKey: SMTP_PORT + labelEn: SMTP Port + labelZh: SMTP 端口 + label: + en: SMTP Port + zh: SMTP 端口 + zh-Hant: SMTP 連接埠 + ja: SMTP ポート + ko: SMTP 포트 + ru: SMTP-порт + ms: Port SMTP + pt-br: Porta SMTP + required: true + type: number + - default: 'false' + edit: true + envKey: SMTP_SECURE + labelEn: SMTP Implicit TLS + labelZh: SMTP 隐式 TLS + label: + en: SMTP Implicit TLS + zh: SMTP 隐式 TLS + zh-Hant: SMTP 隱式 TLS + ja: SMTP 暗黙 TLS + ko: SMTP 암시적 TLS + ru: Неявный TLS SMTP + ms: TLS tersirat SMTP + pt-br: TLS implicito SMTP + required: true + type: select + values: + - label: 'false' + value: 'false' + - label: 'true' + value: 'true' + - default: '' + edit: true + envKey: SMTP_USERNAME + labelEn: SMTP Username + labelZh: SMTP 用户名 + label: + en: SMTP Username + zh: SMTP 用户名 + zh-Hant: SMTP 使用者名稱 + ja: SMTP ユーザー名 + ko: SMTP 사용자 이름 + ru: Имя пользователя SMTP + ms: Nama pengguna SMTP + pt-br: Usuario SMTP + required: false + type: text + - default: '' + edit: true + envKey: SMTP_PASSWORD + labelEn: SMTP Password + labelZh: SMTP 密码 + label: + en: SMTP Password + zh: SMTP 密码 + zh-Hant: SMTP 密碼 + ja: SMTP パスワード + ko: SMTP 비밀번호 + ru: Пароль SMTP + ms: Kata laluan SMTP + pt-br: Senha SMTP + required: false + type: password + - default: Documenso + edit: true + envKey: SMTP_FROM_NAME + labelEn: Email Sender Name + labelZh: 邮件发件人名称 + label: + en: Email Sender Name + zh: 邮件发件人名称 + zh-Hant: 郵件寄件者名稱 + ja: メール送信者名 + ko: 이메일 발신자 이름 + ru: Имя отправителя + ms: Nama penghantar e-mel + pt-br: Nome do remetente + required: true + type: text + - default: documenso@example.com + edit: true + envKey: SMTP_FROM_ADDRESS + labelEn: Email Sender Address + labelZh: 邮件发件人地址 + label: + en: Email Sender Address + zh: 邮件发件人地址 + zh-Hant: 郵件寄件者地址 + ja: メール送信元アドレス + ko: 이메일 발신자 주소 + ru: Адрес отправителя + ms: Alamat penghantar e-mel + pt-br: Endereco do remetente + required: true + type: text + - default: 'false' + edit: true + envKey: DISABLE_SIGNUP + labelEn: Disable New Signups + labelZh: 禁止新用户注册 + label: + en: Disable New Signups + zh: 禁止新用户注册 + zh-Hant: 停用新使用者註冊 + ja: 新規登録を無効化 + ko: 신규 가입 비활성화 + ru: Запретить новые регистрации + ms: Nyahdayakan pendaftaran baharu + pt-br: Desativar novos cadastros + required: true + type: select + values: + - label: 'false' + value: 'false' + - label: 'true' + value: 'true' + - default: generate + disabled: true + edit: true + envKey: NEXTAUTH_SECRET + labelEn: Authentication Secret + labelZh: 认证密钥 + label: + en: Authentication Secret + zh: 认证密钥 + zh-Hant: 驗證密鑰 + ja: 認証シークレット + ko: 인증 비밀 키 + ru: Секрет аутентификации + ms: Rahsia pengesahan + pt-br: Segredo de autenticacao + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: NEXT_PRIVATE_ENCRYPTION_KEY + labelEn: Primary Encryption Key + labelZh: 主加密密钥 + label: + en: Primary Encryption Key + zh: 主加密密钥 + zh-Hant: 主要加密金鑰 + ja: プライマリ暗号化キー + ko: 기본 암호화 키 + ru: Основной ключ шифрования + ms: Kunci penyulitan utama + pt-br: Chave de criptografia primaria + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY + labelEn: Secondary Encryption Key + labelZh: 备用加密密钥 + label: + en: Secondary Encryption Key + zh: 备用加密密钥 + zh-Hant: 備用加密金鑰 + ja: セカンダリ暗号化キー + ko: 보조 암호화 키 + ru: Резервный ключ шифрования + ms: Kunci penyulitan sekunder + pt-br: Chave de criptografia secundaria + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: SIGNING_PASSPHRASE + labelEn: Signing Certificate Passphrase + labelZh: 签名证书密码 + label: + en: Signing Certificate Passphrase + zh: 签名证书密码 + zh-Hant: 簽署憑證密碼 + ja: 署名証明書パスフレーズ + ko: 서명 인증서 암호 + ru: Пароль сертификата подписи + ms: Frasa laluan sijil tandatangan + pt-br: Senha do certificado de assinatura + required: true + type: password + - default: generate + disabled: true + edit: true + envKey: NEXT_PRIVATE_DATABASE_URL + labelEn: Generated Database URL + labelZh: 生成的数据库地址 + label: + en: Generated Database URL + zh: 生成的数据库地址 + zh-Hant: 產生的資料庫網址 + ja: 生成されたデータベース URL + ko: 생성된 데이터베이스 URL + ru: Сформированный URL базы данных + ms: URL pangkalan data dijana + pt-br: URL de banco de dados gerada + required: true + type: password diff --git a/apps/documenso/2.19.0/data/.gitkeep b/apps/documenso/2.19.0/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/documenso/2.19.0/docker-compose.yml b/apps/documenso/2.19.0/docker-compose.yml new file mode 100644 index 000000000..4ac63e83a --- /dev/null +++ b/apps/documenso/2.19.0/docker-compose.yml @@ -0,0 +1,58 @@ +services: + documenso: + image: "documenso/documenso:v2.19.0@sha256:a0592ef8edbc8d74e5e236d9c2dffb3fc4f0104a3ea1c52baaae4673799d8d91" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + command: + - sh + - -c + - ../../node_modules/.bin/prisma migrate deploy --schema ../../packages/prisma/schema.prisma && exec env HOSTNAME=0.0.0.0 node build/server/main.js + networks: + - 1panel-network + ports: + - "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:3000" + environment: + PORT: "3000" + NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} + NEXT_PRIVATE_ENCRYPTION_KEY: ${NEXT_PRIVATE_ENCRYPTION_KEY} + NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: ${NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY} + NEXT_PUBLIC_WEBAPP_URL: ${NEXT_PUBLIC_WEBAPP_URL} + NEXT_PRIVATE_INTERNAL_WEBAPP_URL: http://127.0.0.1:3000 + NEXT_PRIVATE_DATABASE_URL: ${NEXT_PRIVATE_DATABASE_URL} + NEXT_PRIVATE_DIRECT_DATABASE_URL: ${NEXT_PRIVATE_DATABASE_URL} + NEXT_PUBLIC_UPLOAD_TRANSPORT: database + NEXT_PRIVATE_SMTP_TRANSPORT: smtp-auth + NEXT_PRIVATE_SMTP_HOST: ${SMTP_HOST} + NEXT_PRIVATE_SMTP_PORT: ${SMTP_PORT} + NEXT_PRIVATE_SMTP_SECURE: ${SMTP_SECURE} + NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS: "false" + NEXT_PRIVATE_SMTP_USERNAME: ${SMTP_USERNAME} + NEXT_PRIVATE_SMTP_PASSWORD: ${SMTP_PASSWORD} + NEXT_PRIVATE_SMTP_FROM_NAME: ${SMTP_FROM_NAME} + NEXT_PRIVATE_SMTP_FROM_ADDRESS: ${SMTP_FROM_ADDRESS} + NEXT_PRIVATE_SIGNING_TRANSPORT: local + NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH: /opt/documenso/cert.p12 + NEXT_PRIVATE_SIGNING_PASSPHRASE: ${SIGNING_PASSPHRASE} + NEXT_PUBLIC_DISABLE_SIGNUP: ${DISABLE_SIGNUP} + DOCUMENSO_DISABLE_TELEMETRY: "true" + read_only: true + tmpfs: + - /tmp:size=256m,mode=1777 + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + volumes: + - ./data/cert.p12:/opt/documenso/cert.p12:ro + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] + interval: 30s + timeout: 10s + start_period: 90s + retries: 5 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/documenso/2.19.0/scripts/init.sh b/apps/documenso/2.19.0/scripts/init.sh new file mode 100755 index 000000000..0647a5749 --- /dev/null +++ b/apps/documenso/2.19.0/scripts/init.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" +DATA_DIR="${ROOT_DIR}/data" +CERT_FILE="${DATA_DIR}/cert.p12" + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value="" + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +set_env_value() { + local key="$1" + local value="$2" + local temp_file + temp_file="$(mktemp "${ROOT_DIR}/.documenso-env.tmp.XXXXXX")" + awk -v key="$key" -v value="$value" ' + BEGIN { written = 0 } + $0 ~ "^" key "=" { + if (!written) { + print key "=" value + written = 1 + } + next + } + { print } + END { if (!written) print key "=" value } + ' "$ENV_FILE" > "$temp_file" + chmod 600 "$temp_file" + mv -f -- "$temp_file" "$ENV_FILE" +} + +generate_secret() { + local key="$1" + local value + value="$(read_env_value "$key")" + if [[ -z "$value" || "$value" == generate ]]; then + value="$(openssl rand -hex 32)" + set_env_value "$key" "$value" + fi + [[ ${#value} -ge 32 ]] || fail "$key must contain at least 32 characters" +} + +urlencode() { + local LC_ALL=C + local input="$1" + local output="" + local character encoded + local index + for ((index = 0; index < ${#input}; index++)); do + character="${input:index:1}" + case "$character" in + [A-Za-z0-9.~_-]) output+="$character" ;; + *) printf -v encoded '%%%02X' "'$character"; output+="$encoded" ;; + esac + done + printf '%s' "$output" +} + +secure_certificate_file() { + local current_uid owner_uid + current_uid="$(id -u)" + if [[ "$current_uid" == 0 ]]; then + chown 1001:65533 -- "$CERT_FILE" + elif [[ "$current_uid" == 1001 ]]; then + owner_uid="$(stat -c %u -- "$CERT_FILE")" + [[ "$owner_uid" == 1001 ]] || fail "the signing certificate must be owned by UID 1001" + [[ -r "$CERT_FILE" ]] || fail "the signing certificate is not readable by UID 1001" + else + fail "init.sh must run as root or UID 1001" + fi + chmod 0400 -- "$CERT_FILE" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +command -v openssl >/dev/null 2>&1 || fail "openssl is required" + +db_host="$(read_env_value PANEL_DB_HOST)" +db_port="$(read_env_value PANEL_DB_PORT)" +db_name="$(read_env_value PANEL_DB_NAME)" +db_user="$(read_env_value PANEL_DB_USER)" +db_password="$(read_env_value PANEL_DB_USER_PASSWORD)" +[[ "$db_host" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || fail "PANEL_DB_HOST is invalid" +if [[ ! "$db_port" =~ ^[0-9]+$ ]] || ((db_port < 1 || db_port > 65535)); then + fail "PANEL_DB_PORT is invalid" +fi +[[ "$db_name" =~ ^[A-Za-z0-9_][A-Za-z0-9_-]*$ ]] || fail "PANEL_DB_NAME is invalid" +[[ "$db_user" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]] || fail "PANEL_DB_USER is invalid" +[[ -n "$db_password" && "$db_password" != change-me ]] || fail "PANEL_DB_USER_PASSWORD must be configured" + +public_url="$(read_env_value NEXT_PUBLIC_WEBAPP_URL)" +public_url_pattern='^https?://[A-Za-z0-9._~:/?&=%+-]+$' +[[ "$public_url" =~ $public_url_pattern ]] || fail "NEXT_PUBLIC_WEBAPP_URL must be a complete HTTP(S) URL" +smtp_host="$(read_env_value SMTP_HOST)" +smtp_port="$(read_env_value SMTP_PORT)" +smtp_secure="$(read_env_value SMTP_SECURE)" +smtp_from_address="$(read_env_value SMTP_FROM_ADDRESS)" +[[ "$smtp_host" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ && "$smtp_host" != smtp.example.com ]] || fail "SMTP_HOST must name a working SMTP service" +if [[ ! "$smtp_port" =~ ^[0-9]+$ ]] || ((smtp_port < 1 || smtp_port > 65535)); then + fail "SMTP_PORT is invalid" +fi +[[ "$smtp_secure" == true || "$smtp_secure" == false ]] || fail "SMTP_SECURE must be true or false" +[[ "$smtp_from_address" =~ ^[^[:space:]@]+@[^[:space:]@]+$ ]] || fail "SMTP_FROM_ADDRESS is invalid" + +chmod 600 "$ENV_FILE" +generate_secret NEXTAUTH_SECRET +generate_secret NEXT_PRIVATE_ENCRYPTION_KEY +generate_secret NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY + +encoded_user="$(urlencode "$db_user")" +encoded_password="$(urlencode "$db_password")" +set_env_value NEXT_PRIVATE_DATABASE_URL "postgresql://${encoded_user}:${encoded_password}@${db_host}:${db_port}/${db_name}" + +install -d -m 0700 "$DATA_DIR" +passphrase="$(read_env_value SIGNING_PASSPHRASE)" +[[ ! -L "$CERT_FILE" ]] || fail "$CERT_FILE must not be a symbolic link" +if [[ -s "$CERT_FILE" ]]; then + [[ -n "$passphrase" && "$passphrase" != generate ]] || fail "SIGNING_PASSPHRASE is missing for the existing certificate" + CERT_PASS="$passphrase" openssl pkcs12 -in "$CERT_FILE" -passin env:CERT_PASS -noout >/dev/null 2>&1 || fail "the existing signing certificate or passphrase is invalid" + secure_certificate_file +else + if [[ -z "$passphrase" || "$passphrase" == generate ]]; then + passphrase="$(openssl rand -hex 32)" + set_env_value SIGNING_PASSPHRASE "$passphrase" + fi + temp_dir="$(mktemp -d "${ROOT_DIR}/.documenso-cert.tmp.XXXXXX")" + trap 'rm -rf -- "$temp_dir"' EXIT + openssl req -new -newkey rsa:3072 -x509 -sha256 -nodes -days 3650 \ + -subj "/CN=Documenso 1Panel Signing" \ + -keyout "${temp_dir}/private.key" -out "${temp_dir}/certificate.crt" >/dev/null 2>&1 + CERT_PASS="$passphrase" openssl pkcs12 -export \ + -inkey "${temp_dir}/private.key" -in "${temp_dir}/certificate.crt" \ + -out "${temp_dir}/cert.p12" -passout env:CERT_PASS + install -m 0400 "${temp_dir}/cert.p12" "$CERT_FILE" + secure_certificate_file + rm -rf -- "$temp_dir" + trap - EXIT +fi diff --git a/apps/documenso/2.19.0/scripts/uninstall.sh b/apps/documenso/2.19.0/scripts/uninstall.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/documenso/2.19.0/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/documenso/2.19.0/scripts/upgrade.sh b/apps/documenso/2.19.0/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/documenso/2.19.0/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh"