diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..563cafed --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,33 @@ +image: node:26 + +stages: + - test + - build + +cache: + paths: + - node_modules/ + +before_script: + - corepack enable + - pnpm install --frozen-lockfile + +lint: + stage: test + script: + - pnpm run lint + +typecheck: + stage: test + script: + - pnpm run typecheck + +test: + stage: test + script: + - pnpm run test + +build: + stage: build + script: + - pnpm run build diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..66714c1c --- /dev/null +++ b/deploy.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Forcefully free port 3002 and redeploy the CMS +PORT=3002 + +echo "--- Preparing for deployment ---" + +# Check if fuser is installed, if not, warn the user +if ! command -v fuser &> /dev/null; then + echo "Error: 'fuser' command not found. Please install psmisc (e.g., sudo apt install psmisc)." + exit 1 +fi + +echo "Forcefully freeing port $PORT..." +# -k kills processes, -n tcp specifies tcp socket +fuser -k $PORT/tcp || echo "No process found on port $PORT or already free." + +echo "Stopping containers..." +docker compose down + +echo "Starting deployment..." +if docker compose up -d --build; then + ./scripts/alert.sh "Deployment successful for EpicNext-Cms" + echo "--- Deployment successful ---" +else + ./scripts/alert.sh "Deployment FAILED for EpicNext-Cms" + echo "--- Deployment FAILED ---" + exit 1 +fi + diff --git a/docker-compose.yml b/docker-compose.yml index 8aee5b60..c998b03a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: NEXT_DEPLOYMENT_ID: ${CMS_RELEASE:-unknown} network: host container_name: epicnext-cms + stop_grace_period: 10s network_mode: host restart: unless-stopped env_file: diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..e7597d8c --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,32 @@ +# Scripts Documentation + +This directory contains utility scripts for managing the EpicNext-Cms project. + +## Scripts Overview + +| Script | Description | +| :--- | :--- | +| `dashboard.sh` | Main interactive menu to run all other scripts. | +| `deploy.sh` | Safely redeploys the application by freeing the port first. | +| `logs.sh` | View logs for cms and mariadb services. | +| `backup.sh` | Creates a database backup in `/backups`. | +| `db-restore.sh` | Restores a database backup. | +| `db-optimize.sh` | Optimizes database tables. | +| `docker-prune.sh` | Cleans up unused Docker resources. | +| `verify-deploy.sh` | Checks if the CMS is reachable after deployment. | +| `check-env.sh` | Validates `.env` against `.env.example`. | +| `check-security.sh` | Runs `pnpm audit` and checks for image updates. | +| `monitor.sh` | Shows system disk/container resource usage. | +| `perf-report.sh` | Runs performance profiling. | +| `setup-dev.sh` | Configures development environment. | +| `check-updates.sh` | Checks for package and image updates. | +| `maintenance.sh` | Toggles maintenance mode flag. | +| `doctor.sh` | Runs a system diagnostic report. | +| `setup-cron.sh` | Configures automated cron jobs. | +| `alert.sh` | Sends notifications to a configured webhook. | + +## Usage +Run any script directly, or use the dashboard: +```bash +./scripts/dashboard.sh +``` diff --git a/scripts/alert.sh b/scripts/alert.sh new file mode 100755 index 00000000..37500200 --- /dev/null +++ b/scripts/alert.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Script to send alerts via Webhook +# Usage: ./alert.sh "Your message" +WEBHOOK_URL="https://discord.com/api/webhooks/your_webhook_id/your_webhook_token" # Replace with actual URL + +if [ -z "$1" ]; then + echo "Usage: ./alert.sh 'Message'" + exit 1 +fi + +curl -H "Content-Type: application/json" -d "{\"content\": \"$1\"}" $WEBHOOK_URL diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100755 index 00000000..245c98e6 --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Backup mariadb-turbo database +cd "$(dirname "$0")/.." +BACKUP_DIR="backups" +mkdir -p "$BACKUP_DIR" +TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) +FILE="$BACKUP_DIR/db_$TIMESTAMP.sql" + +echo "Creating database backup in: $FILE" + +# Use environment variables from .env if possible +if [ -f .env ]; then + source .env +fi + +# Use the credentials from environment or defaults +DB_USER=${MARIADB_USER:-cms} +DB_PASS=${MARIADB_PASSWORD:-cms} +DB_NAME=${MARIADB_DATABASE:-habbo} + +docker compose exec -T mariadb-turbo mariadbdump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$FILE" + +echo "Backup complete: $FILE" diff --git a/scripts/check-env.sh b/scripts/check-env.sh new file mode 100755 index 00000000..88a677eb --- /dev/null +++ b/scripts/check-env.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Check if .env has all keys from .env.example +cd "$(dirname "$0")/.." +if [ ! -f .env ]; then echo ".env missing"; exit 1; fi +if [ ! -f .env.example ]; then echo ".env.example missing, cannot compare."; exit 0; fi + +# Extract keys and compare +env_keys=$(cut -d'=' -f1 .env | sort) +ex_keys=$(cut -d'=' -f1 .env.example | sort) + +missing=$(comm -23 <(echo "$ex_keys") <(echo "$env_keys")) +if [ -n "$missing" ]; then + echo "Missing env keys:" + echo "$missing" + exit 1 +fi +echo "All env keys present." diff --git a/scripts/check-security.sh b/scripts/check-security.sh new file mode 100755 index 00000000..cc9cf4ec --- /dev/null +++ b/scripts/check-security.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Security scan: check dependencies and Docker images +cd "$(dirname "$0")/.." +echo "--- Running security scan ---" +echo "Checking npm dependencies..." +pnpm audit --audit-level=high + +echo "Checking outdated images (if any)..." +docker compose pull --dry-run diff --git a/scripts/check-updates.sh b/scripts/check-updates.sh new file mode 100755 index 00000000..98e6fe64 --- /dev/null +++ b/scripts/check-updates.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Check for dependency and image updates +cd "$(dirname "$0")/.." +echo "Checking for package updates..." +pnpm outdated + +echo "Checking for newer Docker images..." +docker compose pull --dry-run diff --git a/scripts/dashboard.sh b/scripts/dashboard.sh new file mode 100755 index 00000000..aa839b53 --- /dev/null +++ b/scripts/dashboard.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Dashboard for quick script access +SCRIPT_DIR="$(dirname "$0")" + +echo "=== EpicNext-Cms Dashboard ===" +echo "Select a script to run:" +echo "1) Deploy (./deploy.sh)" +echo "2) Logs (./scripts/logs.sh)" +echo "3) Backup (./scripts/backup.sh)" +echo "4) Prune (./scripts/docker-prune.sh)" +echo "5) Verify (./scripts/verify-deploy.sh)" +echo "6) Check Env (./scripts/check-env.sh)" +echo "7) Optimize DB (./scripts/db-optimize.sh)" +echo "8) Security Scan (./scripts/check-security.sh)" +echo "9) Monitor (./scripts/monitor.sh)" +echo "10) Perf Report (./scripts/perf-report.sh)" +echo "11) Setup Dev (./scripts/setup-dev.sh)" +echo "12) Check Updates (./scripts/check-updates.sh)" +echo "13) Restore DB (./scripts/db-restore.sh)" +echo "14) Maintenance Mode (./scripts/maintenance.sh)" +echo "15) Doctor (./scripts/doctor.sh)" +echo "16) Setup Cron (./scripts/setup-cron.sh)" +echo "17) Send Alert (./scripts/alert.sh)" +echo "q) Quit" + +read -p "Choice: " choice + +case $choice in + 1) ./deploy.sh ;; + 2) $SCRIPT_DIR/logs.sh ;; + 3) $SCRIPT_DIR/backup.sh ;; + 4) $SCRIPT_DIR/docker-prune.sh ;; + 5) $SCRIPT_DIR/verify-deploy.sh ;; + 6) $SCRIPT_DIR/check-env.sh ;; + 7) $SCRIPT_DIR/db-optimize.sh ;; + 8) $SCRIPT_DIR/check-security.sh ;; + 9) $SCRIPT_DIR/monitor.sh ;; + 10) $SCRIPT_DIR/perf-report.sh ;; + 11) $SCRIPT_DIR/setup-dev.sh ;; + 12) $SCRIPT_DIR/check-updates.sh ;; + 13) $SCRIPT_DIR/db-restore.sh ;; + 14) $SCRIPT_DIR/maintenance.sh ;; + 15) $SCRIPT_DIR/doctor.sh ;; + 16) $SCRIPT_DIR/setup-cron.sh ;; + 17) $SCRIPT_DIR/alert.sh ;; + q) exit 0 ;; + *) echo "Invalid choice" ;; +esac diff --git a/scripts/db-optimize.sh b/scripts/db-optimize.sh new file mode 100755 index 00000000..0caaa566 --- /dev/null +++ b/scripts/db-optimize.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Optimize all tables in mariadb-turbo +cd "$(dirname "$0")/.." +[ -f .env ] && source .env +DB_USER=${MARIADB_USER:-cms} +DB_PASS=${MARIADB_PASSWORD:-cms} +DB_NAME=${MARIADB_DATABASE:-habbo} + +echo "Optimizing database tables..." +# Get all tables +tables=$(docker compose exec -T mariadb-turbo mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SHOW TABLES" -s) +# Optimize them +for table in $tables; do + echo "Optimizing $table..." + docker compose exec -T mariadb-turbo mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "OPTIMIZE TABLE $table" +done +echo "Optimization complete." diff --git a/scripts/db-restore.sh b/scripts/db-restore.sh new file mode 100755 index 00000000..30bef31a --- /dev/null +++ b/scripts/db-restore.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Restore database from a backup +cd "$(dirname "$0")/.." +BACKUP_DIR="backups" + +echo "Available backups:" +ls -1 "$BACKUP_DIR" + +read -p "Enter filename to restore: " filename + +if [ -f "$BACKUP_DIR/$filename" ]; then + echo "Restoring $filename..." + [ -f .env ] && source .env + DB_USER=${MARIADB_USER:-cms} + DB_PASS=${MARIADB_PASSWORD:-cms} + DB_NAME=${MARIADB_DATABASE:-habbo} + + docker compose exec -T mariadb-turbo mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$BACKUP_DIR/$filename" + echo "Restore complete." +else + echo "File not found." +fi diff --git a/scripts/doctor.sh b/scripts/doctor.sh new file mode 100755 index 00000000..6077299c --- /dev/null +++ b/scripts/doctor.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# System diagnostic tool +echo "=== Doctor Report ===" +echo "Checking Docker..." +docker ps > /dev/null || echo "Error: Docker not running." + +echo "Checking port 3002..." +if lsof -i :3002 > /dev/null; then echo "Port 3002 is in use."; else echo "Port 3002 is free."; fi + +echo "Checking .env..." +if [ -f .env ]; then echo ".env exists."; else echo "Error: .env missing."; fi + +echo "Checking health..." +if curl -s http://localhost:3002/api/health > /dev/null; then echo "Health endpoint OK."; else echo "Health endpoint failing."; fi + +echo "Disk usage:" +df -h / | tail -n 1 diff --git a/scripts/logrotate.conf b/scripts/logrotate.conf new file mode 100644 index 00000000..e75f8a2c --- /dev/null +++ b/scripts/logrotate.conf @@ -0,0 +1,7 @@ +/var/www/atom-nexst/logs/*.log /var/www/atom-nexst/*.log { + daily + rotate 7 + compress + missingok + notifempty +} diff --git a/scripts/logs.sh b/scripts/logs.sh new file mode 100755 index 00000000..51a86c85 --- /dev/null +++ b/scripts/logs.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# View logs for cms and mariadb services +cd "$(dirname "$0")/.." +docker compose logs -f epicnext-cms mariadb-turbo diff --git a/scripts/maintenance.sh b/scripts/maintenance.sh new file mode 100755 index 00000000..2cf3a6e5 --- /dev/null +++ b/scripts/maintenance.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# Toggle maintenance mode +FILE=".maintenance" +if [ -f "$FILE" ]; then + rm "$FILE" + echo "Maintenance mode disabled." +else + touch "$FILE" + echo "Maintenance mode enabled." +fi diff --git a/scripts/monitor.sh b/scripts/monitor.sh new file mode 100755 index 00000000..3e43b6df --- /dev/null +++ b/scripts/monitor.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Monitor system health +cd "$(dirname "$0")/.." + +echo "=== System Monitoring ===" +echo "Disk Usage:" +df -h / | tail -n 1 +echo "" +echo "Docker Resources:" +docker stats --no-stream +echo "" +echo "Container Status:" +docker compose ps diff --git a/scripts/perf-report.sh b/scripts/perf-report.sh new file mode 100755 index 00000000..52cd91f9 --- /dev/null +++ b/scripts/perf-report.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Run performance report +cd "$(dirname "$0")/.." +echo "Running performance report..." +node scripts/performance-report.mjs diff --git a/scripts/setup-cron.sh b/scripts/setup-cron.sh new file mode 100755 index 00000000..71bb28e7 --- /dev/null +++ b/scripts/setup-cron.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# Setup cron jobs for maintenance +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Run backup every day at 03:00 +echo "0 3 * * * $SCRIPT_DIR/backup.sh" | crontab - +# Run prune every Sunday at 04:00 +echo "0 4 * * 0 $SCRIPT_DIR/docker-prune.sh" | crontab - + +echo "Cron jobs configured." diff --git a/scripts/setup-dev.sh b/scripts/setup-dev.sh new file mode 100755 index 00000000..20bd6e89 --- /dev/null +++ b/scripts/setup-dev.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Setup development environment +cd "$(dirname "$0")/.." +echo "Setting up development environment..." + +if [ ! -f .env ] && [ -f .env.example ]; then + echo "Creating .env from .env.example..." + cp .env.example .env + echo "Please configure your .env file." +fi + +echo "Installing dependencies..." +pnpm install + +echo "Setup complete." diff --git a/scripts/setup-logrotate.sh b/scripts/setup-logrotate.sh new file mode 100755 index 00000000..acef0a69 --- /dev/null +++ b/scripts/setup-logrotate.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Setup log rotation +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="$SCRIPT_DIR/logrotate.conf" + +echo "To apply log rotation, run:" +echo "sudo cp $CONFIG_FILE /etc/logrotate.d/epicnext-cms" diff --git a/scripts/verify-deploy.sh b/scripts/verify-deploy.sh new file mode 100755 index 00000000..6d33c1ce --- /dev/null +++ b/scripts/verify-deploy.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# Verify if CMS is reachable +cd "$(dirname "$0")/.." +echo "Checking health..." +if curl -f http://localhost:3002/api/health > /dev/null 2>&1; then + echo "Success: Service is reachable." +else + echo "Error: Service not reachable." + exit 1 +fi