mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
feat(cli): ✨ Add automatic setup script
This commit is contained in:
parent
36b25e0307
commit
083b77bbb9
399
scripts/versia-install.sh
Executable file
399
scripts/versia-install.sh
Executable file
|
|
@ -0,0 +1,399 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration variables
|
||||
RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 8)
|
||||
DOMAIN="versia-${RANDOM_SUFFIX}.localhost"
|
||||
INSTALL_DIR="./versia-install-${RANDOM_SUFFIX}"
|
||||
COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config/config.toml"
|
||||
CONFIG_EXAMPLE="${INSTALL_DIR}/config/config.example.toml"
|
||||
VERSION="v0.7.0"
|
||||
PORT=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
# Store container names
|
||||
CONTAINER_PREFIX="versia-${RANDOM_SUFFIX}"
|
||||
CONTAINER_NAMES=(
|
||||
"${CONTAINER_PREFIX}-server"
|
||||
"${CONTAINER_PREFIX}-fe"
|
||||
"${CONTAINER_PREFIX}-db"
|
||||
"${CONTAINER_PREFIX}-redis"
|
||||
)
|
||||
|
||||
# Function to log messages
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check for required commands
|
||||
check_requirements() {
|
||||
local required_commands=("docker" "docker-compose" "curl" "mkcert")
|
||||
|
||||
for cmd in "${required_commands[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
error "$cmd is required but not installed. Please install it first."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Create necessary directories
|
||||
setup_directories() {
|
||||
log "Creating installation directories..."
|
||||
mkdir -p "${INSTALL_DIR}"/{config,logs,uploads,store,redis-data,db-data}
|
||||
}
|
||||
|
||||
# Generate SSL certificates using mkcert
|
||||
setup_ssl() {
|
||||
log "Setting up SSL certificates..."
|
||||
|
||||
# Initialize mkcert if not already done
|
||||
mkcert -install
|
||||
|
||||
# Generate certificates for the domain
|
||||
cd "${INSTALL_DIR}"
|
||||
mkcert "${DOMAIN}"
|
||||
|
||||
# Create nginx config directory if it doesn't exist
|
||||
mkdir -p "${INSTALL_DIR}/nginx"
|
||||
}
|
||||
|
||||
# Download necessary files
|
||||
download_files() {
|
||||
log "Downloading configuration files..."
|
||||
|
||||
# Download docker-compose.yml
|
||||
curl -sSL "https://raw.githubusercontent.com/versia-pub/server/${VERSION}/docker-compose.yml" -o "${COMPOSE_FILE}"
|
||||
|
||||
# Download config.example.toml
|
||||
curl -sSL "https://raw.githubusercontent.com/versia-pub/server/${VERSION}/config/config.example.toml" -o "${INSTALL_DIR}/config/config.example.toml"
|
||||
}
|
||||
|
||||
# Generate random passwords
|
||||
generate_passwords() {
|
||||
POSTGRES_PASSWORD=$(openssl rand -base64 32)
|
||||
REDIS_PASSWORD=$(openssl rand -base64 32)
|
||||
}
|
||||
|
||||
# Configure Versia config.toml
|
||||
configure_config_file() {
|
||||
log "Configuring config.toml..."
|
||||
|
||||
cat > "${CONFIG_FILE}" << EOF
|
||||
[database]
|
||||
host = "db"
|
||||
port = 5432
|
||||
username = "versia"
|
||||
password = "${POSTGRES_PASSWORD}"
|
||||
database = "versia"
|
||||
|
||||
[redis.queue]
|
||||
host = "redis"
|
||||
port = 6379
|
||||
password = "${REDIS_PASSWORD}"
|
||||
database = 0
|
||||
enabled = true
|
||||
|
||||
[redis.cache]
|
||||
host = "redis"
|
||||
port = 6379
|
||||
password = "${REDIS_PASSWORD}"
|
||||
database = 1
|
||||
enabled = true
|
||||
|
||||
[sonic]
|
||||
host = "sonic"
|
||||
port = 1491
|
||||
password = ""
|
||||
enabled = false
|
||||
|
||||
[smtp]
|
||||
# SMTP server to use for sending emails
|
||||
server = "smtp.example.com"
|
||||
port = 465
|
||||
username = "test@example.com"
|
||||
password = "password123"
|
||||
tls = true
|
||||
# Disable all email functions (this will allow people to sign up without verifying
|
||||
# their email)
|
||||
enabled = false
|
||||
|
||||
[filters]
|
||||
# Regex filters for federated and local data
|
||||
# Drops data matching the filters
|
||||
# Does not apply retroactively to existing data
|
||||
|
||||
# Note contents
|
||||
note_content = [
|
||||
# "(https?://)?(www\\.)?youtube\\.com/watch\\?v=[a-zA-Z0-9_-]+",
|
||||
# "(https?://)?(www\\.)?youtu\\.be/[a-zA-Z0-9_-]+",
|
||||
]
|
||||
emoji = []
|
||||
# These will drop users matching the filters
|
||||
username = []
|
||||
displayname = []
|
||||
bio = []
|
||||
|
||||
[ratelimits]
|
||||
# These settings apply to every route at once
|
||||
# Amount to multiply every route's duration by
|
||||
duration_coeff = 1.0
|
||||
# Amount to multiply every route's max requests per [duration] by
|
||||
max_coeff = 1.0
|
||||
|
||||
[ratelimits.custom]
|
||||
# Add in any API route in this style here
|
||||
# Applies before the global ratelimit changes
|
||||
# "/api/v1/accounts/:id/block" = { duration = 30, max = 60 }
|
||||
# "/api/v1/timelines/public" = { duration = 60, max = 200 }
|
||||
|
||||
[signups]
|
||||
registration = true
|
||||
rules = [
|
||||
"Do not harass others",
|
||||
"Be nice to people",
|
||||
"Don't spam",
|
||||
"Don't post illegal content",
|
||||
]
|
||||
|
||||
[http]
|
||||
base_url = "https://${DOMAIN}:${PORT}"
|
||||
bind = "0.0.0.0"
|
||||
bind_port = ${PORT}
|
||||
|
||||
[http.tls]
|
||||
enabled = true
|
||||
key = "/app/dist/config/${DOMAIN}-key.pem"
|
||||
cert = "/app/dist/config/${DOMAIN}.pem"
|
||||
|
||||
[frontend]
|
||||
enabled = true
|
||||
url = "http://fe:3000"
|
||||
|
||||
[media]
|
||||
backend = "local"
|
||||
deduplicate_media = true
|
||||
local_uploads_folder = "uploads"
|
||||
|
||||
[media.conversion]
|
||||
convert_images = true
|
||||
convert_to = "image/webp"
|
||||
convert_vector = false
|
||||
|
||||
[validation]
|
||||
max_displayname_size = 50
|
||||
max_bio_size = 5000
|
||||
max_note_size = 5000
|
||||
max_avatar_size = 5000000
|
||||
max_header_size = 5000000
|
||||
max_media_size = 40000000
|
||||
max_media_attachments = 10
|
||||
max_media_description_size = 1000
|
||||
max_poll_options = 20
|
||||
max_poll_option_size = 500
|
||||
min_poll_duration = 60
|
||||
max_poll_duration = 1893456000
|
||||
max_username_size = 30
|
||||
max_field_count = 10
|
||||
max_field_name_size = 1000
|
||||
max_field_value_size = 1000
|
||||
|
||||
[validation.challenges]
|
||||
# "Challenges" (aka captchas) are a way to verify that a user is human
|
||||
# Versia Server's challenges use no external services, and are Proof of Work based
|
||||
# This means that they do not require any user interaction, instead
|
||||
# they require the user's computer to do a small amount of work
|
||||
enabled = false
|
||||
# The difficulty of the challenge, higher is will take more time to solve
|
||||
difficulty = 50000
|
||||
# Challenge expiration time in seconds
|
||||
expiration = 300 # 5 minutes
|
||||
# Leave this empty to generate a new key
|
||||
key = ""
|
||||
|
||||
[instance]
|
||||
name = "Local Versia Instance"
|
||||
description = "A local development instance of Versia Server"
|
||||
|
||||
[instance.keys]
|
||||
public = "MCowBQYDK2VwAyEA39sO9bdtrZbyQ5+tKdQf4VIU/PY+Y5Zx7+3JL5Omxno="
|
||||
private = "MC4CAQAwBQYDK2VwBCIEIMZXcDkHIqRFUmmZVw04l7nZjkzwlXfnQbH5iT1XCYVn"
|
||||
|
||||
[logging]
|
||||
log_level = "debug"
|
||||
log_ip = false
|
||||
|
||||
[logging.storage]
|
||||
requests = "logs/requests.log"
|
||||
|
||||
[plugins.config."@versia/openid".keys]
|
||||
public = "MCowBQYDK2VwAyEAfyZx8r98gVHtdH5EF1NYrBeChOXkt50mqiwKO2TX0f8="
|
||||
private = "MC4CAQAwBQYDK2VwBCIEILDi1g7+bwNjBBvL4CRWHZpCFBR2m2OPCot62Wr+TCbq"
|
||||
EOF
|
||||
}
|
||||
|
||||
# Create a new docker-compose.yml with our modifications
|
||||
create_docker_compose() {
|
||||
cat > "${COMPOSE_FILE}" << EOF
|
||||
services:
|
||||
versia:
|
||||
image: ghcr.io/versia-pub/server:main
|
||||
volumes:
|
||||
- ./logs:/app/dist/logs
|
||||
- ./config:/app/dist/config
|
||||
- ./uploads:/app/dist/uploads
|
||||
restart: unless-stopped
|
||||
container_name: ${CONTAINER_NAMES[0]}
|
||||
tty: true
|
||||
ports:
|
||||
- ${PORT}:${PORT}
|
||||
networks:
|
||||
- ${CONTAINER_PREFIX}-net
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
- fe
|
||||
|
||||
fe:
|
||||
image: ghcr.io/versia-pub/frontend:main
|
||||
container_name: ${CONTAINER_NAMES[1]}
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- ${CONTAINER_PREFIX}-net
|
||||
environment:
|
||||
NUXT_PUBLIC_API_HOST: https://${DOMAIN}:${PORT}
|
||||
|
||||
db:
|
||||
image: ghcr.io/versia-pub/postgres:main
|
||||
container_name: ${CONTAINER_NAMES[2]}
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: versia
|
||||
POSTGRES_USER: versia
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
networks:
|
||||
- ${CONTAINER_PREFIX}-net
|
||||
volumes:
|
||||
- ./db-data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
container_name: ${CONTAINER_NAMES[3]}
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- ./redis-data:/data
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- ${CONTAINER_PREFIX}-net
|
||||
|
||||
networks:
|
||||
${CONTAINER_PREFIX}-net:
|
||||
EOF
|
||||
}
|
||||
|
||||
# Function to create a new user and set password
|
||||
create_user() {
|
||||
local username="$1"
|
||||
local password="$2"
|
||||
|
||||
log "Creating user: ${username}"
|
||||
# Set the password using a heredoc to provide input
|
||||
docker exec -i "${CONTAINER_NAMES[0]}" /bin/sh /app/entrypoint.sh cli user create "${username}" --password "${password}"
|
||||
}
|
||||
|
||||
# Configure the services
|
||||
configure_services() {
|
||||
log "Configuring services..."
|
||||
|
||||
# Configure config.toml
|
||||
configure_config_file
|
||||
|
||||
# Create new docker-compose.yml with our modifications
|
||||
create_docker_compose
|
||||
|
||||
# Copy SSL certificates to config directory
|
||||
cp "${INSTALL_DIR}/${DOMAIN}.pem" "${INSTALL_DIR}/config/"
|
||||
cp "${INSTALL_DIR}/${DOMAIN}-key.pem" "${INSTALL_DIR}/config/"
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
log "Cleaning up installation..."
|
||||
cd "${INSTALL_DIR}"
|
||||
docker-compose down -v
|
||||
rm -rf "${INSTALL_DIR}"
|
||||
log "Cleanup complete!"
|
||||
}
|
||||
|
||||
# Function to handle SIGINT (Ctrl+C)
|
||||
handle_interrupt() {
|
||||
log "Received interrupt signal. Cleaning up..."
|
||||
cleanup
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Main installation function
|
||||
install_versia() {
|
||||
log "Starting Versia installation..."
|
||||
|
||||
check_requirements
|
||||
setup_directories
|
||||
generate_passwords
|
||||
download_files
|
||||
setup_ssl
|
||||
configure_services
|
||||
|
||||
# Start the services
|
||||
log "Starting Versia services..."
|
||||
cd "${INSTALL_DIR}"
|
||||
docker-compose up -d
|
||||
|
||||
# Wait for services to be ready
|
||||
sleep 5
|
||||
|
||||
# Create a default test user
|
||||
TEST_USER="testuser_${RANDOM_SUFFIX}"
|
||||
TEST_PASSWORD=$(openssl rand -base64 12)
|
||||
create_user "${TEST_USER}" "${TEST_PASSWORD}"
|
||||
|
||||
log "Installation complete! Versia is now available at https://${DOMAIN}:${PORT}"
|
||||
log "Installation Details:"
|
||||
log "---------------------"
|
||||
log "Domain: ${DOMAIN}"
|
||||
log "Port: ${PORT}"
|
||||
log "Installation Directory: ${INSTALL_DIR}"
|
||||
log "Test User: ${TEST_USER}"
|
||||
log "Test Password: ${TEST_PASSWORD}"
|
||||
log "PostgreSQL Password: ${POSTGRES_PASSWORD}"
|
||||
log "Redis Password: ${REDIS_PASSWORD}"
|
||||
log "Container Names:"
|
||||
for name in "${CONTAINER_NAMES[@]}"; do
|
||||
log " - ${name}"
|
||||
done
|
||||
log "---------------------"
|
||||
log "To create additional users, use:"
|
||||
log "docker-compose exec -it ${CONTAINER_NAMES[0]} /bin/sh /app/entrypoint.sh cli user create <username> --set-password"
|
||||
log "---------------------"
|
||||
log "Press Ctrl+C to stop and cleanup the installation"
|
||||
|
||||
# Set up interrupt handler
|
||||
trap handle_interrupt SIGINT
|
||||
|
||||
# Wait indefinitely
|
||||
while true; do
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
# Run the installation
|
||||
install_versia
|
||||
Loading…
Reference in a new issue