Initial commit

This commit is contained in:
Michilis
2025-12-19 23:56:07 -03:00
commit 23f716255e
48 changed files with 14834 additions and 0 deletions

View 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

View 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
View 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
View 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;
}
}