diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 000000000..dba9fffc1
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+# deploy.sh - Complete deployment script for Hetzner with Docker Hub and R2
+# This script:
+# 1. Builds and uploads the Docker image to Docker Hub with appropriate tag
+# 2. Copies the update script to Hetzner server
+# 3. Executes the update script on the Hetzner server
+
+set -e # Exit immediately if a command exits with a non-zero status
+
+# Function to print section headers
+print_header() {
+ echo "======================================================"
+ echo "🚀 $1"
+ echo "======================================================"
+}
+
+# Load environment variables
+if [ -f .env ]; then
+ echo "Loading configuration from .env file..."
+ export $(grep -v '^#' .env | xargs)
+fi
+
+# Check command line argument
+if [ $# -ne 1 ] || ([ "$1" != "staging" ] && [ "$1" != "prod" ]); then
+ echo "Error: Please specify environment (staging or prod)"
+ echo "Usage: $0 [staging|prod]"
+ exit 1
+fi
+
+# TODO: fix this - need to build before creating the image
+bun run build-prod
+
+ENV=$1
+VERSION_TAG="latest"
+DOCKER_REPO=""
+
+# Set environment-specific variables
+if [ "$ENV" == "staging" ]; then
+ print_header "DEPLOYING TO STAGING ENVIRONMENT"
+ SERVER_HOST=$SERVER_HOST_STAGING
+ DOCKER_REPO=$DOCKER_REPO_STAGING
+else
+ print_header "DEPLOYING TO PRODUCTION ENVIRONMENT"
+ SERVER_HOST=$SERVER_HOST_PROD
+ DOCKER_REPO=$DOCKER_REPO_PROD
+fi
+
+# Check required environment variables
+if [ -z "$SERVER_HOST" ]; then
+ echo "Error: SERVER_HOST_${ENV^^} not defined in .env file or environment"
+ exit 1
+fi
+
+# Configuration
+SSH_KEY=${SSH_KEY:-"~/.ssh/id_rsa"} # Use default or override from .env
+DOCKER_USERNAME=${DOCKER_USERNAME} # Docker Hub username
+UPDATE_SCRIPT="./update.sh" # Path to your update script
+REMOTE_UPDATE_SCRIPT="/root/update-openfront.sh" # Where to place the script on server
+
+# Check if update script exists
+if [ ! -f "$UPDATE_SCRIPT" ]; then
+ echo "Error: Update script $UPDATE_SCRIPT not found!"
+ exit 1
+fi
+
+# Step 1: Build and upload Docker image to Docker Hub
+print_header "STEP 1: Building and uploading Docker image to Docker Hub"
+echo "Environment: ${ENV}"
+echo "Using version tag: $VERSION_TAG"
+echo "Docker repository: $DOCKER_REPO"
+
+# Get Git commit for build info
+GIT_COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
+echo "Git commit: $GIT_COMMIT"
+
+docker buildx build \
+ --no-cache \
+ --platform linux/amd64 \
+ --build-arg GIT_COMMIT=$GIT_COMMIT \
+ -t $DOCKER_USERNAME/$DOCKER_REPO:$VERSION_TAG \
+ --push \
+ .
+
+if [ $? -ne 0 ]; then
+ echo "❌ Docker build failed. Stopping deployment."
+ exit 1
+fi
+
+if [ $? -ne 0 ]; then
+ echo "❌ Failed to push image to Docker Hub. Stopping deployment."
+ exit 1
+fi
+
+echo "✅ Docker image built and pushed successfully."
+
+# Step 2: Copy update script to Hetzner server
+print_header "STEP 2: Copying update script to server"
+echo "Target: $SERVER_HOST"
+
+# Make sure the update script is executable
+chmod +x $UPDATE_SCRIPT
+
+# Copy the update script to the server
+scp -i $SSH_KEY $UPDATE_SCRIPT $SERVER_HOST:$REMOTE_UPDATE_SCRIPT
+
+# Copy environment variables if needed
+if [ -f .env ]; then
+ scp -i $SSH_KEY .env $SERVER_HOST:/root/.env
+ # Secure the .env file
+ ssh -i $SSH_KEY $SERVER_HOST "chmod 600 /root/.env"
+fi
+
+if [ $? -ne 0 ]; then
+ echo "❌ Failed to copy update script to server. Stopping deployment."
+ exit 1
+fi
+
+echo "✅ Update script successfully copied to server."
+
+# Step 3: Execute the update script on the server
+print_header "STEP 3: Executing update script on server"
+
+# Make the script executable on the remote server and execute it with the environment parameter
+ssh -i $SSH_KEY $SERVER_HOST "chmod +x $REMOTE_UPDATE_SCRIPT && $REMOTE_UPDATE_SCRIPT $ENV $DOCKER_USERNAME $DOCKER_REPO"
+
+if [ $? -ne 0 ]; then
+ echo "❌ Failed to execute update script on server."
+ exit 1
+fi
+
+print_header "DEPLOYMENT COMPLETED SUCCESSFULLY"
+echo "✅ New version deployed to ${ENV} environment!"
+echo "🌐 Check your ${ENV} server to verify the deployment."
+echo "======================================================="
\ No newline at end of file
diff --git a/openfront-setup.sh b/openfront-setup.sh
deleted file mode 100644
index 33b428eff..000000000
--- a/openfront-setup.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-# Executed on ec2 startup
-
-yum update -y
-amazon-linux-extras install docker -y
-service docker start
-systemctl enable docker
-usermod -a -G docker ec2-user
-
-# Install AWS CLI v2
-curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
-unzip awscliv2.zip
-./aws/install
-
-# Authenticate to ECR and run container
-AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
-aws ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-1.amazonaws.com
-
-docker pull ${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-1.amazonaws.com/openfront:latest
-docker run -d -p 80:80 ${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-1.amazonaws.com/openfront:latest
\ No newline at end of file
diff --git a/setup.sh b/setup.sh
new file mode 100644
index 000000000..b6af5bc39
--- /dev/null
+++ b/setup.sh
@@ -0,0 +1,214 @@
+#!/bin/bash
+# Comprehensive idempotent setup script for Hetzner server with Docker, Docker Compose, and Cloudflare R2 configuration
+# Exit on error
+set -e
+
+echo 'export EDITOR=vim' >> ~/.bashrc
+source ~/.bashrc
+
+echo "🔄 Updating system..."
+apt update && apt upgrade -y
+
+# Docker installation - check if already installed
+if command -v docker &> /dev/null; then
+ echo "✅ Docker is already installed"
+else
+ echo "🐳 Installing Docker..."
+ # Install Docker using official script
+ curl -fsSL https://get.docker.com -o get-docker.sh
+ sh get-docker.sh
+ rm get-docker.sh
+ # Make sure Docker is enabled to start at boot
+ systemctl enable docker
+ echo "✅ Docker installed successfully"
+fi
+
+# Check if Docker is running
+if systemctl is-active --quiet docker; then
+ echo "✅ Docker service is already running"
+else
+ echo "🚀 Starting Docker service..."
+ systemctl start docker
+ echo "✅ Docker service started"
+fi
+
+# Docker Compose v1 installation - check if already installed
+if command -v docker-compose &> /dev/null; then
+ echo "✅ Docker Compose v1 is already installed"
+else
+ echo "🔧 Installing Docker Compose v1..."
+ # Get latest docker compose version
+ COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
+ # Install Docker Compose v1
+ curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+ chmod +x /usr/local/bin/docker-compose
+ echo "✅ Docker Compose v1 installed successfully"
+fi
+
+# Docker Compose v2 installation - check if already installed
+if command -v docker compose &> /dev/null; then
+ echo "✅ Docker Compose plugin (v2) is already installed"
+else
+ echo "🔧 Installing Docker Compose plugin (v2)..."
+ # Install Docker Compose v2
+ apt install -y docker-compose-plugin
+ echo "✅ Docker Compose plugin (v2) installed successfully"
+fi
+
+# Verify Docker Compose installations
+echo "Verifying Docker Compose installations..."
+echo "Docker Compose v1:"
+docker-compose --version
+echo "Docker Compose v2:"
+docker compose version
+
+# Docker Hub login - only prompt if not already logged in
+if [ ! -f ~/.docker/config.json ] || ! grep -q "auth" ~/.docker/config.json; then
+ echo "🔐 Setting up Docker Hub login..."
+ docker login
+ echo "✅ Docker Hub login configured"
+else
+ echo "✅ Docker Hub login already configured"
+fi
+
+# AWS CLI installation - check if already installed
+if command -v aws &> /dev/null; then
+ echo "✅ AWS CLI is already installed"
+else
+ echo "☁️ Installing AWS CLI for Cloudflare R2..."
+ # Install AWS CLI
+ apt install -y unzip curl
+ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
+ unzip awscliv2.zip
+ ./aws/install
+ rm -rf aws awscliv2.zip
+ echo "✅ AWS CLI installed successfully"
+fi
+
+# R2 configuration - check if already configured
+if [ -f ~/.aws/credentials ] && grep -q "\[r2\]" ~/.aws/credentials; then
+ echo "✅ R2 configuration already exists"
+ echo "Do you want to update the R2 configuration? (y/n)"
+ read update_r2
+ if [ "$update_r2" = "y" ]; then
+ configure_r2=true
+ else
+ configure_r2=false
+ fi
+else
+ configure_r2=true
+fi
+
+if [ "$configure_r2" = true ]; then
+ # Configure AWS CLI for R2
+ echo "🔧 Configuring AWS CLI for Cloudflare R2..."
+ echo "Enter your Cloudflare R2 Access Key ID:"
+ read R2_ACCESS_KEY
+ echo "Enter your Cloudflare R2 Secret Access Key:"
+ read -s R2_SECRET_KEY
+ echo "Enter your Cloudflare Account ID:"
+ read CLOUDFLARE_ACCOUNT_ID
+
+ # Create R2 profile configuration
+ mkdir -p ~/.aws
+
+ # Update or create credentials file
+ if [ -f ~/.aws/credentials ]; then
+ # Remove existing r2 section if it exists
+ sed -i '/\[r2\]/,/^$/d' ~/.aws/credentials
+ fi
+
+ # Append r2 credentials
+ cat >> ~/.aws/credentials << EOL
+[r2]
+aws_access_key_id = $R2_ACCESS_KEY
+aws_secret_access_key = $R2_SECRET_KEY
+EOL
+
+ # Update or create config file
+ if [ -f ~/.aws/config ]; then
+ # Remove existing r2 profile if it exists
+ sed -i '/\[profile r2\]/,/^$/d' ~/.aws/config
+ fi
+
+ # Append r2 config
+ cat >> ~/.aws/config << EOL
+[profile r2]
+region = auto
+endpoint_url = https://$CLOUDFLARE_ACCOUNT_ID.r2.cloudflarestorage.com
+EOL
+ echo "✅ R2 configuration complete"
+fi
+
+# Setting up Node Exporter for system metrics
+echo "📊 Setting up Node Exporter..."
+
+# Create a monitoring network if it doesn't exist
+if ! docker network inspect monitoring &>/dev/null; then
+ echo "Creating monitoring network..."
+ docker network create monitoring
+else
+ echo "✅ Monitoring network already exists"
+fi
+
+# Check if Node Exporter is already running correctly
+if docker ps | grep -q "node_exporter"; then
+ echo "✅ Node Exporter is already running"
+else
+ # Remove existing container if it exists but not running
+ if docker ps -a | grep -q node_exporter; then
+ echo "Removing existing stopped Node Exporter container..."
+ docker rm -f node_exporter
+ fi
+
+ # Run Node Exporter container
+ echo "Starting Node Exporter..."
+ docker run -d \
+ --name node_exporter \
+ --restart unless-stopped \
+ --network monitoring \
+ -p 9100:9100 \
+ -v "/proc:/host/proc:ro" \
+ -v "/sys:/host/sys:ro" \
+ -v "/:/rootfs:ro" \
+ prom/node-exporter:latest \
+ --path.procfs=/host/proc \
+ --path.sysfs=/host/sys \
+ --path.rootfs=/rootfs \
+ --collector.filesystem.mount-points-exclude="^/(sys|proc|dev|host|etc)($$|/)"
+ echo "✅ Node Exporter is now running and exposing metrics on port 9100"
+fi
+
+# Setting up Loki Docker driver for log collection
+echo "📜 Setting up Loki Docker driver..."
+
+# Check if plugin is already installed and up to date
+if docker plugin ls | grep -q "loki.*latest.*true"; then
+ echo "✅ Loki Docker driver is already installed and enabled"
+else
+ # Remove plugin if it exists but not up to date or not enabled
+ if docker plugin ls | grep -q "loki"; then
+ echo "Updating Loki Docker driver..."
+ docker plugin disable -f loki
+ docker plugin rm -f loki
+ fi
+
+ # Install Loki Docker driver
+ echo "Installing Loki Docker driver..."
+ docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
+ echo "✅ Loki Docker driver installed successfully!"
+fi
+
+echo "Note: Configure your containers with the Loki logging driver by adding this to your docker-compose.yml:"
+echo "
+ logging:
+ driver: loki
+ options:
+ loki-url: \"http://your-loki-server:3100/loki/api/v1/push\"
+ loki-batch-size: \"400\"
+ loki-external-labels: \"job=your_app,environment=production\"
+"
+
+echo "🎉 Setup complete!"
+echo "Test your R2 connection: aws s3 ls --profile r2"
+echo "Metrics available at: http://$(hostname -I | awk '{print $1}'):9100/metrics"
\ No newline at end of file
diff --git a/src/client/index.html b/src/client/index.html
index 49e6fac7f..41cdf8665 100644
--- a/src/client/index.html
+++ b/src/client/index.html
@@ -203,7 +203,7 @@
- v0.17.6
+ v0.17.7
diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts
index 32da46374..13b413fce 100644
--- a/src/core/configuration/Config.ts
+++ b/src/core/configuration/Config.ts
@@ -98,6 +98,13 @@ export interface ServerConfig {
workerPort(gameID: GameID): number;
workerPortByIndex(workerID: number): number;
env(): GameEnv;
+ adminToken(): string;
+ adminHeader(): string;
+ // Only available on the server
+ r2Bucket(): string;
+ r2Endpoint(): string;
+ r2AccessKey(): string;
+ r2SecretKey(): string;
}
export interface Config {
diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts
index 694489a56..09b4d751d 100644
--- a/src/core/configuration/DefaultConfig.ts
+++ b/src/core/configuration/DefaultConfig.ts
@@ -22,6 +22,25 @@ import { pastelTheme } from "./PastelTheme";
import { pastelThemeDark } from "./PastelThemeDark";
export abstract class DefaultServerConfig implements ServerConfig {
+ gitCommit(): string {
+ return process.env.GIT_COMMIT;
+ }
+ r2Endpoint(): string {
+ return process.env.R2_ENDPOINT;
+ }
+ r2AccessKey(): string {
+ return process.env.R2_ACCESS_KEY;
+ }
+ r2SecretKey(): string {
+ return process.env.R2_SECRET_KEY;
+ }
+ abstract r2Bucket(): string;
+ adminHeader(): string {
+ return "x-admin-key";
+ }
+ adminToken(): string {
+ return process.env.ADMIN_TOKEN;
+ }
abstract numWorkers(): number;
abstract env(): GameEnv;
abstract discordRedirectURI(): string;
diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts
index abeb91ecf..639d7f57f 100644
--- a/src/core/configuration/DevConfig.ts
+++ b/src/core/configuration/DevConfig.ts
@@ -5,6 +5,13 @@ import { GameEnv, ServerConfig } from "./Config";
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
export class DevServerConfig extends DefaultServerConfig {
+ r2Bucket(): string {
+ return "openfront-staging";
+ }
+ adminToken(): string {
+ return "WARNING_DEV_ADMIN_KEY_DO_NOT_USE_IN_PRODUCTION";
+ }
+
env(): GameEnv {
return GameEnv.Dev;
}
diff --git a/src/core/configuration/PreprodConfig.ts b/src/core/configuration/PreprodConfig.ts
index 3b1aa061f..9340053d4 100644
--- a/src/core/configuration/PreprodConfig.ts
+++ b/src/core/configuration/PreprodConfig.ts
@@ -2,6 +2,9 @@ import { GameEnv } from "./Config";
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
export const preprodConfig = new (class extends DefaultServerConfig {
+ r2Bucket(): string {
+ return "openfront-staging";
+ }
env(): GameEnv {
return GameEnv.Preprod;
}
diff --git a/src/core/configuration/ProdConfig.ts b/src/core/configuration/ProdConfig.ts
index 71b9bd122..8d6860358 100644
--- a/src/core/configuration/ProdConfig.ts
+++ b/src/core/configuration/ProdConfig.ts
@@ -2,6 +2,9 @@ import { GameEnv } from "./Config";
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
export const prodConfig = new (class extends DefaultServerConfig {
+ r2Bucket(): string {
+ return "openfront-prod";
+ }
numWorkers(): number {
return 6;
}
diff --git a/src/server/Archive.ts b/src/server/Archive.ts
index 01094599f..143fc018d 100644
--- a/src/server/Archive.ts
+++ b/src/server/Archive.ts
@@ -1,6 +1,5 @@
import { GameRecord, GameID } from "../core/Schemas";
import { S3 } from "@aws-sdk/client-s3";
-import { RedshiftData } from "@aws-sdk/client-redshift-data";
import {
GameEnv,
getServerConfigFromServer,
@@ -8,22 +7,31 @@ import {
const config = getServerConfigFromServer();
-const s3 = new S3({ region: "eu-west-1" });
+// R2 client configuration
+const r2 = new S3({
+ region: "auto", // R2 ignores region, but it's required by the SDK
+ endpoint: config.r2Endpoint(), // You'll need to add this to your config
+ credentials: {
+ accessKeyId: config.r2AccessKey(), // You'll need to add these
+ secretAccessKey: config.r2SecretKey(), // credential methods to your config
+ },
+});
-const gameBucket = "openfront-games";
-const analyticsBucket = "openfront-analytics";
+const bucket = config.r2Bucket();
+const gameFolder = "games";
+const analyticsFolder = "analytics";
export async function archive(gameRecord: GameRecord) {
try {
- // Archive to Redshift Serverless
- await archiveAnalyticsToS3(gameRecord);
+ // Archive to R2
+ await archiveAnalyticsToR2(gameRecord);
- // Archive to S3 if there are turns
+ // Archive full game if there are turns
if (gameRecord.turns.length > 0) {
console.log(
- `${gameRecord.id}: game has more than zero turns, attempting to write to full game to S3`,
+ `${gameRecord.id}: game has more than zero turns, attempting to write to full game to R2`,
);
- await archiveFullGameToS3(gameRecord);
+ await archiveFullGameToR2(gameRecord);
}
} catch (error) {
console.error(`${gameRecord.id}: Final archive error: ${error}`, {
@@ -35,8 +43,8 @@ export async function archive(gameRecord: GameRecord) {
}
}
-async function archiveAnalyticsToS3(gameRecord: GameRecord) {
- // Create analytics data object (similar to what was going to Redshift)
+async function archiveAnalyticsToR2(gameRecord: GameRecord) {
+ // Create analytics data object
const analyticsData = {
id: gameRecord.id,
env: config.env(),
@@ -60,17 +68,17 @@ async function archiveAnalyticsToS3(gameRecord: GameRecord) {
// Store analytics data using just the game ID as the key
const analyticsKey = `${gameRecord.id}.json`;
- await s3.putObject({
- Bucket: analyticsBucket,
- Key: analyticsKey,
+ await r2.putObject({
+ Bucket: bucket,
+ Key: `${analyticsFolder}/${analyticsKey}`,
Body: JSON.stringify(analyticsData),
ContentType: "application/json",
});
- console.log(`${gameRecord.id}: successfully wrote game analytics to S3`);
+ console.log(`${gameRecord.id}: successfully wrote game analytics to R2`);
} catch (error) {
console.error(
- `${gameRecord.id}: Error writing game analytics to S3: ${error}`,
+ `${gameRecord.id}: Error writing game analytics to R2: ${error}`,
{
message: error?.message || error,
stack: error?.stack,
@@ -82,7 +90,7 @@ async function archiveAnalyticsToS3(gameRecord: GameRecord) {
}
}
-async function archiveFullGameToS3(gameRecord: GameRecord) {
+async function archiveFullGameToR2(gameRecord: GameRecord) {
// Create a deep copy to avoid modifying the original
const recordCopy = JSON.parse(JSON.stringify(gameRecord));
@@ -93,9 +101,9 @@ async function archiveFullGameToS3(gameRecord: GameRecord) {
});
try {
- await s3.putObject({
- Bucket: gameBucket,
- Key: recordCopy.id,
+ await r2.putObject({
+ Bucket: bucket,
+ Key: `${gameFolder}/${recordCopy.id}`,
Body: JSON.stringify(recordCopy),
ContentType: "application/json",
});
@@ -104,15 +112,15 @@ async function archiveFullGameToS3(gameRecord: GameRecord) {
throw error;
}
- console.log(`${gameRecord.id}: game record successfully written to S3`);
+ console.log(`${gameRecord.id}: game record successfully written to R2`);
}
export async function readGameRecord(gameId: GameID): Promise {
try {
// Check if file exists and download in one operation
- const response = await s3.getObject({
- Bucket: gameBucket,
- Key: gameId,
+ const response = await r2.getObject({
+ Bucket: bucket,
+ Key: `${gameFolder}/${gameId}`, // Fixed - needed to include gameFolder
});
// Parse the response body
@@ -121,7 +129,8 @@ export async function readGameRecord(gameId: GameID): Promise {
return gameRecord as GameRecord;
} catch (error) {
- console.error(`${gameId}: Error reading game record from S3: ${error}`, {
+ // Log the error for monitoring purposes
+ console.error(`${gameId}: Error reading game record from R2: ${error}`, {
message: error?.message || error,
stack: error?.stack,
name: error?.name,
@@ -133,9 +142,9 @@ export async function readGameRecord(gameId: GameID): Promise {
export async function gameRecordExists(gameId: GameID): Promise {
try {
- await s3.headObject({
- Bucket: gameBucket,
- Key: gameId,
+ await r2.headObject({
+ Bucket: bucket,
+ Key: `${gameFolder}/${gameId}`, // Fixed - needed to include gameFolder
});
return true;
} catch (error) {
diff --git a/update-deploy.sh b/update-deploy.sh
deleted file mode 100755
index ca46381f5..000000000
--- a/update-deploy.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/bash
-# Check if the --env flag is provided
-if [[ "$1" != "--env" ]]; then
- echo "Usage: $0 --env [dev|prod]"
- exit 1
-fi
-
-# Get the environment from the command line argument
-ENV="$2"
-
-# Validate the environment
-if [[ "$ENV" != "dev" && "$ENV" != "prod" ]]; then
- echo "Invalid environment. Use 'dev' or 'prod'."
- exit 1
-fi
-
-# Set the instance name based on the environment
-if [[ "$ENV" == "dev" ]]; then
- INSTANCE_NAME="openfrontio-dev-instance"
- TAG="dev"
- GAME_ENV="preprod"
- echo "[DEV] Deploying to openfront.dev"
-else
- INSTANCE_NAME="openfrontio-instance"
- TAG="latest"
- GAME_ENV="prod"
- echo "[PROD] Deploying to openfront.io"
-fi
-
-# Ensure you're authenticated with Google Cloud
-gcloud auth configure-docker us-central1-docker.pkg.dev
-
-# Build the new Docker image with platform specification and GAME_ENV build argument
-docker build --platform linux/amd64 --build-arg GAME_ENV=$GAME_ENV -t openfrontio .
-
-# Tag the new image
-docker tag openfrontio us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$TAG
-
-# Push the new image to Google Container Registry
-docker push us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$TAG
-
-# Prune Docker system on the instance
-gcloud compute ssh $INSTANCE_NAME --zone us-central1-a --command 'docker system prune -f -a'
-gcloud compute ssh $INSTANCE_NAME --zone us-central1-a --command 'docker kill $(docker ps -q)'
-gcloud compute ssh $INSTANCE_NAME --zone us-central1-a --command 'docker rmi $(docker images -q) -f'
-
-# Update the GCE instance with the new container image
-gcloud compute instances update-container $INSTANCE_NAME \
- --container-image us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$TAG \
- --zone=us-central1-a
-
-echo "Deployment to $ENV environment complete. New version should be live soon on $INSTANCE_NAME."
\ No newline at end of file
diff --git a/update.sh b/update.sh
index 5f0d6c50c..230cd2449 100755
--- a/update.sh
+++ b/update.sh
@@ -1,28 +1,38 @@
#!/bin/bash
-# Script to update Docker container
+# update.sh - Script to update Docker container on Hetzner server
+# Called by deploy.sh after uploading Docker image to Docker Hub
# Check if environment parameter is provided
-if [ -z "$1" ]; then
- echo "Error: Environment parameter is required (prod or staging)"
- echo "Usage: $0 "
+if [ $# -lt 3 ]; then
+ echo "Error: Required parameters missing"
+ echo "Usage: $0 "
exit 1
fi
-# Set environment from parameter
+# Set parameters
ENV=$1
+DOCKER_USERNAME=$2
+DOCKER_REPO=$3
+
+# Container and image configuration
CONTAINER_NAME="openfront-${ENV}"
-LOG_GROUP="/aws/ec2/docker-containers/${ENV}"
+IMAGE_NAME="${DOCKER_USERNAME}/${DOCKER_REPO}"
+FULL_IMAGE_NAME="${IMAGE_NAME}:latest"
-# Get AWS account ID
-AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
-ECR_REPO="${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-1.amazonaws.com/openfront:latest"
+echo "======================================================"
+echo "🔄 UPDATING SERVER: ${ENV} ENVIRONMENT"
+echo "======================================================"
+echo "Container name: ${CONTAINER_NAME}"
+echo "Docker image: ${FULL_IMAGE_NAME}"
-echo "Deploying to ${ENV} environment..."
-echo "Logging in to ECR..."
-aws ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-1.amazonaws.com
+# Load environment variables if .env exists
+if [ -f /root/.env ]; then
+ echo "Loading environment variables from .env file..."
+ export $(grep -v '^#' /root/.env | xargs)
+fi
-echo "Pulling latest image..."
-docker pull $ECR_REPO
+echo "Pulling latest image from Docker Hub..."
+docker pull $FULL_IMAGE_NAME
echo "Checking for existing container..."
# Check for running container
@@ -64,17 +74,17 @@ fi
echo "Starting new container for ${ENV} environment..."
docker run -d -p 80:80 \
--restart=always \
- --log-driver=awslogs \
- --log-opt awslogs-region=eu-west-1 \
- --log-opt awslogs-group=${LOG_GROUP} \
- --log-opt awslogs-create-group=true \
+ $VOLUME_MOUNTS \
+ $NETWORK_FLAGS \
--env GAME_ENV=${ENV} \
+ --env-file /root/.env \
--name ${CONTAINER_NAME} \
- $ECR_REPO
+ $FULL_IMAGE_NAME
if [ $? -eq 0 ]; then
echo "Update complete! New ${ENV} container is running."
- # Final cleanup after successful deployment
+
+ # Final cleanup after successful deployment
echo "Performing final cleanup of unused Docker resources..."
echo "Removing unused images (not tagged and not referenced)..."
docker image prune -f
@@ -82,4 +92,11 @@ if [ $? -eq 0 ]; then
echo "Cleanup complete."
else
echo "Failed to start container"
-fi
\ No newline at end of file
+ exit 1
+fi
+
+echo "======================================================"
+echo "✅ SERVER UPDATE COMPLETED SUCCESSFULLY"
+echo "Container name: ${CONTAINER_NAME}"
+echo "Image: ${FULL_IMAGE_NAME}"
+echo "======================================================"
\ No newline at end of file
diff --git a/upload.sh b/upload.sh
deleted file mode 100755
index de92eca61..000000000
--- a/upload.sh
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/bin/bash
-
-# Script to build and upload OpenFront Docker image to ECR
-# Usage: ./upload-openfront.sh [version_tag]
-
-# Load environment variables from .env file if it exists
-if [ -f .env ]; then
- echo "Loading configuration from .env file..."
- export $(grep -v '^#' .env | xargs)
-fi
-
-# Configuration with fallbacks
-AWS_REGION=${AWS_REGION:-"eu-west-1"}
-ECR_REPO_NAME=${ECR_REPO_NAME:-"openfront"}
-AWS_ACCOUNT_ID=${AWS_ACCOUNT_ID:-$(aws sts get-caller-identity --query Account --output text)}
-ECR_REPO_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME"
-
-# Default version tag is 'latest' if not provided
-VERSION_TAG=${1:-"latest"}
-
-echo "===== OpenFront Docker Image Upload Script ====="
-echo "Repository: $ECR_REPO_URI"
-echo "Version tag: $VERSION_TAG"
-echo "================================================"
-
-# Check if Docker is installed
-if ! command -v docker &> /dev/null; then
- echo "Error: Docker is not installed. Please install Docker first."
- exit 1
-fi
-
-# Check if AWS CLI is installed
-if ! command -v aws &> /dev/null; then
- echo "Error: AWS CLI is not installed. Please install AWS CLI first."
- exit 1
-fi
-
-# Check if we're in the correct directory
-if [ ! -f "Dockerfile" ]; then
- echo "Error: Dockerfile not found in current directory."
- echo "Please run this script from the directory containing your Dockerfile."
- exit 1
-fi
-
-# Ensure the ECR repository exists
-echo "Ensuring ECR repository exists..."
-aws ecr describe-repositories --repository-names $ECR_REPO_NAME --region $AWS_REGION &> /dev/null
-if [ $? -ne 0 ]; then
- echo "Creating ECR repository $ECR_REPO_NAME..."
- aws ecr create-repository --repository-name $ECR_REPO_NAME --region $AWS_REGION
- if [ $? -ne 0 ]; then
- echo "Error: Failed to create ECR repository."
- exit 1
- fi
-fi
-
-# Build the Docker image
-echo "Building Docker image..."
-docker buildx build --platform linux/amd64 -t $ECR_REPO_NAME:$VERSION_TAG .
-if [ $? -ne 0 ]; then
- echo "Error: Docker build failed."
- exit 1
-fi
-
-# Authenticate to ECR
-echo "Authenticating to ECR..."
-aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO_URI
-if [ $? -ne 0 ]; then
- echo "Error: Failed to authenticate to ECR."
- exit 1
-fi
-
-# Tag the image for ECR
-echo "Tagging image for ECR..."
-docker tag $ECR_REPO_NAME:$VERSION_TAG $ECR_REPO_URI:$VERSION_TAG
-if [ $? -ne 0 ]; then
- echo "Error: Failed to tag image."
- exit 1
-fi
-
-# Push the image to ECR
-echo "Pushing image to ECR..."
-docker push $ECR_REPO_URI:$VERSION_TAG
-if [ $? -ne 0 ]; then
- echo "Error: Failed to push image to ECR."
- exit 1
-fi
-
-# Also tag and push as 'latest' if we're using a specific version
-if [ "$VERSION_TAG" != "latest" ]; then
- echo "Also tagging as 'latest'..."
- docker tag $ECR_REPO_NAME:$VERSION_TAG $ECR_REPO_URI:latest
- docker push $ECR_REPO_URI:latest
-fi
-
-echo "Verifying upload..."
-aws ecr describe-images --repository-name $ECR_REPO_NAME --region $AWS_REGION --query "imageDetails[?contains(imageTags, '$VERSION_TAG')]"
-
-echo "================================================"
-echo "✅ Success! Image uploaded to $ECR_REPO_URI:$VERSION_TAG"
-echo "================================================"
-
-# Print helpful deployment instructions
-echo "To deploy this image to your EC2 instance, SSH into your instance and run:"
-echo "docker pull $ECR_REPO_URI:$VERSION_TAG"
-echo "docker stop \$(docker ps -q --filter ancestor=$ECR_REPO_URI)"
-echo "docker run -d -p 80:80 $ECR_REPO_URI:$VERSION_TAG"
\ No newline at end of file