274 lines
9.0 KiB
Bash
Executable File
274 lines
9.0 KiB
Bash
Executable File
#!/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
|