Merge pull request #4880 from okxlin/feat/keila-20260716

Add Keila app
This commit is contained in:
okxlin
2026-07-16 09:31:11 +08:00
committed by GitHub
15 changed files with 1288 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
PANEL_APP_PORT_HTTP=4000
PANEL_DB_HOST=
PANEL_DB_PORT=5432
PANEL_DB_NAME=keila
PANEL_DB_USER=keila
PANEL_DB_USER_PASSWORD=
KEILA_USER=root@localhost
KEILA_PASSWORD=
SECRET_KEY_BASE=
URL_HOST=localhost
URL_SCHEMA=http
URL_PORT=4000
APP_DATA_DIR=./data
MAILER_SMTP_HOST=
MAILER_SMTP_PORT=587
MAILER_SMTP_FROM_EMAIL=
MAILER_SMTP_USER=
MAILER_SMTP_PASSWORD=
MAILER_ENABLE_SSL=false
MAILER_ENABLE_STARTTLS=false
LOG_LEVEL=info
TZ=Asia/Shanghai
CONTAINER_NAME=
+401
View File
@@ -0,0 +1,401 @@
additionalProperties:
formFields:
- default: 4000
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: postgresql
envKey: PANEL_DB_TYPE
labelEn: Database Runtime
labelZh: 数据库 Runtime
label:
en: Database Runtime
zh: 数据库 Runtime
zh-Hant: 資料庫 Runtime
ja: データベース Runtime
ko: 데이터베이스 Runtime
ru: Среда базы данных
ms: Runtime Pangkalan Data
pt-br: Runtime 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: Serviço PostgreSQL
required: true
type: service
- default: 5432
edit: true
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
rule: paramPort
type: number
- default: keila
edit: true
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: keila
edit: true
envKey: PANEL_DB_USER
labelEn: Database User
labelZh: 数据库用户
label:
en: Database User
zh: 数据库用户
zh-Hant: 資料庫使用者
ja: データベースユーザー
ko: 데이터베이스 사용자
ru: Пользователь базы данных
ms: Pengguna Pangkalan Data
pt-br: Usuário do banco de dados
random: true
required: true
rule: paramCommon
type: text
- default: ''
edit: true
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: root@localhost
edit: true
envKey: KEILA_USER
labelEn: Initial Administrator Email
labelZh: 初始管理员邮箱
label:
en: Initial Administrator Email
zh: 初始管理员邮箱
zh-Hant: 初始管理員電子郵件
ja: 初期管理者メールアドレス
ko: 초기 관리자 이메일
ru: Email начального администратора
ms: E-mel Pentadbir Awal
pt-br: E-mail do administrador inicial
required: true
type: text
- default: ''
edit: true
envKey: KEILA_PASSWORD
labelEn: Initial Administrator Password (10+ characters)
labelZh: 初始管理员密码(至少 10 个字符)
label:
en: Initial Administrator Password (10+ characters)
zh: 初始管理员密码(至少 10 个字符)
zh-Hant: 初始管理員密碼(至少 10 個字元)
ja: 初期管理者パスワード(10 文字以上)
ko: 초기 관리자 비밀번호(10자 이상)
ru: Пароль начального администратора (не менее 10 символов)
ms: Kata Laluan Pentadbir Awal (10+ aksara)
pt-br: Senha do administrador inicial (10+ caracteres)
required: true
type: password
- default: ''
edit: true
envKey: SECRET_KEY_BASE
labelEn: Session Secret (64+ characters)
labelZh: 会话密钥(至少 64 个字符)
label:
en: Session Secret (64+ characters)
zh: 会话密钥(至少 64 个字符)
zh-Hant: 工作階段密鑰(至少 64 個字元)
ja: セッションシークレット(64 文字以上)
ko: 세션 비밀 키(64자 이상)
ru: Секрет сеанса (не менее 64 символов)
ms: Rahsia Sesi (64+ aksara)
pt-br: Segredo da sessão (64+ caracteres)
required: true
type: password
- default: ''
edit: true
envKey: URL_HOST
labelEn: Public Host (domain or IP)
labelZh: 公开访问主机(域名或 IP)
label:
en: Public Host (domain or IP)
zh: 公开访问主机(域名或 IP)
zh-Hant: 公開存取主機(網域或 IP)
ja: 公開ホスト(ドメインまたは IP)
ko: 공개 호스트(도메인 또는 IP)
ru: Публичный хост (домен или IP)
ms: Hos Awam (domain atau IP)
pt-br: Host público (domínio ou IP)
required: true
type: text
- default: http
edit: true
envKey: URL_SCHEMA
labelEn: Public URL Scheme
labelZh: 公开 URL 协议
label:
en: Public URL Scheme
zh: 公开 URL 协议
zh-Hant: 公開 URL 通訊協定
ja: 公開 URL スキーム
ko: 공개 URL 스키마
ru: Схема публичного URL
ms: Skema URL Awam
pt-br: Esquema da URL pública
required: true
type: select
values:
- label: http
value: http
- label: https
value: https
- default: 4000
edit: true
envKey: URL_PORT
labelEn: Public URL Port
labelZh: 公开 URL 端口
label:
en: Public URL Port
zh: 公开 URL 端口
zh-Hant: 公開 URL 連接埠
ja: 公開 URL ポート
ko: 공개 URL 포트
ru: Порт публичного URL
ms: Port URL Awam
pt-br: Porta da URL pública
required: true
type: number
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori Data
pt-br: Diretório de dados
required: true
type: text
- default: ''
edit: true
envKey: MAILER_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: Host SMTP
required: true
type: text
- default: 587
edit: true
envKey: MAILER_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: ''
edit: true
envKey: MAILER_SMTP_FROM_EMAIL
labelEn: SMTP From Email
labelZh: SMTP 发件邮箱
label:
en: SMTP From Email
zh: SMTP 发件邮箱
zh-Hant: SMTP 寄件者電子郵件
ja: SMTP 送信元メールアドレス
ko: SMTP 발신 이메일
ru: Email отправителя SMTP
ms: E-mel Pengirim SMTP
pt-br: E-mail do remetente SMTP
required: true
type: text
- default: ''
edit: true
envKey: MAILER_SMTP_USER
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: Usuário SMTP
required: true
type: text
- default: ''
edit: true
envKey: MAILER_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: true
type: password
- default: 'false'
edit: true
envKey: MAILER_ENABLE_SSL
labelEn: Enable SMTP SSL/TLS
labelZh: 启用 SMTP SSL/TLS
label:
en: Enable SMTP SSL/TLS
zh: 启用 SMTP SSL/TLS
zh-Hant: 啟用 SMTP SSL/TLS
ja: SMTP SSL/TLS を有効化
ko: SMTP SSL/TLS 활성화
ru: Включить SMTP SSL/TLS
ms: Dayakan SMTP SSL/TLS
pt-br: Ativar SMTP SSL/TLS
required: true
type: select
values:
- label: 'false'
value: 'false'
- label: 'true'
value: 'true'
- default: 'false'
edit: true
envKey: MAILER_ENABLE_STARTTLS
labelEn: Enable SMTP STARTTLS
labelZh: 启用 SMTP STARTTLS
label:
en: Enable SMTP STARTTLS
zh: 启用 SMTP STARTTLS
zh-Hant: 啟用 SMTP STARTTLS
ja: SMTP STARTTLS を有効化
ko: SMTP STARTTLS 활성화
ru: Включить SMTP STARTTLS
ms: Dayakan SMTP STARTTLS
pt-br: Ativar SMTP STARTTLS
required: true
type: select
values:
- label: 'false'
value: 'false'
- label: 'true'
value: 'true'
- default: info
edit: true
envKey: LOG_LEVEL
labelEn: Log Level
labelZh: 日志级别
label:
en: Log Level
zh: 日志级别
zh-Hant: 日誌層級
ja: ログレベル
ko: 로그 수준
ru: Уровень журналирования
ms: Tahap Log
pt-br: Nível de log
required: true
type: select
values:
- label: debug
value: debug
- label: info
value: info
- label: error
value: error
- default: Asia/Shanghai
edit: true
envKey: TZ
labelEn: Timezone
labelZh: 时区
label:
en: Timezone
zh: 时区
zh-Hant: 時區
ja: タイムゾーン
ko: 시간대
ru: Часовой пояс
ms: Zon Waktu
pt-br: Fuso horário
required: true
type: text
+44
View File
@@ -0,0 +1,44 @@
services:
keila:
image: "pentacent/keila:0.30.2"
container_name: ${CONTAINER_NAME}
restart: unless-stopped
networks:
- 1panel-network
ports:
- "${PANEL_APP_PORT_HTTP}:4000"
environment:
- DB_URL=postgres://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${PANEL_DB_HOST}:${PANEL_DB_PORT}/${PANEL_DB_NAME}
- DB_ENABLE_SSL=false
- KEILA_USER=${KEILA_USER}
- KEILA_PASSWORD=${KEILA_PASSWORD}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
- URL_HOST=${URL_HOST}
- URL_SCHEMA=${URL_SCHEMA}
- URL_PORT=${URL_PORT}
- USER_CONTENT_DIR=/opt/app/uploads
- MAILER_SMTP_HOST=${MAILER_SMTP_HOST}
- MAILER_SMTP_PORT=${MAILER_SMTP_PORT}
- MAILER_SMTP_FROM_EMAIL=${MAILER_SMTP_FROM_EMAIL}
- MAILER_SMTP_USER=${MAILER_SMTP_USER}
- MAILER_SMTP_PASSWORD=${MAILER_SMTP_PASSWORD}
- MAILER_ENABLE_SSL=${MAILER_ENABLE_SSL}
- MAILER_ENABLE_STARTTLS=${MAILER_ENABLE_STARTTLS}
- LOG_LEVEL=${LOG_LEVEL}
- TZ=${TZ}
volumes:
- "${APP_DATA_DIR}/uploads:/opt/app/uploads"
healthcheck:
test:
- CMD-SHELL
- wget --quiet --tries=1 --spider http://127.0.0.1:4000/auth/login || exit 1
interval: 30s
timeout: 10s
start_period: 90s
retries: 5
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
strip_matching_quotes() {
local value="$1"
if [[ ${#value} -ge 2 ]]; then
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
value="${value:1:${#value}-2}"
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
value="${value:1:${#value}-2}"
fi
fi
printf '%s\n' "$value"
}
read_env_value() {
local key="$1"
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
strip_matching_quotes "$value"
}
set_env_value() {
local key="$1"
local value="$2"
local temp_file
temp_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { updated = 0 }
$0 ~ ("^" key "=") {
if (!updated) {
print key "=" value
updated = 1
}
next
}
{ print }
END {
if (!updated) print key "=" value
}
' "$ENV_FILE" > "$temp_file"
chmod --reference="$ENV_FILE" "$temp_file"
mv -f -- "$temp_file" "$ENV_FILE"
}
ensure_secret_key_base() {
local secret_key_base
secret_key_base="$(read_env_value SECRET_KEY_BASE)"
if [[ ${#secret_key_base} -lt 64 ]]; then
secret_key_base="$(od -An -N64 -tx1 /dev/urandom | tr -d ' \n')"
set_env_value SECRET_KEY_BASE "$secret_key_base"
fi
}
path_is_dotenv_safe() {
local value="$1"
case "$value" in
*$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*) return 1 ;;
*) return 0 ;;
esac
}
[[ -f "$ENV_FILE" ]] || fail "Environment file not found: ${ENV_FILE}"
ensure_secret_key_base
if [[ ${APP_DATA_DIR+x} ]]; then
APP_DATA_DIR_RAW="$APP_DATA_DIR"
else
APP_DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)"
fi
APP_DATA_DIR_RAW="$(strip_matching_quotes "${APP_DATA_DIR_RAW:-./data}")"
[[ -n "$APP_DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty"
path_is_dotenv_safe "$APP_DATA_DIR_RAW" || fail "APP_DATA_DIR contains unsupported dotenv characters"
case "$APP_DATA_DIR_RAW" in
/*)
APP_DATA_DIR_ABS="$(realpath -m -- "$APP_DATA_DIR_RAW")"
;;
*)
APP_DATA_DIR_ABS="$(realpath -m -- "${ROOT_DIR}/${APP_DATA_DIR_RAW#./}")"
case "$APP_DATA_DIR_ABS" in
"${ROOT_DIR}" | "${ROOT_DIR}"/*) ;;
*) fail "Relative APP_DATA_DIR must stay inside the application directory" ;;
esac
;;
esac
[[ "$APP_DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root"
[[ ! -L "$APP_DATA_DIR_ABS" ]] || fail "APP_DATA_DIR must not be a symbolic link"
if [[ -e "$APP_DATA_DIR_ABS" && ! -d "$APP_DATA_DIR_ABS" ]]; then
fail "APP_DATA_DIR must be a directory"
fi
mkdir -p -- "${APP_DATA_DIR_ABS}/uploads"
chmod 750 -- "$APP_DATA_DIR_ABS" "${APP_DATA_DIR_ABS}/uploads"
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v docker-compose >/dev/null 2>&1; then
docker-compose down --volumes --remove-orphans
else
docker compose down --volumes --remove-orphans
fi
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
exec bash "${SCRIPT_DIR}/init.sh"
+74
View File
@@ -0,0 +1,74 @@
# Keila
## 产品介绍
Keila 是一款开源的自托管新闻简报与邮件营销平台,可用于管理联系人、订阅表单、邮件模板和营销活动,并支持 SMTP、AWS SES、SendGrid、Mailgun 与 Postmark 等发送渠道。
## 主要功能
- 管理联系人、分组、订阅表单、模板和新闻简报活动。
- 提供可视化、Markdown 和纯文本邮件编辑能力。
- 支持公开订阅页面、活动统计和 API。
- 业务数据保存在 PostgreSQL,本地上传文件保存在独立持久化目录。
## 部署拓扑
- 本应用包只运行一个 Keila service,使用上游官方 `pentacent/keila` 镜像。
- 安装时必须选择同一 1Panel 中已运行的 PostgreSQL Runtime;1Panel 会为 Keila 创建独立数据库和独立用户。
- 上游示例 Compose 中的 PostgreSQL service 已替换为 1Panel Runtime 联动,不会在应用包中重复部署数据库。
- SMTP 是生产启动的必填依赖,但属于用户提供的外部服务,不会作为 sidecar 安装。
## 访问说明
默认 Web 端口为 `4000`。安装后访问 `http://服务器地址:安装端口/auth/login`,使用安装表单中的初始管理员邮箱和密码登录。
- `公开访问主机` 只填写域名或 IP,不包含 `http://`、`https://` 或路径。
- 直接按端口访问时,公开 URL 端口通常与 Web 端口相同。
- 通过反向代理发布 HTTPS 时,公开 URL 协议应设为 `https`,公开 URL 端口通常设为 `443`;反向代理必须正确转发 WebSocket。
- 初始管理员只在空数据库第一次启动时创建。后续修改安装参数不会重置已有管理员密码,请在 Keila 中管理账号。
- 上游要求管理员密码至少 10 个字符;会话密钥应为至少 64 个字符的强随机值。
## SMTP
Keila 在生产模式启动时必须配置系统 SMTP,用于注册、激活和密码重置等邮件。
- SMTP 用户名在上游可回退为发件邮箱;本应用包要求显式填写,以兼容用户名与发件邮箱不同的服务。
- 端口 `587` 通常配合 STARTTLS,端口 `465` 通常配合 SSL/TLS。具体组合以邮件服务商文档为准。
- SMTP 密码、数据库密码、管理员密码和会话密钥都会写入 1Panel 生成的运行时 `.env`,不得提交到 Git 或复制到公开日志。
## 数据、备份与升级
- Keila 会在启动时自动执行 PostgreSQL schema migration,并在空数据库中创建初始 root 用户。
- 联系人、活动、模板、账号和任务队列数据位于所选 PostgreSQL Runtime 的独立数据库中。
- 用户上传文件位于 `${APP_DATA_DIR}/uploads`,容器内路径为 `/opt/app/uploads`。
- 升级前必须同时备份 PostgreSQL 数据库和 `${APP_DATA_DIR}/uploads`。升级期间等待数据库 migration 完成后再停止或重启容器。
- 固定版和 `latest` 使用相同的持久化边界;回滚时应恢复与目标版本匹配的升级前数据库及上传文件备份。
- 卸载应用不会删除所选 PostgreSQL Runtime 本身;是否删除 Keila 的独立数据库和用户取决于 1Panel 卸载选项。卸载前请完成备份。
## 安全与许可证
- Compose 不启用 privileged、host network、额外 capability、设备映射或 Docker Socket。
- 建议通过 1Panel 反向代理启用 HTTPS,并限制管理入口的公网暴露范围。
- Keila 源代码采用 GNU Affero General Public License v3.0 或更高版本。部署、修改或向网络用户提供服务时,请遵守上游许可证义务。
- 上游 README 说明 Keila 标志不包含在 AGPL 授权中;标志权利归其权利人,本应用包仅将其用于识别对应产品。
## 参考资料
- 官网:<https://www.keila.io/>
- 官方安装文档:<https://www.keila.io/docs/installation>
- 官方配置文档:<https://www.keila.io/docs/configuration>
- 官方首次启动文档:<https://www.keila.io/docs/setup>
- 源码与官方 Compose:<https://github.com/pentacent/keila>
## Introduction
Keila is an open-source self-hosted newsletter and email marketing platform. This package runs one official Keila container, links it to a selectable PostgreSQL Runtime managed by 1Panel, and persists local uploads separately.
Production startup requires SMTP configuration. Keila automatically runs database migrations and creates the initial root user on an empty database. Back up both PostgreSQL and `${APP_DATA_DIR}/uploads` before upgrades.
## Features
- Contact, segment, subscription-form, template, and newsletter campaign management.
- Visual, Markdown, and plain-text email editing.
- PostgreSQL-backed application data and a persistent local upload directory.
- Explicit initial administrator, public URL, SMTP, and session-secret configuration.
+30
View File
@@ -0,0 +1,30 @@
name: Keila
tags:
- Email
title: 开源的自托管新闻简报与邮件营销平台
description: 开源的自托管新闻简报与邮件营销平台
additionalProperties:
key: keila
name: Keila
tags:
- Email
type: tool
website: https://www.keila.io/
document: https://www.keila.io/docs/installation
github: https://github.com/pentacent/keila
shortDescZh: 开源的自托管新闻简报与邮件营销平台
shortDescEn: An open-source self-hosted newsletter and email marketing platform
crossVersionUpdate: true
limit: 0
recommend: 0
architectures:
- amd64
description:
en: An open-source self-hosted newsletter and email marketing platform
zh: 开源的自托管新闻简报与邮件营销平台
zh-Hant: 開源的自託管電子報與電子郵件行銷平台
ja: オープンソースのセルフホスト型ニュースレター・メールマーケティングプラットフォーム
ko: 오픈 소스 자체 호스팅 뉴스레터 및 이메일 마케팅 플랫폼
ru: Самостоятельно размещаемая платформа с открытым исходным кодом для рассылок и email-маркетинга
ms: Platform sumber terbuka yang dihos sendiri untuk surat berita dan pemasaran e-mel
pt-br: Plataforma de código aberto auto-hospedada para newsletters e marketing por e-mail
+23
View File
@@ -0,0 +1,23 @@
PANEL_APP_PORT_HTTP=4000
PANEL_DB_HOST=
PANEL_DB_PORT=5432
PANEL_DB_NAME=keila
PANEL_DB_USER=keila
PANEL_DB_USER_PASSWORD=
KEILA_USER=root@localhost
KEILA_PASSWORD=
SECRET_KEY_BASE=
URL_HOST=localhost
URL_SCHEMA=http
URL_PORT=4000
APP_DATA_DIR=./data
MAILER_SMTP_HOST=
MAILER_SMTP_PORT=587
MAILER_SMTP_FROM_EMAIL=
MAILER_SMTP_USER=
MAILER_SMTP_PASSWORD=
MAILER_ENABLE_SSL=false
MAILER_ENABLE_STARTTLS=false
LOG_LEVEL=info
TZ=Asia/Shanghai
CONTAINER_NAME=
+401
View File
@@ -0,0 +1,401 @@
additionalProperties:
formFields:
- default: 4000
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: postgresql
envKey: PANEL_DB_TYPE
labelEn: Database Runtime
labelZh: 数据库 Runtime
label:
en: Database Runtime
zh: 数据库 Runtime
zh-Hant: 資料庫 Runtime
ja: データベース Runtime
ko: 데이터베이스 Runtime
ru: Среда базы данных
ms: Runtime Pangkalan Data
pt-br: Runtime 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: Serviço PostgreSQL
required: true
type: service
- default: 5432
edit: true
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
rule: paramPort
type: number
- default: keila
edit: true
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: keila
edit: true
envKey: PANEL_DB_USER
labelEn: Database User
labelZh: 数据库用户
label:
en: Database User
zh: 数据库用户
zh-Hant: 資料庫使用者
ja: データベースユーザー
ko: 데이터베이스 사용자
ru: Пользователь базы данных
ms: Pengguna Pangkalan Data
pt-br: Usuário do banco de dados
random: true
required: true
rule: paramCommon
type: text
- default: ''
edit: true
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: root@localhost
edit: true
envKey: KEILA_USER
labelEn: Initial Administrator Email
labelZh: 初始管理员邮箱
label:
en: Initial Administrator Email
zh: 初始管理员邮箱
zh-Hant: 初始管理員電子郵件
ja: 初期管理者メールアドレス
ko: 초기 관리자 이메일
ru: Email начального администратора
ms: E-mel Pentadbir Awal
pt-br: E-mail do administrador inicial
required: true
type: text
- default: ''
edit: true
envKey: KEILA_PASSWORD
labelEn: Initial Administrator Password (10+ characters)
labelZh: 初始管理员密码(至少 10 个字符)
label:
en: Initial Administrator Password (10+ characters)
zh: 初始管理员密码(至少 10 个字符)
zh-Hant: 初始管理員密碼(至少 10 個字元)
ja: 初期管理者パスワード(10 文字以上)
ko: 초기 관리자 비밀번호(10자 이상)
ru: Пароль начального администратора (не менее 10 символов)
ms: Kata Laluan Pentadbir Awal (10+ aksara)
pt-br: Senha do administrador inicial (10+ caracteres)
required: true
type: password
- default: ''
edit: true
envKey: SECRET_KEY_BASE
labelEn: Session Secret (64+ characters)
labelZh: 会话密钥(至少 64 个字符)
label:
en: Session Secret (64+ characters)
zh: 会话密钥(至少 64 个字符)
zh-Hant: 工作階段密鑰(至少 64 個字元)
ja: セッションシークレット(64 文字以上)
ko: 세션 비밀 키(64자 이상)
ru: Секрет сеанса (не менее 64 символов)
ms: Rahsia Sesi (64+ aksara)
pt-br: Segredo da sessão (64+ caracteres)
required: true
type: password
- default: ''
edit: true
envKey: URL_HOST
labelEn: Public Host (domain or IP)
labelZh: 公开访问主机(域名或 IP)
label:
en: Public Host (domain or IP)
zh: 公开访问主机(域名或 IP)
zh-Hant: 公開存取主機(網域或 IP)
ja: 公開ホスト(ドメインまたは IP)
ko: 공개 호스트(도메인 또는 IP)
ru: Публичный хост (домен или IP)
ms: Hos Awam (domain atau IP)
pt-br: Host público (domínio ou IP)
required: true
type: text
- default: http
edit: true
envKey: URL_SCHEMA
labelEn: Public URL Scheme
labelZh: 公开 URL 协议
label:
en: Public URL Scheme
zh: 公开 URL 协议
zh-Hant: 公開 URL 通訊協定
ja: 公開 URL スキーム
ko: 공개 URL 스키마
ru: Схема публичного URL
ms: Skema URL Awam
pt-br: Esquema da URL pública
required: true
type: select
values:
- label: http
value: http
- label: https
value: https
- default: 4000
edit: true
envKey: URL_PORT
labelEn: Public URL Port
labelZh: 公开 URL 端口
label:
en: Public URL Port
zh: 公开 URL 端口
zh-Hant: 公開 URL 連接埠
ja: 公開 URL ポート
ko: 공개 URL 포트
ru: Порт публичного URL
ms: Port URL Awam
pt-br: Porta da URL pública
required: true
type: number
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori Data
pt-br: Diretório de dados
required: true
type: text
- default: ''
edit: true
envKey: MAILER_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: Host SMTP
required: true
type: text
- default: 587
edit: true
envKey: MAILER_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: ''
edit: true
envKey: MAILER_SMTP_FROM_EMAIL
labelEn: SMTP From Email
labelZh: SMTP 发件邮箱
label:
en: SMTP From Email
zh: SMTP 发件邮箱
zh-Hant: SMTP 寄件者電子郵件
ja: SMTP 送信元メールアドレス
ko: SMTP 발신 이메일
ru: Email отправителя SMTP
ms: E-mel Pengirim SMTP
pt-br: E-mail do remetente SMTP
required: true
type: text
- default: ''
edit: true
envKey: MAILER_SMTP_USER
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: Usuário SMTP
required: true
type: text
- default: ''
edit: true
envKey: MAILER_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: true
type: password
- default: 'false'
edit: true
envKey: MAILER_ENABLE_SSL
labelEn: Enable SMTP SSL/TLS
labelZh: 启用 SMTP SSL/TLS
label:
en: Enable SMTP SSL/TLS
zh: 启用 SMTP SSL/TLS
zh-Hant: 啟用 SMTP SSL/TLS
ja: SMTP SSL/TLS を有効化
ko: SMTP SSL/TLS 활성화
ru: Включить SMTP SSL/TLS
ms: Dayakan SMTP SSL/TLS
pt-br: Ativar SMTP SSL/TLS
required: true
type: select
values:
- label: 'false'
value: 'false'
- label: 'true'
value: 'true'
- default: 'false'
edit: true
envKey: MAILER_ENABLE_STARTTLS
labelEn: Enable SMTP STARTTLS
labelZh: 启用 SMTP STARTTLS
label:
en: Enable SMTP STARTTLS
zh: 启用 SMTP STARTTLS
zh-Hant: 啟用 SMTP STARTTLS
ja: SMTP STARTTLS を有効化
ko: SMTP STARTTLS 활성화
ru: Включить SMTP STARTTLS
ms: Dayakan SMTP STARTTLS
pt-br: Ativar SMTP STARTTLS
required: true
type: select
values:
- label: 'false'
value: 'false'
- label: 'true'
value: 'true'
- default: info
edit: true
envKey: LOG_LEVEL
labelEn: Log Level
labelZh: 日志级别
label:
en: Log Level
zh: 日志级别
zh-Hant: 日誌層級
ja: ログレベル
ko: 로그 수준
ru: Уровень журналирования
ms: Tahap Log
pt-br: Nível de log
required: true
type: select
values:
- label: debug
value: debug
- label: info
value: info
- label: error
value: error
- default: Asia/Shanghai
edit: true
envKey: TZ
labelEn: Timezone
labelZh: 时区
label:
en: Timezone
zh: 时区
zh-Hant: 時區
ja: タイムゾーン
ko: 시간대
ru: Часовой пояс
ms: Zon Waktu
pt-br: Fuso horário
required: true
type: text
+44
View File
@@ -0,0 +1,44 @@
services:
keila:
image: "pentacent/keila:latest"
container_name: ${CONTAINER_NAME}
restart: unless-stopped
networks:
- 1panel-network
ports:
- "${PANEL_APP_PORT_HTTP}:4000"
environment:
- DB_URL=postgres://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${PANEL_DB_HOST}:${PANEL_DB_PORT}/${PANEL_DB_NAME}
- DB_ENABLE_SSL=false
- KEILA_USER=${KEILA_USER}
- KEILA_PASSWORD=${KEILA_PASSWORD}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
- URL_HOST=${URL_HOST}
- URL_SCHEMA=${URL_SCHEMA}
- URL_PORT=${URL_PORT}
- USER_CONTENT_DIR=/opt/app/uploads
- MAILER_SMTP_HOST=${MAILER_SMTP_HOST}
- MAILER_SMTP_PORT=${MAILER_SMTP_PORT}
- MAILER_SMTP_FROM_EMAIL=${MAILER_SMTP_FROM_EMAIL}
- MAILER_SMTP_USER=${MAILER_SMTP_USER}
- MAILER_SMTP_PASSWORD=${MAILER_SMTP_PASSWORD}
- MAILER_ENABLE_SSL=${MAILER_ENABLE_SSL}
- MAILER_ENABLE_STARTTLS=${MAILER_ENABLE_STARTTLS}
- LOG_LEVEL=${LOG_LEVEL}
- TZ=${TZ}
volumes:
- "${APP_DATA_DIR}/uploads:/opt/app/uploads"
healthcheck:
test:
- CMD-SHELL
- wget --quiet --tries=1 --spider http://127.0.0.1:4000/auth/login || exit 1
interval: 30s
timeout: 10s
start_period: 90s
retries: 5
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
strip_matching_quotes() {
local value="$1"
if [[ ${#value} -ge 2 ]]; then
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
value="${value:1:${#value}-2}"
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
value="${value:1:${#value}-2}"
fi
fi
printf '%s\n' "$value"
}
read_env_value() {
local key="$1"
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
strip_matching_quotes "$value"
}
set_env_value() {
local key="$1"
local value="$2"
local temp_file
temp_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { updated = 0 }
$0 ~ ("^" key "=") {
if (!updated) {
print key "=" value
updated = 1
}
next
}
{ print }
END {
if (!updated) print key "=" value
}
' "$ENV_FILE" > "$temp_file"
chmod --reference="$ENV_FILE" "$temp_file"
mv -f -- "$temp_file" "$ENV_FILE"
}
ensure_secret_key_base() {
local secret_key_base
secret_key_base="$(read_env_value SECRET_KEY_BASE)"
if [[ ${#secret_key_base} -lt 64 ]]; then
secret_key_base="$(od -An -N64 -tx1 /dev/urandom | tr -d ' \n')"
set_env_value SECRET_KEY_BASE "$secret_key_base"
fi
}
path_is_dotenv_safe() {
local value="$1"
case "$value" in
*$'\n'* | *$'\r'* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*) return 1 ;;
*) return 0 ;;
esac
}
[[ -f "$ENV_FILE" ]] || fail "Environment file not found: ${ENV_FILE}"
ensure_secret_key_base
if [[ ${APP_DATA_DIR+x} ]]; then
APP_DATA_DIR_RAW="$APP_DATA_DIR"
else
APP_DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)"
fi
APP_DATA_DIR_RAW="$(strip_matching_quotes "${APP_DATA_DIR_RAW:-./data}")"
[[ -n "$APP_DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty"
path_is_dotenv_safe "$APP_DATA_DIR_RAW" || fail "APP_DATA_DIR contains unsupported dotenv characters"
case "$APP_DATA_DIR_RAW" in
/*)
APP_DATA_DIR_ABS="$(realpath -m -- "$APP_DATA_DIR_RAW")"
;;
*)
APP_DATA_DIR_ABS="$(realpath -m -- "${ROOT_DIR}/${APP_DATA_DIR_RAW#./}")"
case "$APP_DATA_DIR_ABS" in
"${ROOT_DIR}" | "${ROOT_DIR}"/*) ;;
*) fail "Relative APP_DATA_DIR must stay inside the application directory" ;;
esac
;;
esac
[[ "$APP_DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root"
[[ ! -L "$APP_DATA_DIR_ABS" ]] || fail "APP_DATA_DIR must not be a symbolic link"
if [[ -e "$APP_DATA_DIR_ABS" && ! -d "$APP_DATA_DIR_ABS" ]]; then
fail "APP_DATA_DIR must be a directory"
fi
mkdir -p -- "${APP_DATA_DIR_ABS}/uploads"
chmod 750 -- "$APP_DATA_DIR_ABS" "${APP_DATA_DIR_ABS}/uploads"
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v docker-compose >/dev/null 2>&1; then
docker-compose down --volumes --remove-orphans
else
docker compose down --volumes --remove-orphans
fi
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
exec bash "${SCRIPT_DIR}/init.sh"
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB