mirror of
https://github.com/okxlin/appstore.git
synced 2026-09-25 08:01:02 +00:00
Merge pull request #5276 from okxlin/feat/kaneo-1panel-batch52
feat: add Kaneo application
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
CONTAINER_NAME=kaneo
|
||||
PANEL_APP_BIND_ADDRESS=127.0.0.1
|
||||
PANEL_APP_PORT_HTTP=5173
|
||||
KANEO_CLIENT_URL=http://localhost:5173
|
||||
DISABLE_REGISTRATION=false
|
||||
POSTGRES_DB=kaneo
|
||||
POSTGRES_USER=kaneo
|
||||
POSTGRES_PASSWORD=generate
|
||||
AUTH_SECRET=generate
|
||||
APP_DATA_DIR=./data
|
||||
@@ -0,0 +1,156 @@
|
||||
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: 5173
|
||||
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://localhost:5173
|
||||
edit: true
|
||||
envKey: KANEO_CLIENT_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: "false"
|
||||
edit: true
|
||||
envKey: DISABLE_REGISTRATION
|
||||
labelEn: Disable Registration
|
||||
labelZh: 禁止新用户注册
|
||||
label:
|
||||
en: Disable Registration
|
||||
zh: 禁止新用户注册
|
||||
zh-Hant: 停用新使用者註冊
|
||||
ja: 新規登録を無効化
|
||||
ko: 신규 등록 비활성화
|
||||
ru: Отключить регистрацию
|
||||
ms: Nyahdayakan pendaftaran
|
||||
pt-br: Desativar cadastro
|
||||
required: true
|
||||
type: select
|
||||
values:
|
||||
- label: "false"
|
||||
value: "false"
|
||||
- label: "true"
|
||||
value: "true"
|
||||
- default: kaneo
|
||||
edit: true
|
||||
envKey: POSTGRES_DB
|
||||
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: kaneo
|
||||
edit: true
|
||||
envKey: POSTGRES_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: generate
|
||||
edit: true
|
||||
envKey: POSTGRES_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
|
||||
type: password
|
||||
- default: generate
|
||||
edit: true
|
||||
envKey: AUTH_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: ./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: Diretorio de dados
|
||||
required: true
|
||||
type: text
|
||||
@@ -0,0 +1,73 @@
|
||||
services:
|
||||
kaneo-postgres:
|
||||
image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777
|
||||
container_name: ${CONTAINER_NAME}-postgres
|
||||
restart: unless-stopped
|
||||
user: "70:70"
|
||||
networks:
|
||||
- kaneo-internal
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data"
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:mode=1777,size=64m
|
||||
- /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
kaneo:
|
||||
image: ghcr.io/usekaneo/kaneo:2.9.9@sha256:d83479f41d437d5c35656f811f345a50357588422fc8d504cfde002756da688e
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: unless-stopped
|
||||
user: "1001:1001"
|
||||
depends_on:
|
||||
kaneo-postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- kaneo-internal
|
||||
- 1panel-network
|
||||
ports:
|
||||
- "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:5173"
|
||||
environment:
|
||||
KANEO_CLIENT_URL: ${KANEO_CLIENT_URL}
|
||||
POSTGRES_HOST: kaneo-postgres
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
AUTH_SECRET: ${AUTH_SECRET}
|
||||
DISABLE_REGISTRATION: ${DISABLE_REGISTRATION}
|
||||
tmpfs:
|
||||
- /tmp:mode=1777,size=64m
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5173/api/health"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 8
|
||||
start_period: 60s
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
networks:
|
||||
kaneo-internal:
|
||||
internal: true
|
||||
1panel-network:
|
||||
external: true
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
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}/.kaneo-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"
|
||||
}
|
||||
|
||||
validate_ipv4() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local octet
|
||||
local -a octets
|
||||
[[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address"
|
||||
IFS=. read -r -a octets <<< "$value"
|
||||
for octet in "${octets[@]}"; do
|
||||
((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet"
|
||||
done
|
||||
}
|
||||
|
||||
validate_port() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
[[ "$value" =~ ^[0-9]+$ ]] || fail "$key must be an integer"
|
||||
((10#$value >= 1 && 10#$value <= 65535)) || fail "$key must be between 1 and 65535"
|
||||
}
|
||||
|
||||
generate_alphanumeric() {
|
||||
local length="$1"
|
||||
local material
|
||||
material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)"
|
||||
[[ ${#material} -ge $length ]] || fail "unable to generate sufficient random material"
|
||||
printf '%s\n' "${material:0:length}"
|
||||
}
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
|
||||
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
|
||||
[[ "$(id -u)" -eq 0 ]] || fail "Kaneo init must run as root"
|
||||
command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials"
|
||||
|
||||
validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)"
|
||||
validate_port PANEL_APP_PORT_HTTP "$(read_env_value PANEL_APP_PORT_HTTP)"
|
||||
|
||||
client_url="$(read_env_value KANEO_CLIENT_URL)"
|
||||
[[ "$client_url" =~ ^https?://([A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?|localhost)(:([0-9]{1,5}))?$ ]] ||
|
||||
fail "KANEO_CLIENT_URL must be an HTTP(S) origin without a path"
|
||||
[[ "$client_url" != *..* ]] || fail "KANEO_CLIENT_URL contains an invalid hostname"
|
||||
if [[ -n "${BASH_REMATCH[4]:-}" ]]; then
|
||||
validate_port KANEO_CLIENT_URL_PORT "${BASH_REMATCH[4]}"
|
||||
fi
|
||||
|
||||
disable_registration="$(read_env_value DISABLE_REGISTRATION)"
|
||||
[[ "$disable_registration" == true || "$disable_registration" == false ]] ||
|
||||
fail "DISABLE_REGISTRATION must be true or false"
|
||||
|
||||
postgres_db="$(read_env_value POSTGRES_DB)"
|
||||
postgres_user="$(read_env_value POSTGRES_USER)"
|
||||
[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid"
|
||||
[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid"
|
||||
|
||||
postgres_password="$(read_env_value POSTGRES_PASSWORD)"
|
||||
if [[ -z "$postgres_password" || "$postgres_password" == generate ]]; then
|
||||
postgres_password="$(generate_alphanumeric 48)"
|
||||
fi
|
||||
[[ "$postgres_password" =~ ^[A-Za-z0-9]{32,128}$ ]] ||
|
||||
fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters"
|
||||
|
||||
auth_secret="$(read_env_value AUTH_SECRET)"
|
||||
if [[ -z "$auth_secret" || "$auth_secret" == generate ]]; then
|
||||
auth_secret="$(openssl rand -hex 32)"
|
||||
fi
|
||||
[[ "$auth_secret" =~ ^[A-Fa-f0-9]{64,128}$ ]] ||
|
||||
fail "AUTH_SECRET must contain 64 to 128 hexadecimal characters"
|
||||
|
||||
data_raw="$(read_env_value APP_DATA_DIR)"
|
||||
[[ -n "$data_raw" && "$data_raw" != /* ]] || fail "APP_DATA_DIR must be a non-empty relative path"
|
||||
case "$data_raw" in
|
||||
*$'\n'* | *$'\r'* | *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*)
|
||||
fail "APP_DATA_DIR contains unsupported characters"
|
||||
;;
|
||||
esac
|
||||
|
||||
relative_data="${data_raw#./}"
|
||||
[[ -n "$relative_data" ]] || fail "APP_DATA_DIR must not resolve to the version root"
|
||||
current="$ROOT_DIR"
|
||||
IFS=/ read -r -a components <<< "$relative_data"
|
||||
for component in "${components[@]}"; do
|
||||
[[ -n "$component" && "$component" != . && "$component" != .. ]] || fail "APP_DATA_DIR contains traversal"
|
||||
current="$current/$component"
|
||||
[[ ! -L "$current" ]] || fail "APP_DATA_DIR must not contain symbolic-link components"
|
||||
done
|
||||
|
||||
data_dir="$(realpath -m -- "$ROOT_DIR/$relative_data")"
|
||||
case "$data_dir" in
|
||||
"$ROOT_DIR"/*) ;;
|
||||
*) fail "APP_DATA_DIR must stay inside the application version directory" ;;
|
||||
esac
|
||||
|
||||
install -d -m 0700 -- "$data_dir/postgres"
|
||||
resolved_data="$(realpath -e -- "$data_dir")"
|
||||
case "$resolved_data" in
|
||||
"$ROOT_DIR"/*) ;;
|
||||
*) fail "APP_DATA_DIR resolves outside the application version directory" ;;
|
||||
esac
|
||||
chown -R 70:70 -- "$resolved_data/postgres"
|
||||
chmod 0700 -- "$resolved_data/postgres"
|
||||
|
||||
set_env_value POSTGRES_PASSWORD "$postgres_password"
|
||||
set_env_value AUTH_SECRET "$auth_secret"
|
||||
chmod 600 "$ENV_FILE"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if command -v docker-compose >/dev/null 2>&1; then
|
||||
docker-compose down --remove-orphans
|
||||
else
|
||||
docker compose down --remove-orphans
|
||||
fi
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
exec "$SCRIPT_DIR/init.sh"
|
||||
@@ -0,0 +1,75 @@
|
||||
# Kaneo
|
||||
|
||||
## 产品介绍
|
||||
|
||||
Kaneo 是一个轻量级的开源项目管理平台,提供看板、任务、标签、评论、
|
||||
时间记录、实时更新和多种第三方集成。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 使用看板、任务、标签、评论和时间记录管理项目
|
||||
- 通过 WebSocket 实时同步项目变更
|
||||
- 支持 GitHub、Gitea、Slack、Discord、Telegram 和通用 Webhook 集成
|
||||
- 提供 API、MCP 接口和可选的对象存储图片附件
|
||||
|
||||
## 访问说明
|
||||
|
||||
- Web 端口默认只绑定 `127.0.0.1`。请在 1Panel 中配置 HTTPS 反向代理。
|
||||
- 安装时的公开访问地址必须填写完整的 HTTP(S) origin,例如
|
||||
`https://kaneo.example.com`,不要包含路径或末尾斜杠。
|
||||
- 第一个注册用户会成为实例管理员。确认管理员账号可用后,可在应用参数中
|
||||
将“禁止新用户注册”设为 `true` 并重启应用。
|
||||
|
||||
## 安全与数据
|
||||
|
||||
- 安装脚本会一次生成数据库密码和认证密钥,并写入权限为 `0600` 的
|
||||
`.env`。升级和重启不会自动轮换。
|
||||
- Kaneo 以官方 UID/GID `1001:1001` 运行;PostgreSQL 以 `70:70`
|
||||
直接运行,不执行镜像内的 root/gosu 切换路径。
|
||||
- 两个容器都丢弃全部 Linux capabilities 并禁止权限提升。PostgreSQL
|
||||
仅连接内部 Docker 网络,不发布主机端口。
|
||||
- PostgreSQL 数据保存在安装版本目录下的 `data/postgres`。卸载不会删除
|
||||
该目录;升级前请备份整个数据目录。
|
||||
|
||||
## Introduction
|
||||
|
||||
Kaneo is a lightweight open-source project management platform with boards,
|
||||
tasks, labels, comments, time tracking, live updates, and integrations.
|
||||
|
||||
## Features
|
||||
|
||||
- Manage projects with boards, tasks, labels, comments, and time tracking
|
||||
- Synchronize project changes in real time over WebSockets
|
||||
- Integrate with GitHub, Gitea, Slack, Discord, Telegram, and generic webhooks
|
||||
- Use its API, MCP interface, and optional object storage for image attachments
|
||||
|
||||
## Access And First Use
|
||||
|
||||
- The web port binds to `127.0.0.1` by default. Publish it through the 1Panel
|
||||
reverse proxy with HTTPS.
|
||||
- Set Public URL to the complete HTTP(S) origin used by browsers, such as
|
||||
`https://kaneo.example.com`. Do not include a path or trailing slash.
|
||||
- The first registered user becomes the instance administrator. After that
|
||||
account works, set Disable Registration to `true` and restart if public
|
||||
signup is not required.
|
||||
|
||||
## Security And Data
|
||||
|
||||
- Installation generates the database password and authentication secret once
|
||||
and stores them in the mode `0600` `.env`. Restart and upgrade preserve them.
|
||||
- Kaneo runs as the upstream UID/GID `1001:1001`. PostgreSQL runs directly as
|
||||
`70:70`, bypassing the image's root/gosu identity-switch path.
|
||||
- Both services drop every Linux capability and prevent privilege escalation.
|
||||
PostgreSQL is restricted to an internal Docker network and publishes no host
|
||||
port.
|
||||
- PostgreSQL data is stored in `data/postgres` below the installed version.
|
||||
Uninstall preserves it. Back up the complete data directory before upgrades.
|
||||
- Kaneo's root filesystem remains writable because the official startup script
|
||||
replaces frontend runtime URLs and nginx discovery metadata in place. The
|
||||
service remains non-root and can write only paths owned by its upstream user.
|
||||
|
||||
## References
|
||||
|
||||
- Source: <https://github.com/usekaneo/kaneo>
|
||||
- Release: <https://github.com/usekaneo/kaneo/releases/tag/v2.9.9>
|
||||
- Self-hosting: <https://github.com/usekaneo/kaneo#self-hosting>
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Kaneo
|
||||
tags:
|
||||
- 项目管理
|
||||
title: 轻量级开源项目管理平台
|
||||
description: 轻量级开源项目管理平台
|
||||
additionalProperties:
|
||||
key: kaneo
|
||||
name: Kaneo
|
||||
tags:
|
||||
- Project Management
|
||||
shortDescZh: 轻量级开源项目管理平台
|
||||
shortDescEn: Lightweight open-source project management platform
|
||||
description:
|
||||
en: A lightweight open-source project management platform
|
||||
zh: 一个轻量级开源项目管理平台
|
||||
zh-Hant: 一個輕量級開源專案管理平台
|
||||
ja: 軽量なオープンソースのプロジェクト管理プラットフォーム
|
||||
ko: 가벼운 오픈 소스 프로젝트 관리 플랫폼
|
||||
ru: Легкая платформа управления проектами с открытым исходным кодом
|
||||
ms: Platform pengurusan projek sumber terbuka yang ringan
|
||||
pt-br: Plataforma leve de gerenciamento de projetos de codigo aberto
|
||||
type: website
|
||||
crossVersionUpdate: true
|
||||
limit: 0
|
||||
recommend: 0
|
||||
website: https://kaneo.app/
|
||||
github: https://github.com/usekaneo/kaneo
|
||||
document: https://github.com/usekaneo/kaneo#self-hosting
|
||||
architectures:
|
||||
- amd64
|
||||
- arm64
|
||||
@@ -0,0 +1,10 @@
|
||||
CONTAINER_NAME=kaneo
|
||||
PANEL_APP_BIND_ADDRESS=127.0.0.1
|
||||
PANEL_APP_PORT_HTTP=5173
|
||||
KANEO_CLIENT_URL=http://localhost:5173
|
||||
DISABLE_REGISTRATION=false
|
||||
POSTGRES_DB=kaneo
|
||||
POSTGRES_USER=kaneo
|
||||
POSTGRES_PASSWORD=generate
|
||||
AUTH_SECRET=generate
|
||||
APP_DATA_DIR=./data
|
||||
@@ -0,0 +1,156 @@
|
||||
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: 5173
|
||||
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://localhost:5173
|
||||
edit: true
|
||||
envKey: KANEO_CLIENT_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: "false"
|
||||
edit: true
|
||||
envKey: DISABLE_REGISTRATION
|
||||
labelEn: Disable Registration
|
||||
labelZh: 禁止新用户注册
|
||||
label:
|
||||
en: Disable Registration
|
||||
zh: 禁止新用户注册
|
||||
zh-Hant: 停用新使用者註冊
|
||||
ja: 新規登録を無効化
|
||||
ko: 신규 등록 비활성화
|
||||
ru: Отключить регистрацию
|
||||
ms: Nyahdayakan pendaftaran
|
||||
pt-br: Desativar cadastro
|
||||
required: true
|
||||
type: select
|
||||
values:
|
||||
- label: "false"
|
||||
value: "false"
|
||||
- label: "true"
|
||||
value: "true"
|
||||
- default: kaneo
|
||||
edit: true
|
||||
envKey: POSTGRES_DB
|
||||
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: kaneo
|
||||
edit: true
|
||||
envKey: POSTGRES_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: generate
|
||||
edit: true
|
||||
envKey: POSTGRES_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
|
||||
type: password
|
||||
- default: generate
|
||||
edit: true
|
||||
envKey: AUTH_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: ./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: Diretorio de dados
|
||||
required: true
|
||||
type: text
|
||||
@@ -0,0 +1,73 @@
|
||||
services:
|
||||
kaneo-postgres:
|
||||
image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777
|
||||
container_name: ${CONTAINER_NAME}-postgres
|
||||
restart: unless-stopped
|
||||
user: "70:70"
|
||||
networks:
|
||||
- kaneo-internal
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- "${APP_DATA_DIR}/postgres:/var/lib/postgresql/data"
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:mode=1777,size=64m
|
||||
- /var/run/postgresql:uid=70,gid=70,mode=0755,size=16m
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
kaneo:
|
||||
image: ghcr.io/usekaneo/kaneo:latest@sha256:d83479f41d437d5c35656f811f345a50357588422fc8d504cfde002756da688e
|
||||
container_name: ${CONTAINER_NAME}
|
||||
restart: unless-stopped
|
||||
user: "1001:1001"
|
||||
depends_on:
|
||||
kaneo-postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- kaneo-internal
|
||||
- 1panel-network
|
||||
ports:
|
||||
- "${PANEL_APP_BIND_ADDRESS}:${PANEL_APP_PORT_HTTP}:5173"
|
||||
environment:
|
||||
KANEO_CLIENT_URL: ${KANEO_CLIENT_URL}
|
||||
POSTGRES_HOST: kaneo-postgres
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
AUTH_SECRET: ${AUTH_SECRET}
|
||||
DISABLE_REGISTRATION: ${DISABLE_REGISTRATION}
|
||||
tmpfs:
|
||||
- /tmp:mode=1777,size=64m
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5173/api/health"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 8
|
||||
start_period: 60s
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
networks:
|
||||
kaneo-internal:
|
||||
internal: true
|
||||
1panel-network:
|
||||
external: true
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
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}/.kaneo-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"
|
||||
}
|
||||
|
||||
validate_ipv4() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local octet
|
||||
local -a octets
|
||||
[[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "$key must be an IPv4 address"
|
||||
IFS=. read -r -a octets <<< "$value"
|
||||
for octet in "${octets[@]}"; do
|
||||
((10#$octet <= 255)) || fail "$key contains an invalid IPv4 octet"
|
||||
done
|
||||
}
|
||||
|
||||
validate_port() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
[[ "$value" =~ ^[0-9]+$ ]] || fail "$key must be an integer"
|
||||
((10#$value >= 1 && 10#$value <= 65535)) || fail "$key must be between 1 and 65535"
|
||||
}
|
||||
|
||||
generate_alphanumeric() {
|
||||
local length="$1"
|
||||
local material
|
||||
material="$(openssl rand -base64 96 | tr -dc A-Za-z0-9)"
|
||||
[[ ${#material} -ge $length ]] || fail "unable to generate sufficient random material"
|
||||
printf '%s\n' "${material:0:length}"
|
||||
}
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
|
||||
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
|
||||
[[ "$(id -u)" -eq 0 ]] || fail "Kaneo init must run as root"
|
||||
command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate credentials"
|
||||
|
||||
validate_ipv4 PANEL_APP_BIND_ADDRESS "$(read_env_value PANEL_APP_BIND_ADDRESS)"
|
||||
validate_port PANEL_APP_PORT_HTTP "$(read_env_value PANEL_APP_PORT_HTTP)"
|
||||
|
||||
client_url="$(read_env_value KANEO_CLIENT_URL)"
|
||||
[[ "$client_url" =~ ^https?://([A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?|localhost)(:([0-9]{1,5}))?$ ]] ||
|
||||
fail "KANEO_CLIENT_URL must be an HTTP(S) origin without a path"
|
||||
[[ "$client_url" != *..* ]] || fail "KANEO_CLIENT_URL contains an invalid hostname"
|
||||
if [[ -n "${BASH_REMATCH[4]:-}" ]]; then
|
||||
validate_port KANEO_CLIENT_URL_PORT "${BASH_REMATCH[4]}"
|
||||
fi
|
||||
|
||||
disable_registration="$(read_env_value DISABLE_REGISTRATION)"
|
||||
[[ "$disable_registration" == true || "$disable_registration" == false ]] ||
|
||||
fail "DISABLE_REGISTRATION must be true or false"
|
||||
|
||||
postgres_db="$(read_env_value POSTGRES_DB)"
|
||||
postgres_user="$(read_env_value POSTGRES_USER)"
|
||||
[[ "$postgres_db" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_DB is invalid"
|
||||
[[ "$postgres_user" =~ ^[A-Za-z_][A-Za-z0-9_]{0,62}$ ]] || fail "POSTGRES_USER is invalid"
|
||||
|
||||
postgres_password="$(read_env_value POSTGRES_PASSWORD)"
|
||||
if [[ -z "$postgres_password" || "$postgres_password" == generate ]]; then
|
||||
postgres_password="$(generate_alphanumeric 48)"
|
||||
fi
|
||||
[[ "$postgres_password" =~ ^[A-Za-z0-9]{32,128}$ ]] ||
|
||||
fail "POSTGRES_PASSWORD must contain 32 to 128 alphanumeric characters"
|
||||
|
||||
auth_secret="$(read_env_value AUTH_SECRET)"
|
||||
if [[ -z "$auth_secret" || "$auth_secret" == generate ]]; then
|
||||
auth_secret="$(openssl rand -hex 32)"
|
||||
fi
|
||||
[[ "$auth_secret" =~ ^[A-Fa-f0-9]{64,128}$ ]] ||
|
||||
fail "AUTH_SECRET must contain 64 to 128 hexadecimal characters"
|
||||
|
||||
data_raw="$(read_env_value APP_DATA_DIR)"
|
||||
[[ -n "$data_raw" && "$data_raw" != /* ]] || fail "APP_DATA_DIR must be a non-empty relative path"
|
||||
case "$data_raw" in
|
||||
*$'\n'* | *$'\r'* | *\\* | *:* | *'$'* | *'#'* | *'"'* | *"'"*)
|
||||
fail "APP_DATA_DIR contains unsupported characters"
|
||||
;;
|
||||
esac
|
||||
|
||||
relative_data="${data_raw#./}"
|
||||
[[ -n "$relative_data" ]] || fail "APP_DATA_DIR must not resolve to the version root"
|
||||
current="$ROOT_DIR"
|
||||
IFS=/ read -r -a components <<< "$relative_data"
|
||||
for component in "${components[@]}"; do
|
||||
[[ -n "$component" && "$component" != . && "$component" != .. ]] || fail "APP_DATA_DIR contains traversal"
|
||||
current="$current/$component"
|
||||
[[ ! -L "$current" ]] || fail "APP_DATA_DIR must not contain symbolic-link components"
|
||||
done
|
||||
|
||||
data_dir="$(realpath -m -- "$ROOT_DIR/$relative_data")"
|
||||
case "$data_dir" in
|
||||
"$ROOT_DIR"/*) ;;
|
||||
*) fail "APP_DATA_DIR must stay inside the application version directory" ;;
|
||||
esac
|
||||
|
||||
install -d -m 0700 -- "$data_dir/postgres"
|
||||
resolved_data="$(realpath -e -- "$data_dir")"
|
||||
case "$resolved_data" in
|
||||
"$ROOT_DIR"/*) ;;
|
||||
*) fail "APP_DATA_DIR resolves outside the application version directory" ;;
|
||||
esac
|
||||
chown -R 70:70 -- "$resolved_data/postgres"
|
||||
chmod 0700 -- "$resolved_data/postgres"
|
||||
|
||||
set_env_value POSTGRES_PASSWORD "$postgres_password"
|
||||
set_env_value AUTH_SECRET "$auth_secret"
|
||||
chmod 600 "$ENV_FILE"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if command -v docker-compose >/dev/null 2>&1; then
|
||||
docker-compose down --remove-orphans
|
||||
else
|
||||
docker compose down --remove-orphans
|
||||
fi
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
exec "$SCRIPT_DIR/init.sh"
|
||||
@@ -0,0 +1,4 @@
|
||||
Source: https://github.com/usekaneo/kaneo/blob/v2.9.9/apps/site/public/logo-512.png
|
||||
License: MIT
|
||||
Copyright: Copyright (c) 2024 Andrej Acevski
|
||||
The source image is used without changing its design.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Reference in New Issue
Block a user