main
This commit is contained in:
Executable
+273
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Создание нового gbrain instance
|
||||
# ================================
|
||||
# Создаёт изолированную БД + пользователя Postgres + systemd unit
|
||||
# для gbrain serve --http на отдельном порту.
|
||||
#
|
||||
# Использование:
|
||||
# sudo bash scripts/create-brain.sh <name> "<display name>" <port>
|
||||
#
|
||||
# Примеры:
|
||||
# sudo bash scripts/create-brain.sh zetit "ZETIT MSP" 3001
|
||||
# sudo bash scripts/create-brain.sh telerapharma "TeleraPharma" 3002
|
||||
# sudo bash scripts/create-brain.sh personal "Personal" 3003
|
||||
# sudo bash scripts/create-brain.sh community "Smolenskaya 10" 3004
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# Аргументы
|
||||
# ============================================================
|
||||
if [[ $# -lt 3 ]]; then
|
||||
cat <<EOF
|
||||
Usage: $0 <name> "<display-name>" <port>
|
||||
|
||||
Arguments:
|
||||
name slug-имя брейна (только [a-z0-9_], <=32 символов)
|
||||
display-name человекочитаемое имя в кавычках
|
||||
port TCP-порт для gbrain serve --http (3001-3099)
|
||||
|
||||
Example:
|
||||
$0 zetit "ZETIT MSP" 3001
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BRAIN_NAME="$1"
|
||||
BRAIN_DISPLAY="$2"
|
||||
BRAIN_PORT="$3"
|
||||
|
||||
# Валидация
|
||||
if [[ ! "$BRAIN_NAME" =~ ^[a-z0-9_]{1,32}$ ]]; then
|
||||
echo "ERROR: name должно содержать только [a-z0-9_] и быть не длиннее 32 символов" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$BRAIN_PORT" =~ ^[0-9]+$ ]] || [[ $BRAIN_PORT -lt 3001 ]] || [[ $BRAIN_PORT -gt 3099 ]]; then
|
||||
echo "ERROR: port должен быть числом в диапазоне 3001-3099" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Константы
|
||||
# ============================================================
|
||||
ZBRAIN_USER="zbrain"
|
||||
ZBRAIN_DATA_DIR="/var/lib/zbrain"
|
||||
ZBRAIN_CONFIG_DIR="/etc/zbrain"
|
||||
GBRAIN_DIR="$ZBRAIN_DATA_DIR/gbrain"
|
||||
|
||||
DB_NAME="gbrain_${BRAIN_NAME}"
|
||||
DB_USER="gbrain_${BRAIN_NAME}"
|
||||
SYSTEMD_UNIT="zbrain-gbrain-${BRAIN_NAME}"
|
||||
BRAIN_HOME="$ZBRAIN_DATA_DIR/brains/$BRAIN_NAME"
|
||||
|
||||
log() { echo -e "\033[1;34m[$(date +'%H:%M:%S')] $*\033[0m"; }
|
||||
err() { echo -e "\033[1;31m[ERROR] $*\033[0m" >&2; }
|
||||
|
||||
# ============================================================
|
||||
# Проверки
|
||||
# ============================================================
|
||||
[[ $EUID -eq 0 ]] || { err "Требуется sudo"; exit 1; }
|
||||
[[ -d "$GBRAIN_DIR" ]] || { err "gbrain не установлен. Запусти сначала bootstrap-vm.sh"; exit 1; }
|
||||
[[ -f "$ZBRAIN_CONFIG_DIR/.env" ]] || { err "$ZBRAIN_CONFIG_DIR/.env не существует"; exit 1; }
|
||||
|
||||
# Проверка занятости порта
|
||||
if ss -tln | grep -q ":${BRAIN_PORT} "; then
|
||||
err "Порт $BRAIN_PORT уже занят"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Проверка существования
|
||||
if sudo -u postgres psql -lqt | cut -d \| -f 1 | grep -qw "$DB_NAME"; then
|
||||
err "БД $DB_NAME уже существует"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Создание Postgres пользователя и БД
|
||||
# ============================================================
|
||||
log "Создаю Postgres пользователя $DB_USER и БД $DB_NAME"
|
||||
|
||||
DB_PASSWORD=$(openssl rand -hex 24)
|
||||
|
||||
sudo -u postgres psql <<EOF
|
||||
CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';
|
||||
CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};
|
||||
GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};
|
||||
EOF
|
||||
|
||||
sudo -u postgres psql -d "$DB_NAME" <<EOF
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
GRANT ALL ON SCHEMA public TO ${DB_USER};
|
||||
EOF
|
||||
|
||||
log "✓ БД создана"
|
||||
|
||||
# ============================================================
|
||||
# Brain home directory + конфиг
|
||||
# ============================================================
|
||||
log "Создаю $BRAIN_HOME"
|
||||
mkdir -p "$BRAIN_HOME"
|
||||
chown -R "$ZBRAIN_USER:$ZBRAIN_USER" "$BRAIN_HOME"
|
||||
|
||||
# gbrain config
|
||||
cat > "$BRAIN_HOME/config.json" <<EOF
|
||||
{
|
||||
"name": "${BRAIN_NAME}",
|
||||
"display_name": "${BRAIN_DISPLAY}",
|
||||
"database_url": "postgresql://${DB_USER}:${DB_PASSWORD}@localhost:5432/${DB_NAME}",
|
||||
"port": ${BRAIN_PORT},
|
||||
"data_dir": "${BRAIN_HOME}/data"
|
||||
}
|
||||
EOF
|
||||
chown "$ZBRAIN_USER:$ZBRAIN_USER" "$BRAIN_HOME/config.json"
|
||||
chmod 600 "$BRAIN_HOME/config.json"
|
||||
|
||||
mkdir -p "$BRAIN_HOME/data"
|
||||
chown -R "$ZBRAIN_USER:$ZBRAIN_USER" "$BRAIN_HOME/data"
|
||||
|
||||
log "✓ Конфиг $BRAIN_HOME/config.json"
|
||||
|
||||
# ============================================================
|
||||
# Инициализация gbrain схемы
|
||||
# ============================================================
|
||||
log "Инициализирую gbrain схему"
|
||||
|
||||
# Загружаем .env для FNA proxy + OpenAI/Anthropic ключей
|
||||
set -a
|
||||
source "$ZBRAIN_CONFIG_DIR/.env"
|
||||
set +a
|
||||
|
||||
sudo -u "$ZBRAIN_USER" bash -c "
|
||||
export PATH=\$HOME/.bun/bin:\$PATH
|
||||
export HTTPS_PROXY='${HTTPS_PROXY}'
|
||||
export HTTP_PROXY='${HTTP_PROXY}'
|
||||
export OPENAI_API_KEY='${OPENAI_API_KEY}'
|
||||
export ANTHROPIC_API_KEY='${ANTHROPIC_API_KEY}'
|
||||
cd '$GBRAIN_DIR' && \
|
||||
bun run gbrain init --url 'postgresql://${DB_USER}:${DB_PASSWORD}@localhost:5432/${DB_NAME}' --yes
|
||||
"
|
||||
|
||||
log "✓ Схема инициализирована"
|
||||
|
||||
# ============================================================
|
||||
# Systemd unit
|
||||
# ============================================================
|
||||
log "Создаю systemd unit $SYSTEMD_UNIT"
|
||||
|
||||
cat > "/etc/systemd/system/${SYSTEMD_UNIT}.service" <<EOF
|
||||
[Unit]
|
||||
Description=ZBrain gbrain instance: ${BRAIN_DISPLAY}
|
||||
Documentation=https://git.zetit.ru/zuevav/ZBrain
|
||||
After=postgresql.service network-online.target
|
||||
Wants=network-online.target
|
||||
Requires=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${ZBRAIN_USER}
|
||||
Group=${ZBRAIN_USER}
|
||||
WorkingDirectory=${GBRAIN_DIR}
|
||||
|
||||
# Окружение
|
||||
Environment="PATH=/home/${ZBRAIN_USER}/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
Environment="DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@localhost:5432/${DB_NAME}"
|
||||
Environment="GBRAIN_DATA_DIR=${BRAIN_HOME}/data"
|
||||
EnvironmentFile=${ZBRAIN_CONFIG_DIR}/.env
|
||||
|
||||
# Запуск gbrain в HTTP MCP режиме
|
||||
ExecStart=/home/${ZBRAIN_USER}/.bun/bin/bun run gbrain serve --http --port ${BRAIN_PORT} --bind 127.0.0.1
|
||||
|
||||
# Restart policy
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
StartLimitInterval=300
|
||||
StartLimitBurst=5
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=${BRAIN_HOME} /var/log/zbrain
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictNamespaces=true
|
||||
RestrictRealtime=true
|
||||
LockPersonality=true
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=65536
|
||||
MemoryMax=2G
|
||||
CPUQuota=200%
|
||||
|
||||
# Логи
|
||||
StandardOutput=append:/var/log/zbrain/${BRAIN_NAME}.log
|
||||
StandardError=append:/var/log/zbrain/${BRAIN_NAME}.error.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable "$SYSTEMD_UNIT"
|
||||
systemctl start "$SYSTEMD_UNIT"
|
||||
|
||||
sleep 3
|
||||
|
||||
if systemctl is-active --quiet "$SYSTEMD_UNIT"; then
|
||||
log "✓ Сервис запущен: $SYSTEMD_UNIT"
|
||||
else
|
||||
err "Сервис не стартовал. Логи:"
|
||||
journalctl -u "$SYSTEMD_UNIT" -n 20 --no-pager
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Проверка health
|
||||
# ============================================================
|
||||
log "Проверяю доступность на localhost:$BRAIN_PORT"
|
||||
|
||||
if curl -sf "http://localhost:${BRAIN_PORT}/health" >/dev/null 2>&1; then
|
||||
log "✓ Health check OK"
|
||||
else
|
||||
log "⚠ Health endpoint недоступен (возможно gbrain ещё стартует или endpoint называется иначе)"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Финальный отчёт
|
||||
# ============================================================
|
||||
cat <<EOF
|
||||
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ Брейн '${BRAIN_NAME}' создан
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
|
||||
Display name: ${BRAIN_DISPLAY}
|
||||
Postgres DB: ${DB_NAME}
|
||||
Postgres user: ${DB_USER}
|
||||
Port: ${BRAIN_PORT}
|
||||
Home: ${BRAIN_HOME}
|
||||
Systemd unit: ${SYSTEMD_UNIT}
|
||||
MCP endpoint: http://localhost:${BRAIN_PORT}/mcp
|
||||
|
||||
Полезные команды:
|
||||
systemctl status ${SYSTEMD_UNIT}
|
||||
journalctl -u ${SYSTEMD_UNIT} -f
|
||||
tail -f /var/log/zbrain/${BRAIN_NAME}.log
|
||||
|
||||
Импорт данных:
|
||||
sudo -u ${ZBRAIN_USER} bash -c '
|
||||
export PATH=\$HOME/.bun/bin:\$PATH
|
||||
export DATABASE_URL="postgresql://${DB_USER}:***@localhost:5432/${DB_NAME}"
|
||||
cd ${GBRAIN_DIR} && bun run gbrain import /path/to/markdown/
|
||||
'
|
||||
|
||||
DB password сохранён в:
|
||||
${BRAIN_HOME}/config.json (доступ только root и ${ZBRAIN_USER})
|
||||
|
||||
EOF
|
||||
Reference in New Issue
Block a user