diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 800f08456..762f7ef2f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -126,8 +126,6 @@ jobs: chmod 600 ~/.ssh/id_rsa - name: 🚢 Deploy env: - CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} GHCR_REPO: ${{ vars.GHCR_REPO }} GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} ENV: ${{ inputs.target_domain == 'openfront.io' && 'prod' || 'staging' }} @@ -147,10 +145,12 @@ jobs: echo "Deployment created in ${SECONDS} seconds" >> $GITHUB_STEP_SUMMARY echo "::endgroup::" - name: ⏳ Wait for deployment to start + env: + API_KEY: ${{ secrets.API_KEY }} run: | echo "::group::Wait for deployment to start" set -euxo pipefail - while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do + while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do if [ "$SECONDS" -ge 300 ]; then echo "Timeout: deployment did not start within 5 minutes" exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5026e502..745d3018e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,8 +64,6 @@ jobs: chmod 600 ~/.ssh/id_rsa - name: 🚀 Deploy image env: - CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} GHCR_REPO: openfront-prod GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} @@ -82,10 +80,11 @@ jobs: - name: ⏳ Wait for deployment to start env: FQDN: alpha.${{ vars.DOMAIN }} + API_KEY: ${{ secrets.API_KEY }} run: | echo "::group::Wait for deployment to start" set -euxo pipefail - while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do + while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do if [ "$SECONDS" -ge 300 ]; then echo "Timeout: deployment did not start within 5 minutes" exit 1 @@ -115,8 +114,6 @@ jobs: chmod 600 ~/.ssh/id_rsa - name: 🚀 Deploy image env: - CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} GHCR_REPO: ${{ vars.GHCR_REPO }} GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} @@ -133,10 +130,11 @@ jobs: - name: ⏳ Wait for deployment to start env: FQDN: beta.${{ vars.DOMAIN }} + API_KEY: ${{ secrets.API_KEY }} run: | echo "::group::Wait for deployment to start" set -euxo pipefail - while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do + while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do if [ "$SECONDS" -ge 300 ]; then echo "Timeout: deployment did not start within 5 minutes" exit 1 @@ -166,8 +164,6 @@ jobs: chmod 600 ~/.ssh/id_rsa - name: 🚀 Deploy image env: - CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} GHCR_REPO: ${{ vars.GHCR_REPO }} GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} @@ -184,10 +180,11 @@ jobs: - name: ⏳ Wait for deployment to start env: FQDN: blue.${{ vars.DOMAIN }} + API_KEY: ${{ secrets.API_KEY }} run: | echo "::group::Wait for deployment to start" set -euxo pipefail - while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do + while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do if [ "$SECONDS" -ge 300 ]; then echo "Timeout: deployment did not start within 5 minutes" exit 1 @@ -217,8 +214,6 @@ jobs: chmod 600 ~/.ssh/id_rsa - name: 🚀 Deploy image env: - CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} GHCR_REPO: ${{ vars.GHCR_REPO }} GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} @@ -235,10 +230,11 @@ jobs: - name: ⏳ Wait for deployment to start env: FQDN: green.${{ vars.DOMAIN }} + API_KEY: ${{ secrets.API_KEY }} run: | echo "::group::Wait for deployment to start" set -euxo pipefail - while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do + while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do if [ "$SECONDS" -ge 300 ]; then echo "Timeout: deployment did not start within 5 minutes" exit 1 diff --git a/Dockerfile b/Dockerfile index 4bf00f01f..193f300b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,24 +38,14 @@ FROM base RUN apt-get update && apt-get install -y \ nginx \ curl \ - jq \ wget \ supervisor \ apache2-utils \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb > cloudflared.deb \ - && dpkg -i cloudflared.deb \ - && rm cloudflared.deb - # Update worker_connections in nginx.conf RUN sed -i 's/worker_connections [0-9]*/worker_connections 8192/' /etc/nginx/nginx.conf -# Create cloudflared directory with proper permissions -RUN mkdir -p /etc/cloudflared && \ - chown -R node:node /etc/cloudflared && \ - chmod -R 755 /etc/cloudflared - # Setup supervisor configuration RUN mkdir -p /var/log/supervisor COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf @@ -64,10 +54,6 @@ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY nginx.conf /etc/nginx/conf.d/default.conf RUN rm -f /etc/nginx/sites-enabled/default -# Copy and make executable the startup script -COPY startup.sh /usr/local/bin/ -RUN chmod +x /usr/local/bin/startup.sh - # Copy production node_modules from prod-deps stage (cached separately from build) COPY --from=prod-deps /usr/src/app/node_modules ./node_modules COPY package*.json ./ @@ -87,8 +73,14 @@ ARG GIT_COMMIT=unknown RUN echo "$GIT_COMMIT" > static/commit.txt ENV GIT_COMMIT="$GIT_COMMIT" -ENV CF_CONFIG_PATH=/etc/cloudflared/config.yml -ENV CF_CREDS_PATH=/etc/cloudflared/creds.json -# Use the startup script as the entrypoint -ENTRYPOINT ["/usr/local/bin/startup.sh"] +RUN <<'EOF' tee /usr/local/bin/start.sh +#!/bin/sh +if [ "$DOMAIN" = openfront.dev ] && [ "$SUBDOMAIN" != main ]; then + exec timeout 18h /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi +EOF +RUN chmod +x /usr/local/bin/start.sh +ENTRYPOINT ["/usr/local/bin/start.sh"] diff --git a/deploy.sh b/deploy.sh index 03bd9b488..e033b5693 100755 --- a/deploy.sh +++ b/deploy.sh @@ -134,8 +134,6 @@ ENV=$ENV HOST=$HOST GHCR_IMAGE=$GHCR_IMAGE GHCR_TOKEN=$GHCR_TOKEN -CF_ACCOUNT_ID=$CF_ACCOUNT_ID -CF_API_TOKEN=$CF_API_TOKEN TURNSTILE_SECRET_KEY=$TURNSTILE_SECRET_KEY API_KEY=$API_KEY DOMAIN=$DOMAIN diff --git a/example.env b/example.env index 38c3a95bd..68226d211 100644 --- a/example.env +++ b/example.env @@ -6,9 +6,6 @@ GHCR_USERNAME=username GHCR_REPO=your-repo-name GHCR_TOKEN=your_docker_token_here -# Cloudflare Configuration -CF_ACCOUNT_ID=your_cloudflare_account_id -CF_API_TOKEN=your_cloudflare_api_token DOMAIN=your-domain.com # API Key diff --git a/nginx.conf b/nginx.conf index 1465684f9..62952fb1c 100644 --- a/nginx.conf +++ b/nginx.conf @@ -12,7 +12,13 @@ proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=API_CACHE:10m inactiv server { listen 80 default_server; - + + # Large cookie support + large_client_header_buffers 4 32k; + proxy_buffer_size 32k; + proxy_busy_buffers_size 48k; + proxy_buffers 4 32k; + # Logging access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; diff --git a/src/client/graphics/layers/RadialMenuElements.ts b/src/client/graphics/layers/RadialMenuElements.ts index 1364bb41d..14e87c5e5 100644 --- a/src/client/graphics/layers/RadialMenuElements.ts +++ b/src/client/graphics/layers/RadialMenuElements.ts @@ -420,18 +420,19 @@ function createMenuElements( : !BuildableAttacks.has(item.unitType)), ) .map((item: BuildItemDisplay) => { - const canBuildOrUpgrade = params.buildMenu.canBuildOrUpgrade(item); return { id: `${elementIdPrefix}_${item.unitType}`, name: item.key ? item.key.replace("unit_type.", "") : item.unitType.toString(), - disabled: () => !canBuildOrUpgrade, - color: canBuildOrUpgrade - ? filterType === "attack" - ? COLORS.attack - : COLORS.building - : undefined, + disabled: (p: MenuElementParams) => + !p.buildMenu.canBuildOrUpgrade(item), + color: (p: MenuElementParams) => + p.buildMenu.canBuildOrUpgrade(item) + ? filterType === "attack" + ? COLORS.attack + : COLORS.building + : COLORS.building, icon: item.icon, tooltipItems: [ { text: translateText(item.key ?? ""), className: "title" }, @@ -456,7 +457,7 @@ function createMenuElements( if (buildableUnit === undefined) { return; } - if (canBuildOrUpgrade) { + if (params.buildMenu.canBuildOrUpgrade(item)) { params.buildMenu.sendBuildOrUpgrade(buildableUnit, params.tile); } params.closeMenu(); diff --git a/src/server/Archive.ts b/src/server/Archive.ts index b540f9c8d..7e89509cc 100644 --- a/src/server/Archive.ts +++ b/src/server/Archive.ts @@ -59,6 +59,7 @@ export async function readGameRecord( method: "GET", headers: { "Content-Type": "application/json", + "x-api-key": config.apiKey(), }, }); const record = await response.json(); diff --git a/src/server/GamePreviewRoute.ts b/src/server/GamePreviewRoute.ts index c9f3dd83c..c67551060 100644 --- a/src/server/GamePreviewRoute.ts +++ b/src/server/GamePreviewRoute.ts @@ -55,6 +55,9 @@ export function registerGamePreviewRoute(opts: { const apiDomain = config.jwtIssuer(); const encodedID = encodeURIComponent(gameID); const response = await fetch(`${apiDomain}/game/${encodedID}`, { + headers: { + "x-api-key": config.apiKey(), + }, signal: controller.signal, }); if (!response.ok) return null; diff --git a/src/server/jwt.ts b/src/server/jwt.ts index e4a09b012..1a05baaa8 100644 --- a/src/server/jwt.ts +++ b/src/server/jwt.ts @@ -74,6 +74,7 @@ export async function getUserMe( const response = await fetch(config.jwtIssuer() + "/users/@me", { headers: { authorization: `Bearer ${token}`, + "x-api-key": config.apiKey(), }, }); if (response.status !== 200) { diff --git a/startup.sh b/startup.sh deleted file mode 100644 index 3eba5480b..000000000 --- a/startup.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash -set -e - -# Check if required environment variables are set -if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ACCOUNT_ID" ] || [ -z "$SUBDOMAIN" ] || [ -z "$DOMAIN" ]; then - echo "Error: Required environment variables not set" - echo "Please set CF_API_TOKEN, CF_ACCOUNT_ID, SUBDOMAIN, and DOMAIN" - exit 1 -fi - -# Generate a unique tunnel name using timestamp -TIMESTAMP=$(date +%Y%m%d%H%M%S) -TUNNEL_NAME="${SUBDOMAIN}-tunnel-${TIMESTAMP}" -echo "Using unique tunnel name: ${TUNNEL_NAME}" - -# Create a new tunnel -echo "Creating Cloudflare tunnel for subdomain ${SUBDOMAIN}..." -TUNNEL_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json" \ - --data "{\"name\":\"${TUNNEL_NAME}\"}") - -# Extract tunnel ID and token -TUNNEL_ID=$(echo $TUNNEL_RESPONSE | jq -r '.result.id') -TUNNEL_TOKEN=$(echo $TUNNEL_RESPONSE | jq -r '.result.token') - -if [ -z "$TUNNEL_ID" ] || [ "$TUNNEL_ID" == "null" ]; then - echo "Failed to create tunnel" - echo $TUNNEL_RESPONSE - exit 1 -fi - -echo "Tunnel created with ID: ${TUNNEL_ID}" - -# Configure the tunnel with hostname -echo "Configuring tunnel to point to tunnel-${SUBDOMAIN}.${DOMAIN}..." -curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json" \ - --data "{\"config\":{\"ingress\":[{\"hostname\":\"tunnel-${SUBDOMAIN}.${DOMAIN}\",\"service\":\"http://localhost:80\"},{\"service\":\"http_status:404\"}]}}" - -# Update DNS record to point to the new tunnel -echo "Updating DNS record to point to the new tunnel..." - -# First check if DNS record exists -DNS_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${DOMAIN}" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json") - -ZONE_ID=$(echo $DNS_RECORDS | jq -r '.result[0].id') - -if [ -z "$ZONE_ID" ] || [ "$ZONE_ID" == "null" ]; then - echo "Could not find zone ID for domain ${DOMAIN}" - exit 1 -fi - -# Check for existing record -EXISTING_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=tunnel-${SUBDOMAIN}.${DOMAIN}" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json") - -RECORD_ID=$(echo $EXISTING_RECORDS | jq -r '.result[0].id') - -# Create or update the DNS record -if [ -z "$RECORD_ID" ] || [ "$RECORD_ID" == "null" ]; then - # Create new record - echo "Creating new DNS record..." - DNS_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json" \ - --data "{\"type\":\"CNAME\",\"name\":\"tunnel-${SUBDOMAIN}.${DOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargotunnel.com\",\"ttl\":1,\"proxied\":true}") -else - # Update existing record - echo "Updating existing DNS record..." - DNS_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \ - -H "Authorization: Bearer ${CF_API_TOKEN}" \ - -H "Content-Type: application/json" \ - --data "{\"type\":\"CNAME\",\"name\":\"tunnel-${SUBDOMAIN}.${DOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargotunnel.com\",\"ttl\":1,\"proxied\":true}") -fi - -# Log the tunnel information -echo "Tunnel configuration is set up! Site will be available at: https://tunnel-${SUBDOMAIN}.${DOMAIN}" - -# Export the tunnel token for supervisord -export CLOUDFLARE_TUNNEL_TOKEN=${TUNNEL_TOKEN} - -# Start supervisord -if [ "$DOMAIN" = openfront.dev ] && [ "$SUBDOMAIN" != main ]; then - exec timeout 18h /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf -else - exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf -fi diff --git a/supervisord.conf b/supervisord.conf index 953ef3b64..086622a6b 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -23,11 +23,3 @@ stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 - -[program:cloudflared] -command=cloudflared tunnel run --token %(ENV_CLOUDFLARE_TUNNEL_TOKEN)s -autostart=true -autorestart=true -user=node -stdout_logfile=/var/log/cloudflared.log -stderr_logfile=/var/log/cloudflared-err.log \ No newline at end of file diff --git a/tests/client/graphics/RadialMenuElements.test.ts b/tests/client/graphics/RadialMenuElements.test.ts index 3404accbf..d6317670d 100644 --- a/tests/client/graphics/RadialMenuElements.test.ts +++ b/tests/client/graphics/RadialMenuElements.test.ts @@ -557,7 +557,11 @@ describe("RadialMenuElements", () => { const subMenu = buildMenuElement.subMenu!(mockParams); const cityElement = subMenu.find((item) => item.id === "build_City"); - expect(cityElement!.color).toBe(COLORS.building); + expect( + (cityElement!.color as (params: MenuElementParams) => string)( + mockParams, + ), + ).toBe(COLORS.building); }); it("should use correct colors for attack elements", () => { @@ -572,16 +576,24 @@ describe("RadialMenuElements", () => { (item) => item.id === "attack_Atom Bomb", ); - expect(atomBombElement!.color).toBe(COLORS.attack); + expect( + (atomBombElement!.color as (params: MenuElementParams) => string)( + mockParams, + ), + ).toBe(COLORS.attack); }); - it("should not set color when element is disabled", () => { + it("should use disabled color when element is disabled", () => { mockBuildMenu.canBuildOrUpgrade = vi.fn(() => false); const subMenu = buildMenuElement.subMenu!(mockParams); const cityElement = subMenu.find((item) => item.id === "build_City"); - expect(cityElement!.color).toBeUndefined(); + expect( + (cityElement!.color as (params: MenuElementParams) => string)( + mockParams, + ), + ).toBe(COLORS.building); }); }); diff --git a/update.sh b/update.sh index 6d16ed9aa..f4c4cd5db 100755 --- a/update.sh +++ b/update.sh @@ -62,14 +62,10 @@ echo "Starting new container for ${HOST} environment..." # Ensure the traefik network exists docker network create web 2> /dev/null || true -# Remove any existing volume for this container if it exists -docker volume rm "cloudflared-${CONTAINER_NAME}" 2> /dev/null || true - docker run -d \ --restart="${RESTART}" \ --env-file "$ENV_FILE" \ --name "${CONTAINER_NAME}" \ - -v "cloudflared-${CONTAINER_NAME}:/etc/cloudflared" \ --network web \ --label "traefik.enable=true" \ --label "traefik.http.routers.${CONTAINER_NAME}.rule=Host(\`${SUBDOMAIN}.${DOMAIN}\`)" \