Initial commit
This commit is contained in:
64
deploy/cashumints-api.service
Normal file
64
deploy/cashumints-api.service
Normal file
@@ -0,0 +1,64 @@
|
||||
# Cashumints.space API - Systemd Service File
|
||||
#
|
||||
# Installation:
|
||||
# sudo cp deploy/cashumints-api.service /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl enable cashumints-api
|
||||
# sudo systemctl start cashumints-api
|
||||
#
|
||||
# Management:
|
||||
# sudo systemctl status cashumints-api
|
||||
# sudo systemctl restart cashumints-api
|
||||
# sudo journalctl -u cashumints-api -f
|
||||
|
||||
[Unit]
|
||||
Description=Cashumints.space API Server
|
||||
Documentation=https://cashumints.space/docs
|
||||
After=network.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=cashumints
|
||||
Group=cashumints
|
||||
WorkingDirectory=/opt/cashumints-api
|
||||
|
||||
# Environment
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=3000
|
||||
EnvironmentFile=/opt/cashumints-api/.env
|
||||
|
||||
# Process
|
||||
ExecStart=/usr/bin/node src/index.js
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
# Restart policy
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StartLimitInterval=60
|
||||
StartLimitBurst=3
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=65536
|
||||
LimitNPROC=4096
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/cashumints-api/data
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictNamespaces=true
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=cashumints-api
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
63
deploy/cashumints-workers.service
Normal file
63
deploy/cashumints-workers.service
Normal file
@@ -0,0 +1,63 @@
|
||||
# Cashumints.space Workers - Systemd Service File
|
||||
#
|
||||
# Installation:
|
||||
# sudo cp deploy/cashumints-workers.service /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl enable cashumints-workers
|
||||
# sudo systemctl start cashumints-workers
|
||||
#
|
||||
# Management:
|
||||
# sudo systemctl status cashumints-workers
|
||||
# sudo systemctl restart cashumints-workers
|
||||
# sudo journalctl -u cashumints-workers -f
|
||||
|
||||
[Unit]
|
||||
Description=Cashumints.space Background Workers
|
||||
Documentation=https://cashumints.space/docs
|
||||
After=network.target cashumints-api.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=cashumints
|
||||
Group=cashumints
|
||||
WorkingDirectory=/opt/cashumints-api
|
||||
|
||||
# Environment
|
||||
Environment=NODE_ENV=production
|
||||
EnvironmentFile=/opt/cashumints-api/.env
|
||||
|
||||
# Process
|
||||
ExecStart=/usr/bin/node src/workers/index.js
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
# Restart policy
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StartLimitInterval=120
|
||||
StartLimitBurst=5
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=65536
|
||||
LimitNPROC=4096
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/cashumints-api/data
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictNamespaces=true
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=cashumints-workers
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
176
deploy/install.sh
Normal file
176
deploy/install.sh
Normal file
@@ -0,0 +1,176 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Cashumints.space API - Installation Script
|
||||
# ============================================
|
||||
#
|
||||
# Usage:
|
||||
# chmod +x deploy/install.sh
|
||||
# sudo ./deploy/install.sh
|
||||
#
|
||||
# This script:
|
||||
# 1. Creates cashumints user
|
||||
# 2. Installs the application to /opt/cashumints-api
|
||||
# 3. Sets up systemd services
|
||||
# 4. Configures basic Nginx
|
||||
#
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
APP_USER="cashumints"
|
||||
APP_DIR="/opt/cashumints-api"
|
||||
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ Cashumints.space API - Installation Script ║${NC}"
|
||||
echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${RED}Error: Please run as root (sudo)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo -e "${RED}Error: Node.js is not installed${NC}"
|
||||
echo "Install with: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt install -y nodejs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||
if [ "$NODE_VERSION" -lt 18 ]; then
|
||||
echo -e "${RED}Error: Node.js 18+ required (found v${NODE_VERSION})${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Node.js $(node -v) detected"
|
||||
|
||||
# Create user
|
||||
echo -e "${YELLOW}Creating application user...${NC}"
|
||||
if id "$APP_USER" &>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} User $APP_USER already exists"
|
||||
else
|
||||
useradd -r -s /bin/false $APP_USER
|
||||
echo -e "${GREEN}✓${NC} Created user $APP_USER"
|
||||
fi
|
||||
|
||||
# Create application directory
|
||||
echo -e "${YELLOW}Setting up application directory...${NC}"
|
||||
if [ -d "$APP_DIR" ]; then
|
||||
echo -e "${YELLOW}⚠${NC} Directory $APP_DIR exists, updating..."
|
||||
else
|
||||
mkdir -p $APP_DIR
|
||||
fi
|
||||
|
||||
# Copy application files
|
||||
cp -r "$CURRENT_DIR"/* $APP_DIR/
|
||||
rm -rf $APP_DIR/node_modules 2>/dev/null || true
|
||||
|
||||
# Create data directory
|
||||
mkdir -p $APP_DIR/data
|
||||
chown -R $APP_USER:$APP_USER $APP_DIR/data
|
||||
|
||||
echo -e "${GREEN}✓${NC} Application files copied to $APP_DIR"
|
||||
|
||||
# Install dependencies
|
||||
echo -e "${YELLOW}Installing dependencies...${NC}"
|
||||
cd $APP_DIR
|
||||
npm install --production --silent
|
||||
echo -e "${GREEN}✓${NC} Dependencies installed"
|
||||
|
||||
# Setup configuration
|
||||
if [ ! -f "$APP_DIR/.env" ]; then
|
||||
echo -e "${YELLOW}Creating configuration...${NC}"
|
||||
cp $APP_DIR/env.example $APP_DIR/.env
|
||||
|
||||
# Generate admin key
|
||||
ADMIN_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/^ADMIN_API_KEY=$/ADMIN_API_KEY=$ADMIN_KEY/" $APP_DIR/.env
|
||||
|
||||
echo -e "${GREEN}✓${NC} Configuration created"
|
||||
echo -e "${YELLOW}⚠ IMPORTANT: Your admin API key is:${NC}"
|
||||
echo -e "${GREEN}$ADMIN_KEY${NC}"
|
||||
echo ""
|
||||
echo "Save this key! You'll need it for admin operations."
|
||||
echo ""
|
||||
else
|
||||
echo -e "${GREEN}✓${NC} Configuration already exists"
|
||||
fi
|
||||
|
||||
# Set permissions
|
||||
chown -R root:$APP_USER $APP_DIR
|
||||
chmod -R 750 $APP_DIR
|
||||
chmod 640 $APP_DIR/.env
|
||||
|
||||
# Initialize database
|
||||
echo -e "${YELLOW}Initializing database...${NC}"
|
||||
sudo -u $APP_USER node $APP_DIR/src/db/migrate.js
|
||||
echo -e "${GREEN}✓${NC} Database initialized"
|
||||
|
||||
# Install systemd services
|
||||
echo -e "${YELLOW}Installing systemd services...${NC}"
|
||||
cp $APP_DIR/deploy/cashumints-api.service /etc/systemd/system/
|
||||
cp $APP_DIR/deploy/cashumints-workers.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable cashumints-api cashumints-workers
|
||||
echo -e "${GREEN}✓${NC} Systemd services installed and enabled"
|
||||
|
||||
# Install nginx config if nginx is available
|
||||
if command -v nginx &> /dev/null; then
|
||||
echo -e "${YELLOW}Installing Nginx configuration...${NC}"
|
||||
cp $APP_DIR/deploy/nginx.conf /etc/nginx/sites-available/cashumints-api
|
||||
|
||||
if [ ! -L /etc/nginx/sites-enabled/cashumints-api ]; then
|
||||
ln -s /etc/nginx/sites-available/cashumints-api /etc/nginx/sites-enabled/
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Nginx configuration installed"
|
||||
echo -e "${YELLOW}⚠ Note: Update server_name and SSL paths in /etc/nginx/sites-available/cashumints-api${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} Nginx not found, skipping Nginx setup"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ Installation Complete! ║${NC}"
|
||||
echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo ""
|
||||
echo "1. Review configuration:"
|
||||
echo " sudo nano $APP_DIR/.env"
|
||||
echo ""
|
||||
echo "2. Start services:"
|
||||
echo " sudo systemctl start cashumints-api"
|
||||
echo " sudo systemctl start cashumints-workers"
|
||||
echo ""
|
||||
echo "3. Check status:"
|
||||
echo " sudo systemctl status cashumints-api"
|
||||
echo " sudo systemctl status cashumints-workers"
|
||||
echo ""
|
||||
echo "4. View logs:"
|
||||
echo " sudo journalctl -u cashumints-api -f"
|
||||
echo " sudo journalctl -u cashumints-workers -f"
|
||||
echo ""
|
||||
echo "5. Test API:"
|
||||
echo " curl http://localhost:3000/v1/health"
|
||||
echo ""
|
||||
if command -v nginx &> /dev/null; then
|
||||
echo "6. Setup SSL (Let's Encrypt):"
|
||||
echo " sudo certbot --nginx -d api.cashumints.space"
|
||||
echo " sudo systemctl reload nginx"
|
||||
echo ""
|
||||
fi
|
||||
echo -e "${GREEN}Documentation: http://localhost:3000/docs${NC}"
|
||||
echo ""
|
||||
|
||||
160
deploy/nginx.conf
Normal file
160
deploy/nginx.conf
Normal file
@@ -0,0 +1,160 @@
|
||||
# Cashumints.space API - Nginx Configuration
|
||||
#
|
||||
# Installation:
|
||||
# sudo cp deploy/nginx.conf /etc/nginx/sites-available/cashumints-api
|
||||
# sudo ln -s /etc/nginx/sites-available/cashumints-api /etc/nginx/sites-enabled/
|
||||
# sudo nginx -t
|
||||
# sudo systemctl reload nginx
|
||||
#
|
||||
# SSL Certificate (Let's Encrypt):
|
||||
# sudo certbot --nginx -d api.cashumints.space
|
||||
|
||||
# Rate limiting zones
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
|
||||
limit_req_zone $binary_remote_addr zone=admin_limit:10m rate=5r/s;
|
||||
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||
|
||||
# Upstream API server
|
||||
upstream cashumints_api {
|
||||
server 127.0.0.1:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name api.cashumints.space;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# Main HTTPS server
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name api.cashumints.space;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/api.cashumints.space/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/api.cashumints.space/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
# Modern SSL configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# HSTS
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/cashumints-api.access.log;
|
||||
error_log /var/log/nginx/cashumints-api.error.log;
|
||||
|
||||
# Connection limits
|
||||
limit_conn conn_limit 20;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types application/json application/javascript text/plain text/css text/xml;
|
||||
|
||||
# Client settings
|
||||
client_max_body_size 1m;
|
||||
client_body_timeout 10s;
|
||||
client_header_timeout 10s;
|
||||
|
||||
# Proxy settings
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
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_set_header Connection "";
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
# Health check (no rate limit)
|
||||
location = /v1/health {
|
||||
proxy_pass http://cashumints_api;
|
||||
proxy_cache_bypass 1;
|
||||
}
|
||||
|
||||
# Swagger documentation (relaxed rate limit)
|
||||
location /docs {
|
||||
limit_req zone=api_limit burst=10 nodelay;
|
||||
proxy_pass http://cashumints_api;
|
||||
}
|
||||
|
||||
location = /openapi.json {
|
||||
limit_req zone=api_limit burst=10 nodelay;
|
||||
proxy_pass http://cashumints_api;
|
||||
# Cache OpenAPI spec
|
||||
proxy_cache_valid 200 5m;
|
||||
}
|
||||
|
||||
# Admin endpoints (strict rate limit)
|
||||
location /v1/admin {
|
||||
limit_req zone=admin_limit burst=5 nodelay;
|
||||
proxy_pass http://cashumints_api;
|
||||
|
||||
# Only allow from specific IPs (optional)
|
||||
# allow 10.0.0.0/8;
|
||||
# allow 192.168.0.0/16;
|
||||
# deny all;
|
||||
}
|
||||
|
||||
# API endpoints
|
||||
location /v1 {
|
||||
limit_req zone=api_limit burst=50 nodelay;
|
||||
proxy_pass http://cashumints_api;
|
||||
|
||||
# Cache GET requests for 10 seconds
|
||||
proxy_cache_valid 200 10s;
|
||||
}
|
||||
|
||||
# Root and favicon
|
||||
location = / {
|
||||
proxy_pass http://cashumints_api;
|
||||
}
|
||||
|
||||
location = /favicon.ico {
|
||||
return 204;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Block common attack vectors
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
location ~* \.(git|env|sql|bak|old|tmp)$ {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user