feat: add CoopCredits Solana media rewards ecosystem
Add complete token system with Plex/Tautulli/Overseer integration: - Anchor program for SPL token mint/burn/transfer - Express backend with OAuth, webhooks, Solana integration - Next.js frontend with dashboard, admin panel, wallet management - Docker deployment for 172.20.1.0/24 infrastructure - Production configs with SSL, Nginx, health monitoring Tautulli webhooks auto-mint on watch events. Overseer integration burns for content requests.
This commit is contained in:
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================
|
||||
# CoopCredits Production Deployment Script
|
||||
# For infrastructure: 172.20.1.0/24
|
||||
# ==========================================
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}"
|
||||
echo "🚀 CoopCredits Production Deployment"
|
||||
echo "===================================="
|
||||
echo -e "${NC}"
|
||||
echo "Time: $(date)"
|
||||
echo "Project: $PROJECT_DIR"
|
||||
echo ""
|
||||
|
||||
# ==========================================
|
||||
# Pre-deployment Checks
|
||||
# ==========================================
|
||||
|
||||
pre_deployment_checks() {
|
||||
echo -e "${BLUE}Running pre-deployment checks...${NC}"
|
||||
|
||||
# Check environment file
|
||||
if [ ! -f "$PROJECT_DIR/.env" ]; then
|
||||
echo -e "${RED}❌ .env file not found!${NC}"
|
||||
echo "Run: ./deployment/setup-infrastructure.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check SSL certificates
|
||||
if [ ! -f "$PROJECT_DIR/docker/nginx/ssl/cert.pem" ] && [ ! -d "$PROJECT_DIR/docker/nginx/letsencrypt" ]; then
|
||||
echo -e "${YELLOW}⚠️ SSL certificates not found${NC}"
|
||||
echo "Place certificates in docker/nginx/ssl/ or setup Let's Encrypt"
|
||||
read -p "Continue without SSL? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}❌ Docker not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo -e "${RED}❌ Docker Compose not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required env variables
|
||||
source "$PROJECT_DIR/.env"
|
||||
|
||||
local required_vars=(
|
||||
"JWT_SECRET"
|
||||
"TAUTULLI_API_KEY"
|
||||
"OVERSEER_API_KEY"
|
||||
"SOLANA_MINT_AUTHORITY_KEYPAIR"
|
||||
"ENCRYPTION_KEY"
|
||||
)
|
||||
|
||||
local missing=0
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
echo -e "${RED}❌ Missing required environment variable: $var${NC}"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $missing -eq 1 ]; then
|
||||
echo "Please edit $PROJECT_DIR/.env and add the missing values"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Pre-deployment checks passed${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Backup Current State
|
||||
# ==========================================
|
||||
|
||||
backup_state() {
|
||||
echo -e "${BLUE}Creating backup...${NC}"
|
||||
|
||||
local backup_dir="$PROJECT_DIR/backups/$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# Backup database if running
|
||||
if docker ps | grep -q "coop-postgres"; then
|
||||
echo "Backing up database..."
|
||||
docker exec coop-postgres pg_dump -U coop coop_credits > "$backup_dir/database.sql" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Backup environment
|
||||
cp "$PROJECT_DIR/.env" "$backup_dir/"
|
||||
|
||||
# Backup Docker volumes list
|
||||
docker volume ls | grep coop > "$backup_dir/volumes.txt" 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}✅ Backup created: $backup_dir${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Build and Deploy
|
||||
# ==========================================
|
||||
|
||||
deploy() {
|
||||
echo -e "${BLUE}Building and deploying...${NC}"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Pull latest images
|
||||
echo "Pulling base images..."
|
||||
docker-compose -f docker-compose.prod.yml pull
|
||||
|
||||
# Build services
|
||||
echo "Building services..."
|
||||
docker-compose -f docker-compose.prod.yml build --no-cache
|
||||
|
||||
# Stop existing services
|
||||
echo "Stopping existing services..."
|
||||
docker-compose -f docker-compose.prod.yml down --remove-orphans
|
||||
|
||||
# Start services
|
||||
echo "Starting services..."
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Wait for database
|
||||
echo "Waiting for database..."
|
||||
sleep 10
|
||||
|
||||
# Run migrations
|
||||
echo "Running database migrations..."
|
||||
docker-compose -f docker-compose.prod.yml exec -T backend npx prisma migrate deploy || {
|
||||
echo -e "${YELLOW}⚠️ Migration may have already been applied${NC}"
|
||||
}
|
||||
|
||||
# Seed initial data if needed
|
||||
echo "Checking for initial data..."
|
||||
docker-compose -f docker-compose.prod.yml exec -T backend npx prisma db seed 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}✅ Deployment complete!${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Post-deployment Verification
|
||||
# ==========================================
|
||||
|
||||
verify_deployment() {
|
||||
echo -e "${BLUE}Verifying deployment...${NC}"
|
||||
|
||||
local retries=30
|
||||
local delay=2
|
||||
|
||||
# Wait for services to be ready
|
||||
echo "Waiting for services to start..."
|
||||
for i in $(seq 1 $retries); do
|
||||
if curl -sf https://coop.hobokenchicken.com/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Website is responding${NC}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ $i -eq $retries ]; then
|
||||
echo -e "${RED}❌ Website failed to start${NC}"
|
||||
echo "Check logs: docker-compose -f docker-compose.prod.yml logs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "."
|
||||
sleep $delay
|
||||
done
|
||||
|
||||
# Check database connection
|
||||
if docker-compose -f docker-compose.prod.yml exec -T postgres pg_isready -U coop > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Database is ready${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Database not responding${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Display Info
|
||||
# ==========================================
|
||||
|
||||
show_info() {
|
||||
echo -e "${BLUE}Deployment Information${NC}"
|
||||
echo "======================"
|
||||
echo ""
|
||||
echo "Website: https://coop.hobokenchicken.com"
|
||||
echo "API: https://coop.hobokenchicken.com/api"
|
||||
echo "Webhooks: https://coop.hobokenchicken.com/webhooks/"
|
||||
echo ""
|
||||
echo "Services:"
|
||||
docker-compose -f "$PROJECT_DIR/docker-compose.prod.yml" ps
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " View logs: docker-compose -f docker-compose.prod.yml logs -f"
|
||||
echo " Stop: docker-compose -f docker-compose.prod.yml down"
|
||||
echo " Restart: docker-compose -f docker-compose.prod.yml restart"
|
||||
echo " Health check: ./deployment/health-check.sh"
|
||||
echo ""
|
||||
echo -e "${GREEN}🎉 Deployment successful!${NC}"
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Main
|
||||
# ==========================================
|
||||
|
||||
main() {
|
||||
pre_deployment_checks
|
||||
backup_state
|
||||
deploy
|
||||
verify_deployment
|
||||
show_info
|
||||
}
|
||||
|
||||
# Handle script arguments
|
||||
case "${1:-}" in
|
||||
--check)
|
||||
pre_deployment_checks
|
||||
exit 0
|
||||
;;
|
||||
--backup)
|
||||
backup_state
|
||||
exit 0
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --check Run pre-deployment checks only"
|
||||
echo " --backup Create backup only"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
main
|
||||
;;
|
||||
esac
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
#!/bin/bash
|
||||
|
||||
# CoopCredits Deployment Script
|
||||
# Usage: ./deploy.sh [environment]
|
||||
|
||||
set -e
|
||||
|
||||
ENVIRONMENT=${1:-production}
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
echo "🚀 CoopCredits Deployment"
|
||||
echo "========================"
|
||||
echo "Environment: $ENVIRONMENT"
|
||||
echo ""
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
echo "Checking prerequisites..."
|
||||
|
||||
command -v docker >/dev/null 2>&1 || { echo "❌ Docker is required but not installed. Aborting." >&2; exit 1; }
|
||||
command -v docker-compose >/dev/null 2>&1 || { echo "❌ Docker Compose is required but not installed. Aborting." >&2; exit 1; }
|
||||
|
||||
echo "✅ Prerequisites met"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
load_env() {
|
||||
if [ -f "$PROJECT_DIR/.env" ]; then
|
||||
echo "Loading environment variables..."
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env"
|
||||
set +a
|
||||
else
|
||||
echo "⚠️ .env file not found. Using default values."
|
||||
fi
|
||||
}
|
||||
|
||||
# Build and deploy
|
||||
build_and_deploy() {
|
||||
echo ""
|
||||
echo "Building and deploying..."
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Pull latest images
|
||||
docker-compose pull
|
||||
|
||||
# Build services
|
||||
docker-compose build --no-cache
|
||||
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
|
||||
# Wait for database
|
||||
echo "Waiting for database..."
|
||||
sleep 10
|
||||
|
||||
# Run migrations
|
||||
echo "Running database migrations..."
|
||||
docker-compose exec -T backend npx prisma migrate deploy
|
||||
|
||||
echo "✅ Deployment complete!"
|
||||
}
|
||||
|
||||
# Setup SSL certificates
|
||||
setup_ssl() {
|
||||
echo ""
|
||||
echo "Setting up SSL certificates..."
|
||||
|
||||
# Check if certificates exist
|
||||
if [ ! -f "$PROJECT_DIR/docker/nginx/ssl/cert.pem" ]; then
|
||||
echo "⚠️ SSL certificates not found at docker/nginx/ssl/"
|
||||
echo "Please place your certificates:"
|
||||
echo " - cert.pem (certificate)"
|
||||
echo " - key.pem (private key)"
|
||||
echo ""
|
||||
read -p "Continue without SSL? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Health check
|
||||
health_check() {
|
||||
echo ""
|
||||
echo "Running health checks..."
|
||||
|
||||
# Check if services are running
|
||||
if docker-compose ps | grep -q "Up"; then
|
||||
echo "✅ Services are running"
|
||||
else
|
||||
echo "❌ Some services failed to start"
|
||||
docker-compose ps
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test API endpoint
|
||||
if curl -sf http://localhost:3001/health > /dev/null; then
|
||||
echo "✅ Backend is responding"
|
||||
else
|
||||
echo "❌ Backend is not responding"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Health checks passed"
|
||||
}
|
||||
|
||||
# Main deployment flow
|
||||
main() {
|
||||
check_prerequisites
|
||||
load_env
|
||||
setup_ssl
|
||||
build_and_deploy
|
||||
health_check
|
||||
|
||||
echo ""
|
||||
echo "🎉 CoopCredits has been deployed!"
|
||||
echo ""
|
||||
echo "Access your application:"
|
||||
echo " - Website: https://coop.hobokenchicken.com"
|
||||
echo " - API: https://coop.hobokenchicken.com/api"
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " - View logs: docker-compose logs -f"
|
||||
echo " - Stop: docker-compose down"
|
||||
echo " - Restart: docker-compose restart"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Executable
+228
@@ -0,0 +1,228 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================
|
||||
# CoopCredits Health Check Script
|
||||
# Monitors all services in the infrastructure
|
||||
# ==========================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
PLEX_IP="172.20.1.220"
|
||||
OVERSEER_IP="172.20.1.225"
|
||||
TAUTULLI_IP="172.20.1.255"
|
||||
|
||||
PLEX_PORT="32400"
|
||||
OVERSEER_PORT="5055"
|
||||
TAUTULLI_PORT="8181"
|
||||
|
||||
# Track overall health
|
||||
HEALTHY=0
|
||||
UNHEALTHY=0
|
||||
|
||||
print_header() {
|
||||
echo ""
|
||||
echo "🏥 CoopCredits Health Check"
|
||||
echo "=========================="
|
||||
echo "$(date)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_service() {
|
||||
local name=$1
|
||||
local url=$2
|
||||
local expected_code=${3:-200}
|
||||
|
||||
echo -n "Checking $name... "
|
||||
|
||||
if curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null | grep -q "$expected_code"; then
|
||||
echo -e "${GREEN}✅ OK${NC}"
|
||||
((HEALTHY++))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC}"
|
||||
((UNHEALTHY++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_docker_service() {
|
||||
local name=$1
|
||||
local container=$2
|
||||
|
||||
echo -n "Checking Docker container: $name... "
|
||||
|
||||
if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
local status=$(docker inspect --format='{{.State.Status}}' "$container" 2>/dev/null)
|
||||
if [ "$status" = "running" ]; then
|
||||
echo -e "${GREEN}✅ Running${NC}"
|
||||
((HEALTHY++))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Not running (status: $status)${NC}"
|
||||
((UNHEALTHY++))
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}❌ Not found${NC}"
|
||||
((UNHEALTHY++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_network_connectivity() {
|
||||
local name=$1
|
||||
local ip=$2
|
||||
local port=$3
|
||||
|
||||
echo -n "Network connectivity to $name ($ip:$port)... "
|
||||
|
||||
if timeout 5 bash -c "</dev/tcp/$ip/$port" 2>/dev/null; then
|
||||
echo -e "${GREEN}✅ Reachable${NC}"
|
||||
((HEALTHY++))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ Unreachable${NC}"
|
||||
((UNHEALTHY++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_disk_space() {
|
||||
echo ""
|
||||
echo "📊 Disk Space"
|
||||
echo "-------------"
|
||||
|
||||
df -h / | awk 'NR==2 {printf "Root: %s used of %s (%s)\n", $3, $2, $5}'
|
||||
|
||||
local usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [ "$usage" -gt 90 ]; then
|
||||
echo -e "${RED}⚠️ Critical disk usage!${NC}"
|
||||
((UNHEALTHY++))
|
||||
elif [ "$usage" -gt 80 ]; then
|
||||
echo -e "${YELLOW}⚠️ High disk usage${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✅ Disk usage OK${NC}"
|
||||
((HEALTHY++))
|
||||
fi
|
||||
}
|
||||
|
||||
check_memory() {
|
||||
echo ""
|
||||
echo "🧠 Memory Usage"
|
||||
echo "--------------"
|
||||
|
||||
if command -v free >/dev/null 2>&1; then
|
||||
free -h | grep "Mem:" | awk '{printf "Used: %s / %s\n", $3, $2}'
|
||||
|
||||
local usage=$(free | grep "Mem:" | awk '{printf "%.0f", $3/$2 * 100}')
|
||||
if [ "$usage" -gt 90 ]; then
|
||||
echo -e "${RED}⚠️ Critical memory usage!${NC}"
|
||||
((UNHEALTHY++))
|
||||
elif [ "$usage" -gt 80 ]; then
|
||||
echo -e "${YELLOW}⚠️ High memory usage${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✅ Memory usage OK${NC}"
|
||||
((HEALTHY++))
|
||||
fi
|
||||
else
|
||||
echo "Memory check not available"
|
||||
fi
|
||||
}
|
||||
|
||||
check_ssl_certificate() {
|
||||
echo ""
|
||||
echo "🔒 SSL Certificate"
|
||||
echo "------------------"
|
||||
|
||||
local domain="coop.hobokenchicken.com"
|
||||
local expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | openssl x509 -noout -dates | grep notAfter | cut -d= -f2)
|
||||
|
||||
if [ -n "$expiry" ]; then
|
||||
local expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$expiry" +%s)
|
||||
local now_epoch=$(date +%s)
|
||||
local days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
|
||||
|
||||
echo "Certificate expires: $expiry ($days_left days)"
|
||||
|
||||
if [ "$days_left" -lt 7 ]; then
|
||||
echo -e "${RED}⚠️ Certificate expires soon!${NC}"
|
||||
((UNHEALTHY++))
|
||||
elif [ "$days_left" -lt 30 ]; then
|
||||
echo -e "${YELLOW}⚠️ Certificate expires in $days_left days${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✅ Certificate valid${NC}"
|
||||
((HEALTHY++))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Could not check certificate${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_checks() {
|
||||
print_header
|
||||
|
||||
# Docker Services
|
||||
echo "🐳 Docker Services"
|
||||
echo "------------------"
|
||||
check_docker_service "Nginx" "coop-nginx"
|
||||
check_docker_service "Frontend" "coop-frontend"
|
||||
check_docker_service "Backend" "coop-backend"
|
||||
check_docker_service "PostgreSQL" "coop-postgres"
|
||||
check_docker_service "Redis" "coop-redis"
|
||||
|
||||
# Network Connectivity
|
||||
echo ""
|
||||
echo "🌐 Network Connectivity"
|
||||
echo "-----------------------"
|
||||
check_network_connectivity "Plex" "$PLEX_IP" "$PLEX_PORT"
|
||||
check_network_connectivity "Tautulli" "$TAUTULLI_IP" "$TAUTULLI_PORT"
|
||||
check_network_connectivity "Overseer" "$OVERSEER_IP" "$OVERSEER_PORT"
|
||||
|
||||
# External Services
|
||||
echo ""
|
||||
echo "🔗 External Services"
|
||||
echo "--------------------"
|
||||
check_service "CoopCredits Website" "https://coop.hobokenchicken.com/health"
|
||||
check_service "Backend API" "https://coop.hobokenchicken.com/api/health"
|
||||
|
||||
# System Resources
|
||||
check_disk_space
|
||||
check_memory
|
||||
|
||||
# SSL
|
||||
check_ssl_certificate
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "📋 Summary"
|
||||
echo "----------"
|
||||
echo -e "${GREEN}✅ $HEALTHY healthy${NC}"
|
||||
if [ $UNHEALTHY -gt 0 ]; then
|
||||
echo -e "${RED}❌ $UNHEALTHY unhealthy${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}All systems operational!${NC}"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Run in watch mode if requested
|
||||
if [ "$1" == "--watch" ]; then
|
||||
while true; do
|
||||
clear
|
||||
run_checks
|
||||
echo ""
|
||||
echo "Refreshing in 30 seconds... (Ctrl+C to stop)"
|
||||
sleep 30
|
||||
done
|
||||
else
|
||||
run_checks
|
||||
fi
|
||||
Executable
+357
@@ -0,0 +1,357 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================
|
||||
# CoopCredits Infrastructure Setup Script
|
||||
# Configures integration with existing services
|
||||
# Server Infrastructure: 172.20.1.0/24
|
||||
# ==========================================
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
echo "🏗️ CoopCredits Infrastructure Setup"
|
||||
echo "===================================="
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# ==========================================
|
||||
# Configuration
|
||||
# ==========================================
|
||||
PLEX_IP="172.20.1.220"
|
||||
OVERSEER_IP="172.20.1.225"
|
||||
TAUTULLI_IP="172.20.1.255"
|
||||
|
||||
PLEX_PORT="32400"
|
||||
OVERSEER_PORT="5055"
|
||||
TAUTULLI_PORT="8181"
|
||||
|
||||
echo "📋 Configuration:"
|
||||
echo " Plex: http://${PLEX_IP}:${PLEX_PORT}"
|
||||
echo " Overseer: http://${OVERSEER_IP}:${OVERSEER_PORT}"
|
||||
echo " Tautulli: http://${TAUTULLI_IP}:${TAUTULLI_PORT}"
|
||||
echo ""
|
||||
|
||||
# ==========================================
|
||||
# Test Connectivity
|
||||
# ==========================================
|
||||
test_connectivity() {
|
||||
echo "🔍 Testing connectivity to services..."
|
||||
|
||||
# Test Plex
|
||||
if curl -sf "http://${PLEX_IP}:${PLEX_PORT}/identity" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅${NC} Plex is reachable"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️${NC} Plex not responding (may need auth or be offline)"
|
||||
fi
|
||||
|
||||
# Test Tautulli
|
||||
if curl -sf "http://${TAUTULLI_IP}:${TAUTULLI_PORT}/api/v2?apikey=test&cmd=get_server_info" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅${NC} Tautulli is reachable"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️${NC} Tautulli not responding (check API key)"
|
||||
fi
|
||||
|
||||
# Test Overseer
|
||||
if curl -sf "http://${OVERSEER_IP}:${OVERSEER_PORT}/api/v1/status" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅${NC} Overseer is reachable"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️${NC} Overseer not responding (may need API key)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Generate Environment File
|
||||
# ==========================================
|
||||
generate_env() {
|
||||
echo "📝 Generating environment configuration..."
|
||||
|
||||
ENV_FILE="${PROJECT_DIR}/.env"
|
||||
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
echo -e "${YELLOW}⚠️${NC} .env file already exists. Creating backup..."
|
||||
cp "$ENV_FILE" "${ENV_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
fi
|
||||
|
||||
# Generate secure keys
|
||||
JWT_SECRET=$(openssl rand -hex 32)
|
||||
ENCRYPTION_KEY=$(openssl rand -base64 32)
|
||||
WEBHOOK_SECRET=$(openssl rand -hex 16)
|
||||
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '=+/')
|
||||
REDIS_PASSWORD=$(openssl rand -base64 24 | tr -d '=+/')
|
||||
|
||||
cat > "$ENV_FILE" << EOF
|
||||
# ==========================================
|
||||
# CoopCredits Environment Configuration
|
||||
# Generated: $(date)
|
||||
# Network: 172.20.1.0/24
|
||||
# ==========================================
|
||||
|
||||
# Database
|
||||
DATABASE_URL="postgresql://coop:${POSTGRES_PASSWORD}@postgres:5432/coop_credits?schema=public"
|
||||
POSTGRES_PASSWORD="${POSTGRES_PASSWORD}"
|
||||
|
||||
# Server
|
||||
PORT=3001
|
||||
NODE_ENV=production
|
||||
API_URL=https://coop.hobokenchicken.com
|
||||
FRONTEND_URL=https://coop.hobokenchicken.com
|
||||
|
||||
# JWT Secret
|
||||
JWT_SECRET="${JWT_SECRET}"
|
||||
|
||||
# ==========================================
|
||||
# Plex Server (172.20.1.220:32400)
|
||||
# ==========================================
|
||||
# Get from: https://plex.tv/claim
|
||||
PLEX_CLIENT_ID=""
|
||||
PLEX_CLIENT_SECRET=""
|
||||
PLEX_REDIRECT_URI="https://coop.hobokenchicken.com/auth/callback"
|
||||
|
||||
# ==========================================
|
||||
# Tautulli (172.20.1.255:8181)
|
||||
# ==========================================
|
||||
# Get from: Settings > Web Interface > API > API Key
|
||||
TAUTULLI_URL="http://${TAUTULLI_IP}:${TAUTULLI_PORT}"
|
||||
TAUTULLI_API_KEY=""
|
||||
TAUTULLI_WEBHOOK_SECRET="${WEBHOOK_SECRET}"
|
||||
|
||||
# ==========================================
|
||||
# Overseer (172.20.1.225:5055)
|
||||
# ==========================================
|
||||
# Get from: Settings > General > API Key
|
||||
OVERSEER_URL="http://${OVERSEER_IP}:${OVERSEER_PORT}"
|
||||
OVERSEER_API_KEY=""
|
||||
|
||||
# ==========================================
|
||||
# Solana (Devnet)
|
||||
# ==========================================
|
||||
# Run: npm run setup:solana to generate
|
||||
SOLANA_RPC_URL="https://api.devnet.solana.com"
|
||||
SOLANA_PROGRAM_ID="CoopCredits111111111111111111111111111111111"
|
||||
SOLANA_MINT_AUTHORITY_KEYPAIR=""
|
||||
SOLANA_TOKEN_DECIMALS=6
|
||||
|
||||
# ==========================================
|
||||
# Redis
|
||||
# ==========================================
|
||||
REDIS_URL="redis://:${REDIS_PASSWORD}@redis:6379"
|
||||
REDIS_PASSWORD="${REDIS_PASSWORD}"
|
||||
|
||||
# ==========================================
|
||||
# Encryption
|
||||
# ==========================================
|
||||
ENCRYPTION_KEY="${ENCRYPTION_KEY}"
|
||||
|
||||
# ==========================================
|
||||
# Frontend
|
||||
# ==========================================
|
||||
NEXT_PUBLIC_API_URL="https://coop.hobokenchicken.com"
|
||||
NEXT_PUBLIC_SOLANA_NETWORK="devnet"
|
||||
NEXT_PUBLIC_SOLANA_RPC_URL="https://api.devnet.solana.com"
|
||||
NEXT_PUBLIC_APP_NAME="CoopCredits"
|
||||
NEXT_PUBLIC_APP_URL="https://coop.hobokenchicken.com"
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✅${NC} Environment file created: ${ENV_FILE}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️${NC} IMPORTANT: Edit .env and add your API keys:"
|
||||
echo " 1. PLEX_CLIENT_ID and PLEX_CLIENT_SECRET from plex.tv"
|
||||
echo " 2. TAUTULLI_API_KEY from Tautulli settings"
|
||||
echo " 3. OVERSEER_API_KEY from Overseer settings"
|
||||
echo " 4. SOLANA_MINT_AUTHORITY_KEYPAIR from 'npm run setup:solana'"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Configure Tautulli Webhook
|
||||
# ==========================================
|
||||
configure_tautulli() {
|
||||
echo "📺 Tautulli Webhook Configuration"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
echo "To complete Tautulli integration:"
|
||||
echo ""
|
||||
echo "1. Open Tautulli: http://${TAUTULLI_IP}:${TAUTULLI_PORT}"
|
||||
echo "2. Go to: Settings > Notification Agents"
|
||||
echo "3. Click 'Add a new notification agent'"
|
||||
echo "4. Select 'Webhook'"
|
||||
echo ""
|
||||
echo "Configure the webhook:"
|
||||
echo " Webhook URL: https://coop.hobokenchicken.com/webhooks/tautulli"
|
||||
echo " Webhook Method: POST"
|
||||
echo " Content Type: application/json"
|
||||
echo ""
|
||||
echo "JSON Payload (copy this exactly):"
|
||||
cat << 'JSONEOF'
|
||||
{
|
||||
"action": "watched",
|
||||
"user_id": "{user_id}",
|
||||
"username": "{username}",
|
||||
"rating_key": "{rating_key}",
|
||||
"session_key": "{session_key}",
|
||||
"media_type": "{media_type}",
|
||||
"title": "{title}",
|
||||
"grandparent_title": "{grandparent_title}",
|
||||
"started": "{started}",
|
||||
"stopped": "{stopped}",
|
||||
"percent_complete": "{percent_complete}",
|
||||
"is_new": "{is_new}"
|
||||
}
|
||||
JSONEOF
|
||||
echo ""
|
||||
echo "Triggers: Enable 'Watched'"
|
||||
echo "Conditions: None (or customize as needed)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Configure Overseer Webhook
|
||||
# ==========================================
|
||||
configure_overseer() {
|
||||
echo "🎬 Overseer Webhook Configuration"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
echo "To complete Overseer integration:"
|
||||
echo ""
|
||||
echo "1. Open Overseer: http://${OVERSEER_IP}:${OVERSEER_PORT}"
|
||||
echo "2. Go to: Settings > Notifications"
|
||||
echo "3. Click 'Webhook'"
|
||||
echo ""
|
||||
echo "Configure the webhook:"
|
||||
echo " Webhook URL: https://coop.hobokenchicken.com/webhooks/overseer"
|
||||
echo " Authorization Header: Bearer <your-webhook-secret-from-env>"
|
||||
echo ""
|
||||
echo "JSON Payload:"
|
||||
cat << 'JSONEOF'
|
||||
{
|
||||
"request_id": "{{request.id}}",
|
||||
"status": "{{request.status}}",
|
||||
"media_type": "{{media.media_type}}",
|
||||
"title": "{{media.title}}"
|
||||
}
|
||||
JSONEOF
|
||||
echo ""
|
||||
echo "Events: Enable 'Request Approved' and 'Request Declined'"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Configure Plex OAuth
|
||||
# ==========================================
|
||||
configure_plex() {
|
||||
echo "🎭 Plex OAuth Configuration"
|
||||
echo "==========================="
|
||||
echo ""
|
||||
echo "To enable Plex authentication:"
|
||||
echo ""
|
||||
echo "1. Go to: https://plex.tv/claim"
|
||||
echo " Or: https://www.plex.tv/link/"
|
||||
echo ""
|
||||
echo "2. For OAuth app registration, you may need to contact Plex"
|
||||
echo " or use a third-party OAuth provider that supports Plex"
|
||||
echo ""
|
||||
echo "3. Alternative: Use custom authentication with Plex credentials"
|
||||
echo " stored securely and linked to wallets"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Firewall Rules
|
||||
# ==========================================
|
||||
configure_firewall() {
|
||||
echo "🔥 Firewall Configuration"
|
||||
echo "========================="
|
||||
echo ""
|
||||
echo "Recommended firewall rules for CoopCredits server:"
|
||||
echo ""
|
||||
echo "# Allow web traffic"
|
||||
echo "sudo ufw allow 80/tcp"
|
||||
echo "sudo ufw allow 443/tcp"
|
||||
echo ""
|
||||
echo "# Allow internal network access to services"
|
||||
echo "sudo ufw allow from 172.20.1.0/24 to any port 3001"
|
||||
echo ""
|
||||
echo "# Allow PostgreSQL only from localhost"
|
||||
echo "sudo ufw allow from 127.0.0.1 to any port 5432"
|
||||
echo ""
|
||||
echo "# Allow Redis only from localhost"
|
||||
echo "sudo ufw allow from 127.0.0.1 to any port 6379"
|
||||
echo ""
|
||||
echo "Current UFW status:"
|
||||
sudo ufw status verbose 2>/dev/null || echo "UFW not installed or not active"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# SSL Certificate Setup
|
||||
# ==========================================
|
||||
setup_ssl() {
|
||||
echo "🔒 SSL Certificate Setup"
|
||||
echo "======================="
|
||||
echo ""
|
||||
|
||||
SSL_DIR="${PROJECT_DIR}/docker/nginx/ssl"
|
||||
mkdir -p "$SSL_DIR"
|
||||
|
||||
if [ -f "${SSL_DIR}/cert.pem" ] && [ -f "${SSL_DIR}/key.pem" ]; then
|
||||
echo -e "${GREEN}✅${NC} SSL certificates already exist"
|
||||
else
|
||||
echo "SSL certificates not found at ${SSL_DIR}/"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo ""
|
||||
echo "1. Let's Encrypt (recommended for production):"
|
||||
echo " docker-compose -f docker-compose.prod.yml run --rm certbot certonly"
|
||||
echo " --webroot -w /var/www/certbot"
|
||||
echo " -d coop.hobokenchicken.com"
|
||||
echo " --agree-tos --no-eff-email"
|
||||
echo ""
|
||||
echo "2. Self-signed (for testing only):"
|
||||
echo " openssl req -x509 -nodes -days 365 -newkey rsa:2048 \\"
|
||||
echo " -keyout ${SSL_DIR}/key.pem \\"
|
||||
echo " -out ${SSL_DIR}/cert.pem \\"
|
||||
echo " -subj '/CN=coop.hobokenchicken.com'"
|
||||
echo ""
|
||||
echo "3. Place existing certificates:"
|
||||
echo " cp your-cert.pem ${SSL_DIR}/cert.pem"
|
||||
echo " cp your-key.pem ${SSL_DIR}/key.pem"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# Main
|
||||
# ==========================================
|
||||
main() {
|
||||
test_connectivity
|
||||
generate_env
|
||||
setup_ssl
|
||||
configure_tautulli
|
||||
configure_overseer
|
||||
configure_plex
|
||||
configure_firewall
|
||||
|
||||
echo ""
|
||||
echo "🎉 Setup complete!"
|
||||
echo "=================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Edit .env file with your API keys"
|
||||
echo " 2. Run: npm run setup:solana"
|
||||
echo " 3. Setup SSL certificates"
|
||||
echo " 4. Run: npm run deploy"
|
||||
echo ""
|
||||
echo "After deployment, configure webhooks in Tautulli and Overseer"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Solana Setup Script for CoopCredits
|
||||
# This script sets up the Solana environment for the CoopCredits token
|
||||
|
||||
set -e
|
||||
|
||||
echo "🪙 CoopCredits Solana Setup"
|
||||
echo "==========================="
|
||||
echo ""
|
||||
|
||||
# Check for Solana CLI
|
||||
if ! command -v solana &> /dev/null; then
|
||||
echo "Installing Solana CLI..."
|
||||
sh -c "$(curl -sSfL https://release.solana.com/v1.17.0/install)"
|
||||
export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
|
||||
fi
|
||||
|
||||
echo "✅ Solana CLI version: $(solana --version)"
|
||||
|
||||
# Set network to devnet
|
||||
echo ""
|
||||
echo "Setting network to devnet..."
|
||||
solana config set --url devnet
|
||||
|
||||
# Create or load keypair
|
||||
KEYPAIR_PATH="$HOME/.config/solana/coop-credits-authority.json"
|
||||
|
||||
if [ -f "$KEYPAIR_PATH" ]; then
|
||||
echo "✅ Using existing keypair: $KEYPAIR_PATH"
|
||||
else
|
||||
echo "Creating new keypair..."
|
||||
solana-keygen new --outfile "$KEYPAIR_PATH" --no-bip39-passphrase
|
||||
echo "✅ Keypair created: $KEYPAIR_PATH"
|
||||
fi
|
||||
|
||||
# Get public key
|
||||
AUTHORITY_PUBKEY=$(solana-keygen pubkey "$KEYPAIR_PATH")
|
||||
echo ""
|
||||
echo "Authority Public Key: $AUTHORITY_PUBKEY"
|
||||
|
||||
# Request airdrop
|
||||
echo ""
|
||||
echo "Requesting airdrop..."
|
||||
solana airdrop 2 "$AUTHORITY_PUBKEY" || echo "⚠️ Airdrop failed (may have rate limit)"
|
||||
|
||||
# Check balance
|
||||
BALANCE=$(solana balance "$AUTHORITY_PUBKEY")
|
||||
echo "Authority Balance: $BALANCE SOL"
|
||||
|
||||
# Export private key for backend
|
||||
echo ""
|
||||
echo "Exporting private key for backend..."
|
||||
PRIVATE_KEY=$(cat "$KEYPAIR_PATH" | jq -r '.[]' | xxd -p -c 999999 | head -c 128)
|
||||
BASE58_KEY=$(solana-keygen recover --force --outfile /dev/stdout "$KEYPAIR_PATH" 2>/dev/null | head -1)
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo "IMPORTANT: Add these to your backend .env file:"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
echo "SOLANA_MINT_AUTHORITY_KEYPAIR=$BASE58_KEY"
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Build Anchor program
|
||||
echo "Building Anchor program..."
|
||||
cd ../anchor-program
|
||||
|
||||
if command -v anchor &> /dev/null; then
|
||||
anchor build
|
||||
echo "✅ Program built successfully"
|
||||
|
||||
echo ""
|
||||
echo "To deploy the program, run:"
|
||||
echo " cd anchor-program"
|
||||
echo " anchor deploy"
|
||||
else
|
||||
echo "⚠️ Anchor not installed. Install with:"
|
||||
echo " npm install -g @coral-xyz/anchor-cli"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ Setup complete!"
|
||||
Reference in New Issue
Block a user