feat: add Hostinger VPS remote deployment tooling
Add complete deployment infrastructure for Hostinger VPS servers: - setup-server.sh: initial VPS provisioning (Docker/binary, firewall, fail2ban) - deploy.sh: automated remote deploy via SSH (Docker or binary methods) - rollback.sh: quick rollback to previous version - status.sh: remote health and status monitoring - docker-compose.production.yml: production-ready compose with resource limits - picoclaw.service: systemd unit with security hardening - DEPLOY.md: step-by-step deployment guide (Portuguese) - Makefile targets: deploy-hostinger, deploy-hostinger-setup, status, rollback https://claude.ai/code/session_019vXaqxGmkdCjM8m3jp6rYj
This commit is contained in:
parent
13e4028d42
commit
42a2918665
8 changed files with 1122 additions and 5 deletions
51
Makefile
51
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
.PHONY: all build install uninstall clean help test
|
||||
.PHONY: all build install uninstall clean help test deploy-hostinger deploy-hostinger-setup deploy-hostinger-status deploy-hostinger-rollback
|
||||
|
||||
# Build variables
|
||||
BINARY_NAME=picoclaw
|
||||
|
|
@ -148,6 +148,44 @@ check: deps fmt vet test
|
|||
run: build
|
||||
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)
|
||||
|
||||
# ── Hostinger Deployment ─────────────────────────────
|
||||
HOSTINGER_HOST?=
|
||||
HOSTINGER_USER?=root
|
||||
HOSTINGER_SSH_KEY?=$(HOME)/.ssh/id_rsa
|
||||
HOSTINGER_SSH_PORT?=22
|
||||
HOSTINGER_DEPLOY_METHOD?=docker
|
||||
|
||||
## deploy-hostinger-setup: Run initial server setup on Hostinger VPS
|
||||
deploy-hostinger-setup:
|
||||
@bash deploy/hostinger/setup-server.sh
|
||||
|
||||
## deploy-hostinger: Deploy PicoClaw to Hostinger VPS
|
||||
deploy-hostinger:
|
||||
@bash deploy/hostinger/deploy.sh \
|
||||
-h "$(HOSTINGER_HOST)" \
|
||||
-u "$(HOSTINGER_USER)" \
|
||||
-k "$(HOSTINGER_SSH_KEY)" \
|
||||
-p "$(HOSTINGER_SSH_PORT)" \
|
||||
-m "$(HOSTINGER_DEPLOY_METHOD)"
|
||||
|
||||
## deploy-hostinger-status: Check PicoClaw status on Hostinger VPS
|
||||
deploy-hostinger-status:
|
||||
@bash deploy/hostinger/status.sh \
|
||||
-h "$(HOSTINGER_HOST)" \
|
||||
-u "$(HOSTINGER_USER)" \
|
||||
-k "$(HOSTINGER_SSH_KEY)" \
|
||||
-p "$(HOSTINGER_SSH_PORT)" \
|
||||
-m "$(HOSTINGER_DEPLOY_METHOD)"
|
||||
|
||||
## deploy-hostinger-rollback: Rollback PicoClaw on Hostinger VPS
|
||||
deploy-hostinger-rollback:
|
||||
@bash deploy/hostinger/rollback.sh \
|
||||
-h "$(HOSTINGER_HOST)" \
|
||||
-u "$(HOSTINGER_USER)" \
|
||||
-k "$(HOSTINGER_SSH_KEY)" \
|
||||
-p "$(HOSTINGER_SSH_PORT)" \
|
||||
-m "$(HOSTINGER_DEPLOY_METHOD)"
|
||||
|
||||
## help: Show this help message
|
||||
help:
|
||||
@echo "picoclaw Makefile"
|
||||
|
|
@ -162,12 +200,21 @@ help:
|
|||
@echo " make build # Build for current platform"
|
||||
@echo " make install # Install to ~/.local/bin"
|
||||
@echo " make uninstall # Remove from /usr/local/bin"
|
||||
@echo " make install-skills # Install skills to workspace"
|
||||
@echo ""
|
||||
@echo "Hostinger Deployment:"
|
||||
@echo " make deploy-hostinger-setup HOSTINGER_HOST=1.2.3.4 # Initial server setup"
|
||||
@echo " make deploy-hostinger HOSTINGER_HOST=1.2.3.4 # Deploy to VPS"
|
||||
@echo " make deploy-hostinger-status HOSTINGER_HOST=1.2.3.4 # Check status"
|
||||
@echo " make deploy-hostinger-rollback HOSTINGER_HOST=1.2.3.4 # Rollback"
|
||||
@echo ""
|
||||
@echo "Environment Variables:"
|
||||
@echo " INSTALL_PREFIX # Installation prefix (default: ~/.local)"
|
||||
@echo " WORKSPACE_DIR # Workspace directory (default: ~/.picoclaw/workspace)"
|
||||
@echo " VERSION # Version string (default: git describe)"
|
||||
@echo " HOSTINGER_HOST # Hostinger VPS IP address"
|
||||
@echo " HOSTINGER_USER # SSH user (default: root)"
|
||||
@echo " HOSTINGER_SSH_KEY # SSH key path (default: ~/.ssh/id_rsa)"
|
||||
@echo " HOSTINGER_DEPLOY_METHOD # Deploy method: docker or binary (default: docker)"
|
||||
@echo ""
|
||||
@echo "Current Configuration:"
|
||||
@echo " Platform: $(PLATFORM)/$(ARCH)"
|
||||
|
|
|
|||
205
deploy/hostinger/DEPLOY.md
Normal file
205
deploy/hostinger/DEPLOY.md
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# PicoClaw - Deploy na Hostinger VPS
|
||||
|
||||
Guia completo para deploy do PicoClaw em um servidor VPS da Hostinger.
|
||||
|
||||
## Pre-requisitos
|
||||
|
||||
- VPS Hostinger com Ubuntu 22.04+ ou Debian 12+ (KVM)
|
||||
- Acesso SSH configurado (chave SSH recomendado)
|
||||
- API keys dos provedores que deseja usar (Anthropic, OpenAI, etc.)
|
||||
|
||||
> **Nota:** Hospedagem compartilhada da Hostinger **nao** suporta PicoClaw.
|
||||
> Voce precisa de um plano VPS ou Cloud com acesso root.
|
||||
|
||||
## Metodo 1: Deploy via Docker (Recomendado)
|
||||
|
||||
### Passo 1: Configurar acesso SSH
|
||||
|
||||
```bash
|
||||
# Gerar chave SSH (se ainda nao tem)
|
||||
ssh-keygen -t ed25519 -C "picoclaw-hostinger"
|
||||
|
||||
# Copiar chave para o servidor
|
||||
ssh-copy-id -i ~/.ssh/id_ed25519 root@SEU_IP_VPS
|
||||
```
|
||||
|
||||
### Passo 2: Setup inicial do servidor
|
||||
|
||||
Execute uma unica vez para preparar o servidor:
|
||||
|
||||
```bash
|
||||
# Opcao A: Via make
|
||||
make deploy-hostinger-setup HOSTINGER_HOST=SEU_IP_VPS
|
||||
|
||||
# Opcao B: Direto via SSH
|
||||
ssh root@SEU_IP_VPS 'bash -s' < deploy/hostinger/setup-server.sh
|
||||
```
|
||||
|
||||
Isso instala: Docker, firewall (ufw), fail2ban, e cria a estrutura de diretorios.
|
||||
|
||||
### Passo 3: Configurar API keys no servidor
|
||||
|
||||
```bash
|
||||
ssh root@SEU_IP_VPS
|
||||
|
||||
# Editar chaves de API
|
||||
nano /opt/picoclaw/config/.env
|
||||
|
||||
# Editar configuracao do PicoClaw
|
||||
nano /opt/picoclaw/config/config.json
|
||||
```
|
||||
|
||||
Exemplo minimo do `.env`:
|
||||
```bash
|
||||
ANTHROPIC_API_KEY=sk-ant-sua-chave-aqui
|
||||
TELEGRAM_BOT_TOKEN=123456:ABC-seu-token
|
||||
TZ=America/Sao_Paulo
|
||||
```
|
||||
|
||||
### Passo 4: Deploy
|
||||
|
||||
```bash
|
||||
# Deploy com Docker
|
||||
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS
|
||||
|
||||
# Ou com variaveis de ambiente
|
||||
export HOSTINGER_HOST=SEU_IP_VPS
|
||||
export HOSTINGER_USER=root
|
||||
make deploy-hostinger
|
||||
```
|
||||
|
||||
### Passo 5: Verificar
|
||||
|
||||
```bash
|
||||
# Status completo
|
||||
make deploy-hostinger-status HOSTINGER_HOST=SEU_IP_VPS
|
||||
|
||||
# Health check rapido
|
||||
curl http://SEU_IP_VPS:18790/health
|
||||
```
|
||||
|
||||
## Metodo 2: Deploy via Binario (Menor consumo)
|
||||
|
||||
Para VPS com pouca memoria (512MB-1GB), o deploy via binario usa menos recursos.
|
||||
|
||||
### Setup do servidor
|
||||
|
||||
```bash
|
||||
ssh root@SEU_IP_VPS 'bash -s -- binary' < deploy/hostinger/setup-server.sh
|
||||
```
|
||||
|
||||
### Deploy
|
||||
|
||||
```bash
|
||||
# Build local e upload (mais rapido se sua maquina e potente)
|
||||
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS HOSTINGER_DEPLOY_METHOD=binary
|
||||
|
||||
# Ou build no servidor (nao precisa de Go local)
|
||||
# O script sincroniza o codigo e compila no VPS
|
||||
```
|
||||
|
||||
### Gerenciar servico
|
||||
|
||||
```bash
|
||||
ssh root@SEU_IP_VPS
|
||||
|
||||
# Status
|
||||
systemctl status picoclaw
|
||||
|
||||
# Logs em tempo real
|
||||
tail -f /opt/picoclaw/logs/picoclaw.log
|
||||
|
||||
# Reiniciar
|
||||
systemctl restart picoclaw
|
||||
|
||||
# Parar
|
||||
systemctl stop picoclaw
|
||||
```
|
||||
|
||||
## Comandos Make
|
||||
|
||||
| Comando | Descricao |
|
||||
|---------|-----------|
|
||||
| `make deploy-hostinger-setup` | Setup inicial do servidor |
|
||||
| `make deploy-hostinger` | Deploy/atualizar PicoClaw |
|
||||
| `make deploy-hostinger-status` | Verificar status |
|
||||
| `make deploy-hostinger-rollback` | Reverter para versao anterior |
|
||||
|
||||
### Variaveis de ambiente
|
||||
|
||||
| Variavel | Padrao | Descricao |
|
||||
|----------|--------|-----------|
|
||||
| `HOSTINGER_HOST` | (obrigatorio) | IP ou hostname do VPS |
|
||||
| `HOSTINGER_USER` | `root` | Usuario SSH |
|
||||
| `HOSTINGER_SSH_KEY` | `~/.ssh/id_rsa` | Caminho da chave SSH |
|
||||
| `HOSTINGER_SSH_PORT` | `22` | Porta SSH |
|
||||
| `HOSTINGER_DEPLOY_METHOD` | `docker` | `docker` ou `binary` |
|
||||
|
||||
## Estrutura no Servidor
|
||||
|
||||
```
|
||||
/opt/picoclaw/
|
||||
├── bin/ # Binario do PicoClaw (metodo binary)
|
||||
├── config/
|
||||
│ ├── .env # Chaves de API (chmod 600)
|
||||
│ └── config.json # Configuracao do PicoClaw
|
||||
├── workspace/ # Workspace persistente
|
||||
│ ├── memory/ # Memoria de sessoes
|
||||
│ └── skills/ # Skills customizadas
|
||||
├── logs/ # Logs (metodo binary)
|
||||
├── backups/ # Backups de versoes anteriores
|
||||
└── src/ # Codigo fonte (para build remoto)
|
||||
```
|
||||
|
||||
## Seguranca
|
||||
|
||||
- Firewall (ufw) configurado: apenas SSH (22) e Gateway (18790)
|
||||
- fail2ban ativo para protecao contra brute-force SSH
|
||||
- Servico roda com usuario dedicado `picoclaw` (sem root)
|
||||
- Hardening via systemd (ProtectSystem, NoNewPrivileges, etc.)
|
||||
- Arquivos `.env` e `config.json` com permissao 600
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container nao inicia (Docker)
|
||||
```bash
|
||||
ssh root@SEU_IP_VPS
|
||||
docker compose -f /opt/picoclaw/docker-compose.yml logs picoclaw-gateway
|
||||
```
|
||||
|
||||
### Servico nao inicia (Binary)
|
||||
```bash
|
||||
ssh root@SEU_IP_VPS
|
||||
journalctl -u picoclaw -n 50 --no-pager
|
||||
cat /opt/picoclaw/logs/picoclaw-error.log
|
||||
```
|
||||
|
||||
### Health check falha
|
||||
```bash
|
||||
# Verificar se a porta esta aberta
|
||||
ss -tlnp | grep 18790
|
||||
|
||||
# Verificar firewall
|
||||
ufw status
|
||||
|
||||
# Testar localmente no servidor
|
||||
curl -v http://localhost:18790/health
|
||||
```
|
||||
|
||||
### Memoria insuficiente
|
||||
Se o VPS tem pouca RAM, use o metodo binary em vez de Docker:
|
||||
```bash
|
||||
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS HOSTINGER_DEPLOY_METHOD=binary
|
||||
```
|
||||
|
||||
## Atualizacoes
|
||||
|
||||
Para atualizar o PicoClaw, basta rodar novamente:
|
||||
```bash
|
||||
make deploy-hostinger HOSTINGER_HOST=SEU_IP_VPS
|
||||
```
|
||||
|
||||
Para reverter:
|
||||
```bash
|
||||
make deploy-hostinger-rollback HOSTINGER_HOST=SEU_IP_VPS
|
||||
```
|
||||
255
deploy/hostinger/deploy.sh
Executable file
255
deploy/hostinger/deploy.sh
Executable file
|
|
@ -0,0 +1,255 @@
|
|||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# PicoClaw - Remote Deploy to Hostinger VPS
|
||||
# ============================================================
|
||||
# Deploys PicoClaw to a remote Hostinger VPS via SSH.
|
||||
# Supports both Docker and binary deployment methods.
|
||||
#
|
||||
# Usage:
|
||||
# ./deploy/hostinger/deploy.sh [OPTIONS]
|
||||
#
|
||||
# Options:
|
||||
# -h, --host HOST VPS IP or hostname (required, or set HOSTINGER_HOST)
|
||||
# -u, --user USER SSH user (default: root)
|
||||
# -k, --key KEY SSH key path (default: ~/.ssh/id_rsa)
|
||||
# -m, --method METHOD Deploy method: "docker" or "binary" (default: docker)
|
||||
# -p, --port PORT SSH port (default: 22)
|
||||
# --build-local Build binary locally and upload (for binary method)
|
||||
# --help Show this help
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[DEPLOY]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
|
||||
# ── Default Configuration ────────────────────────────
|
||||
HOST="${HOSTINGER_HOST:-}"
|
||||
USER="${HOSTINGER_USER:-root}"
|
||||
SSH_KEY="${HOSTINGER_SSH_KEY:-${HOME}/.ssh/id_rsa}"
|
||||
SSH_PORT="${HOSTINGER_SSH_PORT:-22}"
|
||||
METHOD="${HOSTINGER_DEPLOY_METHOD:-docker}"
|
||||
BUILD_LOCAL=false
|
||||
REMOTE_DIR="/opt/picoclaw"
|
||||
|
||||
# ── Parse Arguments ──────────────────────────────────
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--host) HOST="$2"; shift 2 ;;
|
||||
-u|--user) USER="$2"; shift 2 ;;
|
||||
-k|--key) SSH_KEY="$2"; shift 2 ;;
|
||||
-m|--method) METHOD="$2"; shift 2 ;;
|
||||
-p|--port) SSH_PORT="$2"; shift 2 ;;
|
||||
--build-local) BUILD_LOCAL=true; shift ;;
|
||||
--help)
|
||||
head -20 "$0" | tail -15
|
||||
exit 0
|
||||
;;
|
||||
*) error "Unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Validate ─────────────────────────────────────────
|
||||
[ -z "${HOST}" ] && error "Host required. Use -h/--host or set HOSTINGER_HOST env var."
|
||||
[ ! -f "${SSH_KEY}" ] && error "SSH key not found: ${SSH_KEY}"
|
||||
|
||||
SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p ${SSH_PORT} -i ${SSH_KEY}"
|
||||
SSH_CMD="ssh ${SSH_OPTS} ${USER}@${HOST}"
|
||||
SCP_CMD="scp ${SSH_OPTS}"
|
||||
|
||||
# ── Verify Connection ────────────────────────────────
|
||||
log "Verifying SSH connection to ${USER}@${HOST}:${SSH_PORT}..."
|
||||
${SSH_CMD} "echo 'Connection OK'" || error "Cannot connect to ${HOST}. Check SSH settings."
|
||||
|
||||
# ── Get project root ─────────────────────────────────
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
log "Deploying PicoClaw to ${HOST} via ${METHOD} method"
|
||||
echo ""
|
||||
|
||||
# ── Deploy via Docker ────────────────────────────────
|
||||
deploy_docker() {
|
||||
log "=== Docker Deployment ==="
|
||||
|
||||
# 1. Sync project files to server
|
||||
log "Syncing project files..."
|
||||
rsync -avz --delete \
|
||||
-e "ssh ${SSH_OPTS}" \
|
||||
--exclude '.git' \
|
||||
--exclude 'build/' \
|
||||
--exclude '.env' \
|
||||
--exclude 'config/config.json' \
|
||||
--exclude '*.test' \
|
||||
"${PROJECT_ROOT}/" "${USER}@${HOST}:${REMOTE_DIR}/src/"
|
||||
|
||||
# 2. Copy docker-compose production file
|
||||
log "Copying Docker Compose production config..."
|
||||
${SCP_CMD} "${PROJECT_ROOT}/deploy/hostinger/docker-compose.production.yml" \
|
||||
"${USER}@${HOST}:${REMOTE_DIR}/docker-compose.yml"
|
||||
|
||||
# 3. Build and restart on server
|
||||
log "Building and starting containers on server..."
|
||||
${SSH_CMD} <<'REMOTEOF'
|
||||
set -e
|
||||
cd /opt/picoclaw
|
||||
|
||||
# Build the image
|
||||
echo "[REMOTE] Building Docker image..."
|
||||
docker compose build --no-cache picoclaw-gateway
|
||||
|
||||
# Stop existing container gracefully
|
||||
echo "[REMOTE] Stopping existing container..."
|
||||
docker compose down --timeout 30 2>/dev/null || true
|
||||
|
||||
# Start new container
|
||||
echo "[REMOTE] Starting PicoClaw gateway..."
|
||||
docker compose up -d picoclaw-gateway
|
||||
|
||||
# Wait and verify
|
||||
sleep 5
|
||||
if docker compose ps picoclaw-gateway | grep -q "Up"; then
|
||||
echo "[REMOTE] PicoClaw is running!"
|
||||
docker compose ps
|
||||
else
|
||||
echo "[REMOTE] ERROR: Container failed to start"
|
||||
docker compose logs --tail=50 picoclaw-gateway
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Health check
|
||||
echo "[REMOTE] Checking health..."
|
||||
sleep 3
|
||||
if curl -sf http://localhost:18790/health > /dev/null 2>&1; then
|
||||
echo "[REMOTE] Health check PASSED"
|
||||
else
|
||||
echo "[REMOTE] Health check failed (may still be starting up)"
|
||||
fi
|
||||
|
||||
# Cleanup old images
|
||||
echo "[REMOTE] Cleaning up old Docker images..."
|
||||
docker image prune -f
|
||||
REMOTEOF
|
||||
|
||||
log "Docker deployment complete!"
|
||||
}
|
||||
|
||||
# ── Deploy via Binary ────────────────────────────────
|
||||
deploy_binary() {
|
||||
log "=== Binary Deployment ==="
|
||||
|
||||
if [ "${BUILD_LOCAL}" = true ]; then
|
||||
# Build locally for linux/amd64
|
||||
log "Building binary locally for linux/amd64..."
|
||||
cd "${PROJECT_ROOT}"
|
||||
make generate
|
||||
GOOS=linux GOARCH=amd64 go build -v \
|
||||
-ldflags "-X main.version=$(git describe --tags --always --dirty 2>/dev/null || echo dev) \
|
||||
-X main.gitCommit=$(git rev-parse --short=8 HEAD 2>/dev/null || echo dev) \
|
||||
-X main.buildTime=$(date +%FT%T%z)" \
|
||||
-o build/picoclaw-linux-amd64 ./cmd/picoclaw
|
||||
BINARY_PATH="build/picoclaw-linux-amd64"
|
||||
log "Binary built: ${BINARY_PATH}"
|
||||
else
|
||||
# Build on server
|
||||
log "Syncing source code to server..."
|
||||
rsync -avz --delete \
|
||||
-e "ssh ${SSH_OPTS}" \
|
||||
--exclude '.git' \
|
||||
--exclude 'build/' \
|
||||
--exclude '.env' \
|
||||
--exclude 'config/config.json' \
|
||||
"${PROJECT_ROOT}/" "${USER}@${HOST}:${REMOTE_DIR}/src/"
|
||||
|
||||
log "Building on server..."
|
||||
${SSH_CMD} <<'REMOTEOF'
|
||||
set -e
|
||||
cd /opt/picoclaw/src
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
make build
|
||||
cp build/picoclaw /opt/picoclaw/bin/picoclaw
|
||||
chmod +x /opt/picoclaw/bin/picoclaw
|
||||
echo "[REMOTE] Build complete: $(/opt/picoclaw/bin/picoclaw version 2>/dev/null || echo 'built')"
|
||||
REMOTEOF
|
||||
fi
|
||||
|
||||
if [ "${BUILD_LOCAL}" = true ]; then
|
||||
# Upload binary
|
||||
log "Uploading binary to server..."
|
||||
${SCP_CMD} "${BINARY_PATH}" "${USER}@${HOST}:${REMOTE_DIR}/bin/picoclaw"
|
||||
${SSH_CMD} "chmod +x ${REMOTE_DIR}/bin/picoclaw"
|
||||
|
||||
# Initialize workspace if needed
|
||||
${SSH_CMD} <<REMOTEOF
|
||||
set -e
|
||||
if [ ! -d "${REMOTE_DIR}/workspace/memory" ]; then
|
||||
echo "[REMOTE] Initializing workspace..."
|
||||
sudo -u picoclaw ${REMOTE_DIR}/bin/picoclaw onboard
|
||||
fi
|
||||
REMOTEOF
|
||||
fi
|
||||
|
||||
# Restart service
|
||||
log "Restarting PicoClaw service..."
|
||||
${SSH_CMD} <<'REMOTEOF'
|
||||
set -e
|
||||
echo "[REMOTE] Restarting picoclaw service..."
|
||||
systemctl restart picoclaw
|
||||
|
||||
sleep 3
|
||||
if systemctl is-active --quiet picoclaw; then
|
||||
echo "[REMOTE] PicoClaw is running!"
|
||||
systemctl status picoclaw --no-pager
|
||||
else
|
||||
echo "[REMOTE] ERROR: Service failed to start"
|
||||
journalctl -u picoclaw --no-pager -n 30
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Health check
|
||||
sleep 3
|
||||
if curl -sf http://localhost:18790/health > /dev/null 2>&1; then
|
||||
echo "[REMOTE] Health check PASSED"
|
||||
else
|
||||
echo "[REMOTE] Health check failed (may still be starting up)"
|
||||
tail -20 /opt/picoclaw/logs/picoclaw.log 2>/dev/null || true
|
||||
fi
|
||||
REMOTEOF
|
||||
|
||||
log "Binary deployment complete!"
|
||||
}
|
||||
|
||||
# ── Execute Deploy ───────────────────────────────────
|
||||
DEPLOY_START=$(date +%s)
|
||||
|
||||
case "${METHOD}" in
|
||||
docker) deploy_docker ;;
|
||||
binary) deploy_binary ;;
|
||||
*) error "Unknown method: ${METHOD}. Use 'docker' or 'binary'." ;;
|
||||
esac
|
||||
|
||||
DEPLOY_END=$(date +%s)
|
||||
DEPLOY_DURATION=$((DEPLOY_END - DEPLOY_START))
|
||||
|
||||
echo ""
|
||||
log "=========================================="
|
||||
log " Deployment successful!"
|
||||
log " Duration: ${DEPLOY_DURATION}s"
|
||||
log " Host: ${HOST}"
|
||||
log " Method: ${METHOD}"
|
||||
log "=========================================="
|
||||
echo ""
|
||||
info "Useful commands:"
|
||||
echo " Check status: ${SSH_CMD} 'systemctl status picoclaw'"
|
||||
echo " View logs: ${SSH_CMD} 'tail -f /opt/picoclaw/logs/picoclaw.log'"
|
||||
echo " Health check: curl http://${HOST}:18790/health"
|
||||
echo ""
|
||||
48
deploy/hostinger/docker-compose.production.yml
Normal file
48
deploy/hostinger/docker-compose.production.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# ============================================================
|
||||
# PicoClaw - Production Docker Compose for Hostinger VPS
|
||||
# ============================================================
|
||||
# Usage:
|
||||
# docker compose up -d picoclaw-gateway
|
||||
# docker compose logs -f picoclaw-gateway
|
||||
# docker compose down
|
||||
# ============================================================
|
||||
|
||||
services:
|
||||
picoclaw-gateway:
|
||||
build:
|
||||
context: ./src
|
||||
dockerfile: Dockerfile
|
||||
container_name: picoclaw-gateway
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "18790:18790"
|
||||
volumes:
|
||||
# Configuration (read-only)
|
||||
- /opt/picoclaw/config/config.json:/root/.picoclaw/config.json:ro
|
||||
# Persistent workspace (sessions, memory, logs)
|
||||
- picoclaw-workspace:/root/.picoclaw/workspace
|
||||
env_file:
|
||||
- /opt/picoclaw/config/.env
|
||||
environment:
|
||||
- TZ=${TZ:-America/Sao_Paulo}
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:18790/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 32M
|
||||
|
||||
volumes:
|
||||
picoclaw-workspace:
|
||||
driver: local
|
||||
51
deploy/hostinger/picoclaw.service
Normal file
51
deploy/hostinger/picoclaw.service
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# ============================================================
|
||||
# PicoClaw - systemd Service File
|
||||
# ============================================================
|
||||
# Install: sudo cp picoclaw.service /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl enable picoclaw
|
||||
# sudo systemctl start picoclaw
|
||||
# ============================================================
|
||||
|
||||
[Unit]
|
||||
Description=PicoClaw AI Assistant Gateway
|
||||
Documentation=https://github.com/agenciaspace/picoclaw
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=picoclaw
|
||||
Group=picoclaw
|
||||
WorkingDirectory=/opt/picoclaw
|
||||
ExecStart=/opt/picoclaw/bin/picoclaw gateway
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
TimeoutStopSec=30
|
||||
|
||||
# Logging
|
||||
StandardOutput=append:/opt/picoclaw/logs/picoclaw.log
|
||||
StandardError=append:/opt/picoclaw/logs/picoclaw-error.log
|
||||
|
||||
# Environment
|
||||
EnvironmentFile=-/opt/picoclaw/config/.env
|
||||
Environment=HOME=/opt/picoclaw
|
||||
Environment=PICOCLAW_CONFIG=/opt/picoclaw/config/config.json
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
ReadWritePaths=/opt/picoclaw
|
||||
PrivateTmp=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RestrictNamespaces=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
109
deploy/hostinger/rollback.sh
Executable file
109
deploy/hostinger/rollback.sh
Executable file
|
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# PicoClaw - Rollback to Previous Version
|
||||
# ============================================================
|
||||
# Usage:
|
||||
# ./deploy/hostinger/rollback.sh -h YOUR_VPS_IP
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[ROLLBACK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
|
||||
# Configuration
|
||||
HOST="${HOSTINGER_HOST:-}"
|
||||
USER="${HOSTINGER_USER:-root}"
|
||||
SSH_KEY="${HOSTINGER_SSH_KEY:-${HOME}/.ssh/id_rsa}"
|
||||
SSH_PORT="${HOSTINGER_SSH_PORT:-22}"
|
||||
METHOD="${HOSTINGER_DEPLOY_METHOD:-docker}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--host) HOST="$2"; shift 2 ;;
|
||||
-u|--user) USER="$2"; shift 2 ;;
|
||||
-k|--key) SSH_KEY="$2"; shift 2 ;;
|
||||
-m|--method) METHOD="$2"; shift 2 ;;
|
||||
-p|--port) SSH_PORT="$2"; shift 2 ;;
|
||||
*) error "Unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -z "${HOST}" ] && error "Host required. Use -h/--host or set HOSTINGER_HOST env var."
|
||||
|
||||
SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p ${SSH_PORT} -i ${SSH_KEY}"
|
||||
SSH_CMD="ssh ${SSH_OPTS} ${USER}@${HOST}"
|
||||
|
||||
log "Rolling back PicoClaw on ${HOST}..."
|
||||
|
||||
if [ "${METHOD}" = "docker" ]; then
|
||||
${SSH_CMD} <<'REMOTEOF'
|
||||
set -e
|
||||
cd /opt/picoclaw
|
||||
|
||||
# List available images
|
||||
echo "Available images:"
|
||||
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.CreatedAt}}" | grep picoclaw || true
|
||||
|
||||
# Docker rollback: restart with previous image
|
||||
echo "[REMOTE] Stopping current container..."
|
||||
docker compose down --timeout 30
|
||||
|
||||
echo "[REMOTE] Starting previous version..."
|
||||
# The previous image should still be cached
|
||||
docker compose up -d picoclaw-gateway
|
||||
|
||||
sleep 5
|
||||
if docker compose ps picoclaw-gateway | grep -q "Up"; then
|
||||
echo "[REMOTE] Rollback successful!"
|
||||
docker compose ps
|
||||
else
|
||||
echo "[REMOTE] Rollback failed!"
|
||||
docker compose logs --tail=20 picoclaw-gateway
|
||||
exit 1
|
||||
fi
|
||||
REMOTEOF
|
||||
else
|
||||
${SSH_CMD} <<'REMOTEOF'
|
||||
set -e
|
||||
BACKUP_DIR="/opt/picoclaw/backups"
|
||||
|
||||
# Find latest backup
|
||||
LATEST_BACKUP=$(ls -t ${BACKUP_DIR}/picoclaw-*.bak 2>/dev/null | head -1)
|
||||
|
||||
if [ -z "${LATEST_BACKUP}" ]; then
|
||||
echo "[REMOTE] No backup found to rollback to!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[REMOTE] Rolling back to: ${LATEST_BACKUP}"
|
||||
|
||||
# Stop service
|
||||
systemctl stop picoclaw
|
||||
|
||||
# Restore binary
|
||||
cp "${LATEST_BACKUP}" /opt/picoclaw/bin/picoclaw
|
||||
chmod +x /opt/picoclaw/bin/picoclaw
|
||||
|
||||
# Start service
|
||||
systemctl start picoclaw
|
||||
|
||||
sleep 3
|
||||
if systemctl is-active --quiet picoclaw; then
|
||||
echo "[REMOTE] Rollback successful!"
|
||||
systemctl status picoclaw --no-pager
|
||||
else
|
||||
echo "[REMOTE] Rollback failed!"
|
||||
journalctl -u picoclaw --no-pager -n 20
|
||||
exit 1
|
||||
fi
|
||||
REMOTEOF
|
||||
fi
|
||||
|
||||
log "Rollback complete!"
|
||||
304
deploy/hostinger/setup-server.sh
Executable file
304
deploy/hostinger/setup-server.sh
Executable file
|
|
@ -0,0 +1,304 @@
|
|||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# PicoClaw - Hostinger VPS Initial Setup
|
||||
# ============================================================
|
||||
# Run this script ONCE on a fresh Hostinger VPS to prepare it
|
||||
# for PicoClaw deployment.
|
||||
#
|
||||
# Usage:
|
||||
# ssh root@YOUR_VPS_IP 'bash -s' < deploy/hostinger/setup-server.sh
|
||||
#
|
||||
# Or copy and run directly on the server:
|
||||
# scp deploy/hostinger/setup-server.sh root@YOUR_VPS_IP:/tmp/
|
||||
# ssh root@YOUR_VPS_IP 'bash /tmp/setup-server.sh'
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[SETUP]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
|
||||
# ── Configuration ──────────────────────────────────────
|
||||
PICOCLAW_USER="picoclaw"
|
||||
PICOCLAW_HOME="/opt/picoclaw"
|
||||
DEPLOY_METHOD="${1:-docker}" # "docker" or "binary"
|
||||
|
||||
log "PicoClaw Hostinger VPS Setup"
|
||||
log "Deploy method: ${DEPLOY_METHOD}"
|
||||
echo ""
|
||||
|
||||
# ── 1. System Update ──────────────────────────────────
|
||||
log "Updating system packages..."
|
||||
if command -v apt-get &>/dev/null; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get upgrade -y -qq
|
||||
apt-get install -y -qq curl wget git ufw fail2ban unzip jq
|
||||
elif command -v yum &>/dev/null; then
|
||||
yum update -y -q
|
||||
yum install -y -q curl wget git firewalld fail2ban unzip jq
|
||||
elif command -v dnf &>/dev/null; then
|
||||
dnf update -y -q
|
||||
dnf install -y -q curl wget git firewalld fail2ban unzip jq
|
||||
else
|
||||
error "Unsupported package manager. This script supports apt, yum, and dnf."
|
||||
fi
|
||||
|
||||
# ── 2. Create dedicated user ─────────────────────────
|
||||
if ! id "${PICOCLAW_USER}" &>/dev/null; then
|
||||
log "Creating dedicated user: ${PICOCLAW_USER}"
|
||||
useradd --system --create-home --home-dir "${PICOCLAW_HOME}" \
|
||||
--shell /bin/bash "${PICOCLAW_USER}"
|
||||
else
|
||||
log "User ${PICOCLAW_USER} already exists"
|
||||
fi
|
||||
|
||||
# ── 3. Create directory structure ─────────────────────
|
||||
log "Creating directory structure..."
|
||||
mkdir -p "${PICOCLAW_HOME}"/{bin,config,workspace,logs,backups}
|
||||
chown -R "${PICOCLAW_USER}:${PICOCLAW_USER}" "${PICOCLAW_HOME}"
|
||||
|
||||
# ── 4. Install Docker (if docker method) ─────────────
|
||||
if [ "${DEPLOY_METHOD}" = "docker" ]; then
|
||||
if ! command -v docker &>/dev/null; then
|
||||
log "Installing Docker..."
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
usermod -aG docker "${PICOCLAW_USER}"
|
||||
log "Docker installed successfully"
|
||||
else
|
||||
log "Docker already installed: $(docker --version)"
|
||||
fi
|
||||
|
||||
# Install Docker Compose plugin if not present
|
||||
if ! docker compose version &>/dev/null; then
|
||||
log "Installing Docker Compose plugin..."
|
||||
apt-get install -y -qq docker-compose-plugin 2>/dev/null || \
|
||||
yum install -y -q docker-compose-plugin 2>/dev/null || \
|
||||
dnf install -y -q docker-compose-plugin 2>/dev/null || {
|
||||
COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)
|
||||
curl -fsSL "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
|
||||
-o /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
}
|
||||
fi
|
||||
log "Docker Compose ready: $(docker compose version 2>/dev/null || docker-compose --version 2>/dev/null)"
|
||||
fi
|
||||
|
||||
# ── 5. Install Go (if binary method) ─────────────────
|
||||
if [ "${DEPLOY_METHOD}" = "binary" ]; then
|
||||
if ! command -v go &>/dev/null; then
|
||||
log "Installing Go..."
|
||||
GO_VERSION="1.23.4"
|
||||
ARCH=$(uname -m)
|
||||
case "${ARCH}" in
|
||||
x86_64) GO_ARCH="amd64" ;;
|
||||
aarch64) GO_ARCH="arm64" ;;
|
||||
*) GO_ARCH="${ARCH}" ;;
|
||||
esac
|
||||
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz
|
||||
rm -rf /usr/local/go
|
||||
tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||
rm /tmp/go.tar.gz
|
||||
echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/go.sh
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
log "Go installed: $(go version)"
|
||||
else
|
||||
log "Go already installed: $(go version)"
|
||||
fi
|
||||
|
||||
# Install make if not present
|
||||
if ! command -v make &>/dev/null; then
|
||||
apt-get install -y -qq make 2>/dev/null || \
|
||||
yum install -y -q make 2>/dev/null || \
|
||||
dnf install -y -q make 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 6. Configure Firewall ────────────────────────────
|
||||
log "Configuring firewall..."
|
||||
if command -v ufw &>/dev/null; then
|
||||
ufw --force reset
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
ufw allow ssh
|
||||
ufw allow 18790/tcp comment "PicoClaw Gateway"
|
||||
# Uncomment if you need webhook ports:
|
||||
# ufw allow 18791/tcp comment "PicoClaw Line Webhook"
|
||||
ufw --force enable
|
||||
log "UFW firewall configured"
|
||||
elif command -v firewall-cmd &>/dev/null; then
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
firewall-cmd --permanent --add-service=ssh
|
||||
firewall-cmd --permanent --add-port=18790/tcp
|
||||
firewall-cmd --reload
|
||||
log "firewalld configured"
|
||||
fi
|
||||
|
||||
# ── 7. Configure fail2ban ────────────────────────────
|
||||
log "Configuring fail2ban..."
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# ── 8. Create environment template ───────────────────
|
||||
if [ ! -f "${PICOCLAW_HOME}/config/.env" ]; then
|
||||
log "Creating environment template..."
|
||||
cat > "${PICOCLAW_HOME}/config/.env" <<'ENVEOF'
|
||||
# ============================================================
|
||||
# PicoClaw Production Environment
|
||||
# ============================================================
|
||||
# Edit this file with your actual API keys and tokens.
|
||||
# NEVER commit this file to version control.
|
||||
# ============================================================
|
||||
|
||||
# ── LLM Provider (uncomment one) ──────────────────────
|
||||
# ANTHROPIC_API_KEY=sk-ant-xxx
|
||||
# OPENAI_API_KEY=sk-xxx
|
||||
# OPENROUTER_API_KEY=sk-or-v1-xxx
|
||||
# GEMINI_API_KEY=xxx
|
||||
|
||||
# ── Chat Channel ─────────────────────────────────────
|
||||
# TELEGRAM_BOT_TOKEN=123456:ABC...
|
||||
# DISCORD_BOT_TOKEN=xxx
|
||||
|
||||
# ── Web Search (optional) ────────────────────────────
|
||||
# BRAVE_SEARCH_API_KEY=BSA...
|
||||
|
||||
# ── Timezone ─────────────────────────────────────────
|
||||
TZ=America/Sao_Paulo
|
||||
ENVEOF
|
||||
chown "${PICOCLAW_USER}:${PICOCLAW_USER}" "${PICOCLAW_HOME}/config/.env"
|
||||
chmod 600 "${PICOCLAW_HOME}/config/.env"
|
||||
fi
|
||||
|
||||
# ── 9. Create config.json template ───────────────────
|
||||
if [ ! -f "${PICOCLAW_HOME}/config/config.json" ]; then
|
||||
log "Creating config.json template..."
|
||||
cat > "${PICOCLAW_HOME}/config/config.json" <<'JSONEOF'
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"workspace": "/opt/picoclaw/workspace",
|
||||
"restrict_to_workspace": true,
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
}
|
||||
},
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "",
|
||||
"allow_from": []
|
||||
}
|
||||
},
|
||||
"providers": {
|
||||
"anthropic": {
|
||||
"api_key": "",
|
||||
"api_base": ""
|
||||
}
|
||||
},
|
||||
"heartbeat": {
|
||||
"enabled": true,
|
||||
"interval": 30
|
||||
},
|
||||
"gateway": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 18790
|
||||
}
|
||||
}
|
||||
JSONEOF
|
||||
chown "${PICOCLAW_USER}:${PICOCLAW_USER}" "${PICOCLAW_HOME}/config/config.json"
|
||||
chmod 600 "${PICOCLAW_HOME}/config/config.json"
|
||||
fi
|
||||
|
||||
# ── 10. Install systemd service (for binary method) ──
|
||||
if [ "${DEPLOY_METHOD}" = "binary" ]; then
|
||||
log "Installing systemd service..."
|
||||
cat > /etc/systemd/system/picoclaw.service <<SVCEOF
|
||||
[Unit]
|
||||
Description=PicoClaw AI Assistant Gateway
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${PICOCLAW_USER}
|
||||
Group=${PICOCLAW_USER}
|
||||
WorkingDirectory=${PICOCLAW_HOME}
|
||||
ExecStart=${PICOCLAW_HOME}/bin/picoclaw gateway
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
StandardOutput=append:${PICOCLAW_HOME}/logs/picoclaw.log
|
||||
StandardError=append:${PICOCLAW_HOME}/logs/picoclaw-error.log
|
||||
|
||||
# Environment
|
||||
EnvironmentFile=-${PICOCLAW_HOME}/config/.env
|
||||
Environment=HOME=${PICOCLAW_HOME}
|
||||
Environment=PICOCLAW_CONFIG=${PICOCLAW_HOME}/config/config.json
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
ReadWritePaths=${PICOCLAW_HOME}
|
||||
PrivateTmp=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SVCEOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable picoclaw
|
||||
log "systemd service installed and enabled"
|
||||
fi
|
||||
|
||||
# ── 11. Create logrotate config ──────────────────────
|
||||
cat > /etc/logrotate.d/picoclaw <<LOGEOF
|
||||
${PICOCLAW_HOME}/logs/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 14
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 ${PICOCLAW_USER} ${PICOCLAW_USER}
|
||||
postrotate
|
||||
systemctl reload picoclaw 2>/dev/null || true
|
||||
endscript
|
||||
}
|
||||
LOGEOF
|
||||
|
||||
# ── Done ─────────────────────────────────────────────
|
||||
echo ""
|
||||
log "=========================================="
|
||||
log " Server setup complete!"
|
||||
log "=========================================="
|
||||
echo ""
|
||||
log "Next steps:"
|
||||
echo " 1. Edit API keys: nano ${PICOCLAW_HOME}/config/.env"
|
||||
echo " 2. Edit config: nano ${PICOCLAW_HOME}/config/config.json"
|
||||
if [ "${DEPLOY_METHOD}" = "docker" ]; then
|
||||
echo " 3. Deploy: Run 'make deploy-hostinger' from your local machine"
|
||||
else
|
||||
echo " 3. Deploy: Run 'make deploy-hostinger' from your local machine"
|
||||
echo " 4. Start service: systemctl start picoclaw"
|
||||
echo " 5. Check status: systemctl status picoclaw"
|
||||
echo " 6. View logs: tail -f ${PICOCLAW_HOME}/logs/picoclaw.log"
|
||||
fi
|
||||
echo ""
|
||||
log "Firewall ports open: SSH (22), PicoClaw Gateway (18790)"
|
||||
echo ""
|
||||
98
deploy/hostinger/status.sh
Executable file
98
deploy/hostinger/status.sh
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# PicoClaw - Check Remote Server Status
|
||||
# ============================================================
|
||||
# Usage:
|
||||
# ./deploy/hostinger/status.sh -h YOUR_VPS_IP
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[STATUS]${NC} $*"; }
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
|
||||
HOST="${HOSTINGER_HOST:-}"
|
||||
USER="${HOSTINGER_USER:-root}"
|
||||
SSH_KEY="${HOSTINGER_SSH_KEY:-${HOME}/.ssh/id_rsa}"
|
||||
SSH_PORT="${HOSTINGER_SSH_PORT:-22}"
|
||||
METHOD="${HOSTINGER_DEPLOY_METHOD:-docker}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--host) HOST="$2"; shift 2 ;;
|
||||
-u|--user) USER="$2"; shift 2 ;;
|
||||
-k|--key) SSH_KEY="$2"; shift 2 ;;
|
||||
-m|--method) METHOD="$2"; shift 2 ;;
|
||||
-p|--port) SSH_PORT="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -z "${HOST}" ] && { echo "Usage: $0 -h HOST"; exit 1; }
|
||||
|
||||
SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p ${SSH_PORT} -i ${SSH_KEY}"
|
||||
SSH_CMD="ssh ${SSH_OPTS} ${USER}@${HOST}"
|
||||
|
||||
log "Checking PicoClaw status on ${HOST}..."
|
||||
echo ""
|
||||
|
||||
${SSH_CMD} <<REMOTEOF
|
||||
echo "── System ──────────────────────────────────"
|
||||
echo "Hostname: \$(hostname)"
|
||||
echo "Uptime: \$(uptime -p)"
|
||||
echo "Memory: \$(free -h | awk '/Mem:/ {print \$3 "/" \$2}')"
|
||||
echo "Disk: \$(df -h / | awk 'NR==2 {print \$3 "/" \$2 " (" \$5 " used)"}')"
|
||||
echo "Load: \$(cat /proc/loadavg | awk '{print \$1, \$2, \$3}')"
|
||||
echo ""
|
||||
|
||||
echo "── PicoClaw ────────────────────────────────"
|
||||
if [ "${METHOD}" = "docker" ]; then
|
||||
if command -v docker &>/dev/null; then
|
||||
echo "Method: Docker"
|
||||
docker compose -f /opt/picoclaw/docker-compose.yml ps 2>/dev/null || echo "Container: not running"
|
||||
echo ""
|
||||
echo "── Recent Logs ─────────────────────────────"
|
||||
docker compose -f /opt/picoclaw/docker-compose.yml logs --tail=10 picoclaw-gateway 2>/dev/null || true
|
||||
else
|
||||
echo "Docker not installed"
|
||||
fi
|
||||
else
|
||||
echo "Method: Binary (systemd)"
|
||||
if systemctl is-active --quiet picoclaw 2>/dev/null; then
|
||||
echo "Status: RUNNING"
|
||||
version=\$(/opt/picoclaw/bin/picoclaw version 2>/dev/null || echo "unknown")
|
||||
echo "Version: \${version}"
|
||||
else
|
||||
echo "Status: STOPPED"
|
||||
fi
|
||||
systemctl status picoclaw --no-pager -l 2>/dev/null || true
|
||||
echo ""
|
||||
echo "── Recent Logs ─────────────────────────────"
|
||||
tail -10 /opt/picoclaw/logs/picoclaw.log 2>/dev/null || echo "No logs found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "── Health Check ────────────────────────────"
|
||||
if curl -sf http://localhost:18790/health > /dev/null 2>&1; then
|
||||
echo "Gateway: HEALTHY (port 18790)"
|
||||
else
|
||||
echo "Gateway: UNREACHABLE"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "── Firewall ────────────────────────────────"
|
||||
if command -v ufw &>/dev/null; then
|
||||
ufw status 2>/dev/null | head -10
|
||||
elif command -v firewall-cmd &>/dev/null; then
|
||||
firewall-cmd --list-all 2>/dev/null | head -10
|
||||
fi
|
||||
REMOTEOF
|
||||
|
||||
echo ""
|
||||
log "Status check complete"
|
||||
Loading…
Add table
Reference in a new issue