Files
vortexi-web/setup.sh
T
2026-08-16 15:08:52 -04:00

272 lines
11 KiB
Bash

#!/bin/bash
set -e
cat <<'EOF'
__ __ __ __
/ | / | / | / |
$$ | $$ | ______ ______ _$$ |_ ______ __ __ $$/
$$ | $$ |/ \ / \ / $$ | / \ / \ / |/ |
$$ \ /$$//$$$$$$ |/$$$$$$ |$$$$$$/ /$$$$$$ |$$ \/$$/ $$ |
$$ /$$/ $$ | $$ |$$ | $$/ $$ | __ $$ $$ | $$ $$< $$ |
$$ $$/ $$ \__$$ |$$ | $$ |/ |$$$$$$$$/ /$$$$ \ $$ |
$$$/ $$ $$/ $$ | $$ $$/ $$ |/$$/ $$ |$$ |
$/ $$$$$$/ $$/ $$$$/ $$$$$$$/ $$/ $$/ $$/
SETUP WIZARD
EOF
echo ""
echo "=== Basic Configuration ==="
read -p "Enter your domain [default: localhost]: " DOMAIN_INPUT
DOMAIN="${DOMAIN_INPUT:-localhost}"
DEFAULT_RCC_KEY=$(python3 -c "import secrets; print(secrets.token_hex(6))" 2>/dev/null || openssl rand -hex 6 2>/dev/null || echo "rcc_$(date +%s)")
read -p "Enter RCC Access Key [press enter to auto-generate: ${DEFAULT_RCC_KEY}]: " RCC_INPUT
RCC_KEY="${RCC_INPUT:-${DEFAULT_RCC_KEY}}"
read -p "PostgreSQL User [default: vortexi]: " DB_USER_INPUT
DB_USER="${DB_USER_INPUT:-vortexi}"
DEFAULT_DB_PASS=$(python3 -c "import secrets; print(secrets.token_hex(16))" 2>/dev/null || openssl rand -hex 16 2>/dev/null || echo "dbpass_$(date +%s)")
read -p "PostgreSQL Password [press enter to auto-generate]: " DB_PASS_INPUT
DB_PASS="${DB_PASS_INPUT:-${DEFAULT_DB_PASS}}"
read -p "PostgreSQL Database Name [default: vortexidb]: " DB_NAME_INPUT
DB_NAME="${DB_NAME_INPUT:-vortexidb}"
DEFAULT_SESSION_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))" 2>/dev/null || openssl rand -hex 32 2>/dev/null || echo "sess_$(date +%s)")
read -p "Flask Session Secret Key [press enter to auto-generate]: " SESSION_INPUT
SESSION_KEY="${SESSION_INPUT:-${DEFAULT_SESSION_KEY}}"
DEFAULT_AUTH_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))" 2>/dev/null || openssl rand -hex 32 2>/dev/null || echo "auth_$(date +%s)")
read -p "API Authorization Key [press enter to auto-generate]: " AUTH_INPUT
AUTH_KEY="${AUTH_INPUT:-${DEFAULT_AUTH_KEY}}"
echo ""
echo "=== Optional External Services (Press Enter to leave blank / skip) ==="
read -p "Cloudflare Turnstile Site Key (optional) []: " TURNSTILE_SITE_KEY_INPUT
CLOUDFLARE_TURNSTILE_SITE_KEY="${TURNSTILE_SITE_KEY_INPUT:-}"
read -p "Cloudflare Turnstile Secret Key (optional) []: " TURNSTILE_SECRET_KEY_INPUT
CLOUDFLARE_TURNSTILE_SECRET_KEY="${TURNSTILE_SECRET_KEY_INPUT:-}"
read -p "Discord Client ID (optional) []: " DISCORD_CLIENT_ID_INPUT
DISCORD_CLIENT_ID="${DISCORD_CLIENT_ID_INPUT:-}"
read -p "Discord Client Secret (optional) []: " DISCORD_CLIENT_SECRET_INPUT
DISCORD_CLIENT_SECRET="${DISCORD_CLIENT_SECRET_INPUT:-}"
read -p "Discord Bot Token (optional) []: " DISCORD_BOT_TOKEN_INPUT
DISCORD_BOT_TOKEN="${DISCORD_BOT_TOKEN_INPUT:-}"
read -p "Discord Admin Logs Webhook (optional) []: " DISCORD_ADMIN_LOGS_WEBHOOK_INPUT
DISCORD_ADMIN_LOGS_WEBHOOK="${DISCORD_ADMIN_LOGS_WEBHOOK_INPUT:-}"
read -p "Mailjet API Key (optional) []: " MAILJET_APIKEY_INPUT
MAILJET_APIKEY="${MAILJET_APIKEY_INPUT:-}"
read -p "Mailjet Secret Key (optional) []: " MAILJET_SECRETKEY_INPUT
MAILJET_SECRETKEY="${MAILJET_SECRETKEY_INPUT:-}"
read -p "AWS S3 Access Key (optional, press enter for local storage) []: " AWS_ACCESS_KEY_INPUT
AWS_ACCESS_KEY="${AWS_ACCESS_KEY_INPUT:-}"
read -p "AWS S3 Secret Key (optional) []: " AWS_SECRET_KEY_INPUT
AWS_SECRET_KEY="${AWS_SECRET_KEY_INPUT:-}"
read -p "AWS S3 Bucket Name (optional) []: " AWS_S3_BUCKET_NAME_INPUT
AWS_S3_BUCKET_NAME="${AWS_S3_BUCKET_NAME_INPUT:-}"
read -p "AWS S3 Custom Domain / CDN URL (optional) []: " AWS_S3_CUSTOM_DOMAIN_INPUT
AWS_S3_CUSTOM_DOMAIN="${AWS_S3_CUSTOM_DOMAIN_INPUT:-}"
USE_LOCAL_STORAGE="True"
if [ -n "$AWS_ACCESS_KEY" ] && [ -n "$AWS_SECRET_KEY" ] && [ -n "$AWS_S3_BUCKET_NAME" ]; then
USE_LOCAL_STORAGE="False"
fi
BASE_URL="https://www.${DOMAIN}"
if [ "$DOMAIN" = "localhost" ] || [ "$DOMAIN" = "127.0.0.1" ]; then
BASE_URL="http://${DOMAIN}:3003"
fi
cat > .env <<EOF
BASE_DOMAIN=${DOMAIN}
BASE_URL=${BASE_URL}
USE_LOCAL_STORAGE=${USE_LOCAL_STORAGE}
DEBUG_IPS=0.0.0.0,127.0.0.1
POSTGRES_USER=${DB_USER}
POSTGRES_PASSWORD=${DB_PASS}
POSTGRES_DB=${DB_NAME}
POSTGRES_HOST=db
POSTGRES_PORT=5432
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB=0
FLASK_SESSION_KEY=${SESSION_KEY}
AUTHORIZATION_KEY=${AUTH_KEY}
RCC_ACCESS_KEY=${RCC_KEY}
SWITCH_TO_ARGON_PASSWORD_HASH=True
CLOUDFLARE_TURNSTILE_SITE_KEY=${CLOUDFLARE_TURNSTILE_SITE_KEY}
CLOUDFLARE_TURNSTILE_SECRET_KEY=${CLOUDFLARE_TURNSTILE_SECRET_KEY}
DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}
DISCORD_CLIENT_SECRET=${DISCORD_CLIENT_SECRET}
DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN}
DISCORD_BOT_AUTHTOKEN=
DISCORD_BOT_AUTHORISED_IPS=127.0.0.1,23.136.44.116,2602:f6b7:0:7::a
DISCORD_ADMIN_LOGS_WEBHOOK=${DISCORD_ADMIN_LOGS_WEBHOOK}
FILTER_DISCORD_WEBHOOK=
ITEMRELEASER_DISCORD_WEBHOOK=
CHEATER_REPORTS_DISCORD_WEBHOOK=
ITEMRELEASER_ITEM_PING_ROLE_ID=1364011164387770553
MAILJET_APIKEY=${MAILJET_APIKEY}
MAILJET_SECRETKEY=${MAILJET_SECRETKEY}
MAILJET_NOREPLY_SENDER=no-reply@${DOMAIN}
MAILJET_DONATION_TEMPLATE_ID=6984478
MAILJET_EMAILVERIFY_TEMPLATE_ID=6932923
MAILJET_PASSWORDRESET_TEMPLATE_ID=6932939
AWS_ACCESS_KEY=${AWS_ACCESS_KEY}
AWS_SECRET_KEY=${AWS_SECRET_KEY}
AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME}
AWS_S3_ENDPOINT_URL=
AWS_S3_DOWNLOAD_CACHE_DIR=./download_cache
AWS_REGION_NAME=eu-north-1
AWS_S3_CACHE_LIFETIME=3600
AWS_S3_CUSTOM_DOMAIN=${AWS_S3_CUSTOM_DOMAIN}
KOFI_ENABLED=False
KOFI_VERIFICATION_TOKEN=
DISCOURSE_SSO_ENABLED=False
DISCOURSE_FORUM_BASEURL=
DISCOURSE_SECRET_KEY=
PROMETHEUS_ENABLED=False
PROMETHEUS_ALLOWED_IPS=127.0.0.1
ROLIMONS_API_ENABLED=False
ROLIMONS_API_KEY=ExampleKey
CRYPTOMUS_PAYMENT_ENABLED=False
CRYPTOMUS_MERCHANT_ID=
CRYPTOMUS_API_KEY=
IPAPI_AUTH_KEY=xxxxxxxxx
IPAPI_CACHE_LIFETIME=86400
ADMIN_GROUP_ID=1
VERIFIED_EMAIL_REWARD_ASSET=197
ASSETMIGRATOR_ROBLOSECURITY=
ASSETMIGRATOR_USE_PROXIES=False
ASSETMIGRATOR_PROXY_LIST_LOCATION=./example_file.txt
EOF
mkdir -p logs download_cache app/files tools
python3 - "$DOMAIN" "$RCC_KEY" <<'PYEOF'
import sys
import os
import shutil
import struct
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
new_domain = sys.argv[1].strip()
rcc_key = sys.argv[2].strip()
if new_domain and new_domain != "vortexi.cc":
valid_exts = ('.py', '.html', '.js', '.json', '.lua', '.rbxmx', '.rbxlx', '.yml', '.yaml', '.txt', '.md', '.sql', '.css', '.svg', '.sh', '.example')
ignore_dirs = {'.git', '__pycache__', 'venv', '.venv', 'node_modules', 'logs', '.idea', '.vscode'}
replaced_count = 0
for root, dirs, files in os.walk('.'):
dirs[:] = [d for d in dirs if d not in ignore_dirs]
for file in files:
if file == "setup.sh":
continue
if file.endswith(valid_exts) or file in ('.env', '.env.example'):
filepath = os.path.join(root, file)
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
if "vortexi.cc" in content:
if new_domain in ("localhost", "127.0.0.1"):
new_content = content.replace("https://www.vortexi.cc", "http://localhost:3003")
new_content = new_content.replace("http://www.vortexi.cc", "http://localhost:3003")
new_content = new_content.replace("https://vortexi.cc", "http://localhost:3003")
new_content = new_content.replace("http://vortexi.cc", "http://localhost:3003")
new_content = new_content.replace("www.vortexi.cc", "localhost:3003")
new_content = new_content.replace("vortexi.cc", "localhost")
else:
new_content = content.replace("vortexi.cc", new_domain)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(new_content)
replaced_count += 1
except Exception:
pass
print(f"Replaced domain in {replaced_count} files.")
files_dir = os.path.join("app", "files")
if os.path.exists(files_dir):
src = os.path.join(files_dir, "RCCService.json")
dst = os.path.join(files_dir, f"RCCService{rcc_key}.json")
if os.path.exists(src) and not os.path.exists(dst):
shutil.copyfile(src, dst)
def generate_rsa_keypair(key_size, priv_path, pub_path):
if not os.path.exists(priv_path):
os.makedirs(os.path.dirname(priv_path), exist_ok=True)
priv_key = rsa.generate_private_key(
public_exponent=65537,
key_size=key_size,
backend=default_backend()
)
with open(priv_path, "wb") as f:
f.write(priv_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
))
with open(pub_path, "wb") as f:
f.write(priv_key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
))
print(f"Generated {key_size}-bit RSA keypair ({priv_path}, {pub_path})")
def generate_ms_blob(pub_path, blob_path):
try:
with open(pub_path, "rb") as f:
pub_key = serialization.load_pem_public_key(f.read(), backend=default_backend())
blobheader = struct.pack('<BBHI', 0x06, 0x02, 0, 0x0000a400)
numbers = pub_key.public_numbers()
bitlen = pub_key.key_size
rsapubkey = struct.pack('<4sII', b'RSA1', bitlen, numbers.e)
n_bytes = numbers.n.to_bytes(bitlen // 8, byteorder='little')
blob = blobheader + rsapubkey + n_bytes
b64_blob = base64.b64encode(blob).decode('ascii')
with open(blob_path, "w") as f:
f.write(b64_blob)
print(f"Generated MS PUBLICKEYBLOB ({blob_path})")
except Exception as e:
print(f"Failed to generate MS blob for {pub_path}: {e}")
generate_rsa_keypair(1024, "app/files/rsa_private_1024.pem", "app/files/rsa_public_1024.pub")
generate_rsa_keypair(2048, "app/files/rsa_private_2048.pem", "app/files/rsa_public_2048.pub")
generate_rsa_keypair(2048, "app/files/rsa_private_gameserver.pem", "app/files/rsa_public_gameserver.pub")
generate_ms_blob("app/files/rsa_public_1024.pub", "app/files/2016pub.blob")
generate_ms_blob("app/files/rsa_public_2048.pub", "app/files/2018pub.blob")
PYEOF
if command -v openssl >/dev/null 2>&1; then
openssl rsa -pubin -inform PEM -in ./app/files/rsa_public_1024.pub -outform "MS PUBLICKEYBLOB" 2>/dev/null | base64 > ./app/files/2016pub.blob || true
openssl rsa -pubin -inform PEM -in ./app/files/rsa_public_2048.pub -outform "MS PUBLICKEYBLOB" 2>/dev/null | base64 > ./app/files/2018pub.blob || true
fi
echo ""
echo "Setup finished successfully!"
echo "Keys, blobs, and configuration generated."
echo ""
read -p "Start the site now? [Y/n]: " START_INPUT
START_INPUT="${START_INPUT:-Y}"
if [[ "$START_INPUT" =~ ^[Yy]$ ]]; then
exec ./start.sh
else
echo "To start all services later, run: ./start.sh"
fi