Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5880ebdf6 |
@@ -78,6 +78,7 @@ RUN cd nostr_core_lib && \
|
||||
./build.sh --nips=1,6,13,17,19,42,44,59
|
||||
|
||||
# Copy web interface files for embedding
|
||||
# Note: Changes to api/ files will trigger rebuild from this point
|
||||
COPY api/ /build/api/
|
||||
COPY scripts/embed_web_files.sh /build/scripts/
|
||||
|
||||
@@ -87,6 +88,8 @@ RUN mkdir -p src && \
|
||||
./scripts/embed_web_files.sh
|
||||
|
||||
# Copy Ginxsom source files LAST (only this layer rebuilds on source changes)
|
||||
# Note: The embedded header from previous step will be overwritten by this COPY
|
||||
# So we need to ensure src/admin_interface_embedded.h is NOT in src/ directory
|
||||
COPY src/ /build/src/
|
||||
COPY include/ /build/include/
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Blossom Admin</title>
|
||||
<title>Blossom Admin 2</title>
|
||||
<link rel="stylesheet" href="/api/index.css">
|
||||
</head>
|
||||
|
||||
@@ -100,6 +100,10 @@
|
||||
<td>Total Size</td>
|
||||
<td id="total-size">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td id="version">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Process ID</td>
|
||||
<td id="process-id">-</td>
|
||||
@@ -116,6 +120,14 @@
|
||||
<td>CPU Usage</td>
|
||||
<td id="cpu-usage">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Filesystem Blob Count</td>
|
||||
<td id="fs-blob-count">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Filesystem Blob Size</td>
|
||||
<td id="fs-blob-size">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Oldest Blob</td>
|
||||
<td id="oldest-event">-</td>
|
||||
|
||||
30
api/index.js
30
api/index.js
@@ -4143,12 +4143,32 @@ function populateStatsOverview(data) {
|
||||
if (!data) return;
|
||||
|
||||
// Update individual cells with flash animation for changed values
|
||||
// Backend sends: total_bytes, total_blobs, first_upload, last_upload
|
||||
// Backend sends: total_bytes, total_blobs, first_upload, last_upload, version, process_id, memory_mb, cpu_core, fs_blob_count, fs_blob_size_mb
|
||||
updateStatsCell('db-size', data.total_bytes ? formatFileSize(data.total_bytes) : '-');
|
||||
updateStatsCell('total-size', data.total_bytes ? formatFileSize(data.total_bytes) : '-');
|
||||
updateStatsCell('total-events', data.total_blobs || '-');
|
||||
updateStatsCell('oldest-event', data.first_upload ? formatTimestamp(data.first_upload) : '-');
|
||||
updateStatsCell('newest-event', data.last_upload ? formatTimestamp(data.last_upload) : '-');
|
||||
|
||||
// System metrics from system table
|
||||
if (data.version) {
|
||||
updateStatsCell('version', data.version);
|
||||
}
|
||||
if (data.process_id) {
|
||||
updateStatsCell('process-id', data.process_id);
|
||||
}
|
||||
if (data.memory_mb) {
|
||||
updateStatsCell('memory-usage', data.memory_mb + ' MB');
|
||||
}
|
||||
if (data.cpu_core) {
|
||||
updateStatsCell('cpu-core', 'Core ' + data.cpu_core);
|
||||
}
|
||||
if (data.fs_blob_count !== undefined) {
|
||||
updateStatsCell('fs-blob-count', data.fs_blob_count);
|
||||
}
|
||||
if (data.fs_blob_size_mb !== undefined) {
|
||||
updateStatsCell('fs-blob-size', data.fs_blob_size_mb + ' MB');
|
||||
}
|
||||
}
|
||||
|
||||
// Populate event kinds distribution table
|
||||
@@ -4526,6 +4546,14 @@ function updateStatsCell(cellId, newValue) {
|
||||
// Start polling for statistics (every 10 seconds)
|
||||
function startStatsPolling() {
|
||||
console.log('=== STARTING STATISTICS POLLING ===');
|
||||
|
||||
// Initialize the event rate chart if not already initialized
|
||||
if (!eventRateChart) {
|
||||
console.log('Initializing event rate chart from startStatsPolling...');
|
||||
setTimeout(() => {
|
||||
initializeEventRateChart();
|
||||
}, 1000); // Delay to ensure text_graph.js is loaded
|
||||
}
|
||||
console.log('Current page:', currentPage);
|
||||
console.log('Is logged in:', isLoggedIn);
|
||||
console.log('User pubkey:', userPubkey);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
495
deploy_lt.sh
495
deploy_lt.sh
@@ -22,278 +22,226 @@ fi
|
||||
# Configuration
|
||||
REMOTE_HOST="laantungir.net"
|
||||
REMOTE_USER="ubuntu"
|
||||
REMOTE_DIR="/home/ubuntu/ginxsom"
|
||||
REMOTE_DB_PATH="/home/ubuntu/ginxsom/db/ginxsom.db"
|
||||
REMOTE_NGINX_CONFIG="/etc/nginx/conf.d/default.conf"
|
||||
REMOTE_BINARY_PATH="/home/ubuntu/ginxsom/ginxsom.fcgi"
|
||||
|
||||
# Deployment paths
|
||||
REMOTE_BINARY_DIR="/usr/local/bin/ginxsom"
|
||||
REMOTE_BINARY_PATH="$REMOTE_BINARY_DIR/ginxsom-fcgi"
|
||||
REMOTE_DB_PATH="$REMOTE_BINARY_DIR"
|
||||
REMOTE_BLOB_DIR="/var/www/blobs"
|
||||
REMOTE_SOCKET="/tmp/ginxsom-fcgi.sock"
|
||||
REMOTE_DATA_DIR="/var/www/html/blossom"
|
||||
|
||||
print_status "Starting deployment to $REMOTE_HOST..."
|
||||
# Production keys
|
||||
ADMIN_PUBKEY="1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139"
|
||||
SERVER_PRIVKEY="90df3fe61e7d19e50f387e4c5db87eff1a7d2a1037cd55026c4b21a4fda8ecf6"
|
||||
|
||||
# Step 1: Build and prepare local binary
|
||||
print_status "Building ginxsom binary..."
|
||||
make clean && make
|
||||
if [[ ! -f "build/ginxsom-fcgi" ]]; then
|
||||
print_error "Build failed - binary not found"
|
||||
# Local paths
|
||||
LOCAL_BINARY="build/ginxsom-fcgi_static_x86_64"
|
||||
|
||||
print_status "=========================================="
|
||||
print_status "Ginxsom Static Binary Deployment"
|
||||
print_status "=========================================="
|
||||
print_status "Target: $REMOTE_HOST"
|
||||
print_status "Binary: $REMOTE_BINARY_PATH"
|
||||
print_status "Database: $REMOTE_DB_PATH"
|
||||
print_status "Blobs: $REMOTE_BLOB_DIR"
|
||||
print_status "Fresh install: $FRESH_INSTALL"
|
||||
print_status "=========================================="
|
||||
echo ""
|
||||
|
||||
# Step 1: Verify local binary exists
|
||||
print_status "Step 1: Verifying local static binary..."
|
||||
if [[ ! -f "$LOCAL_BINARY" ]]; then
|
||||
print_error "Static binary not found: $LOCAL_BINARY"
|
||||
print_status "Please run: ./build_static.sh"
|
||||
exit 1
|
||||
fi
|
||||
print_success "Binary built successfully"
|
||||
|
||||
# Step 2: Setup remote environment first (before copying files)
|
||||
print_status "Setting up remote environment..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
# Verify it's actually static
|
||||
if ldd "$LOCAL_BINARY" 2>&1 | grep -q "not a dynamic executable\|statically linked"; then
|
||||
print_success "Binary is static"
|
||||
else
|
||||
print_warning "Binary may not be fully static - proceeding anyway"
|
||||
fi
|
||||
|
||||
BINARY_SIZE=$(du -h "$LOCAL_BINARY" | cut -f1)
|
||||
print_success "Found static binary ($BINARY_SIZE)"
|
||||
echo ""
|
||||
|
||||
# Step 2: Upload binary to server
|
||||
print_status "Step 2: Uploading binary to server..."
|
||||
scp "$LOCAL_BINARY" $REMOTE_USER@$REMOTE_HOST:~/ginxsom-fcgi_new || {
|
||||
print_error "Failed to upload binary"
|
||||
exit 1
|
||||
}
|
||||
print_success "Binary uploaded to ~/ginxsom-fcgi_new"
|
||||
echo ""
|
||||
|
||||
# Step 3: Setup directories
|
||||
print_status "Step 3: Setting up directories..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
# Create data directory if it doesn't exist (using existing /var/www/html/blossom)
|
||||
sudo mkdir -p /var/www/html/blossom
|
||||
sudo chown www-data:www-data /var/www/html/blossom
|
||||
sudo chmod 755 /var/www/html/blossom
|
||||
|
||||
# Ensure socket directory exists
|
||||
sudo mkdir -p /tmp
|
||||
sudo chmod 755 /tmp
|
||||
|
||||
# Install required dependencies
|
||||
echo "Installing required dependencies..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y spawn-fcgi libfcgi-dev
|
||||
|
||||
# Stop any existing ginxsom processes
|
||||
echo "Stopping existing ginxsom processes..."
|
||||
sudo pkill -f ginxsom-fcgi || true
|
||||
sudo rm -f /tmp/ginxsom-fcgi.sock || true
|
||||
|
||||
echo "Remote environment setup complete"
|
||||
EOF
|
||||
|
||||
print_success "Remote environment configured"
|
||||
|
||||
# Step 3: Copy files to remote server
|
||||
print_status "Copying files to remote server..."
|
||||
|
||||
# Copy entire project directory (excluding unnecessary files)
|
||||
# Note: We include .git and .gitmodules to allow submodule initialization on remote
|
||||
print_status "Copying entire ginxsom project..."
|
||||
rsync -avz --exclude='build' --exclude='logs' --exclude='Trash' --exclude='blobs' --exclude='db' --no-g --no-o --no-perms --omit-dir-times . $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/
|
||||
|
||||
# Initialize git submodules on remote server
|
||||
print_status "Initializing git submodules on remote server..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
cd /home/ubuntu/ginxsom
|
||||
|
||||
# Check if .git exists
|
||||
if [ ! -d .git ]; then
|
||||
echo "ERROR: .git directory not found - git repository not copied"
|
||||
exit 1
|
||||
fi
|
||||
# Create binary/database directory
|
||||
echo "Creating application directory..."
|
||||
sudo mkdir -p $REMOTE_BINARY_DIR
|
||||
sudo chown www-data:www-data $REMOTE_BINARY_DIR
|
||||
sudo chmod 755 $REMOTE_BINARY_DIR
|
||||
|
||||
# Check if .gitmodules exists
|
||||
if [ ! -f .gitmodules ]; then
|
||||
echo "ERROR: .gitmodules file not found"
|
||||
exit 1
|
||||
fi
|
||||
# Create blob storage directory
|
||||
echo "Creating blob storage directory..."
|
||||
sudo mkdir -p $REMOTE_BLOB_DIR
|
||||
sudo chown www-data:www-data $REMOTE_BLOB_DIR
|
||||
sudo chmod 755 $REMOTE_BLOB_DIR
|
||||
|
||||
echo "Initializing git submodules..."
|
||||
git submodule update --init --recursive
|
||||
# Create logs directory
|
||||
echo "Creating logs directory..."
|
||||
sudo mkdir -p $REMOTE_BINARY_DIR/logs/app
|
||||
sudo chown -R www-data:www-data $REMOTE_BINARY_DIR/logs
|
||||
sudo chmod -R 755 $REMOTE_BINARY_DIR/logs
|
||||
|
||||
# Verify submodule was initialized
|
||||
if [ ! -f nostr_core_lib/cjson/cJSON.h ]; then
|
||||
echo "ERROR: Submodule initialization failed - cJSON.h not found"
|
||||
echo "Checking nostr_core_lib directory:"
|
||||
ls -la nostr_core_lib/ || echo "nostr_core_lib directory not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Submodules initialized successfully"
|
||||
|
||||
# Build nostr_core_lib
|
||||
echo "Building nostr_core_lib..."
|
||||
cd nostr_core_lib
|
||||
./build.sh
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Failed to build nostr_core_lib"
|
||||
exit 1
|
||||
fi
|
||||
echo "nostr_core_lib built successfully"
|
||||
echo "Directories created successfully"
|
||||
EOF
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
print_error "Failed to initialize git submodules or build nostr_core_lib"
|
||||
print_error "Failed to create directories"
|
||||
exit 1
|
||||
fi
|
||||
print_success "Directories created"
|
||||
echo ""
|
||||
|
||||
# Build on remote server to ensure compatibility
|
||||
print_status "Building ginxsom on remote server..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST "cd $REMOTE_DIR && make clean && make" || {
|
||||
print_error "Build failed on remote server"
|
||||
print_status "Checking what packages are actually installed..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST "dpkg -l | grep -E '(sqlite|fcgi)'"
|
||||
# Step 4: Handle fresh install if requested
|
||||
if [ "$FRESH_INSTALL" = true ]; then
|
||||
print_status "Step 4: Fresh install - removing existing data..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
sudo rm -f $REMOTE_DB_PATH/*.db
|
||||
sudo rm -rf $REMOTE_BLOB_DIR/*
|
||||
echo "Existing data removed"
|
||||
EOF
|
||||
print_success "Fresh install prepared"
|
||||
echo ""
|
||||
else
|
||||
print_status "Step 4: Preserving existing data"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Step 5: Install minimal dependencies
|
||||
print_status "Step 5: Installing minimal dependencies..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
set -e
|
||||
|
||||
# Check if spawn-fcgi is installed
|
||||
if ! command -v spawn-fcgi &> /dev/null; then
|
||||
echo "Installing spawn-fcgi..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y spawn-fcgi
|
||||
echo "spawn-fcgi installed"
|
||||
else
|
||||
echo "spawn-fcgi already installed"
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Dependencies verified"
|
||||
else
|
||||
print_error "Failed to install dependencies"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 6: Upload and install systemd service file
|
||||
print_status "Step 6: Installing systemd service file..."
|
||||
scp ginxsom.service $REMOTE_USER@$REMOTE_HOST:~/ginxsom.service || {
|
||||
print_error "Failed to upload service file"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Copy binary to application directory
|
||||
print_status "Copying ginxsom binary to application directory..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
# Stop any running process first
|
||||
sudo pkill -f ginxsom-fcgi || true
|
||||
sleep 1
|
||||
|
||||
# Remove old binary if it exists
|
||||
rm -f $REMOTE_BINARY_PATH
|
||||
|
||||
# Copy new binary
|
||||
cp $REMOTE_DIR/build/ginxsom-fcgi $REMOTE_BINARY_PATH
|
||||
chmod +x $REMOTE_BINARY_PATH
|
||||
chown ubuntu:ubuntu $REMOTE_BINARY_PATH
|
||||
|
||||
echo "Binary copied successfully"
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
sudo cp ~/ginxsom.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
echo "Service file installed"
|
||||
EOF
|
||||
|
||||
# NOTE: Do NOT update nginx configuration automatically
|
||||
# The deployment script should only update ginxsom binaries and do nothing else with the system
|
||||
# Nginx configuration should be managed manually by the system administrator
|
||||
print_status "Skipping nginx configuration update (manual control required)"
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Service file installed"
|
||||
else
|
||||
print_error "Failed to install service file"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
print_success "Files copied to remote server"
|
||||
|
||||
# Step 3: Setup remote environment
|
||||
print_status "Setting up remote environment..."
|
||||
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
# Step 7: Stop existing service and install new binary
|
||||
print_status "Step 7: Stopping existing service and installing new binary..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
# Create data directory if it doesn't exist (using existing /var/www/html/blossom)
|
||||
sudo mkdir -p /var/www/html/blossom
|
||||
sudo chown www-data:www-data /var/www/html/blossom
|
||||
sudo chmod 755 /var/www/html/blossom
|
||||
|
||||
# Ensure socket directory exists
|
||||
sudo mkdir -p /tmp
|
||||
sudo chmod 755 /tmp
|
||||
|
||||
# Install required dependencies
|
||||
echo "Installing required dependencies..."
|
||||
sudo apt-get update 2>/dev/null || true # Continue even if apt update has issues
|
||||
sudo apt-get install -y spawn-fcgi libfcgi-dev libsqlite3-dev sqlite3 libcurl4-openssl-dev
|
||||
|
||||
# Verify installations
|
||||
echo "Verifying installations..."
|
||||
if ! dpkg -l libsqlite3-dev >/dev/null 2>&1; then
|
||||
echo "libsqlite3-dev not found, trying alternative..."
|
||||
sudo apt-get install -y libsqlite3-dev || {
|
||||
echo "Failed to install libsqlite3-dev"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
if ! dpkg -l libfcgi-dev >/dev/null 2>&1; then
|
||||
echo "libfcgi-dev not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if sqlite3.h exists
|
||||
if [ ! -f /usr/include/sqlite3.h ]; then
|
||||
echo "sqlite3.h not found in /usr/include/"
|
||||
find /usr -name "sqlite3.h" 2>/dev/null || echo "sqlite3.h not found anywhere"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Stop any existing ginxsom processes
|
||||
echo "Stopping existing ginxsom processes..."
|
||||
sudo pkill -f ginxsom-fcgi || true
|
||||
sudo rm -f /tmp/ginxsom-fcgi.sock || true
|
||||
|
||||
echo "Remote environment setup complete"
|
||||
EOF
|
||||
|
||||
print_success "Remote environment configured"
|
||||
|
||||
# Step 4: Setup database directory and migrate database
|
||||
print_status "Setting up database directory..."
|
||||
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
# Create db directory if it doesn't exist
|
||||
mkdir -p $REMOTE_DIR/db
|
||||
|
||||
if [ "$FRESH_INSTALL" = "true" ]; then
|
||||
echo "Fresh install: removing existing database and blobs..."
|
||||
# Remove existing database
|
||||
sudo rm -f $REMOTE_DB_PATH
|
||||
sudo rm -f /var/www/html/blossom/ginxsom.db
|
||||
# Remove existing blobs
|
||||
sudo rm -rf $REMOTE_DATA_DIR/*
|
||||
echo "Existing data removed"
|
||||
else
|
||||
# Backup current database if it exists in old location
|
||||
if [ -f /var/www/html/blossom/ginxsom.db ]; then
|
||||
echo "Backing up existing database..."
|
||||
cp /var/www/html/blossom/ginxsom.db /var/www/html/blossom/ginxsom.db.backup.\$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# Migrate database to new location if not already there
|
||||
if [ ! -f $REMOTE_DB_PATH ]; then
|
||||
echo "Migrating database to new location..."
|
||||
cp /var/www/html/blossom/ginxsom.db $REMOTE_DB_PATH
|
||||
else
|
||||
echo "Database already exists at new location"
|
||||
fi
|
||||
elif [ ! -f $REMOTE_DB_PATH ]; then
|
||||
echo "No existing database found - will be created on first run"
|
||||
else
|
||||
echo "Database already exists at $REMOTE_DB_PATH"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set proper permissions - www-data needs write access to db directory for SQLite journal files
|
||||
sudo chown -R www-data:www-data $REMOTE_DIR/db
|
||||
sudo chmod 755 $REMOTE_DIR/db
|
||||
sudo chmod 644 $REMOTE_DB_PATH 2>/dev/null || true
|
||||
|
||||
# Allow www-data to access the application directory for spawn-fcgi chdir
|
||||
chmod 755 $REMOTE_DIR
|
||||
|
||||
echo "Database directory setup complete"
|
||||
EOF
|
||||
|
||||
print_success "Database directory configured"
|
||||
|
||||
# Step 5: Start ginxsom FastCGI process
|
||||
print_status "Starting ginxsom FastCGI process..."
|
||||
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
# Clean up any existing socket
|
||||
sleep 2
|
||||
|
||||
# Remove old socket
|
||||
sudo rm -f $REMOTE_SOCKET
|
||||
|
||||
# Install new binary
|
||||
echo "Installing new binary..."
|
||||
sudo mv ~/ginxsom-fcgi_new $REMOTE_BINARY_PATH
|
||||
sudo chmod +x $REMOTE_BINARY_PATH
|
||||
sudo chown www-data:www-data $REMOTE_BINARY_PATH
|
||||
|
||||
echo "Binary installed successfully"
|
||||
EOF
|
||||
|
||||
# Start FastCGI process with explicit paths
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Binary installed"
|
||||
else
|
||||
print_error "Failed to install binary"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 8: Start ginxsom FastCGI process
|
||||
print_status "Step 8: Starting ginxsom service..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
echo "Starting ginxsom FastCGI with configuration:"
|
||||
echo " Working directory: $REMOTE_DIR"
|
||||
echo " Binary: $REMOTE_BINARY_PATH"
|
||||
echo " Database: $REMOTE_DB_PATH"
|
||||
echo " Storage: $REMOTE_DATA_DIR"
|
||||
echo " Storage: $REMOTE_BLOB_DIR"
|
||||
echo " Socket: $REMOTE_SOCKET"
|
||||
echo ""
|
||||
|
||||
sudo spawn-fcgi \
|
||||
-M 666 \
|
||||
-u www-data \
|
||||
-g www-data \
|
||||
-s $REMOTE_SOCKET \
|
||||
-U www-data \
|
||||
-G www-data \
|
||||
-d $REMOTE_BINARY_DIR \
|
||||
-- $REMOTE_BINARY_PATH \
|
||||
--admin-pubkey $ADMIN_PUBKEY \
|
||||
--server-privkey $SERVER_PRIVKEY \
|
||||
--db-path $REMOTE_DB_PATH \
|
||||
--storage-dir $REMOTE_BLOB_DIR
|
||||
|
||||
sudo spawn-fcgi -M 666 -u www-data -g www-data -s $REMOTE_SOCKET -U www-data -G www-data -d $REMOTE_DIR -- $REMOTE_BINARY_PATH --db-path "$REMOTE_DB_PATH" --storage-dir "$REMOTE_DATA_DIR"
|
||||
|
||||
# Give it a moment to start
|
||||
sleep 2
|
||||
|
||||
|
||||
# Verify process is running
|
||||
if pgrep -f "ginxsom-fcgi" > /dev/null; then
|
||||
echo "FastCGI process started successfully"
|
||||
echo "PID: \$(pgrep -f ginxsom-fcgi)"
|
||||
else
|
||||
echo "Process not found by pgrep, but socket exists - this may be normal for FastCGI"
|
||||
echo "Checking socket..."
|
||||
if [ -S $REMOTE_SOCKET ]; then
|
||||
echo "FastCGI socket created successfully"
|
||||
ls -la $REMOTE_SOCKET
|
||||
echo "Checking if binary exists and is executable..."
|
||||
ls -la $REMOTE_BINARY_PATH
|
||||
echo "Testing if we can connect to the socket..."
|
||||
# Try to test the FastCGI connection
|
||||
if command -v cgi-fcgi >/dev/null 2>&1; then
|
||||
echo "Testing FastCGI connection..."
|
||||
SCRIPT_NAME=/health SCRIPT_FILENAME=$REMOTE_BINARY_PATH REQUEST_METHOD=GET cgi-fcgi -bind -connect $REMOTE_SOCKET 2>/dev/null | head -5 || echo "Connection test failed"
|
||||
else
|
||||
echo "cgi-fcgi not available for testing"
|
||||
fi
|
||||
# Don't exit - the socket existing means spawn-fcgi worked
|
||||
else
|
||||
echo "ERROR: Socket not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if process is running
|
||||
if pgrep -f ginxsom-fcgi > /dev/null; then
|
||||
echo "Process is running (PID: \$(pgrep -f ginxsom-fcgi))"
|
||||
else
|
||||
echo "WARNING: Process not found by pgrep (may be normal for FastCGI)"
|
||||
fi
|
||||
EOF
|
||||
|
||||
@@ -303,51 +251,84 @@ else
|
||||
print_error "Failed to start FastCGI process"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 6: Test nginx configuration and reload
|
||||
print_status "Testing and reloading nginx..."
|
||||
|
||||
# Step 8: Test nginx configuration and reload
|
||||
print_status "Step 8: Testing and reloading nginx..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
# Test nginx configuration
|
||||
if sudo nginx -t; then
|
||||
if sudo nginx -t 2>&1; then
|
||||
echo "Nginx configuration test passed"
|
||||
sudo nginx -s reload
|
||||
echo "Nginx reloaded successfully"
|
||||
else
|
||||
echo "Nginx configuration test failed"
|
||||
exit 1
|
||||
echo "WARNING: Nginx configuration test failed"
|
||||
echo "You may need to update nginx configuration manually"
|
||||
echo "See docs/STATIC_DEPLOYMENT_PLAN.md for details"
|
||||
fi
|
||||
EOF
|
||||
|
||||
print_success "Nginx reloaded"
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Nginx reloaded"
|
||||
else
|
||||
print_warning "Nginx reload had issues - check configuration"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 7: Test deployment
|
||||
print_status "Testing deployment..."
|
||||
# Step 9: Test deployment
|
||||
print_status "Step 9: Testing deployment..."
|
||||
echo ""
|
||||
|
||||
# Wait a moment for service to fully start
|
||||
sleep 2
|
||||
|
||||
# Test health endpoint
|
||||
echo "Testing health endpoint..."
|
||||
if curl -k -s --max-time 10 "https://blossom.laantungir.net/health" | grep -q "OK"; then
|
||||
print_success "Health check passed"
|
||||
print_success "✓ Health check passed"
|
||||
else
|
||||
print_warning "Health check failed - checking response..."
|
||||
print_warning "✗ Health check failed - checking response..."
|
||||
curl -k -v --max-time 10 "https://blossom.laantungir.net/health" 2>&1 | head -10
|
||||
fi
|
||||
|
||||
# Test basic endpoints
|
||||
# Test root endpoint
|
||||
echo ""
|
||||
echo "Testing root endpoint..."
|
||||
if curl -k -s --max-time 10 "https://blossom.laantungir.net/" | grep -q "Ginxsom"; then
|
||||
print_success "Root endpoint responding"
|
||||
print_success "✓ Root endpoint responding"
|
||||
else
|
||||
print_warning "Root endpoint not responding as expected - checking response..."
|
||||
curl -k -v --max-time 10 "https://blossom.laantungir.net/" 2>&1 | head -10
|
||||
print_warning "✗ Root endpoint not responding as expected"
|
||||
fi
|
||||
|
||||
print_success "Deployment to $REMOTE_HOST completed!"
|
||||
print_status "Ginxsom should now be available at: https://blossom.laantungir.net"
|
||||
print_status "Test endpoints:"
|
||||
echo ""
|
||||
print_status "=========================================="
|
||||
print_success "Deployment completed!"
|
||||
print_status "=========================================="
|
||||
echo ""
|
||||
print_status "Service Information:"
|
||||
echo " URL: https://blossom.laantungir.net"
|
||||
echo " Binary: $REMOTE_BINARY_PATH"
|
||||
echo " Database: $REMOTE_DB_PATH"
|
||||
echo " Blobs: $REMOTE_BLOB_DIR"
|
||||
echo " Socket: $REMOTE_SOCKET"
|
||||
echo ""
|
||||
print_status "Test Commands:"
|
||||
echo " Health: curl -k https://blossom.laantungir.net/health"
|
||||
echo " Root: curl -k https://blossom.laantungir.net/"
|
||||
echo " List: curl -k https://blossom.laantungir.net/list"
|
||||
if [ "$FRESH_INSTALL" = "true" ]; then
|
||||
echo " Info: curl -k https://blossom.laantungir.net/"
|
||||
echo " Upload: ./tests/file_put_bud02.sh"
|
||||
echo ""
|
||||
print_status "Server Commands:"
|
||||
echo " Check status: ssh $REMOTE_USER@$REMOTE_HOST 'ps aux | grep ginxsom-fcgi'"
|
||||
echo " View logs: ssh $REMOTE_USER@$REMOTE_HOST 'sudo journalctl -f | grep ginxsom'"
|
||||
echo " Restart: ssh $REMOTE_USER@$REMOTE_HOST 'sudo pkill ginxsom-fcgi && sudo spawn-fcgi ...'"
|
||||
echo ""
|
||||
|
||||
if [ "$FRESH_INSTALL" = true ]; then
|
||||
print_warning "Fresh install completed - database and blobs have been reset"
|
||||
fi
|
||||
else
|
||||
print_status "Existing data preserved - verify database and blobs"
|
||||
echo " Check blobs: ssh $REMOTE_USER@$REMOTE_HOST 'ls -la $REMOTE_BLOB_DIR | wc -l'"
|
||||
echo " Check DB: ssh $REMOTE_USER@$REMOTE_HOST 'sudo -u www-data sqlite3 $REMOTE_DB_PATH \"SELECT COUNT(*) FROM blobs;\"'"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
388
docs/NEW_DEPLOY_SCRIPT.md
Normal file
388
docs/NEW_DEPLOY_SCRIPT.md
Normal file
@@ -0,0 +1,388 @@
|
||||
# New deploy_lt.sh Script
|
||||
|
||||
This is the complete new deployment script for static binary deployment. Save this as `deploy_lt.sh` in the project root.
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
# Parse command line arguments
|
||||
FRESH_INSTALL=false
|
||||
MIGRATE_DATA=true
|
||||
if [[ "$1" == "--fresh" ]]; then
|
||||
FRESH_INSTALL=true
|
||||
MIGRATE_DATA=false
|
||||
elif [[ "$1" == "--no-migrate" ]]; then
|
||||
MIGRATE_DATA=false
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
REMOTE_HOST="laantungir.net"
|
||||
REMOTE_USER="ubuntu"
|
||||
|
||||
# New paths (static binary deployment)
|
||||
REMOTE_BINARY_DIR="/usr/local/bin/ginxsom"
|
||||
REMOTE_BINARY_PATH="$REMOTE_BINARY_DIR/ginxsom-fcgi"
|
||||
REMOTE_DB_DIR="/var/lib/ginxsom"
|
||||
REMOTE_DB_PATH="$REMOTE_DB_DIR/ginxsom.db"
|
||||
REMOTE_BLOB_DIR="/var/www/blobs"
|
||||
REMOTE_SOCKET="/tmp/ginxsom-fcgi.sock"
|
||||
|
||||
# Old paths (for migration)
|
||||
OLD_BINARY_PATH="/home/ubuntu/ginxsom/ginxsom.fcgi"
|
||||
OLD_DB_PATH="/home/ubuntu/ginxsom/db/ginxsom.db"
|
||||
OLD_BLOB_DIR="/var/www/html/blossom"
|
||||
|
||||
# Local paths
|
||||
LOCAL_BINARY="build/ginxsom-fcgi_static_x86_64"
|
||||
|
||||
print_status "=========================================="
|
||||
print_status "Ginxsom Static Binary Deployment"
|
||||
print_status "=========================================="
|
||||
print_status "Target: $REMOTE_HOST"
|
||||
print_status "Binary: $REMOTE_BINARY_PATH"
|
||||
print_status "Database: $REMOTE_DB_PATH"
|
||||
print_status "Blobs: $REMOTE_BLOB_DIR"
|
||||
print_status "Fresh install: $FRESH_INSTALL"
|
||||
print_status "Migrate data: $MIGRATE_DATA"
|
||||
print_status "=========================================="
|
||||
echo ""
|
||||
|
||||
# Step 1: Verify local binary exists
|
||||
print_status "Step 1: Verifying local static binary..."
|
||||
if [[ ! -f "$LOCAL_BINARY" ]]; then
|
||||
print_error "Static binary not found: $LOCAL_BINARY"
|
||||
print_status "Please run: ./build_static.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify it's actually static
|
||||
if ldd "$LOCAL_BINARY" 2>&1 | grep -q "not a dynamic executable\|statically linked"; then
|
||||
print_success "Binary is static"
|
||||
else
|
||||
print_warning "Binary may not be fully static - proceeding anyway"
|
||||
fi
|
||||
|
||||
BINARY_SIZE=$(du -h "$LOCAL_BINARY" | cut -f1)
|
||||
print_success "Found static binary ($BINARY_SIZE)"
|
||||
echo ""
|
||||
|
||||
# Step 2: Upload binary to server
|
||||
print_status "Step 2: Uploading binary to server..."
|
||||
scp "$LOCAL_BINARY" $REMOTE_USER@$REMOTE_HOST:/tmp/ginxsom-fcgi_new || {
|
||||
print_error "Failed to upload binary"
|
||||
exit 1
|
||||
}
|
||||
print_success "Binary uploaded to /tmp/ginxsom-fcgi_new"
|
||||
echo ""
|
||||
|
||||
# Step 3: Setup directories and install binary
|
||||
print_status "Step 3: Setting up directories and installing binary..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
# Create binary directory
|
||||
echo "Creating binary directory..."
|
||||
sudo mkdir -p $REMOTE_BINARY_DIR
|
||||
|
||||
# Create database directory
|
||||
echo "Creating database directory..."
|
||||
sudo mkdir -p $REMOTE_DB_DIR/backups
|
||||
sudo chown www-data:www-data $REMOTE_DB_DIR
|
||||
sudo chmod 755 $REMOTE_DB_DIR
|
||||
|
||||
# Create blob storage directory
|
||||
echo "Creating blob storage directory..."
|
||||
sudo mkdir -p $REMOTE_BLOB_DIR
|
||||
sudo chown www-data:www-data $REMOTE_BLOB_DIR
|
||||
sudo chmod 755 $REMOTE_BLOB_DIR
|
||||
|
||||
echo "Directories created successfully"
|
||||
EOF
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
print_error "Failed to create directories"
|
||||
exit 1
|
||||
fi
|
||||
print_success "Directories created"
|
||||
echo ""
|
||||
|
||||
# Step 4: Migrate data if requested
|
||||
if [ "$MIGRATE_DATA" = true ] && [ "$FRESH_INSTALL" = false ]; then
|
||||
print_status "Step 4: Migrating existing data..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
# Migrate database
|
||||
if [ -f $OLD_DB_PATH ]; then
|
||||
echo "Migrating database from $OLD_DB_PATH..."
|
||||
sudo cp $OLD_DB_PATH $REMOTE_DB_PATH
|
||||
sudo chown www-data:www-data $REMOTE_DB_PATH
|
||||
sudo chmod 644 $REMOTE_DB_PATH
|
||||
echo "Database migrated"
|
||||
elif [ -f $OLD_BLOB_DIR/ginxsom.db ]; then
|
||||
echo "Migrating database from $OLD_BLOB_DIR/ginxsom.db..."
|
||||
sudo cp $OLD_BLOB_DIR/ginxsom.db $REMOTE_DB_PATH
|
||||
sudo chown www-data:www-data $REMOTE_DB_PATH
|
||||
sudo chmod 644 $REMOTE_DB_PATH
|
||||
echo "Database migrated"
|
||||
else
|
||||
echo "No existing database found - will be created on first run"
|
||||
fi
|
||||
|
||||
# Migrate blobs
|
||||
if [ -d $OLD_BLOB_DIR ] && [ "\$(ls -A $OLD_BLOB_DIR 2>/dev/null)" ]; then
|
||||
echo "Migrating blobs from $OLD_BLOB_DIR..."
|
||||
# Copy only blob files (SHA256 hashes with extensions)
|
||||
sudo find $OLD_BLOB_DIR -type f -regextype posix-extended -regex '.*/[a-f0-9]{64}\.[a-z0-9]+' -exec cp {} $REMOTE_BLOB_DIR/ \; 2>/dev/null || true
|
||||
sudo chown -R www-data:www-data $REMOTE_BLOB_DIR
|
||||
BLOB_COUNT=\$(ls -1 $REMOTE_BLOB_DIR | wc -l)
|
||||
echo "Migrated \$BLOB_COUNT blob files"
|
||||
else
|
||||
echo "No existing blobs found"
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Data migration completed"
|
||||
else
|
||||
print_warning "Data migration had issues - check manually"
|
||||
fi
|
||||
echo ""
|
||||
elif [ "$FRESH_INSTALL" = true ]; then
|
||||
print_status "Step 4: Fresh install - removing existing data..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
sudo rm -f $REMOTE_DB_PATH
|
||||
sudo rm -rf $REMOTE_BLOB_DIR/*
|
||||
echo "Existing data removed"
|
||||
EOF
|
||||
print_success "Fresh install prepared"
|
||||
echo ""
|
||||
else
|
||||
print_status "Step 4: Skipping data migration (--no-migrate)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Step 5: Install minimal dependencies
|
||||
print_status "Step 5: Installing minimal dependencies..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
set -e
|
||||
|
||||
# Check if spawn-fcgi is installed
|
||||
if ! command -v spawn-fcgi &> /dev/null; then
|
||||
echo "Installing spawn-fcgi..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y spawn-fcgi
|
||||
echo "spawn-fcgi installed"
|
||||
else
|
||||
echo "spawn-fcgi already installed"
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Dependencies verified"
|
||||
else
|
||||
print_error "Failed to install dependencies"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 6: Stop existing service and install new binary
|
||||
print_status "Step 6: Stopping existing service and installing new binary..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
# Stop any existing ginxsom processes
|
||||
echo "Stopping existing ginxsom processes..."
|
||||
sudo pkill -f ginxsom-fcgi || true
|
||||
sleep 2
|
||||
|
||||
# Remove old socket
|
||||
sudo rm -f $REMOTE_SOCKET
|
||||
|
||||
# Install new binary
|
||||
echo "Installing new binary..."
|
||||
sudo mv /tmp/ginxsom-fcgi_new $REMOTE_BINARY_PATH
|
||||
sudo chmod +x $REMOTE_BINARY_PATH
|
||||
sudo chown root:root $REMOTE_BINARY_PATH
|
||||
|
||||
echo "Binary installed successfully"
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Binary installed"
|
||||
else
|
||||
print_error "Failed to install binary"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 7: Start ginxsom FastCGI process
|
||||
print_status "Step 7: Starting ginxsom FastCGI process..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << EOF
|
||||
set -e
|
||||
|
||||
echo "Starting ginxsom FastCGI with configuration:"
|
||||
echo " Binary: $REMOTE_BINARY_PATH"
|
||||
echo " Database: $REMOTE_DB_PATH"
|
||||
echo " Storage: $REMOTE_BLOB_DIR"
|
||||
echo " Socket: $REMOTE_SOCKET"
|
||||
echo ""
|
||||
|
||||
sudo spawn-fcgi \
|
||||
-M 666 \
|
||||
-u www-data \
|
||||
-g www-data \
|
||||
-s $REMOTE_SOCKET \
|
||||
-U www-data \
|
||||
-G www-data \
|
||||
-d $REMOTE_DB_DIR \
|
||||
-- $REMOTE_BINARY_PATH \
|
||||
--db-path $REMOTE_DB_PATH \
|
||||
--storage-dir $REMOTE_BLOB_DIR
|
||||
|
||||
# Give it a moment to start
|
||||
sleep 2
|
||||
|
||||
# Verify process is running
|
||||
if [ -S $REMOTE_SOCKET ]; then
|
||||
echo "FastCGI socket created successfully"
|
||||
ls -la $REMOTE_SOCKET
|
||||
else
|
||||
echo "ERROR: Socket not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if process is running
|
||||
if pgrep -f ginxsom-fcgi > /dev/null; then
|
||||
echo "Process is running (PID: \$(pgrep -f ginxsom-fcgi))"
|
||||
else
|
||||
echo "WARNING: Process not found by pgrep (may be normal for FastCGI)"
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "FastCGI process started"
|
||||
else
|
||||
print_error "Failed to start FastCGI process"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 8: Test nginx configuration and reload
|
||||
print_status "Step 8: Testing and reloading nginx..."
|
||||
ssh $REMOTE_USER@$REMOTE_HOST << 'EOF'
|
||||
# Test nginx configuration
|
||||
if sudo nginx -t 2>&1; then
|
||||
echo "Nginx configuration test passed"
|
||||
sudo nginx -s reload
|
||||
echo "Nginx reloaded successfully"
|
||||
else
|
||||
echo "WARNING: Nginx configuration test failed"
|
||||
echo "You may need to update nginx configuration manually"
|
||||
echo "See docs/STATIC_DEPLOYMENT_PLAN.md for details"
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_success "Nginx reloaded"
|
||||
else
|
||||
print_warning "Nginx reload had issues - check configuration"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 9: Test deployment
|
||||
print_status "Step 9: Testing deployment..."
|
||||
echo ""
|
||||
|
||||
# Wait a moment for service to fully start
|
||||
sleep 2
|
||||
|
||||
# Test health endpoint
|
||||
echo "Testing health endpoint..."
|
||||
if curl -k -s --max-time 10 "https://blossom.laantungir.net/health" | grep -q "OK"; then
|
||||
print_success "✓ Health check passed"
|
||||
else
|
||||
print_warning "✗ Health check failed - checking response..."
|
||||
curl -k -v --max-time 10 "https://blossom.laantungir.net/health" 2>&1 | head -10
|
||||
fi
|
||||
|
||||
# Test root endpoint
|
||||
echo ""
|
||||
echo "Testing root endpoint..."
|
||||
if curl -k -s --max-time 10 "https://blossom.laantungir.net/" | grep -q "Ginxsom"; then
|
||||
print_success "✓ Root endpoint responding"
|
||||
else
|
||||
print_warning "✗ Root endpoint not responding as expected"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_status "=========================================="
|
||||
print_success "Deployment completed!"
|
||||
print_status "=========================================="
|
||||
echo ""
|
||||
print_status "Service Information:"
|
||||
echo " URL: https://blossom.laantungir.net"
|
||||
echo " Binary: $REMOTE_BINARY_PATH"
|
||||
echo " Database: $REMOTE_DB_PATH"
|
||||
echo " Blobs: $REMOTE_BLOB_DIR"
|
||||
echo " Socket: $REMOTE_SOCKET"
|
||||
echo ""
|
||||
print_status "Test Commands:"
|
||||
echo " Health: curl -k https://blossom.laantungir.net/health"
|
||||
echo " Info: curl -k https://blossom.laantungir.net/"
|
||||
echo " Upload: ./tests/file_put_bud02.sh"
|
||||
echo ""
|
||||
print_status "Server Commands:"
|
||||
echo " Check status: ssh $REMOTE_USER@$REMOTE_HOST 'ps aux | grep ginxsom-fcgi'"
|
||||
echo " View logs: ssh $REMOTE_USER@$REMOTE_HOST 'sudo journalctl -f | grep ginxsom'"
|
||||
echo " Restart: ssh $REMOTE_USER@$REMOTE_HOST 'sudo pkill ginxsom-fcgi && sudo spawn-fcgi ...'"
|
||||
echo ""
|
||||
|
||||
if [ "$FRESH_INSTALL" = true ]; then
|
||||
print_warning "Fresh install completed - database and blobs have been reset"
|
||||
fi
|
||||
|
||||
if [ "$MIGRATE_DATA" = true ] && [ "$FRESH_INSTALL" = false ]; then
|
||||
print_status "Data migration completed - verify blob count and database"
|
||||
echo " Check blobs: ssh $REMOTE_USER@$REMOTE_HOST 'ls -la $REMOTE_BLOB_DIR | wc -l'"
|
||||
echo " Check DB: ssh $REMOTE_USER@$REMOTE_HOST 'sudo -u www-data sqlite3 $REMOTE_DB_PATH \"SELECT COUNT(*) FROM blobs;\"'"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_status "For nginx configuration updates, see: docs/STATIC_DEPLOYMENT_PLAN.md"
|
||||
print_status "=========================================="
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Normal deployment with data migration
|
||||
./deploy_lt.sh
|
||||
|
||||
# Fresh install (removes all data)
|
||||
./deploy_lt.sh --fresh
|
||||
|
||||
# Deploy without migrating data
|
||||
./deploy_lt.sh --no-migrate
|
||||
```
|
||||
|
||||
## Key Changes from Old Script
|
||||
|
||||
1. **No remote compilation** - uploads pre-built static binary
|
||||
2. **New directory structure** - follows FHS standards
|
||||
3. **Minimal dependencies** - only spawn-fcgi needed
|
||||
4. **Data migration** - automatically migrates from old locations
|
||||
5. **Simplified process** - ~30 seconds vs ~5-10 minutes
|
||||
478
docs/NGINX_CONFIG_UPDATES.md
Normal file
478
docs/NGINX_CONFIG_UPDATES.md
Normal file
@@ -0,0 +1,478 @@
|
||||
# Nginx Configuration Updates for Static Binary Deployment
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the required nginx configuration changes to support the new static binary deployment with updated directory paths.
|
||||
|
||||
## Changes Required
|
||||
|
||||
### 1. Blob Storage Root Directory
|
||||
|
||||
**Change from:**
|
||||
```nginx
|
||||
root /var/www/html/blossom;
|
||||
```
|
||||
|
||||
**Change to:**
|
||||
```nginx
|
||||
root /var/www/blobs;
|
||||
```
|
||||
|
||||
### 2. FastCGI Script Filename
|
||||
|
||||
**Change from:**
|
||||
```nginx
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
```
|
||||
|
||||
**Change to:**
|
||||
```nginx
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
```
|
||||
|
||||
## Complete Updated Configuration
|
||||
|
||||
Save this as `/etc/nginx/conf.d/default.conf` on the server (or update the existing file):
|
||||
|
||||
```nginx
|
||||
# FastCGI upstream configuration
|
||||
upstream ginxsom_backend {
|
||||
server unix:/tmp/ginxsom-fcgi.sock;
|
||||
}
|
||||
|
||||
# Main domains
|
||||
server {
|
||||
if ($host = laantungir.net) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
listen 80;
|
||||
server_name laantungir.com www.laantungir.com laantungir.net www.laantungir.net laantungir.org www.laantungir.org;
|
||||
|
||||
root /var/www/html;
|
||||
index index.html index.htm;
|
||||
# CORS for Nostr NIP-05 verification
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range" always;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /var/www/html;
|
||||
}
|
||||
}
|
||||
|
||||
# Main domains HTTPS - using the main certificate
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name laantungir.com www.laantungir.com laantungir.net www.laantungir.net laantungir.org www.laantungir.org;
|
||||
ssl_certificate /etc/letsencrypt/live/laantungir.net/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/laantungir.net/privkey.pem; # managed by Certbot
|
||||
|
||||
root /var/www/html;
|
||||
index index.html index.htm;
|
||||
# CORS for Nostr NIP-05 verification
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range" always;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /var/www/html;
|
||||
}
|
||||
}
|
||||
|
||||
# Blossom subdomains HTTP - redirect to HTTPS (keep for ACME)
|
||||
server {
|
||||
listen 80;
|
||||
server_name blossom.laantungir.net;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# Blossom subdomains HTTPS - ginxsom FastCGI
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name blossom.laantungir.net;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/git.laantungir.net/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/git.laantungir.net/privkey.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# CORS for Blossom protocol
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH" always;
|
||||
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Content-Length, Accept, Origin, User-Agent, DNT, Cache-Control, X-Mx-ReqToken, Keep-Alive, X-Requested-With, If-Modified-Since, *" always;
|
||||
add_header Access-Control-Max-Age 86400 always;
|
||||
|
||||
# UPDATED: Root directory for blob storage
|
||||
root /var/www/blobs;
|
||||
|
||||
# Maximum upload size
|
||||
client_max_body_size 100M;
|
||||
|
||||
# OPTIONS preflight handler
|
||||
if ($request_method = OPTIONS) {
|
||||
return 204;
|
||||
}
|
||||
|
||||
# PUT /upload - File uploads
|
||||
location = /upload {
|
||||
if ($request_method !~ ^(PUT|HEAD)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# GET /list/<pubkey> - List user blobs
|
||||
location ~ "^/list/([a-f0-9]{64})$" {
|
||||
if ($request_method !~ ^(GET)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# PUT /mirror - Mirror content
|
||||
location = /mirror {
|
||||
if ($request_method !~ ^(PUT)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# PUT /report - Report content
|
||||
location = /report {
|
||||
if ($request_method !~ ^(PUT)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# GET /auth - NIP-42 challenges
|
||||
location = /auth {
|
||||
if ($request_method !~ ^(GET)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# Admin API
|
||||
location /api/ {
|
||||
if ($request_method !~ ^(GET|PUT)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# Blob serving - SHA256 patterns
|
||||
location ~ "^/([a-f0-9]{64})(\.[a-zA-Z0-9]+)?$" {
|
||||
# Handle DELETE via rewrite
|
||||
if ($request_method = DELETE) {
|
||||
rewrite ^/(.*)$ /fcgi-delete/$1 last;
|
||||
}
|
||||
|
||||
# Route HEAD to FastCGI
|
||||
if ($request_method = HEAD) {
|
||||
rewrite ^/(.*)$ /fcgi-head/$1 last;
|
||||
}
|
||||
|
||||
# GET requests - serve files directly
|
||||
if ($request_method != GET) {
|
||||
return 405;
|
||||
}
|
||||
|
||||
try_files /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404;
|
||||
|
||||
# Cache headers
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# Internal FastCGI handlers
|
||||
location ~ "^/fcgi-delete/([a-f0-9]{64}).*$" {
|
||||
internal;
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
}
|
||||
|
||||
location ~ "^/fcgi-head/([a-f0-9]{64}).*$" {
|
||||
internal;
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
}
|
||||
|
||||
# Health check
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "OK\n";
|
||||
add_header Content-Type text/plain;
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH" always;
|
||||
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Content-Length, Accept, Origin, User-Agent, DNT, Cache-Control, X-Mx-ReqToken, Keep-Alive, X-Requested-With, If-Modified-Since, *" always;
|
||||
add_header Access-Control-Max-Age 86400 always;
|
||||
}
|
||||
|
||||
# Default location - Server info from FastCGI
|
||||
location / {
|
||||
if ($request_method !~ ^(GET)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
# UPDATED: Direct path to binary
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name relay.laantungir.com relay.laantungir.net relay.laantungir.org;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8888;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
|
||||
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
gzip off;
|
||||
}
|
||||
}
|
||||
|
||||
# Relay HTTPS - proxy to c-relay
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name relay.laantungir.com relay.laantungir.net relay.laantungir.org;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/git.laantungir.net/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/git.laantungir.net/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8888;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
|
||||
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
gzip off;
|
||||
}
|
||||
}
|
||||
|
||||
# Git subdomains HTTP - redirect to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name git.laantungir.com git.laantungir.net git.laantungir.org;
|
||||
|
||||
# Allow larger file uploads for Git releases
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# Auth subdomains HTTP - redirect to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name auth.laantungir.com auth.laantungir.net auth.laantungir.org;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
}
|
||||
}
|
||||
|
||||
# Git subdomains HTTPS - proxy to gitea
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name git.laantungir.com git.laantungir.net git.laantungir.org;
|
||||
|
||||
# Allow larger file uploads for Git releases
|
||||
client_max_body_size 50M;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/git.laantungir.net/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/git.laantungir.net/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_connect_timeout 60s;
|
||||
gzip off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
|
||||
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
# Auth subdomains HTTPS - proxy to nostr-auth
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name auth.laantungir.com auth.laantungir.net auth.laantungir.org;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/git.laantungir.net/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/git.laantungir.net/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_connect_timeout 60s;
|
||||
gzip off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
|
||||
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Manual Update Steps
|
||||
|
||||
If you prefer to update the existing configuration manually:
|
||||
|
||||
```bash
|
||||
# 1. Backup current configuration
|
||||
ssh ubuntu@laantungir.net
|
||||
sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup
|
||||
|
||||
# 2. Edit the configuration
|
||||
sudo nano /etc/nginx/conf.d/default.conf
|
||||
|
||||
# 3. Find and replace (in the blossom server block):
|
||||
# - Change: root /var/www/html/blossom;
|
||||
# - To: root /var/www/blobs;
|
||||
|
||||
# 4. Find and replace (all FastCGI locations):
|
||||
# - Change: fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
# - To: fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
|
||||
# 5. Test configuration
|
||||
sudo nginx -t
|
||||
|
||||
# 6. If test passes, reload nginx
|
||||
sudo nginx -s reload
|
||||
|
||||
# 7. If test fails, restore backup
|
||||
sudo cp /etc/nginx/conf.d/default.conf.backup /etc/nginx/conf.d/default.conf
|
||||
sudo nginx -s reload
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
After updating the configuration:
|
||||
|
||||
```bash
|
||||
# Check nginx syntax
|
||||
sudo nginx -t
|
||||
|
||||
# Check if ginxsom is responding
|
||||
curl -k https://blossom.laantungir.net/health
|
||||
|
||||
# Check blob serving (if you have existing blobs)
|
||||
curl -k https://blossom.laantungir.net/<some-sha256-hash>.jpg
|
||||
```
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
| Item | Old Value | New Value |
|
||||
|------|-----------|-----------|
|
||||
| Blob root | `/var/www/html/blossom` | `/var/www/blobs` |
|
||||
| Binary path | `$document_root/ginxsom.fcgi` | `/usr/local/bin/ginxsom/ginxsom-fcgi` |
|
||||
| Binary location | `/home/ubuntu/ginxsom/ginxsom.fcgi` | `/usr/local/bin/ginxsom/ginxsom-fcgi` |
|
||||
|
||||
These changes align with the new static binary deployment architecture and Linux FHS standards.
|
||||
383
docs/STATIC_DEPLOYMENT_PLAN.md
Normal file
383
docs/STATIC_DEPLOYMENT_PLAN.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# Static MUSL Binary Deployment Plan
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the deployment architecture for ginxsom using static MUSL binaries. The new approach eliminates remote compilation and simplifies deployment to a single binary upload.
|
||||
|
||||
## Architecture Changes
|
||||
|
||||
### Current Deployment (Old)
|
||||
```
|
||||
Local Machine:
|
||||
- Build dynamic binary with make
|
||||
- Upload entire project via rsync
|
||||
- Remote server compiles from source
|
||||
- Install dependencies (libsqlite3-dev, libfcgi-dev, etc.)
|
||||
- Build nostr_core_lib submodules remotely
|
||||
- Binary location: /home/ubuntu/ginxsom/ginxsom.fcgi
|
||||
- Database: /home/ubuntu/ginxsom/db/ginxsom.db
|
||||
- Blobs: /var/www/html/blossom/
|
||||
```
|
||||
|
||||
### New Deployment (Static MUSL)
|
||||
```
|
||||
Local Machine:
|
||||
- Build static MUSL binary with Docker (build_static.sh)
|
||||
- Upload only the binary (no source code needed)
|
||||
- No remote compilation required
|
||||
- Minimal dependencies (only spawn-fcgi)
|
||||
- Binary location: /usr/local/bin/ginxsom/ginxsom-fcgi
|
||||
- Database: /var/lib/ginxsom/ginxsom.db
|
||||
- Blobs: /var/www/blobs/
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
### Production Server Layout
|
||||
```
|
||||
/usr/local/bin/ginxsom/
|
||||
├── ginxsom-fcgi # Static binary (executable)
|
||||
└── README.md # Version info and deployment notes
|
||||
|
||||
/var/lib/ginxsom/
|
||||
├── ginxsom.db # SQLite database
|
||||
└── backups/ # Database backups
|
||||
|
||||
/var/www/blobs/
|
||||
├── <sha256>.jpg # Blob files
|
||||
├── <sha256>.png
|
||||
└── ...
|
||||
|
||||
/tmp/
|
||||
└── ginxsom-fcgi.sock # FastCGI socket
|
||||
```
|
||||
|
||||
## Deployment Process
|
||||
|
||||
### Phase 1: Build Static Binary (Local)
|
||||
```bash
|
||||
# Build the static binary
|
||||
./build_static.sh
|
||||
|
||||
# Output: build/ginxsom-fcgi_static_x86_64
|
||||
# Size: ~7-10 MB
|
||||
# Dependencies: NONE (fully static)
|
||||
```
|
||||
|
||||
### Phase 2: Upload Binary
|
||||
```bash
|
||||
# Upload to server
|
||||
scp build/ginxsom-fcgi_static_x86_64 ubuntu@laantungir.net:/tmp/
|
||||
|
||||
# Install to /usr/local/bin/ginxsom/
|
||||
ssh ubuntu@laantungir.net << 'EOF'
|
||||
sudo mkdir -p /usr/local/bin/ginxsom
|
||||
sudo mv /tmp/ginxsom-fcgi_static_x86_64 /usr/local/bin/ginxsom/ginxsom-fcgi
|
||||
sudo chmod +x /usr/local/bin/ginxsom/ginxsom-fcgi
|
||||
sudo chown root:root /usr/local/bin/ginxsom/ginxsom-fcgi
|
||||
EOF
|
||||
```
|
||||
|
||||
### Phase 3: Setup Data Directories
|
||||
```bash
|
||||
ssh ubuntu@laantungir.net << 'EOF'
|
||||
# Create database directory
|
||||
sudo mkdir -p /var/lib/ginxsom/backups
|
||||
sudo chown www-data:www-data /var/lib/ginxsom
|
||||
sudo chmod 755 /var/lib/ginxsom
|
||||
|
||||
# Create blob storage directory
|
||||
sudo mkdir -p /var/www/blobs
|
||||
sudo chown www-data:www-data /var/www/blobs
|
||||
sudo chmod 755 /var/www/blobs
|
||||
|
||||
# Migrate existing data if needed
|
||||
if [ -f /var/www/html/blossom/ginxsom.db ]; then
|
||||
sudo cp /var/www/html/blossom/ginxsom.db /var/lib/ginxsom/
|
||||
sudo chown www-data:www-data /var/lib/ginxsom/ginxsom.db
|
||||
fi
|
||||
|
||||
if [ -d /var/www/html/blossom ]; then
|
||||
sudo cp -r /var/www/html/blossom/* /var/www/blobs/ 2>/dev/null || true
|
||||
sudo chown -R www-data:www-data /var/www/blobs
|
||||
fi
|
||||
EOF
|
||||
```
|
||||
|
||||
### Phase 4: Install Minimal Dependencies
|
||||
```bash
|
||||
ssh ubuntu@laantungir.net << 'EOF'
|
||||
# Only spawn-fcgi is needed (no build tools!)
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y spawn-fcgi
|
||||
EOF
|
||||
```
|
||||
|
||||
### Phase 5: Start Service
|
||||
```bash
|
||||
ssh ubuntu@laantungir.net << 'EOF'
|
||||
# Stop existing process
|
||||
sudo pkill -f ginxsom-fcgi || true
|
||||
sudo rm -f /tmp/ginxsom-fcgi.sock
|
||||
|
||||
# Start with spawn-fcgi
|
||||
sudo spawn-fcgi \
|
||||
-M 666 \
|
||||
-u www-data \
|
||||
-g www-data \
|
||||
-s /tmp/ginxsom-fcgi.sock \
|
||||
-U www-data \
|
||||
-G www-data \
|
||||
-d /var/lib/ginxsom \
|
||||
-- /usr/local/bin/ginxsom/ginxsom-fcgi \
|
||||
--db-path /var/lib/ginxsom/ginxsom.db \
|
||||
--storage-dir /var/www/blobs
|
||||
EOF
|
||||
```
|
||||
|
||||
## Nginx Configuration Updates
|
||||
|
||||
### Required Changes to `/etc/nginx/conf.d/default.conf`
|
||||
|
||||
```nginx
|
||||
# Blossom subdomains HTTPS - ginxsom FastCGI
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name blossom.laantungir.net;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/git.laantungir.net/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/git.laantungir.net/privkey.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# CORS for Blossom protocol
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH" always;
|
||||
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Content-Length, Accept, Origin, User-Agent, DNT, Cache-Control, X-Mx-ReqToken, Keep-Alive, X-Requested-With, If-Modified-Since, *" always;
|
||||
add_header Access-Control-Max-Age 86400 always;
|
||||
|
||||
# CHANGED: Root directory for blob storage
|
||||
root /var/www/blobs; # Was: /var/www/html/blossom
|
||||
|
||||
# Maximum upload size
|
||||
client_max_body_size 100M;
|
||||
|
||||
# ... rest of configuration remains the same ...
|
||||
|
||||
# CHANGED: Update SCRIPT_FILENAME references
|
||||
location = /upload {
|
||||
if ($request_method !~ ^(PUT|HEAD)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi; # Was: $document_root/ginxsom.fcgi
|
||||
}
|
||||
|
||||
# Apply same change to all other FastCGI locations...
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits of New Architecture
|
||||
|
||||
### 1. Simplified Deployment
|
||||
- **Before**: Upload source → Install deps → Build submodules → Compile → Deploy
|
||||
- **After**: Upload binary → Start service
|
||||
|
||||
### 2. Reduced Dependencies
|
||||
- **Before**: gcc, make, git, libsqlite3-dev, libfcgi-dev, libcurl4-openssl-dev, etc.
|
||||
- **After**: spawn-fcgi only
|
||||
|
||||
### 3. Better Security
|
||||
- No build tools on production server
|
||||
- No source code on production server
|
||||
- Smaller attack surface
|
||||
|
||||
### 4. Faster Deployments
|
||||
- **Before**: ~5-10 minutes (build time)
|
||||
- **After**: ~30 seconds (upload + restart)
|
||||
|
||||
### 5. Consistent Binaries
|
||||
- Same binary works on any Linux distribution
|
||||
- No "works on my machine" issues
|
||||
- Reproducible builds via Docker
|
||||
|
||||
### 6. Cleaner Organization
|
||||
- Binary in standard location (`/usr/local/bin/`)
|
||||
- Data in standard location (`/var/lib/`)
|
||||
- Blobs separate from web root (`/var/www/blobs/`)
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Option 1: In-Place Migration (Recommended)
|
||||
1. Build static binary locally
|
||||
2. Upload to `/tmp/`
|
||||
3. Stop current service
|
||||
4. Create new directories
|
||||
5. Migrate data
|
||||
6. Update nginx config
|
||||
7. Start new service
|
||||
8. Verify functionality
|
||||
9. Clean up old files
|
||||
|
||||
### Option 2: Blue-Green Deployment
|
||||
1. Setup new directories alongside old
|
||||
2. Deploy static binary
|
||||
3. Test on different port
|
||||
4. Switch nginx config
|
||||
5. Remove old deployment
|
||||
|
||||
### Option 3: Fresh Install
|
||||
1. Backup database and blobs
|
||||
2. Remove old installation
|
||||
3. Deploy static binary
|
||||
4. Restore data
|
||||
5. Configure nginx
|
||||
6. Start service
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues occur, rollback is simple:
|
||||
|
||||
```bash
|
||||
# Stop new service
|
||||
sudo pkill -f ginxsom-fcgi
|
||||
|
||||
# Restore old binary location
|
||||
sudo spawn-fcgi \
|
||||
-M 666 \
|
||||
-u www-data \
|
||||
-g www-data \
|
||||
-s /tmp/ginxsom-fcgi.sock \
|
||||
-U www-data \
|
||||
-G www-data \
|
||||
-d /home/ubuntu/ginxsom \
|
||||
-- /home/ubuntu/ginxsom/ginxsom.fcgi \
|
||||
--db-path /home/ubuntu/ginxsom/db/ginxsom.db \
|
||||
--storage-dir /var/www/html/blossom
|
||||
|
||||
# Revert nginx config
|
||||
sudo cp /etc/nginx/conf.d/default.conf.backup /etc/nginx/conf.d/default.conf
|
||||
sudo nginx -s reload
|
||||
```
|
||||
|
||||
## SystemD Service (Future Enhancement)
|
||||
|
||||
Create `/etc/systemd/system/ginxsom.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Ginxsom Blossom Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=www-data
|
||||
Group=www-data
|
||||
WorkingDirectory=/var/lib/ginxsom
|
||||
ExecStart=/usr/bin/spawn-fcgi \
|
||||
-M 666 \
|
||||
-u www-data \
|
||||
-g www-data \
|
||||
-s /tmp/ginxsom-fcgi.sock \
|
||||
-U www-data \
|
||||
-G www-data \
|
||||
-d /var/lib/ginxsom \
|
||||
-- /usr/local/bin/ginxsom/ginxsom-fcgi \
|
||||
--db-path /var/lib/ginxsom/ginxsom.db \
|
||||
--storage-dir /var/www/blobs
|
||||
ExecStop=/usr/bin/pkill -f ginxsom-fcgi
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Enable and start:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable ginxsom
|
||||
sudo systemctl start ginxsom
|
||||
sudo systemctl status ginxsom
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
After deployment, verify:
|
||||
|
||||
1. **Binary is static**:
|
||||
```bash
|
||||
ldd /usr/local/bin/ginxsom/ginxsom-fcgi
|
||||
# Should show: "not a dynamic executable"
|
||||
```
|
||||
|
||||
2. **Service is running**:
|
||||
```bash
|
||||
ps aux | grep ginxsom-fcgi
|
||||
ls -la /tmp/ginxsom-fcgi.sock
|
||||
```
|
||||
|
||||
3. **Health endpoint**:
|
||||
```bash
|
||||
curl -k https://blossom.laantungir.net/health
|
||||
# Should return: OK
|
||||
```
|
||||
|
||||
4. **Upload test**:
|
||||
```bash
|
||||
# Use existing test scripts
|
||||
./tests/file_put_bud02.sh
|
||||
```
|
||||
|
||||
5. **Database access**:
|
||||
```bash
|
||||
sudo -u www-data sqlite3 /var/lib/ginxsom/ginxsom.db "SELECT COUNT(*) FROM blobs;"
|
||||
```
|
||||
|
||||
6. **Blob storage**:
|
||||
```bash
|
||||
ls -la /var/www/blobs/ | head
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
Key metrics to monitor:
|
||||
|
||||
- Binary size: `du -h /usr/local/bin/ginxsom/ginxsom-fcgi`
|
||||
- Database size: `du -h /var/lib/ginxsom/ginxsom.db`
|
||||
- Blob storage: `du -sh /var/www/blobs/`
|
||||
- Process status: `systemctl status ginxsom` (if using systemd)
|
||||
- Socket status: `ls -la /tmp/ginxsom-fcgi.sock`
|
||||
|
||||
## Backup Strategy
|
||||
|
||||
### Database Backups
|
||||
```bash
|
||||
# Daily backup
|
||||
sudo -u www-data sqlite3 /var/lib/ginxsom/ginxsom.db ".backup /var/lib/ginxsom/backups/ginxsom-$(date +%Y%m%d).db"
|
||||
|
||||
# Keep last 7 days
|
||||
find /var/lib/ginxsom/backups/ -name "ginxsom-*.db" -mtime +7 -delete
|
||||
```
|
||||
|
||||
### Blob Backups
|
||||
```bash
|
||||
# Sync to backup location
|
||||
rsync -av /var/www/blobs/ /backup/ginxsom-blobs/
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
The static MUSL binary deployment provides:
|
||||
- ✅ Simpler deployment process
|
||||
- ✅ Fewer dependencies
|
||||
- ✅ Better security
|
||||
- ✅ Faster updates
|
||||
- ✅ Universal compatibility
|
||||
- ✅ Cleaner organization
|
||||
|
||||
This architecture follows Linux FHS (Filesystem Hierarchy Standard) best practices and provides a solid foundation for production deployment.
|
||||
25
ginxsom.service
Normal file
25
ginxsom.service
Normal file
@@ -0,0 +1,25 @@
|
||||
[Unit]
|
||||
Description=Ginxsom Blossom Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStartPre=/bin/rm -f /tmp/ginxsom-fcgi.sock
|
||||
ExecStart=/usr/bin/spawn-fcgi \
|
||||
-s /tmp/ginxsom-fcgi.sock \
|
||||
-M 666 \
|
||||
-u www-data \
|
||||
-g www-data \
|
||||
-d /usr/local/bin/ginxsom \
|
||||
-- /usr/local/bin/ginxsom/ginxsom-fcgi \
|
||||
--admin-pubkey 1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139 \
|
||||
--server-privkey 90df3fe61e7d19e50f387e4c5db87eff1a7d2a1037cd55026c4b21a4fda8ecf6 \
|
||||
--db-path /usr/local/bin/ginxsom \
|
||||
--storage-dir /var/www/blobs
|
||||
ExecStop=/usr/bin/pkill -f ginxsom-fcgi
|
||||
ExecStopPost=/bin/rm -f /tmp/ginxsom-fcgi.sock
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -97,7 +97,7 @@ server {
|
||||
add_header Access-Control-Max-Age 86400 always;
|
||||
|
||||
# Root directory for blob storage
|
||||
root /var/www/html/blossom;
|
||||
root /var/www/blobs;
|
||||
|
||||
# Maximum upload size
|
||||
client_max_body_size 100M;
|
||||
@@ -114,7 +114,7 @@ server {
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# GET /list/<pubkey> - List user blobs
|
||||
@@ -124,7 +124,7 @@ server {
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# PUT /mirror - Mirror content
|
||||
@@ -134,7 +134,7 @@ server {
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# PUT /report - Report content
|
||||
@@ -144,7 +144,7 @@ server {
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# GET /auth - NIP-42 challenges
|
||||
@@ -154,17 +154,17 @@ server {
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# Admin API
|
||||
location /api/ {
|
||||
if ($request_method !~ ^(GET|PUT)$) {
|
||||
if ($request_method !~ ^(GET|POST|PUT)$) {
|
||||
return 405;
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
|
||||
# Blob serving - SHA256 patterns
|
||||
@@ -195,7 +195,7 @@ server {
|
||||
internal;
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ server {
|
||||
internal;
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ server {
|
||||
}
|
||||
fastcgi_pass ginxsom_backend;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,10 @@ echo -e "${GREEN}FastCGI cleanup complete${NC}"
|
||||
|
||||
# Step 3: Always rebuild FastCGI binary with static build
|
||||
echo -e "\n${YELLOW}3. Rebuilding FastCGI binary (static build)...${NC}"
|
||||
echo "Cleaning old build artifacts to ensure fresh embedding..."
|
||||
make clean
|
||||
echo "Removing local embedded header to prevent Docker cache issues..."
|
||||
rm -f src/admin_interface_embedded.h
|
||||
echo "Building static binary with Docker..."
|
||||
make static
|
||||
if [ $? -ne 0 ]; then
|
||||
|
||||
@@ -547,7 +547,13 @@ static int handle_query_view_command(cJSON* command_array, cJSON* response_data)
|
||||
cJSON_AddStringToObject(response_data, "view_name", view_name);
|
||||
cJSON_AddItemToObject(response_data, "data", results);
|
||||
|
||||
app_log(LOG_DEBUG, "ADMIN_EVENT: Query view '%s' returned %d rows", view_name, cJSON_GetArraySize(results));
|
||||
// Debug: Log the complete response data
|
||||
char* debug_response = cJSON_Print(response_data);
|
||||
if (debug_response) {
|
||||
app_log(LOG_DEBUG, "ADMIN_EVENT: Query view '%s' returned %d rows. Full response: %s",
|
||||
view_name, cJSON_GetArraySize(results), debug_response);
|
||||
free(debug_response);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
static void serve_embedded_file(const unsigned char* data, size_t size, const char* content_type) {
|
||||
printf("Status: 200 OK\r\n");
|
||||
printf("Content-Type: %s\r\n", content_type);
|
||||
printf("Content-Length: %zu\r\n", size);
|
||||
printf("Content-Length: %lu\r\n", (unsigned long)size);
|
||||
printf("Cache-Control: public, max-age=3600\r\n");
|
||||
printf("\r\n");
|
||||
fwrite((void*)data, 1, size, stdout);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@
|
||||
// Version information (auto-updated by build system)
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 1
|
||||
#define VERSION_PATCH 18
|
||||
#define VERSION "v0.1.18"
|
||||
#define VERSION_PATCH 19
|
||||
#define VERSION "v0.1.19"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
410
src/main.c
410
src/main.c
@@ -21,6 +21,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sched.h>
|
||||
|
||||
// Centralized logging system (declaration in ginxsom.h)
|
||||
void app_log(log_level_t level, const char *format, ...) {
|
||||
@@ -176,6 +178,22 @@ int initialize_database(const char *db_path) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create system table for runtime metrics (read-only, updated by server)
|
||||
const char *create_system =
|
||||
"CREATE TABLE IF NOT EXISTS system ("
|
||||
" key TEXT PRIMARY KEY NOT NULL,"
|
||||
" value TEXT NOT NULL,"
|
||||
" updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))"
|
||||
");";
|
||||
|
||||
rc = sqlite3_exec(db, create_system, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create system table: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create auth_rules table
|
||||
const char *create_auth_rules =
|
||||
"CREATE TABLE IF NOT EXISTS auth_rules ("
|
||||
@@ -268,14 +286,20 @@ int initialize_database(const char *db_path) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create blob_overview view for admin dashboard
|
||||
// Create blob_overview view for admin dashboard with system metrics
|
||||
const char *create_overview_view =
|
||||
"CREATE VIEW IF NOT EXISTS blob_overview AS "
|
||||
"SELECT "
|
||||
" COUNT(*) as total_blobs, "
|
||||
" COALESCE(SUM(size), 0) as total_bytes, "
|
||||
" MIN(uploaded_at) as first_upload, "
|
||||
" MAX(uploaded_at) as last_upload "
|
||||
" MAX(uploaded_at) as last_upload, "
|
||||
" (SELECT value FROM system WHERE key = 'version') as version, "
|
||||
" (SELECT value FROM system WHERE key = 'process_id') as process_id, "
|
||||
" (SELECT value FROM system WHERE key = 'memory_mb') as memory_mb, "
|
||||
" (SELECT value FROM system WHERE key = 'cpu_core') as cpu_core, "
|
||||
" (SELECT value FROM system WHERE key = 'fs_blob_count') as fs_blob_count, "
|
||||
" (SELECT value FROM system WHERE key = 'fs_blob_size_mb') as fs_blob_size_mb "
|
||||
"FROM blobs;";
|
||||
|
||||
rc = sqlite3_exec(db, create_overview_view, NULL, NULL, &err_msg);
|
||||
@@ -732,6 +756,171 @@ int get_blossom_private_key(char *seckey_out, size_t max_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Helper function to count filesystem blobs
|
||||
static int count_filesystem_blobs(long *total_count, long *total_size_bytes) {
|
||||
DIR *dir = opendir(g_storage_dir);
|
||||
if (!dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*total_count = 0;
|
||||
*total_size_bytes = 0;
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
// Skip . and ..
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build full path
|
||||
char filepath[MAX_PATH_LEN];
|
||||
snprintf(filepath, sizeof(filepath), "%s/%s", g_storage_dir, entry->d_name);
|
||||
|
||||
// Get file stats
|
||||
struct stat st;
|
||||
if (stat(filepath, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
(*total_count)++;
|
||||
*total_size_bytes += st.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper function to get memory usage in MB from /proc/self/status
|
||||
static long get_memory_usage_mb(void) {
|
||||
FILE *fp = fopen("/proc/self/status", "r");
|
||||
if (!fp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
long vmrss_kb = -1;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (strncmp(line, "VmRSS:", 6) == 0) {
|
||||
// Parse VmRSS value (in kB)
|
||||
char *p = line + 6;
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
vmrss_kb = atol(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (vmrss_kb > 0) {
|
||||
return vmrss_kb / 1024; // Convert kB to MB
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Helper function to get CPU core
|
||||
static int get_cpu_core(void) {
|
||||
#ifdef __linux__
|
||||
return sched_getcpu();
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update system metrics in system table (key-value pairs)
|
||||
static int update_system_metrics(void) {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get system metrics
|
||||
int pid = getpid();
|
||||
long memory_mb = get_memory_usage_mb();
|
||||
int cpu_core = get_cpu_core();
|
||||
long fs_blob_count = 0;
|
||||
long fs_blob_size = 0;
|
||||
count_filesystem_blobs(&fs_blob_count, &fs_blob_size);
|
||||
long fs_blob_size_mb = fs_blob_size / (1024 * 1024);
|
||||
|
||||
// Prepare INSERT OR REPLACE statement for key-value updates
|
||||
const char *sql = "INSERT OR REPLACE INTO system (key, value, updated_at) VALUES (?, ?, strftime('%s', 'now'))";
|
||||
|
||||
// Update version
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, "version", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, VERSION, -1, SQLITE_STATIC);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
// Update process_id
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
char pid_str[32];
|
||||
snprintf(pid_str, sizeof(pid_str), "%d", pid);
|
||||
sqlite3_bind_text(stmt, 1, "process_id", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, pid_str, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
// Update memory_mb
|
||||
if (memory_mb > 0) {
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
char mem_str[32];
|
||||
snprintf(mem_str, sizeof(mem_str), "%ld", memory_mb);
|
||||
sqlite3_bind_text(stmt, 1, "memory_mb", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, mem_str, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// Update cpu_core
|
||||
if (cpu_core >= 0) {
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
char core_str[32];
|
||||
snprintf(core_str, sizeof(core_str), "%d", cpu_core);
|
||||
sqlite3_bind_text(stmt, 1, "cpu_core", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, core_str, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// Update fs_blob_count
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
char count_str[32];
|
||||
snprintf(count_str, sizeof(count_str), "%ld", fs_blob_count);
|
||||
sqlite3_bind_text(stmt, 1, "fs_blob_count", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, count_str, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
// Update fs_blob_size_mb
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
char size_str[32];
|
||||
snprintf(size_str, sizeof(size_str), "%ld", fs_blob_size_mb);
|
||||
sqlite3_bind_text(stmt, 1, "fs_blob_size_mb", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, size_str, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Insert blob metadata into database
|
||||
int insert_blob_metadata(const char *sha256, long size, const char *type,
|
||||
long uploaded_at, const char *uploader_pubkey,
|
||||
@@ -1848,6 +2037,9 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
|
||||
return;
|
||||
}
|
||||
|
||||
// Update system metrics after successful blob upload
|
||||
update_system_metrics();
|
||||
|
||||
// Get origin from config
|
||||
char origin[256];
|
||||
nip94_get_origin(origin, sizeof(origin));
|
||||
@@ -2142,37 +2334,105 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
fprintf(stderr, "KEYS: Derived pubkey: %s\n", g_blossom_pubkey);
|
||||
|
||||
// Scenario 5: Both database and keys specified - validate match
|
||||
// Scenario 5: Both database path and keys specified
|
||||
if (db_path_specified) {
|
||||
fprintf(stderr, "\n=== SCENARIO 5: DATABASE + KEYS (VALIDATION) ===\n");
|
||||
strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
|
||||
g_db_path[sizeof(g_db_path) - 1] = '\0';
|
||||
fprintf(stderr, "\n=== SCENARIO 5: DATABASE PATH + KEYS ===\n");
|
||||
|
||||
// Check if specified path is a directory or file
|
||||
struct stat st;
|
||||
int is_directory = 0;
|
||||
|
||||
if (stat(specified_db_path, &st) == 0) {
|
||||
is_directory = S_ISDIR(st.st_mode);
|
||||
} else {
|
||||
// Path doesn't exist - assume it's meant to be a directory
|
||||
is_directory = (specified_db_path[strlen(specified_db_path) - 1] == '/' ||
|
||||
strstr(specified_db_path, ".db") == NULL);
|
||||
}
|
||||
|
||||
if (is_directory) {
|
||||
// Build database path from directory + derived pubkey
|
||||
snprintf(g_db_path, sizeof(g_db_path), "%s/%s.db", specified_db_path, g_blossom_pubkey);
|
||||
fprintf(stderr, "DATABASE: Using directory path, derived database: %s\n", g_db_path);
|
||||
} else {
|
||||
// Use specified file path directly
|
||||
strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
|
||||
g_db_path[sizeof(g_db_path) - 1] = '\0';
|
||||
fprintf(stderr, "DATABASE: Using file path: %s\n", g_db_path);
|
||||
}
|
||||
|
||||
// Check if database exists
|
||||
struct stat st;
|
||||
if (stat(g_db_path, &st) != 0) {
|
||||
fprintf(stderr, "ERROR: Database file not found: %s\n", g_db_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load keys from database
|
||||
if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
|
||||
fprintf(stderr, "ERROR: Invalid database: missing server keys\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compare with provided key
|
||||
if (strcmp(g_blossom_seckey, test_server_privkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Server private key doesn't match database\n");
|
||||
fprintf(stderr, " Provided key and database keys are different\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "VALIDATION: Keys match database - continuing\n");
|
||||
|
||||
// Validate pubkey matches filename
|
||||
if (validate_database_pubkey_match(g_db_path, g_blossom_pubkey) != 0) {
|
||||
return 1;
|
||||
if (stat(g_db_path, &st) == 0) {
|
||||
// Database exists - validate keys match
|
||||
fprintf(stderr, "DATABASE: Found existing database, validating keys...\n");
|
||||
|
||||
// Load keys from database
|
||||
if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
|
||||
fprintf(stderr, "ERROR: Invalid database: missing server keys\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compare with provided key
|
||||
if (strcmp(g_blossom_seckey, test_server_privkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Server private key doesn't match database\n");
|
||||
fprintf(stderr, " Provided key and database keys are different\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "VALIDATION: Keys match database - continuing\n");
|
||||
|
||||
// Validate pubkey matches filename
|
||||
if (validate_database_pubkey_match(g_db_path, g_blossom_pubkey) != 0) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Database doesn't exist - create it with provided keys
|
||||
fprintf(stderr, "DATABASE: No existing database, creating new one...\n");
|
||||
|
||||
// Initialize new database
|
||||
if (initialize_database(g_db_path) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to initialize database\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Store keys
|
||||
strncpy(g_blossom_seckey, test_server_privkey, sizeof(g_blossom_seckey) - 1);
|
||||
g_blossom_seckey[64] = '\0';
|
||||
|
||||
if (store_blossom_private_key(test_server_privkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to store private key\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Store pubkey in config
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, "blossom_pubkey", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, g_blossom_pubkey, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, "Blossom server's public key", -1, SQLITE_STATIC);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
if (strlen(g_admin_pubkey) > 0) {
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, g_admin_pubkey, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, "Admin public key", -1, SQLITE_STATIC);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
}
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: New database created successfully\n");
|
||||
}
|
||||
}
|
||||
// Scenario 3 continued: Create new database with provided keys
|
||||
@@ -2238,30 +2498,78 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Scenario 2: Database Specified (--db-path)
|
||||
// Scenario 2: Database Path Specified (--db-path)
|
||||
// Note: --db-path should specify a DIRECTORY, not a full file path
|
||||
// The actual database filename will be derived from the server's pubkey
|
||||
else if (db_path_specified) {
|
||||
fprintf(stderr, "\n=== SCENARIO 2: DATABASE SPECIFIED ===\n");
|
||||
strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
|
||||
g_db_path[sizeof(g_db_path) - 1] = '\0';
|
||||
fprintf(stderr, "\n=== SCENARIO 2: DATABASE DIRECTORY SPECIFIED ===\n");
|
||||
|
||||
// Check if database exists
|
||||
// Check if specified path is a directory or file
|
||||
struct stat st;
|
||||
if (stat(g_db_path, &st) != 0) {
|
||||
fprintf(stderr, "ERROR: Database file not found: %s\n", g_db_path);
|
||||
fprintf(stderr, " → Specify a different database or let the application create a new one\n");
|
||||
return 1;
|
||||
int is_directory = 0;
|
||||
|
||||
if (stat(specified_db_path, &st) == 0) {
|
||||
is_directory = S_ISDIR(st.st_mode);
|
||||
} else {
|
||||
// Path doesn't exist - assume it's meant to be a directory
|
||||
is_directory = (specified_db_path[strlen(specified_db_path) - 1] == '/' ||
|
||||
strstr(specified_db_path, ".db") == NULL);
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: Opening existing database: %s\n", g_db_path);
|
||||
|
||||
// Load keys from database
|
||||
if (load_server_keys() != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to load keys from database\n");
|
||||
fprintf(stderr, " → Database may be corrupted or not a valid ginxsom database\n");
|
||||
return 1;
|
||||
if (is_directory) {
|
||||
// Treat as directory - will derive filename from pubkey after loading keys
|
||||
fprintf(stderr, "DATABASE: Directory specified: %s\n", specified_db_path);
|
||||
fprintf(stderr, "DATABASE: Will derive filename from server pubkey\n");
|
||||
|
||||
// Look for any .db file that matches the pubkey pattern
|
||||
DIR *dir = opendir(specified_db_path);
|
||||
int found_db = 0;
|
||||
|
||||
if (dir) {
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
// Check if filename matches pattern: <64-hex-chars>.db
|
||||
size_t name_len = strlen(entry->d_name);
|
||||
if (name_len == 67 && strcmp(entry->d_name + 64, ".db") == 0) {
|
||||
// Found a potential database file
|
||||
snprintf(g_db_path, sizeof(g_db_path), "%s/%s", specified_db_path, entry->d_name);
|
||||
found_db = 1;
|
||||
fprintf(stderr, "DATABASE: Found existing database: %s\n", g_db_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
if (!found_db) {
|
||||
// No database found - this is OK, we'll create one if we have keys
|
||||
fprintf(stderr, "DATABASE: No existing database found in directory\n");
|
||||
// g_db_path will be set later based on pubkey
|
||||
}
|
||||
} else {
|
||||
// Treat as full file path (legacy behavior)
|
||||
strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
|
||||
g_db_path[sizeof(g_db_path) - 1] = '\0';
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: Keys loaded and validated successfully\n");
|
||||
// If we found a database file, try to load it
|
||||
if (g_db_path[0] != '\0' && stat(g_db_path, &st) == 0) {
|
||||
fprintf(stderr, "DATABASE: Opening existing database: %s\n", g_db_path);
|
||||
|
||||
// Load keys from database
|
||||
if (load_server_keys() != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to load keys from database\n");
|
||||
fprintf(stderr, " → Database may be corrupted or not a valid ginxsom database\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: Keys loaded and validated successfully\n");
|
||||
} else {
|
||||
// No database file exists - we need keys to create one
|
||||
fprintf(stderr, "ERROR: No database found and no --server-privkey provided\n");
|
||||
fprintf(stderr, " → Use --server-privkey to create a new database\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Scenario 1: No Arguments (Fresh Start)
|
||||
@@ -2353,6 +2661,14 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
app_log(LOG_INFO, "Admin commands system initialized successfully");
|
||||
}
|
||||
|
||||
// Initialize system metrics at startup
|
||||
app_log(LOG_INFO, "Initializing system metrics...");
|
||||
if (update_system_metrics() == 0) {
|
||||
app_log(LOG_INFO, "System metrics initialized successfully");
|
||||
} else {
|
||||
app_log(LOG_WARN, "Failed to initialize system metrics");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// THIS IS WHERE THE REQUESTS ENTER THE FastCGI
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user