mirror of
https://github.com/okxlin/appstore.git
synced 2026-10-05 16:00:26 +00:00
@@ -0,0 +1,9 @@
|
||||
PANEL_APP_PORT_HTTP=9000
|
||||
PANEL_APP_PORT_PG=8812
|
||||
QUESTDB_HTTP_USER=admin
|
||||
QUESTDB_HTTP_PASSWORD=replace-with-at-least-32-safe-characters
|
||||
QUESTDB_PG_USER=admin
|
||||
QUESTDB_PG_PASSWORD=replace-with-another-32-safe-characters
|
||||
QUESTDB_TELEMETRY_ENABLED=false
|
||||
APP_DATA_DIR=./data
|
||||
CONTAINER_NAME=questdb
|
||||
@@ -0,0 +1,139 @@
|
||||
additionalProperties:
|
||||
formFields:
|
||||
- default: 9000
|
||||
edit: true
|
||||
envKey: PANEL_APP_PORT_HTTP
|
||||
labelEn: Web and HTTP API Port
|
||||
labelZh: Web 与 HTTP API 端口
|
||||
label:
|
||||
en: Web and HTTP API Port
|
||||
zh: Web 与 HTTP API 端口
|
||||
zh-Hant: Web 與 HTTP API 連接埠
|
||||
ja: Web および HTTP API ポート
|
||||
ko: Web 및 HTTP API 포트
|
||||
ru: Порт Web и HTTP API
|
||||
ms: Port Web dan API HTTP
|
||||
pt-br: Porta Web e da API HTTP
|
||||
required: true
|
||||
rule: paramPort
|
||||
type: number
|
||||
- default: 8812
|
||||
edit: true
|
||||
envKey: PANEL_APP_PORT_PG
|
||||
labelEn: PGWire Port
|
||||
labelZh: PGWire 端口
|
||||
label:
|
||||
en: PGWire Port
|
||||
zh: PGWire 端口
|
||||
zh-Hant: PGWire 連接埠
|
||||
ja: PGWire ポート
|
||||
ko: PGWire 포트
|
||||
ru: Порт PGWire
|
||||
ms: Port PGWire
|
||||
pt-br: Porta PGWire
|
||||
required: true
|
||||
rule: paramPort
|
||||
type: number
|
||||
- default: admin
|
||||
edit: true
|
||||
envKey: QUESTDB_HTTP_USER
|
||||
labelEn: HTTP Username
|
||||
labelZh: HTTP 用户名
|
||||
label:
|
||||
en: HTTP Username
|
||||
zh: HTTP 用户名
|
||||
zh-Hant: HTTP 使用者名稱
|
||||
ja: HTTP ユーザー名
|
||||
ko: HTTP 사용자 이름
|
||||
ru: Имя пользователя HTTP
|
||||
ms: Nama Pengguna HTTP
|
||||
pt-br: Usuário HTTP
|
||||
required: true
|
||||
rule: paramCommon
|
||||
type: text
|
||||
- default: generate
|
||||
edit: true
|
||||
envKey: QUESTDB_HTTP_PASSWORD
|
||||
labelEn: HTTP Password
|
||||
labelZh: HTTP 密码
|
||||
label:
|
||||
en: HTTP Password
|
||||
zh: HTTP 密码
|
||||
zh-Hant: HTTP 密碼
|
||||
ja: HTTP パスワード
|
||||
ko: HTTP 비밀번호
|
||||
ru: Пароль HTTP
|
||||
ms: Kata Laluan HTTP
|
||||
pt-br: Senha HTTP
|
||||
required: true
|
||||
type: password
|
||||
- default: admin
|
||||
edit: true
|
||||
envKey: QUESTDB_PG_USER
|
||||
labelEn: PGWire Username
|
||||
labelZh: PGWire 用户名
|
||||
label:
|
||||
en: PGWire Username
|
||||
zh: PGWire 用户名
|
||||
zh-Hant: PGWire 使用者名稱
|
||||
ja: PGWire ユーザー名
|
||||
ko: PGWire 사용자 이름
|
||||
ru: Имя пользователя PGWire
|
||||
ms: Nama Pengguna PGWire
|
||||
pt-br: Usuário PGWire
|
||||
required: true
|
||||
rule: paramCommon
|
||||
type: text
|
||||
- default: generate
|
||||
edit: true
|
||||
envKey: QUESTDB_PG_PASSWORD
|
||||
labelEn: PGWire Password
|
||||
labelZh: PGWire 密码
|
||||
label:
|
||||
en: PGWire Password
|
||||
zh: PGWire 密码
|
||||
zh-Hant: PGWire 密碼
|
||||
ja: PGWire パスワード
|
||||
ko: PGWire 비밀번호
|
||||
ru: Пароль PGWire
|
||||
ms: Kata Laluan PGWire
|
||||
pt-br: Senha PGWire
|
||||
required: true
|
||||
type: password
|
||||
- default: "false"
|
||||
edit: true
|
||||
envKey: QUESTDB_TELEMETRY_ENABLED
|
||||
labelEn: Enable Anonymous Telemetry
|
||||
labelZh: 启用匿名遥测
|
||||
label:
|
||||
en: Enable Anonymous Telemetry
|
||||
zh: 启用匿名遥测
|
||||
zh-Hant: 啟用匿名遙測
|
||||
ja: 匿名テレメトリを有効化
|
||||
ko: 익명 원격 측정 활성화
|
||||
ru: Включить анонимную телеметрию
|
||||
ms: Dayakan Telemetri Tanpa Nama
|
||||
pt-br: Ativar telemetria anônima
|
||||
required: true
|
||||
type: select
|
||||
values:
|
||||
- label: "false"
|
||||
value: "false"
|
||||
- label: "true"
|
||||
value: "true"
|
||||
- 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
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
services:
|
||||
questdb:
|
||||
image: "questdb/questdb:9.4.3"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
init: true
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- 1panel-network
|
||||
ports:
|
||||
- "${PANEL_APP_PORT_HTTP}:9000"
|
||||
- "${PANEL_APP_PORT_PG}:8812"
|
||||
environment:
|
||||
- QDB_HTTP_USER=${QUESTDB_HTTP_USER}
|
||||
- QDB_HTTP_PASSWORD=${QUESTDB_HTTP_PASSWORD}
|
||||
- QDB_PG_USER=${QUESTDB_PG_USER}
|
||||
- QDB_PG_PASSWORD=${QUESTDB_PG_PASSWORD}
|
||||
- QDB_TELEMETRY_ENABLED=${QUESTDB_TELEMETRY_ENABLED}
|
||||
- QDB_LINE_TCP_ENABLED=false
|
||||
- QDB_HTTP_PESSIMISTIC_HEALTH_CHECK_ENABLED=true
|
||||
- QDB_HTTP_HEALTH_CHECK_AUTHENTICATION_REQUIRED=false
|
||||
volumes:
|
||||
- "${APP_DATA_DIR}:/var/lib/questdb"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl --fail --silent http://127.0.0.1:9003/status >/dev/null"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 60s
|
||||
retries: 5
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
networks:
|
||||
1panel-network:
|
||||
external: true
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/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"
|
||||
}
|
||||
|
||||
env_has_key() {
|
||||
local key="$1"
|
||||
[[ -f "$ENV_FILE" ]] && grep -qE "^${key}=" "$ENV_FILE"
|
||||
}
|
||||
|
||||
read_env_value() {
|
||||
local key="$1"
|
||||
local value=""
|
||||
|
||||
if env_has_key "$key"; then
|
||||
value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2-)"
|
||||
fi
|
||||
strip_matching_quotes "$value"
|
||||
}
|
||||
|
||||
read_effective_value() {
|
||||
local key="$1"
|
||||
|
||||
if [[ -v "$key" ]]; then
|
||||
strip_matching_quotes "${!key}"
|
||||
else
|
||||
read_env_value "$key"
|
||||
fi
|
||||
}
|
||||
|
||||
username_is_safe() {
|
||||
local value="$1"
|
||||
[[ "$value" =~ ^[A-Za-z_][A-Za-z0-9_.-]{0,62}$ ]]
|
||||
}
|
||||
|
||||
secret_is_safe() {
|
||||
local value="$1"
|
||||
[[ "$value" =~ ^[-A-Za-z0-9._~!@#%^\&*+=:,/?]{32,}$ ]]
|
||||
}
|
||||
|
||||
generate_secret() {
|
||||
local value=""
|
||||
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
value="$(openssl rand -hex 32)"
|
||||
elif [[ -r /dev/urandom ]] && command -v od >/dev/null 2>&1; then
|
||||
value="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
|
||||
fi
|
||||
[[ "$value" =~ ^[0-9a-f]{64}$ ]] || fail "Unable to generate a secure QuestDB password"
|
||||
printf '%s\n' "$value"
|
||||
}
|
||||
|
||||
write_secret_cache() {
|
||||
local cache_file="$1"
|
||||
local value="$2"
|
||||
local temp_file
|
||||
|
||||
umask 077
|
||||
temp_file="$(mktemp "${cache_file}.tmp.XXXXXX")"
|
||||
printf '%s\n' "$value" > "$temp_file"
|
||||
chmod 600 "$temp_file"
|
||||
mv -f -- "$temp_file" "$cache_file"
|
||||
}
|
||||
|
||||
set_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local env_dir
|
||||
local temp_file
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
|
||||
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
|
||||
env_dir="$(dirname "$ENV_FILE")"
|
||||
temp_file="$(mktemp "${env_dir}/.questdb-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 --reference="$ENV_FILE" "$temp_file"
|
||||
mv -f -- "$temp_file" "$ENV_FILE"
|
||||
}
|
||||
|
||||
resolve_secret() {
|
||||
local env_key="$1"
|
||||
local cache_file="$2"
|
||||
local value
|
||||
local cached_value=""
|
||||
|
||||
[[ ! -L "$cache_file" ]] || fail "QuestDB password cache must not be a symbolic link"
|
||||
if [[ -e "$cache_file" && ! -f "$cache_file" ]]; then
|
||||
fail "QuestDB password cache must be a regular file"
|
||||
fi
|
||||
if [[ -s "$cache_file" ]]; then
|
||||
cached_value="$(sed -n '1p' "$cache_file")"
|
||||
secret_is_safe "$cached_value" || fail "Persisted QuestDB password is invalid"
|
||||
fi
|
||||
|
||||
value="$(read_effective_value "$env_key")"
|
||||
if [[ -z "$value" || "$value" == "generate" ]]; then
|
||||
if [[ -n "$cached_value" ]]; then
|
||||
value="$cached_value"
|
||||
else
|
||||
value="$(generate_secret)"
|
||||
fi
|
||||
else
|
||||
secret_is_safe "$value" || fail "${env_key} must contain at least 32 safe characters"
|
||||
fi
|
||||
|
||||
write_secret_cache "$cache_file" "$value"
|
||||
set_env_value "$env_key" "$value"
|
||||
}
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
|
||||
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
|
||||
|
||||
app_data_dir_explicit=false
|
||||
if [[ ${APP_DATA_DIR+x} ]]; then
|
||||
APP_DATA_DIR_RAW="$APP_DATA_DIR"
|
||||
app_data_dir_explicit=true
|
||||
elif env_has_key APP_DATA_DIR; then
|
||||
APP_DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)"
|
||||
app_data_dir_explicit=true
|
||||
else
|
||||
APP_DATA_DIR_RAW="./data"
|
||||
fi
|
||||
APP_DATA_DIR_RAW="$(strip_matching_quotes "$APP_DATA_DIR_RAW")"
|
||||
|
||||
if [[ "$app_data_dir_explicit" == "true" && -z "$APP_DATA_DIR_RAW" ]]; then
|
||||
fail "APP_DATA_DIR must not be empty"
|
||||
fi
|
||||
|
||||
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"
|
||||
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"
|
||||
|
||||
http_user="$(read_effective_value QUESTDB_HTTP_USER)"
|
||||
pg_user="$(read_effective_value QUESTDB_PG_USER)"
|
||||
username_is_safe "$http_user" || fail "QUESTDB_HTTP_USER contains unsupported characters"
|
||||
username_is_safe "$pg_user" || fail "QUESTDB_PG_USER contains unsupported characters"
|
||||
|
||||
resolve_secret QUESTDB_HTTP_PASSWORD "${APP_DATA_DIR_ABS}/.questdb_http_password"
|
||||
resolve_secret QUESTDB_PG_PASSWORD "${APP_DATA_DIR_ABS}/.questdb_pg_password"
|
||||
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 --volumes --remove-orphans
|
||||
else
|
||||
docker compose down --volumes --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 bash "${SCRIPT_DIR}/init.sh"
|
||||
@@ -0,0 +1,57 @@
|
||||
# QuestDB
|
||||
|
||||
## 产品介绍
|
||||
|
||||
QuestDB 是一个面向实时分析的开源时序数据库,提供高吞吐写入、低延迟 SQL 查询、Web Console、REST API、InfluxDB Line Protocol 和 PostgreSQL Wire Protocol。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 面向时序数据的列式存储、WAL 和分区管理
|
||||
- Web Console、REST API 与 SQL 查询
|
||||
- 通过 HTTP 接收 InfluxDB Line Protocol 数据
|
||||
- 兼容 PostgreSQL Wire Protocol 的查询接口
|
||||
- 快照、检查点和持久化数据目录
|
||||
|
||||
## 访问说明
|
||||
|
||||
- Web Console、REST API 和 ILP-over-HTTP 共用默认端口 `9000`。
|
||||
- PostgreSQL Wire Protocol 默认使用端口 `8812`,数据库名为 `qdb`。
|
||||
- 本应用默认启用 HTTP Basic 认证和 PGWire 密码认证,两个接口使用独立用户名与密码。
|
||||
- QuestDB 的 ILP TCP 端口 `9009` 默认不提供简单的用户名/密码保护。本应用为减少未认证写入面,默认关闭且不发布该端口;常见写入场景可直接使用已受 HTTP Basic 保护的 ILP-over-HTTP。
|
||||
- 内部 `9003` 端口仅用于容器健康检查,不发布到宿主机。健康状态端点不要求业务凭据,其他 HTTP 接口仍受 Basic 认证保护。
|
||||
|
||||
## 数据与安全
|
||||
|
||||
- QuestDB 的完整数据根目录持久化到安装表单选择的数据目录,其中包含 `db`、`conf`、`public`、快照和检查点等运行数据。
|
||||
- 安装脚本会生成并持久化独立的 HTTP 与 PGWire 密码。数据目录中的 `.questdb_http_password` 和 `.questdb_pg_password` 用于防止 1Panel 升级回放安装参数时轮换密码,请与数据库一起备份。
|
||||
- 官方镜像入口以 root 启动,用于检查数据目录属主,然后以 UID/GID `10001:10001` 运行 QuestDB。首次挂载既有数据目录时,上游入口可能调整其已知数据子目录的属主;迁移前应先备份并核对权限。
|
||||
- Web Console 和协议端口本身不提供 HTTPS。对外访问时应使用 HTTPS 反向代理、限制端口来源,并按需配置网络层 TLS。
|
||||
- 默认关闭匿名遥测,可在安装表单中按需开启。
|
||||
|
||||
## 升级说明
|
||||
|
||||
- 升级前停止写入并完整备份数据目录及两个密码缓存文件。
|
||||
- 固定版与 `latest` 使用相同的数据路径和配置键,可通过 1Panel 执行跨版本升级。
|
||||
- 数据格式迁移可能在启动阶段完成。生产数据应先在副本环境验证目标版本,并在升级过程中等待健康检查通过后再恢复流量。
|
||||
|
||||
## Introduction
|
||||
|
||||
QuestDB is an open-source time-series database for high-throughput ingestion and low-latency SQL analytics. This package exposes the authenticated Web/REST/ILP-over-HTTP endpoint and PGWire endpoint, while disabling unauthenticated ILP TCP by default.
|
||||
|
||||
Back up the full data directory, `.questdb_http_password` and `.questdb_pg_password` before upgrades. Public deployments should place the Web endpoint behind HTTPS and restrict direct access to the database ports.
|
||||
|
||||
## Features
|
||||
|
||||
- Column-oriented time-series storage, WAL and partition management
|
||||
- Web Console, REST API and SQL queries
|
||||
- Authenticated InfluxDB Line Protocol over HTTP
|
||||
- PostgreSQL Wire Protocol compatibility
|
||||
- Persistent checkpoints and snapshots
|
||||
|
||||
## 参考资料
|
||||
|
||||
- Docker 部署:<https://questdb.com/docs/deployment/docker/>
|
||||
- 配置选项:<https://questdb.com/docs/configuration/>
|
||||
- 安全指南:<https://questdb.com/docs/operations/secure/>
|
||||
- 源码仓库:<https://github.com/questdb/questdb>
|
||||
- 官方镜像:<https://hub.docker.com/r/questdb/questdb>
|
||||
@@ -0,0 +1,31 @@
|
||||
name: QuestDB
|
||||
tags:
|
||||
- 数据库
|
||||
title: 面向实时分析的高性能开源时序数据库
|
||||
description: 面向实时分析的高性能开源时序数据库
|
||||
additionalProperties:
|
||||
key: questdb
|
||||
name: QuestDB
|
||||
tags:
|
||||
- Database
|
||||
shortDescZh: 面向实时分析的高性能开源时序数据库
|
||||
shortDescEn: A high-performance open-source time-series database for real-time analytics
|
||||
description:
|
||||
en: A high-performance open-source time-series database for real-time analytics
|
||||
zh: 面向实时分析的高性能开源时序数据库
|
||||
zh-Hant: 面向即時分析的高效能開源時序資料庫
|
||||
ja: リアルタイム分析向けの高性能オープンソース時系列データベース
|
||||
ko: 실시간 분석을 위한 고성능 오픈 소스 시계열 데이터베이스
|
||||
ru: Высокопроизводительная база данных временных рядов с открытым исходным кодом для аналитики в реальном времени
|
||||
ms: Pangkalan data siri masa sumber terbuka berprestasi tinggi untuk analitik masa nyata
|
||||
pt-br: Banco de dados de séries temporais de alto desempenho e código aberto para análises em tempo real
|
||||
type: website
|
||||
crossVersionUpdate: true
|
||||
limit: 0
|
||||
recommend: 0
|
||||
website: https://questdb.com/
|
||||
github: https://github.com/questdb/questdb
|
||||
document: https://questdb.com/docs/deployment/docker/
|
||||
architectures:
|
||||
- amd64
|
||||
- arm64
|
||||
@@ -0,0 +1,9 @@
|
||||
PANEL_APP_PORT_HTTP=9000
|
||||
PANEL_APP_PORT_PG=8812
|
||||
QUESTDB_HTTP_USER=admin
|
||||
QUESTDB_HTTP_PASSWORD=replace-with-at-least-32-safe-characters
|
||||
QUESTDB_PG_USER=admin
|
||||
QUESTDB_PG_PASSWORD=replace-with-another-32-safe-characters
|
||||
QUESTDB_TELEMETRY_ENABLED=false
|
||||
APP_DATA_DIR=./data
|
||||
CONTAINER_NAME=questdb
|
||||
@@ -0,0 +1,139 @@
|
||||
additionalProperties:
|
||||
formFields:
|
||||
- default: 9000
|
||||
edit: true
|
||||
envKey: PANEL_APP_PORT_HTTP
|
||||
labelEn: Web and HTTP API Port
|
||||
labelZh: Web 与 HTTP API 端口
|
||||
label:
|
||||
en: Web and HTTP API Port
|
||||
zh: Web 与 HTTP API 端口
|
||||
zh-Hant: Web 與 HTTP API 連接埠
|
||||
ja: Web および HTTP API ポート
|
||||
ko: Web 및 HTTP API 포트
|
||||
ru: Порт Web и HTTP API
|
||||
ms: Port Web dan API HTTP
|
||||
pt-br: Porta Web e da API HTTP
|
||||
required: true
|
||||
rule: paramPort
|
||||
type: number
|
||||
- default: 8812
|
||||
edit: true
|
||||
envKey: PANEL_APP_PORT_PG
|
||||
labelEn: PGWire Port
|
||||
labelZh: PGWire 端口
|
||||
label:
|
||||
en: PGWire Port
|
||||
zh: PGWire 端口
|
||||
zh-Hant: PGWire 連接埠
|
||||
ja: PGWire ポート
|
||||
ko: PGWire 포트
|
||||
ru: Порт PGWire
|
||||
ms: Port PGWire
|
||||
pt-br: Porta PGWire
|
||||
required: true
|
||||
rule: paramPort
|
||||
type: number
|
||||
- default: admin
|
||||
edit: true
|
||||
envKey: QUESTDB_HTTP_USER
|
||||
labelEn: HTTP Username
|
||||
labelZh: HTTP 用户名
|
||||
label:
|
||||
en: HTTP Username
|
||||
zh: HTTP 用户名
|
||||
zh-Hant: HTTP 使用者名稱
|
||||
ja: HTTP ユーザー名
|
||||
ko: HTTP 사용자 이름
|
||||
ru: Имя пользователя HTTP
|
||||
ms: Nama Pengguna HTTP
|
||||
pt-br: Usuário HTTP
|
||||
required: true
|
||||
rule: paramCommon
|
||||
type: text
|
||||
- default: generate
|
||||
edit: true
|
||||
envKey: QUESTDB_HTTP_PASSWORD
|
||||
labelEn: HTTP Password
|
||||
labelZh: HTTP 密码
|
||||
label:
|
||||
en: HTTP Password
|
||||
zh: HTTP 密码
|
||||
zh-Hant: HTTP 密碼
|
||||
ja: HTTP パスワード
|
||||
ko: HTTP 비밀번호
|
||||
ru: Пароль HTTP
|
||||
ms: Kata Laluan HTTP
|
||||
pt-br: Senha HTTP
|
||||
required: true
|
||||
type: password
|
||||
- default: admin
|
||||
edit: true
|
||||
envKey: QUESTDB_PG_USER
|
||||
labelEn: PGWire Username
|
||||
labelZh: PGWire 用户名
|
||||
label:
|
||||
en: PGWire Username
|
||||
zh: PGWire 用户名
|
||||
zh-Hant: PGWire 使用者名稱
|
||||
ja: PGWire ユーザー名
|
||||
ko: PGWire 사용자 이름
|
||||
ru: Имя пользователя PGWire
|
||||
ms: Nama Pengguna PGWire
|
||||
pt-br: Usuário PGWire
|
||||
required: true
|
||||
rule: paramCommon
|
||||
type: text
|
||||
- default: generate
|
||||
edit: true
|
||||
envKey: QUESTDB_PG_PASSWORD
|
||||
labelEn: PGWire Password
|
||||
labelZh: PGWire 密码
|
||||
label:
|
||||
en: PGWire Password
|
||||
zh: PGWire 密码
|
||||
zh-Hant: PGWire 密碼
|
||||
ja: PGWire パスワード
|
||||
ko: PGWire 비밀번호
|
||||
ru: Пароль PGWire
|
||||
ms: Kata Laluan PGWire
|
||||
pt-br: Senha PGWire
|
||||
required: true
|
||||
type: password
|
||||
- default: "false"
|
||||
edit: true
|
||||
envKey: QUESTDB_TELEMETRY_ENABLED
|
||||
labelEn: Enable Anonymous Telemetry
|
||||
labelZh: 启用匿名遥测
|
||||
label:
|
||||
en: Enable Anonymous Telemetry
|
||||
zh: 启用匿名遥测
|
||||
zh-Hant: 啟用匿名遙測
|
||||
ja: 匿名テレメトリを有効化
|
||||
ko: 익명 원격 측정 활성화
|
||||
ru: Включить анонимную телеметрию
|
||||
ms: Dayakan Telemetri Tanpa Nama
|
||||
pt-br: Ativar telemetria anônima
|
||||
required: true
|
||||
type: select
|
||||
values:
|
||||
- label: "false"
|
||||
value: "false"
|
||||
- label: "true"
|
||||
value: "true"
|
||||
- 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
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
services:
|
||||
questdb:
|
||||
image: "questdb/questdb:latest"
|
||||
container_name: ${CONTAINER_NAME}
|
||||
init: true
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- 1panel-network
|
||||
ports:
|
||||
- "${PANEL_APP_PORT_HTTP}:9000"
|
||||
- "${PANEL_APP_PORT_PG}:8812"
|
||||
environment:
|
||||
- QDB_HTTP_USER=${QUESTDB_HTTP_USER}
|
||||
- QDB_HTTP_PASSWORD=${QUESTDB_HTTP_PASSWORD}
|
||||
- QDB_PG_USER=${QUESTDB_PG_USER}
|
||||
- QDB_PG_PASSWORD=${QUESTDB_PG_PASSWORD}
|
||||
- QDB_TELEMETRY_ENABLED=${QUESTDB_TELEMETRY_ENABLED}
|
||||
- QDB_LINE_TCP_ENABLED=false
|
||||
- QDB_HTTP_PESSIMISTIC_HEALTH_CHECK_ENABLED=true
|
||||
- QDB_HTTP_HEALTH_CHECK_AUTHENTICATION_REQUIRED=false
|
||||
volumes:
|
||||
- "${APP_DATA_DIR}:/var/lib/questdb"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl --fail --silent http://127.0.0.1:9003/status >/dev/null"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 60s
|
||||
retries: 5
|
||||
labels:
|
||||
createdBy: "Apps"
|
||||
|
||||
networks:
|
||||
1panel-network:
|
||||
external: true
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/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"
|
||||
}
|
||||
|
||||
env_has_key() {
|
||||
local key="$1"
|
||||
[[ -f "$ENV_FILE" ]] && grep -qE "^${key}=" "$ENV_FILE"
|
||||
}
|
||||
|
||||
read_env_value() {
|
||||
local key="$1"
|
||||
local value=""
|
||||
|
||||
if env_has_key "$key"; then
|
||||
value="$(grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2-)"
|
||||
fi
|
||||
strip_matching_quotes "$value"
|
||||
}
|
||||
|
||||
read_effective_value() {
|
||||
local key="$1"
|
||||
|
||||
if [[ -v "$key" ]]; then
|
||||
strip_matching_quotes "${!key}"
|
||||
else
|
||||
read_env_value "$key"
|
||||
fi
|
||||
}
|
||||
|
||||
username_is_safe() {
|
||||
local value="$1"
|
||||
[[ "$value" =~ ^[A-Za-z_][A-Za-z0-9_.-]{0,62}$ ]]
|
||||
}
|
||||
|
||||
secret_is_safe() {
|
||||
local value="$1"
|
||||
[[ "$value" =~ ^[-A-Za-z0-9._~!@#%^\&*+=:,/?]{32,}$ ]]
|
||||
}
|
||||
|
||||
generate_secret() {
|
||||
local value=""
|
||||
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
value="$(openssl rand -hex 32)"
|
||||
elif [[ -r /dev/urandom ]] && command -v od >/dev/null 2>&1; then
|
||||
value="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
|
||||
fi
|
||||
[[ "$value" =~ ^[0-9a-f]{64}$ ]] || fail "Unable to generate a secure QuestDB password"
|
||||
printf '%s\n' "$value"
|
||||
}
|
||||
|
||||
write_secret_cache() {
|
||||
local cache_file="$1"
|
||||
local value="$2"
|
||||
local temp_file
|
||||
|
||||
umask 077
|
||||
temp_file="$(mktemp "${cache_file}.tmp.XXXXXX")"
|
||||
printf '%s\n' "$value" > "$temp_file"
|
||||
chmod 600 "$temp_file"
|
||||
mv -f -- "$temp_file" "$cache_file"
|
||||
}
|
||||
|
||||
set_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local env_dir
|
||||
local temp_file
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
|
||||
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
|
||||
env_dir="$(dirname "$ENV_FILE")"
|
||||
temp_file="$(mktemp "${env_dir}/.questdb-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 --reference="$ENV_FILE" "$temp_file"
|
||||
mv -f -- "$temp_file" "$ENV_FILE"
|
||||
}
|
||||
|
||||
resolve_secret() {
|
||||
local env_key="$1"
|
||||
local cache_file="$2"
|
||||
local value
|
||||
local cached_value=""
|
||||
|
||||
[[ ! -L "$cache_file" ]] || fail "QuestDB password cache must not be a symbolic link"
|
||||
if [[ -e "$cache_file" && ! -f "$cache_file" ]]; then
|
||||
fail "QuestDB password cache must be a regular file"
|
||||
fi
|
||||
if [[ -s "$cache_file" ]]; then
|
||||
cached_value="$(sed -n '1p' "$cache_file")"
|
||||
secret_is_safe "$cached_value" || fail "Persisted QuestDB password is invalid"
|
||||
fi
|
||||
|
||||
value="$(read_effective_value "$env_key")"
|
||||
if [[ -z "$value" || "$value" == "generate" ]]; then
|
||||
if [[ -n "$cached_value" ]]; then
|
||||
value="$cached_value"
|
||||
else
|
||||
value="$(generate_secret)"
|
||||
fi
|
||||
else
|
||||
secret_is_safe "$value" || fail "${env_key} must contain at least 32 safe characters"
|
||||
fi
|
||||
|
||||
write_secret_cache "$cache_file" "$value"
|
||||
set_env_value "$env_key" "$value"
|
||||
}
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found"
|
||||
[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link"
|
||||
|
||||
app_data_dir_explicit=false
|
||||
if [[ ${APP_DATA_DIR+x} ]]; then
|
||||
APP_DATA_DIR_RAW="$APP_DATA_DIR"
|
||||
app_data_dir_explicit=true
|
||||
elif env_has_key APP_DATA_DIR; then
|
||||
APP_DATA_DIR_RAW="$(read_env_value APP_DATA_DIR)"
|
||||
app_data_dir_explicit=true
|
||||
else
|
||||
APP_DATA_DIR_RAW="./data"
|
||||
fi
|
||||
APP_DATA_DIR_RAW="$(strip_matching_quotes "$APP_DATA_DIR_RAW")"
|
||||
|
||||
if [[ "$app_data_dir_explicit" == "true" && -z "$APP_DATA_DIR_RAW" ]]; then
|
||||
fail "APP_DATA_DIR must not be empty"
|
||||
fi
|
||||
|
||||
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"
|
||||
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"
|
||||
|
||||
http_user="$(read_effective_value QUESTDB_HTTP_USER)"
|
||||
pg_user="$(read_effective_value QUESTDB_PG_USER)"
|
||||
username_is_safe "$http_user" || fail "QUESTDB_HTTP_USER contains unsupported characters"
|
||||
username_is_safe "$pg_user" || fail "QUESTDB_PG_USER contains unsupported characters"
|
||||
|
||||
resolve_secret QUESTDB_HTTP_PASSWORD "${APP_DATA_DIR_ABS}/.questdb_http_password"
|
||||
resolve_secret QUESTDB_PG_PASSWORD "${APP_DATA_DIR_ABS}/.questdb_pg_password"
|
||||
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 --volumes --remove-orphans
|
||||
else
|
||||
docker compose down --volumes --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 bash "${SCRIPT_DIR}/init.sh"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Reference in New Issue
Block a user