diff --git a/config.sh b/config.sh index 9bbba49..bdf0670 100755 --- a/config.sh +++ b/config.sh @@ -25,6 +25,7 @@ TUNNEL_CONFIG_DIR="$SCRIPT_DIR/configs/cloudflare" TUNNEL_CONFIG_FILE="$TUNNEL_CONFIG_DIR/tunnel-config.yml" SERVICES_YAML="$SCRIPT_DIR/configs/homepage/services.yaml" MAIN_HTML="$SCRIPT_DIR/mkdocs/docs/overrides/main.html" +MAP_ENV_FILE="$SCRIPT_DIR/map/.env" # Add the map's .env file path echo "Looking for .env file at: $ENV_FILE" @@ -180,6 +181,8 @@ initialize_available_ports() { ["HOMEPAGE_PORT"]=3010 ["GITEA_WEB_PORT"]=3030 ["GITEA_SSH_PORT"]=2222 + ["MAP_PORT"]=3000 + ["MINI_QR_PORT"]=8089 ) # Find available ports for each service @@ -243,6 +246,8 @@ NOCODB_PORT=${NOCODB_PORT:-8090} HOMEPAGE_PORT=${HOMEPAGE_PORT:-3010} GITEA_WEB_PORT=${GITEA_WEB_PORT:-3030} GITEA_SSH_PORT=${GITEA_SSH_PORT:-2222} +MAP_PORT=${MAP_PORT:-3000} +MINI_QR_PORT=${MINI_QR_PORT:-8089} # Domain Configuration BASE_DOMAIN=https://changeme.org @@ -251,12 +256,18 @@ LISTMONK_HOSTNAME=listmonk.changeme.org N8N_HOST=n8n.changeme.org GITEA_DOMAIN=git.changeme.org GITEA_ROOT_URL=https://git.changeme.org +HOMEPAGE_VAR_BASE_URL=https://changeme.org + +# Cookie and CORS Configuration +COOKIE_DOMAIN=.changeme.org +ALLOWED_ORIGINS=https://map.changeme.org,http://localhost:3000 # Cloudflare Configuration CF_API_TOKEN=your_cloudflare_api_token CF_ZONE_ID=your_cloudflare_zone_id CF_TUNNEL_ID=your_cloudflared_tunnel_id CF_DOMAIN=changeme.org +CF_ACCOUNT_ID=your_cloudflare_account_id # Database Configuration (PostgreSQL for Listmonk) POSTGRES_USER=listmonk @@ -279,12 +290,14 @@ N8N_ENCRYPTION_KEY=changeMe GENERIC_TIMEZONE=UTC # Nocodb Configuration -NOCODB_PORT=${NOCODB_PORT:-8090} NOCODB_JWT_SECRET=changeMe NOCODB_DB_NAME=nocodb NOCODB_DB_USER=noco NOCODB_DB_PASSWORD=changeMe -HOMEPAGE_VAR_BASE_URL=https://changeme.org + +# Gitea Database Configuration +GITEA_DB_PASSWD=changeMe +GITEA_DB_ROOT_PASSWORD=changeMe EOL echo "New .env file created with conflict-free port assignments." @@ -302,6 +315,8 @@ EOL echo "Homepage: ${HOMEPAGE_PORT:-3010}" echo "Gitea Web: ${GITEA_WEB_PORT:-3030}" echo "Gitea SSH: ${GITEA_SSH_PORT:-2222}" + echo "Map: ${MAP_PORT:-3000}" + echo "Mini QR: ${MINI_QR_PORT:-8089}" echo "================================" } @@ -404,42 +419,55 @@ update_main_html() { return 0 } +# Function to get Cloudflare API Token and Zone ID +get_cloudflare_credentials() { + echo "" + echo "To use Cloudflare tunnels, you'll need to configure your credentials." + echo "" + echo "=== Cloudflare Configuration ===" + echo "" + echo "You'll need:" + echo "1. Your Cloudflare API Token (with Zone.DNS and Account.Cloudflare Tunnel permissions)" + echo "2. Your Cloudflare Zone ID (found in the Cloudflare dashboard overview)" + echo "3. Your Cloudflare Account ID (found in the Cloudflare dashboard)" + echo "" + echo "These credentials will be used for:" + echo "- Creating and configuring tunnels" + echo "- Managing DNS records" + echo "- Setting up access policies" + echo "" +} + # Function to update the tunnel configuration file with new domain update_tunnel_config() { local new_domain=$1 + local tunnel_id=${2:-"(insert-tunnel-id)"} + local creds_path=${3:-"/path/to/credentials/file"} - if [ ! -f "$TUNNEL_CONFIG_FILE" ]; then - echo "Warning: tunnel-config.yml not found at $TUNNEL_CONFIG_FILE" - echo "Creating new tunnel configuration file..." - - # Create the file if it doesn't exist - create_tunnel_config "$new_domain" "${CF_TUNNEL_ID:-\${CF_TUNNEL_ID}}" - return $? - fi + # Create directory if it doesn't exist + mkdir -p "$TUNNEL_CONFIG_DIR" echo "Updating tunnel configuration with new domain..." - # Create a backup of the tunnel-config.yml file - local timestamp=$(date +"%Y%m%d_%H%M%S") - local backup_file="${TUNNEL_CONFIG_FILE}.backup_${timestamp}" - cp "$TUNNEL_CONFIG_FILE" "$backup_file" - echo "Created backup of tunnel-config.yml at $backup_file" - - # Since the current file has all entries as cmlite.org pointing to port 3010, - # we need to recreate it with the proper configuration - echo "Regenerating tunnel configuration with correct service mappings..." + # Create a backup if file exists + if [ -f "$TUNNEL_CONFIG_FILE" ]; then + local timestamp=$(date +"%Y%m%d_%H%M%S") + local backup_file="${TUNNEL_CONFIG_FILE}.backup_${timestamp}" + cp "$TUNNEL_CONFIG_FILE" "$backup_file" + echo "Created backup of tunnel-config.yml at $backup_file" + fi # Load current environment variables to get port numbers load_env_vars - # Recreate the tunnel configuration with proper mappings + # Create/update the tunnel configuration cat > "$TUNNEL_CONFIG_FILE" << EOL # filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml # Cloudflare Tunnel Configuration # Auto-generated by Changemaker Configuration Wizard -tunnel: ${CF_TUNNEL_ID:-\${CF_TUNNEL_ID}} # e.g. 1234567890abcdef -credentials-file: /home/coder/.cloudflared/${CF_TUNNEL_ID:-\${CF_TUNNEL_ID}}.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json +tunnel: $tunnel_id # e.g. 1234567890abcdef +credentials-file: $creds_path # e.g. /home/bunker-admin/.cloudflared/[insert tunnel number].json ingress: - hostname: homepage.$new_domain @@ -449,13 +477,13 @@ ingress: service: http://localhost:${CODE_SERVER_PORT:-8888} - hostname: listmonk.$new_domain - service: http://localhost:${LISTMONK_PORT:-9000} + service: http://localhost:${LISTMONK_PORT:-9001} - hostname: docs.$new_domain service: http://localhost:${MKDOCS_PORT:-4000} - hostname: $new_domain - service: http://localhost:${MKDOCS_SITE_SERVER_PORT:-4001} + service: http://localhost:${MKDOCS_SITE_SERVER_PORT:-4002} - hostname: n8n.$new_domain service: http://localhost:${N8N_PORT:-5678} @@ -466,64 +494,20 @@ ingress: - hostname: git.$new_domain service: http://localhost:${GITEA_WEB_PORT:-3030} + - hostname: map.$new_domain + service: http://localhost:${MAP_PORT:-3000} + + - hostname: qr.$new_domain + service: http://localhost:${MINI_QR_PORT:-8089} + # Catch-all rule (required) - service: http_status:404 EOL - echo "✅ Regenerated tunnel configuration with correct service mappings for domain: $new_domain" + echo "✅ Updated tunnel configuration for domain: $new_domain" return 0 } -# Function to get Cloudflare API Token and Zone ID -get_cloudflare_credentials() { - echo "" - echo "To use the DNS setup script, you'll need Cloudflare credentials." - echo "You can find these values in your Cloudflare dashboard:" - echo " - API Token: https://dash.cloudflare.com/profile/api-tokens" - echo " (Create a token with Zone:DNS:Edit and Access:Apps:Edit permissions)" - echo " - Zone ID: On your domain's overview page" - echo "" -} - -# Function to show tunnel setup instructions -show_tunnel_instructions() { - local domain=$1 - - echo "" - echo "=== Cloudflare Tunnel Setup Instructions ===" - echo "" - echo "To complete the tunnel setup:" - echo "" - echo "1. Install cloudflared on your server:" - echo " https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation" - echo "" - echo "2. Create a tunnel using the Cloudflare dashboard or run:" - echo " cloudflared tunnel create changemaker-tunnel" - echo "" - echo "3. Copy the credentials file to the correct location:" - echo " mkdir -p /home/coder/.cloudflared" - echo " cp ~/.cloudflared/[TUNNEL-ID].json /home/coder/.cloudflared/" - echo "" - echo "4. Update your .env with the correct CF_TUNNEL_ID if not already done" - echo "" - echo "5. Start the tunnel with your configuration file:" - echo " cloudflared tunnel --config $TUNNEL_CONFIG_FILE run" - echo "" - echo "6. After verifying it works, you can create a systemd service for automatic startup:" - echo " sudo cloudflared service install" - echo "" - echo "7. Your services will be available at the following URLs:" - echo " - Documentation: https://$domain" - echo " - Homepage: https://homepage.$domain" - echo " - Code Server: https://code.$domain" - echo " - Listmonk: https://listmonk.$domain" - echo " - N8N: https://n8n.$domain" - echo " - NocoDB: https://db.$domain" - echo " - MkDocs Dev: https://docs.$domain" - echo " - Gitea: https://git.$domain" - echo "" -} - # Function to load environment variables from .env file load_env_vars() { if [ -f "$ENV_FILE" ]; then @@ -536,6 +520,50 @@ load_env_vars() { fi } +# Function to update the map's .env file with domain settings +update_map_env() { + local new_domain=$1 + + if [ ! -f "$MAP_ENV_FILE" ]; then + echo "Warning: Map .env file not found at $MAP_ENV_FILE" + return 1 + fi + + echo "Updating map .env file with new domain settings..." + + # Create a backup of the map's .env file + local timestamp=$(date +"%Y%m%d_%H%M%S") + local backup_file="${MAP_ENV_FILE}.backup_${timestamp}" + cp "$MAP_ENV_FILE" "$backup_file" + echo "Created backup of map .env at $backup_file" + + # Update COOKIE_DOMAIN + if grep -q "^COOKIE_DOMAIN=" "$MAP_ENV_FILE"; then + sed -i "s|^COOKIE_DOMAIN=.*|COOKIE_DOMAIN=.$new_domain|" "$MAP_ENV_FILE" + else + echo "COOKIE_DOMAIN=.$new_domain" >> "$MAP_ENV_FILE" + fi + + # Update ALLOWED_ORIGINS + local allowed_origins="https://map.$new_domain,http://localhost:3000" + if grep -q "^ALLOWED_ORIGINS=" "$MAP_ENV_FILE"; then + sed -i "s|^ALLOWED_ORIGINS=.*|ALLOWED_ORIGINS=$allowed_origins|" "$MAP_ENV_FILE" + else + echo "ALLOWED_ORIGINS=$allowed_origins" >> "$MAP_ENV_FILE" + fi + + # Also update the NOCODB URLs if they contain domain references + sed -i "s|example\.org|$new_domain|g" "$MAP_ENV_FILE" + sed -i "s|changeme\.org|$new_domain|g" "$MAP_ENV_FILE" + sed -i "s|albertademocracytaskforce\.org|$new_domain|g" "$MAP_ENV_FILE" + + echo "✅ Updated map .env file with:" + echo " - COOKIE_DOMAIN=.$new_domain" + echo " - ALLOWED_ORIGINS=$allowed_origins" + echo " - Updated all NocoDB URLs to use $new_domain" + return 0 +} + # Initialize a new .env file if it doesn't exist if [ ! -f "$ENV_FILE" ]; then echo "No .env file found. Creating a new one from scratch." @@ -579,8 +607,16 @@ update_env_var "CF_DOMAIN" "$domain_name" update_env_var "GITEA_DOMAIN" "git.$domain_name" update_env_var "GITEA_ROOT_URL" "https://git.$domain_name" +# Update cookie and CORS settings +update_env_var "COOKIE_DOMAIN" ".$domain_name" +update_env_var "ALLOWED_ORIGINS" "https://map.$domain_name,http://localhost:3000" + echo "Domain settings updated successfully!" +# Update the map's .env file +echo -e "\nUpdating map configuration..." +update_map_env "$domain_name" + # Cloudflare Configuration echo -e "\n---- Cloudflare Configuration ----" get_cloudflare_credentials @@ -588,35 +624,43 @@ get_cloudflare_credentials read -p "Do you want to configure Cloudflare settings now? [Y/n]: " configure_cf if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then - # Get Cloudflare API Token + # Get Cloudflare API token read -p "Enter your Cloudflare API Token: " cf_api_token - while [ -z "$cf_api_token" ] || [ "$cf_api_token" == "your_cloudflare_api_token" ]; do - echo "API Token is required. Please enter a valid token." - read -p "Enter your Cloudflare API Token: " cf_api_token - done - + if [ -z "$cf_api_token" ] || [ "$cf_api_token" = "your_cloudflare_api_token" ]; then + echo "Warning: API token is required for Cloudflare integration" + cf_api_token="your_cloudflare_api_token" + fi + update_env_var "CF_API_TOKEN" "$cf_api_token" + # Get Cloudflare Zone ID read -p "Enter your Cloudflare Zone ID: " cf_zone_id - while [ -z "$cf_zone_id" ] || [ "$cf_zone_id" == "your_cloudflare_zone_id" ]; do - echo "Zone ID is required. Please enter a valid Zone ID." - read -p "Enter your Cloudflare Zone ID: " cf_zone_id - done - - # Optional: Get Account ID now - read -p "Enter your Cloudflare Account ID (optional, can be added later): " cf_account_id - - # Update .env file with Cloudflare credentials - update_env_var "CF_API_TOKEN" "$cf_api_token" - update_env_var "CF_ZONE_ID" "$cf_zone_id" - update_env_var "CF_DOMAIN" "$domain_name" - - if [ ! -z "$cf_account_id" ]; then - update_env_var "CF_ACCOUNT_ID" "$cf_account_id" + if [ -z "$cf_zone_id" ] || [ "$cf_zone_id" = "your_cloudflare_zone_id" ]; then + echo "Warning: Zone ID is required for DNS configuration" + cf_zone_id="your_cloudflare_zone_id" fi + update_env_var "CF_ZONE_ID" "$cf_zone_id" + + # Get Cloudflare Account ID + read -p "Enter your Cloudflare Account ID: " cf_account_id + if [ -z "$cf_account_id" ] || [ "$cf_account_id" = "your_cloudflare_account_id" ]; then + echo "Warning: Account ID is required for tunnel operations" + cf_account_id="your_cloudflare_account_id" + fi + update_env_var "CF_ACCOUNT_ID" "$cf_account_id" - echo "Cloudflare credentials saved successfully!" + # Note: CF_TUNNEL_ID will be set by start-production.sh when the tunnel is created + update_env_var "CF_TUNNEL_ID" "will_be_set_by_start_production" + + echo "" + echo "Cloudflare configuration saved to .env file." + echo "" + echo "To complete the setup and deploy your services:" + echo "1. Start your services with: docker compose up -d" + echo "2. Run ./start-production.sh to configure the Cloudflare tunnel" + echo "" else - echo "Skipping Cloudflare configuration. You can configure it later by editing the .env file." + echo "Skipping Cloudflare configuration." + echo "You'll need to configure Cloudflare settings manually before running start-production.sh." fi # Update the site_url in mkdocs.yml @@ -631,10 +675,6 @@ update_services_yaml "$domain_name" echo -e "\nUpdating login URL in main.html..." update_main_html "$domain_name" -# Update the tunnel configuration file -echo -e "\nUpdating tunnel configuration..." -update_tunnel_config "$domain_name" - # Listmonk Admin Credentials configuration echo -e "\n---- Listmonk Admin Credentials ----" read -p "Enter Listmonk admin email/username [default: admin@example.com]: " listmonk_user @@ -692,7 +732,7 @@ update_env_var "NOCODB_JWT_SECRET" "$nocodb_jwt_secret" nocodb_db_password=$(generate_password 20) update_env_var "NOCODB_DB_PASSWORD" "$nocodb_db_password" -# Generate and update Gitea passwords +# Generate and update Gitea passwords (these should already exist in the file) gitea_db_password=$(generate_password 20) update_env_var "GITEA_DB_PASSWD" "$gitea_db_password" @@ -704,14 +744,16 @@ echo "Secure passwords generated and updated." echo -e "\n✅ Configuration completed successfully!" echo "Your .env file has been configured with:" echo "- Domain: $domain_name" +echo "- Cookie Domain: .$domain_name" +echo "- Allowed Origins: https://map.$domain_name,http://localhost:3000" +echo "- Map .env updated with domain settings" echo "- Listmonk Admin: $listmonk_user" echo "- N8N Admin Email: $n8n_email" echo "- Secure random passwords for database, encryption, and NocoDB" -if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then - echo "- Cloudflare credentials for DNS management" -fi +echo "- Tunnel configuration updated at: $TUNNEL_CONFIG_FILE" echo -e "\nYour .env file is located at: $ENV_FILE" echo "A backup of your original .env file was created before modifications." + echo "" echo "======================================" echo "Next Steps:" @@ -728,6 +770,8 @@ echo " - Documentation: http://localhost:${MKDOCS_PORT:-4000}" echo " - n8n: http://localhost:${N8N_PORT:-5678}" echo " - NocoDB: http://localhost:${NOCODB_PORT:-8090}" echo " - Gitea: http://localhost:${GITEA_WEB_PORT:-3030}" +echo " - Map: http://localhost:${MAP_PORT:-3000}" +echo " - Mini QR: http://localhost:${MINI_QR_PORT:-8089}" echo "" echo "3. When ready for production:" echo " ./start-production.sh" diff --git a/configs/cloudflare/843f83a4-247a-4c29-8a7e-cde50e2f4222.json b/configs/cloudflare/843f83a4-247a-4c29-8a7e-cde50e2f4222.json new file mode 100644 index 0000000..2142f0d --- /dev/null +++ b/configs/cloudflare/843f83a4-247a-4c29-8a7e-cde50e2f4222.json @@ -0,0 +1 @@ +{"AccountTag":"a421828402ca13fbcaad955f285f16f6","TunnelSecret":"949mtkdN202INtEohadCgnEe7QXykBL26dq2uQMt+HQ=","TunnelID":"843f83a4-247a-4c29-8a7e-cde50e2f4222","Endpoint":""} \ No newline at end of file diff --git a/configs/cloudflare/tunnel-config.yml b/configs/cloudflare/tunnel-config.yml index eb0639b..8e59c70 100644 --- a/configs/cloudflare/tunnel-config.yml +++ b/configs/cloudflare/tunnel-config.yml @@ -1,34 +1,27 @@ -# filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml -# Cloudflare Tunnel Configuration -# Auto-generated by Changemaker Configuration Wizard +# Cloudflare Tunnel Configuration for cmlite.org +# Generated by Changemaker.lite start-production.sh on Sun 29 Jun 2025 09:10:15 PM MDT -tunnel: your_cloudflared_tunnel_id} # e.g. 1234567890abcdef -credentials-file: /home/coder/.cloudflared/your_cloudflared_tunnel_id}.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json +tunnel: 843f83a4-247a-4c29-8a7e-cde50e2f4222 +credentials-file: /mnt/storagessd1tb/changemaker.lite.dev/changemaker.lite/configs/cloudflare/843f83a4-247a-4c29-8a7e-cde50e2f4222.json ingress: - - hostname: homepage.cmlite.org service: http://localhost:3010 - - hostname: code.cmlite.org service: http://localhost:8888 - - hostname: listmonk.cmlite.org service: http://localhost:9001 - - hostname: docs.cmlite.org service: http://localhost:4000 - - hostname: cmlite.org service: http://localhost:4002 - - hostname: n8n.cmlite.org service: http://localhost:5678 - - hostname: db.cmlite.org service: http://localhost:8090 - - hostname: git.cmlite.org service: http://localhost:3030 - - # Catch-all rule (required) + - hostname: map.cmlite.org + service: http://localhost:3000 + - hostname: qr.cmlite.org + service: http://localhost:8089 - service: http_status:404 diff --git a/configs/cloudflare/tunnel-config.yml.backup_20250629_154241 b/configs/cloudflare/tunnel-config.yml.backup_20250629_154241 deleted file mode 100644 index 4f28d65..0000000 --- a/configs/cloudflare/tunnel-config.yml.backup_20250629_154241 +++ /dev/null @@ -1,34 +0,0 @@ -# filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml -# Cloudflare Tunnel Configuration -# Auto-generated by Changemaker Configuration Wizard - -tunnel: ${CF_TUNNEL_ID} # e.g. 1234567890abcdef -credentials-file: /home/coder/.cloudflared/${CF_TUNNEL_ID}.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json -ingress: - - - hostname: homepage.test.com - service: http://localhost:3010 - - - hostname: code.test.com - service: http://localhost:8888 - - - hostname: listmonk.test.com - service: http://localhost:9001 - - - hostname: docs.test.com - service: http://localhost:4000 - - - hostname: test.com - service: http://localhost:4002 - - - hostname: n8n.test.com - service: http://localhost:5678 - - - hostname: db.test.com - service: http://localhost:8090 - - - hostname: git.test.com - service: http://localhost:3030 - - # Catch-all rule (required) - - service: http_status:404 diff --git a/configs/cloudflare/tunnel-config.yml.backup_20250629_154929 b/configs/cloudflare/tunnel-config.yml.backup_20250629_154929 deleted file mode 100644 index 1e29c9d..0000000 --- a/configs/cloudflare/tunnel-config.yml.backup_20250629_154929 +++ /dev/null @@ -1,34 +0,0 @@ -# filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml -# Cloudflare Tunnel Configuration -# Auto-generated by Changemaker Configuration Wizard - -tunnel: ${CF_TUNNEL_ID} # e.g. 1234567890abcdef -credentials-file: /home/coder/.cloudflared/${CF_TUNNEL_ID}.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json -ingress: - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - - hostname: cmlite.org - service: http://localhost:3010 - - # Catch-all rule (required) - - service: http_status:404 diff --git a/configs/homepage/logs/homepage.log b/configs/homepage/logs/homepage.log index 0de4c20..3caeca4 100644 --- a/configs/homepage/logs/homepage.log +++ b/configs/homepage/logs/homepage.log @@ -190,3 +190,514 @@ [2025-05-28T14:56:56.936Z] error: Error: connect EACCES /var/run/docker.sock at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1636:16) at PipeConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) +[2025-06-30T03:14:15.516Z] error: undefined +[2025-06-30T03:14:15.521Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:14:21.458Z] error: undefined +[2025-06-30T03:14:21.460Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:14:26.553Z] error: undefined +[2025-06-30T03:14:26.554Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:14:41.640Z] error: undefined +[2025-06-30T03:14:41.641Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:15:25.889Z] error: undefined +[2025-06-30T03:15:25.891Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:15:31.720Z] error: undefined +[2025-06-30T03:15:31.721Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:15:35.980Z] error: undefined +[2025-06-30T03:15:35.981Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:16:01.061Z] error: undefined +[2025-06-30T03:16:01.062Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:16:11.809Z] error: undefined +[2025-06-30T03:16:11.810Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:16:26.149Z] error: undefined +[2025-06-30T03:16:26.150Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:17:07.675Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:17:07.675Z] error: YAMLException: bad indentation of a sequence entry (15:5) + + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | showStats: false + 15 | - Listmonk: +----------^ + 16 | href: "http://localhost:9000" + 17 | # href: "https://cmlite.org ... +[2025-06-30T03:17:07.755Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:17:07.757Z] error: YAMLException: bad indentation of a sequence entry (15:5) + + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | showStats: false + 15 | - Listmonk: +----------^ + 16 | href: "http://localhost:9000" + 17 | # href: "https://cmlite.org ... + at generateError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1273:10) + at throwError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1277:9) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2105:7) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2562:45) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2098:5) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2530:12) + at readDocument (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2715:3) + at loadDocuments (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2778:5) +[2025-06-30T03:17:07.756Z] error: YAMLException: bad indentation of a sequence entry (15:5) + + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | showStats: false + 15 | - Listmonk: +----------^ + 16 | href: "http://localhost:9000" + 17 | # href: "https://cmlite.org ... +[2025-06-30T03:17:08.098Z] error: YAMLException: bad indentation of a sequence entry (15:5) + + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | showStats: false + 15 | - Listmonk: +----------^ + 16 | href: "http://localhost:9000" + 17 | # href: "https://cmlite.org ... + at generateError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1273:10) + at throwError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1277:9) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2105:7) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2562:45) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2098:5) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2530:12) + at readDocument (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2715:3) + at loadDocuments (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2778:5) +[2025-06-30T03:17:08.161Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:17:08.161Z] error: YAMLException: bad indentation of a sequence entry (15:5) + + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | showStats: false + 15 | - Listmonk: +----------^ + 16 | href: "http://localhost:9000" + 17 | # href: "https://cmlite.org ... +[2025-06-30T03:17:15.271Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:17:15.271Z] error: YAMLException: bad indentation of a sequence entry (14:5) + + 11 | type: docker + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | - Listmonk: +----------^ + 15 | href: "http://localhost:9000" + 16 | # href: "https://cmlite.org ... +[2025-06-30T03:17:15.511Z] error: YAMLException: bad indentation of a sequence entry (14:5) + + 11 | type: docker + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | - Listmonk: +----------^ + 15 | href: "http://localhost:9000" + 16 | # href: "https://cmlite.org ... + at generateError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1273:10) + at throwError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1277:9) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2105:7) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2562:45) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2098:5) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2530:12) + at readDocument (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2715:3) + at loadDocuments (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2778:5) +[2025-06-30T03:17:15.572Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:17:15.573Z] error: YAMLException: bad indentation of a sequence entry (14:5) + + 11 | type: docker + 12 | container: code-server-change ... + 13 | server: my-docker + 14 | - Listmonk: +----------^ + 15 | href: "http://localhost:9000" + 16 | # href: "https://cmlite.org ... +[2025-06-30T03:17:45.214Z] error: undefined +[2025-06-30T03:17:45.216Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:17:50.333Z] error: undefined +[2025-06-30T03:17:50.334Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:17:56.035Z] error: undefined +[2025-06-30T03:17:56.036Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:18:01.448Z] error: undefined +[2025-06-30T03:18:01.449Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:18:15.473Z] error: undefined +[2025-06-30T03:18:15.474Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:18:36.889Z] error: undefined +[2025-06-30T03:18:36.890Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:19:00.275Z] error: undefined +[2025-06-30T03:19:00.276Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:21:04.087Z] error: YAMLException: bad indentation of a mapping entry (10:20) + + 7 | # href: "https://cmlite.org" # Uncomment ... + 8 | description: VS Code in the browser + 9 | icon: mdi-code-braces + 10 | container: code-server-changemaker +-------------------------^ + 11 | server: my-docker + 12 | - Listmonk: + at generateError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1273:10) + at throwError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1277:9) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2272:7) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2098:5) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2530:12) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) +[2025-06-30T03:21:04.511Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:21:04.511Z] error: YAMLException: bad indentation of a mapping entry (10:20) + + 7 | # href: "https://cmlite.org" # Uncomment ... + 8 | description: VS Code in the browser + 9 | icon: mdi-code-braces + 10 | container: code-server-changemaker +-------------------------^ + 11 | server: my-docker + 12 | - Listmonk: +[2025-06-30T03:21:04.585Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:21:04.585Z] error: YAMLException: bad indentation of a mapping entry (10:20) + + 7 | # href: "https://cmlite.org" # Uncomment ... + 8 | description: VS Code in the browser + 9 | icon: mdi-code-braces + 10 | container: code-server-changemaker +-------------------------^ + 11 | server: my-docker + 12 | - Listmonk: +[2025-06-30T03:21:04.867Z] error: YAMLException: bad indentation of a mapping entry (10:20) + + 7 | # href: "https://cmlite.org" # Uncomment ... + 8 | description: VS Code in the browser + 9 | icon: mdi-code-braces + 10 | container: code-server-changemaker +-------------------------^ + 11 | server: my-docker + 12 | - Listmonk: + at generateError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1273:10) + at throwError (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:1277:9) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2272:7) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) + at readBlockSequence (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2098:5) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2530:12) + at readBlockMapping (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2254:11) + at composeNode (file:///app/node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs:2531:12) +[2025-06-30T03:21:04.954Z] error: Failed to load services.yaml, please check for errors +[2025-06-30T03:21:04.954Z] error: YAMLException: bad indentation of a mapping entry (10:20) + + 7 | # href: "https://cmlite.org" # Uncomment ... + 8 | description: VS Code in the browser + 9 | icon: mdi-code-braces + 10 | container: code-server-changemaker +-------------------------^ + 11 | server: my-docker + 12 | - Listmonk: +[2025-06-30T03:21:12.819Z] error: undefined +[2025-06-30T03:21:12.820Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:21:13.108Z] error: undefined +[2025-06-30T03:21:13.110Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:21:18.234Z] error: undefined +[2025-06-30T03:21:18.236Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:21:23.680Z] error: undefined +[2025-06-30T03:21:23.681Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:21:38.468Z] error: undefined +[2025-06-30T03:21:38.469Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:22:04.123Z] error: undefined +[2025-06-30T03:22:04.125Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:22:09.222Z] error: undefined +[2025-06-30T03:22:09.223Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:22:31.975Z] error: undefined +[2025-06-30T03:22:31.976Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:22:34.309Z] error: undefined +[2025-06-30T03:22:34.310Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:22:56.905Z] error: undefined +[2025-06-30T03:22:56.907Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) +[2025-06-30T03:23:24.463Z] error: undefined +[2025-06-30T03:23:24.464Z] error: TypeError: Invalid URL + at new URL (node:internal/url:818:25) + at d (/app/.next/server/pages/api/releases.js:1:5196) + at h (/app/.next/server/pages/api/services/proxy.js:45:33437) + at async X (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:18441) + at async z.render (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:19237) + at async NextNodeServer.runApi (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:671:9) + at async NextNodeServer.handleCatchallRenderRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/next-server.js:302:37) + at async NextNodeServer.handleRequestImpl (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/base-server.js:899:17) + at async invokeRender (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:237:21) + at async handleRequest (/app/node_modules/.pnpm/next@15.3.1_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/next/dist/server/lib/router-server.js:428:24) diff --git a/configs/homepage/services.yaml b/configs/homepage/services.yaml index 3c72435..845093b 100644 --- a/configs/homepage/services.yaml +++ b/configs/homepage/services.yaml @@ -2,86 +2,73 @@ # For public access, replace "http://localhost" with your subdomain URLs - Essential Tools: + - Code Server: - href: "http://localhost:8888" - # href: "https://cmlite.org" # Uncomment for public access - description: VS Code in the browser icon: mdi-code-braces - widget: - type: docker - container: code-server-changemaker - server: my-docker + href: "https://code.cmlite.org" + description: VS Code in the browser - Platform Editor + container: code-server-changemaker + - Listmonk: - href: "http://localhost:9000" - # href: "https://cmlite.org" # Uncomment for public access - description: Newsletter & mailing list manager icon: mdi-email-newsletter - widget: - type: docker - container: listmonk_app - server: my-docker + href: "https://listmonk.cmlite.org" + description: Newsletter & mailing list manager + container: listmonk_app + - NocoDB: - href: "http://localhost:8090" - # href: "https://cmlite.org" # Uncomment for public access - description: No-code database platform icon: mdi-database - widget: - type: docker - container: changemakerlite-nocodb-1 - server: my-docker - - Gitea: - href: "http://localhost:3030" - # href: "https://cmlite.org" # Uncomment for public access - description: Git repository hosting - icon: mdi-git - widget: - type: docker - container: gitea_changemaker - server: my-docker + href: "https://db.cmlite.org" + description: No-code database platform + container: changemakerlite-nocodb-1 + + - Map Server: + icon: mdi-map + href: "https://map.cmlite.org" + description: Map server for geospatial data + container: nocodb-map-viewer + - Content & Documentation: - - MkDocs (Live): - href: "http://localhost:4000" - # href: "https://cmlite.org" # Uncomment for public access - description: Live documentation server with hot reload - icon: mdi-book-open-page-variant - widget: - type: docker - container: mkdocs-changemaker - server: my-docker - - Static Site: - href: "http://localhost:4001" - # href: "https://cmlite.org" # Uncomment for public access - description: Built documentation hosting + - Main Site: icon: mdi-web - widget: - type: docker - container: mkdocs-site-server-changemaker - server: my-docker + href: "https://cmlite.org" + description: CM-lite campaign website + container: mkdocs-site-server-changemaker + + - MkDocs (Live): + icon: mdi-book-open-page-variant + href: "https://docs.cmlite.org" + description: Live documentation server with hot reload + container: mkdocs-changemaker + + - Mini QR: + icon: mdi-qrcode + href: "https://qr.cmlite.org" + description: QR code generator + container: mini-qr + - Automation & Infrastructure: - n8n: - href: "http://localhost:5678" - # href: "https://cmlite.org" # Uncomment for public access + icon: mdi-robot-industrial + href: "https://n8n.cmlite.org" description: Workflow automation platform - icon: mdi-workflow - widget: - type: docker - container: n8n-changemaker - server: my-docker + container: n8n-changemaker + - PostgreSQL (Listmonk): + icon: mdi-database-outline href: "#" description: Database for Listmonk - icon: mdi-database-outline - widget: - type: docker - container: listmonk_db - server: my-docker + container: listmonk_db + - PostgreSQL (NocoDB): + icon: mdi-database-outline href: "#" description: Database for NocoDB - icon: mdi-database-outline - widget: - type: docker - container: changemakerlite-root_db-1 - server: my-docker + container: changemakerlite-root_db-1 + + - Gitea: + icon: mdi-git + href: "https://git.cmlite.org" + description: Git repository hosting + container: gitea_changemaker \ No newline at end of file diff --git a/configs/homepage/services.yaml.backup_20250629_154241 b/configs/homepage/services.yaml.backup_20250629_154241 deleted file mode 100644 index ea833ac..0000000 --- a/configs/homepage/services.yaml.backup_20250629_154241 +++ /dev/null @@ -1,87 +0,0 @@ ---- -# For public access, replace "http://localhost" with your subdomain URLs - -- Essential Tools: - - Code Server: - href: "http://localhost:8888" - # href: "https://code.albertademocracytaskforce.org" # Uncomment for public access - description: VS Code in the browser - icon: mdi-code-braces - widget: - type: docker - container: code-server-changemaker - server: my-docker - - Listmonk: - href: "http://localhost:9000" - # href: "https://listmonk.albertademocracytaskforce.org" # Uncomment for public access - description: Newsletter & mailing list manager - icon: mdi-email-newsletter - widget: - type: docker - container: listmonk_app - server: my-docker - - NocoDB: - href: "http://localhost:8090" - # href: "https://db.albertademocracytaskforce.org" # Uncomment for public access - description: No-code database platform - icon: mdi-database - widget: - type: docker - container: changemakerlite-nocodb-1 - server: my-docker - - Gitea: - href: "http://localhost:3030" - # href: "https://git.albertademocracytaskforce.org" # Uncomment for public access - description: Git repository hosting - icon: mdi-git - widget: - type: docker - container: gitea_changemaker - server: my-docker - -- Content & Documentation: - - MkDocs (Live): - href: "http://localhost:4000" - # href: "https://docs.albertademocracytaskforce.org" # Uncomment for public access - description: Live documentation server with hot reload - icon: mdi-book-open-page-variant - widget: - type: docker - container: mkdocs-changemaker - server: my-docker - - Static Site: - href: "http://localhost:4001" - # href: "https://albertademocracytaskforce.org" # Uncomment for public access - description: Built documentation hosting - icon: mdi-web - widget: - type: docker - container: mkdocs-site-server-changemaker - server: my-docker - -- Automation & Infrastructure: - - n8n: - href: "http://localhost:5678" - # href: "https://n8n.albertademocracytaskforce.org" # Uncomment for public access - description: Workflow automation platform - icon: mdi-workflow - widget: - type: docker - container: n8n-changemaker - server: my-docker - - PostgreSQL (Listmonk): - href: "#" - description: Database for Listmonk - icon: mdi-database-outline - widget: - type: docker - container: listmonk_db - server: my-docker - - PostgreSQL (NocoDB): - href: "#" - description: Database for NocoDB - icon: mdi-database-outline - widget: - type: docker - container: changemakerlite-root_db-1 - server: my-docker diff --git a/configs/homepage/services.yaml.backup_20250629_154929 b/configs/homepage/services.yaml.backup_20250629_154929 deleted file mode 100644 index ea833ac..0000000 --- a/configs/homepage/services.yaml.backup_20250629_154929 +++ /dev/null @@ -1,87 +0,0 @@ ---- -# For public access, replace "http://localhost" with your subdomain URLs - -- Essential Tools: - - Code Server: - href: "http://localhost:8888" - # href: "https://code.albertademocracytaskforce.org" # Uncomment for public access - description: VS Code in the browser - icon: mdi-code-braces - widget: - type: docker - container: code-server-changemaker - server: my-docker - - Listmonk: - href: "http://localhost:9000" - # href: "https://listmonk.albertademocracytaskforce.org" # Uncomment for public access - description: Newsletter & mailing list manager - icon: mdi-email-newsletter - widget: - type: docker - container: listmonk_app - server: my-docker - - NocoDB: - href: "http://localhost:8090" - # href: "https://db.albertademocracytaskforce.org" # Uncomment for public access - description: No-code database platform - icon: mdi-database - widget: - type: docker - container: changemakerlite-nocodb-1 - server: my-docker - - Gitea: - href: "http://localhost:3030" - # href: "https://git.albertademocracytaskforce.org" # Uncomment for public access - description: Git repository hosting - icon: mdi-git - widget: - type: docker - container: gitea_changemaker - server: my-docker - -- Content & Documentation: - - MkDocs (Live): - href: "http://localhost:4000" - # href: "https://docs.albertademocracytaskforce.org" # Uncomment for public access - description: Live documentation server with hot reload - icon: mdi-book-open-page-variant - widget: - type: docker - container: mkdocs-changemaker - server: my-docker - - Static Site: - href: "http://localhost:4001" - # href: "https://albertademocracytaskforce.org" # Uncomment for public access - description: Built documentation hosting - icon: mdi-web - widget: - type: docker - container: mkdocs-site-server-changemaker - server: my-docker - -- Automation & Infrastructure: - - n8n: - href: "http://localhost:5678" - # href: "https://n8n.albertademocracytaskforce.org" # Uncomment for public access - description: Workflow automation platform - icon: mdi-workflow - widget: - type: docker - container: n8n-changemaker - server: my-docker - - PostgreSQL (Listmonk): - href: "#" - description: Database for Listmonk - icon: mdi-database-outline - widget: - type: docker - container: listmonk_db - server: my-docker - - PostgreSQL (NocoDB): - href: "#" - description: Database for NocoDB - icon: mdi-database-outline - widget: - type: docker - container: changemakerlite-root_db-1 - server: my-docker diff --git a/configs/homepage/settings.yaml b/configs/homepage/settings.yaml index 3ed96b3..034c9c9 100755 --- a/configs/homepage/settings.yaml +++ b/configs/homepage/settings.yaml @@ -21,6 +21,15 @@ layout: style: columns columns: 3 +docker: + widget: + # Set this to false to hide CPU, memory, and network stats + showStats: false + + # You can still keep health indicators + showHealth: true + + quicklaunch: searchDescriptions: true hideInternetSearch: true diff --git a/configs/homepage/widgets.yaml b/configs/homepage/widgets.yaml index 6b53a00..cfb5950 100644 --- a/configs/homepage/widgets.yaml +++ b/configs/homepage/widgets.yaml @@ -6,6 +6,16 @@ cpu: true memory: true disk: / + +- openmeteo: + label: Edmonton # optional + latitude: 53.5461 + longitude: -113.4938 + timezone: America/Edmonton # optional + units: metric # or imperial + cache: 5 # Time in minutes to cache API responses, to stay within limits + format: # optional, Intl.NumberFormat options + maximumFractionDigits: 1 - greeting: text_size: xl @@ -20,8 +30,4 @@ - search: provider: duckduckgo - target: _blank - -- unifi_console: - text_size: md - text: "Services Available: Code Server, Listmonk, NocoDB, MkDocs, n8n, Gitea" + target: _blank \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c0035b4..36054b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -230,25 +230,14 @@ services: timeout: 5s retries: 5 - # Cloudflare Tunnel (uncommented by start-production.sh) - # cloudflared: - # image: cloudflare/cloudflared:latest - # container_name: cloudflared-changemaker - # restart: unless-stopped - # command: tunnel run - # environment: - # - TUNNEL_TOKEN=${CF_TUNNEL_TOKEN} - # networks: - # - changemaker-lite - # depends_on: - # - homepage-changemaker - # - code-server - # - listmonk-app - # - mkdocs - # - mkdocs-site-server - # - n8n - # - nocodb - # - gitea-app + mini-qr: + image: ghcr.io/lyqht/mini-qr:latest + container_name: mini-qr + ports: + - "${MINI_QR_PORT:-8089}:8080" + restart: unless-stopped + networks: + - changemaker-lite networks: changemaker-lite: diff --git a/docs-cloudflare.md b/docs-cloudflare.md new file mode 100644 index 0000000..a56e45d --- /dev/null +++ b/docs-cloudflare.md @@ -0,0 +1,40 @@ +# Cloudflare Configuration Guide for Changemaker.lite + +This guide will help you properly configure Cloudflare credentials for use with Changemaker.lite's production deployment. + +## Finding Your Zone ID + +The Zone ID is a unique identifier for your domain in Cloudflare. + +1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com) +2. Select your domain (e.g., cmlite.org) +3. On the right sidebar under "API", you'll see "Zone ID" +4. Copy this value - it should look something like: `1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p` + +## Creating an API Token + +You need an API token with proper permissions to manage DNS records and access policies. + +1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com) > Profile (top right) > API Tokens +2. Click "Create Token" +3. Use the "Edit zone DNS" template (or create a custom token) +4. For a custom token, ensure it has the following permissions: + - Zone - DNS - Edit + - Zone - Settings - Read + - Account - Cloudflare Tunnel - Read +5. Under "Zone Resources", select "Include - Specific zone" and choose your domain +6. Create the token and copy its value + +## Finding Your Account ID + +The Account ID is needed for some Cloudflare Tunnel operations. + +1. Go to the [Cloudflare Dashboard](https://dash.cloudflare.com) +2. Look at the URL when logged in - it will contain your account ID: + `https://dash.cloudflare.com/1a2b3c4d5e6f7g8h9i0j/your-domain.com` +3. The string after `/dash.cloudflare.com/` is your Account ID (e.g., `1a2b3c4d5e6f7g8h9i0j`) + +## Updating Your .env File + +Update your `.env` file with these values: + diff --git a/map/docker-compose.yml b/map/docker-compose.yml index ff49eba..71edb80 100644 --- a/map/docker-compose.yml +++ b/map/docker-compose.yml @@ -25,3 +25,9 @@ services: options: max-size: "10m" max-file: "3" + networks: + - changemakerlite_changemaker-lite + +networks: + changemakerlite_changemaker-lite: + external: true diff --git a/mkdocs/.cache/plugin/social/112206e21dc1efc3b2fdcaf783b198ac.png b/mkdocs/.cache/plugin/social/112206e21dc1efc3b2fdcaf783b198ac.png new file mode 100644 index 0000000..ea7810f Binary files /dev/null and b/mkdocs/.cache/plugin/social/112206e21dc1efc3b2fdcaf783b198ac.png differ diff --git a/mkdocs/docs/overrides/main.html.backup_20250629_154241 b/mkdocs/docs/overrides/main.html.backup_20250629_154241 deleted file mode 100644 index 732197a..0000000 --- a/mkdocs/docs/overrides/main.html.backup_20250629_154241 +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block extrahead %} -{% endblock %} - -{% block announce %} - -Changemaker Archive. Learn more -{% endblock %} diff --git a/mkdocs/docs/overrides/main.html.backup_20250629_154929 b/mkdocs/docs/overrides/main.html.backup_20250629_154929 deleted file mode 100644 index 732197a..0000000 --- a/mkdocs/docs/overrides/main.html.backup_20250629_154929 +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block extrahead %} -{% endblock %} - -{% block announce %} - -Changemaker Archive. Learn more -{% endblock %} diff --git a/mkdocs/mkdocs.yml.backup_20250629_154929 b/mkdocs/mkdocs.yml.backup_20250629_154929 deleted file mode 100644 index 5c2ec6c..0000000 --- a/mkdocs/mkdocs.yml.backup_20250629_154929 +++ /dev/null @@ -1,66 +0,0 @@ -site_name: Changemaker Lite Documentation -site_description: Self-hosted platform for documentation and development -site_url: https://changeme.org -site_author: Bunker Ops -docs_dir: docs -site_dir: site - -# Theme -theme: - name: material - custom_dir: docs/overrides - palette: - scheme: slate - primary: deep purple - accent: amber - features: - - navigation.tracking - - navigation.indexes - - navigation.collapse - - navigation.path - - content.code.copy - - navigation.top - - navigation.tabs # Added for top-level navigation tabs - -extra_css: - - stylesheets/extra.css -markdown_extensions: - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.superfences - - admonition - - pymdownx.details - - attr_list - - md_in_html - - pymdownx.emoji # Simplified emoji config - - footnotes - - toc: - permalink: true - # The specific slugify line was removed to avoid previous tool error, - # you may need to add back your preferred slugify option: - # slugify: !!python/name:pymdownx.slugs.uslugify - -copyright: Copyright © 2024 The Bunker Operations - Built with Change Maker - -# Plugins -plugins: - - social - - search - - blog - # - tags # Consider adding if you use tags for your blog or docs - -# Navigation -nav: - - Home: index.md - - Getting Started: getting-started.md - - Services: - - Overview: services/index.md - - Homepage: services/homepage.md - - Code Server: services/code-server.md - - MkDocs Material: services/mkdocs.md - - Static Site Server: services/static-server.md - - Listmonk: services/listmonk.md - - PostgreSQL: services/postgresql.md - - n8n: services/n8n.md - - NocoDB: services/nocodb.md - - Blog: blog/index.md diff --git a/start-production.sh b/start-production.sh old mode 100644 new mode 100755 index 02e1361..4b931e2 --- a/start-production.sh +++ b/start-production.sh @@ -2,13 +2,13 @@ echo "#############################################################" echo "# " -echo "# Changemaker.lite Production Setup " +echo "# Changemaker.lite Production Deployment " echo "# " echo "# This script will: " -echo "# 1. Create a Cloudflare tunnel " -echo "# 2. Configure DNS records " -echo "# 3. Set up access policies " -echo "# 4. Enable the cloudflared container " +echo "# 1. Install and configure cloudflared " +echo "# 2. Create a systemd service for the tunnel " +echo "# 3. Configure DNS records " +echo "# 4. Set up access policies " echo "# " echo "#############################################################" echo "" @@ -16,7 +16,9 @@ echo "" # Get script directory SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ENV_FILE="$SCRIPT_DIR/.env" -DOCKER_COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml" +TUNNEL_CONFIG_FILE="$SCRIPT_DIR/configs/cloudflare/tunnel-config.yml" +TUNNEL_CREDS_DIR="$SCRIPT_DIR/configs/cloudflare" +SERVICE_NAME="cloudflared-changemaker" # Source environment variables if [ -f "$ENV_FILE" ]; then @@ -29,191 +31,332 @@ fi # Check if Cloudflare credentials are properly configured if [ -z "$CF_API_TOKEN" ] || [ "$CF_API_TOKEN" == "your_cloudflare_api_token" ] || \ [ -z "$CF_ZONE_ID" ] || [ "$CF_ZONE_ID" == "your_cloudflare_zone_id" ] || \ - [ -z "$CF_DOMAIN" ]; then - echo "Error: Cloudflare credentials not properly configured in .env file." + [ -z "$CF_DOMAIN" ] || [ -z "$CF_ACCOUNT_ID" ] || [ "$CF_ACCOUNT_ID" == "your_cloudflare_account_id" ]; then + echo "Error: Cloudflare configuration incomplete." echo "" echo "Current values:" echo " CF_API_TOKEN: ${CF_API_TOKEN:-not set}" echo " CF_ZONE_ID: ${CF_ZONE_ID:-not set}" echo " CF_DOMAIN: ${CF_DOMAIN:-not set}" + echo " CF_ACCOUNT_ID: ${CF_ACCOUNT_ID:-not set}" echo "" - echo "Please run ./config.sh and configure Cloudflare settings, or manually update your .env file." + echo "Please run ./config.sh and complete the Cloudflare configuration." exit 1 fi -# Check if CF_ACCOUNT_ID exists in .env, if not prompt for it -if [ -z "$CF_ACCOUNT_ID" ]; then - echo "Cloudflare Account ID is required for tunnel creation." - echo "You can find it in your Cloudflare dashboard at the top right." - read -p "Enter your Cloudflare Account ID: " CF_ACCOUNT_ID - - # Update .env with account ID - echo "" >> "$ENV_FILE" - echo "# Cloudflare Account ID (added by start-production.sh)" >> "$ENV_FILE" - echo "CF_ACCOUNT_ID=$CF_ACCOUNT_ID" >> "$ENV_FILE" - - # Re-export the variable - export CF_ACCOUNT_ID -fi - -# Check if required variables are set -if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ZONE_ID" ] || [ -z "$CF_DOMAIN" ]; then - echo "Error: Cloudflare credentials not found in .env file." - echo "Please run ./config.sh and configure Cloudflare settings." - exit 1 -fi - -# Check if services are running -echo "Checking if services are running..." -if ! docker compose ps | grep -q "Up"; then - echo "Error: No services are running. Please run 'docker compose up -d' first." - exit 1 -fi - -echo "✓ Services are running" -echo "" - -# Show current service URLs -echo "Current local service URLs:" -echo " - Homepage: http://localhost:${HOMEPAGE_PORT:-3010}" -echo " - Code Server: http://localhost:${CODE_SERVER_PORT:-8888}" -echo " - Listmonk: http://localhost:${LISTMONK_PORT:-9000}" -echo " - Documentation (Dev): http://localhost:${MKDOCS_PORT:-4000}" -echo " - Documentation (Built): http://localhost:${MKDOCS_SITE_SERVER_PORT:-4001}" -echo " - n8n: http://localhost:${N8N_PORT:-5678}" -echo " - NocoDB: http://localhost:${NOCODB_PORT:-8090}" -echo " - Gitea: http://localhost:${GITEA_WEB_PORT:-3030}" -echo "" - -read -p "Have you tested all services locally and are ready to go to production? (yes/no): " confirm -if [[ "$confirm" != "yes" ]]; then - echo "Please test all services locally before running production setup." - exit 0 -fi - -echo "" -echo "Starting production setup..." - -# Install jq if not present -if ! command -v jq &> /dev/null; then - echo "Installing jq..." - sudo apt-get update && sudo apt-get install -y jq -fi - -# Create Cloudflare tunnel -echo "" -echo "Creating Cloudflare tunnel..." - -TUNNEL_NAME="changemaker-${HOSTNAME}-$(date +%s)" - -# Create tunnel via API -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\",\"config_src\":\"cloudflare\"}") - -# Check if we need account ID -if echo "$TUNNEL_RESPONSE" | jq -e '.errors[] | select(.code == 0)' > /dev/null 2>&1; then - echo "" - echo "Account ID is required. You can find it in your Cloudflare dashboard." - read -p "Enter your Cloudflare Account ID: " CF_ACCOUNT_ID - - # Update .env with account ID - if grep -q "^CF_ACCOUNT_ID=" "$ENV_FILE"; then - sed -i "s/^CF_ACCOUNT_ID=.*/CF_ACCOUNT_ID=$CF_ACCOUNT_ID/" "$ENV_FILE" +# Check if cloudflared is installed +check_cloudflared() { + if ! command -v cloudflared &> /dev/null; then + echo "cloudflared not found. Installing..." + install_cloudflared else - echo "CF_ACCOUNT_ID=$CF_ACCOUNT_ID" >> "$ENV_FILE" + echo "✅ cloudflared is already installed: $(cloudflared --version | head -n1)" + fi +} + +# Install cloudflared +install_cloudflared() { + echo "Installing cloudflared..." + + # Check if we need to add the Cloudflare repository + if [ ! -f /usr/share/keyrings/cloudflare-main.gpg ]; then + # Add the cloudflare gpg key + curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg + + # Add the repository + echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list fi - # Retry with account ID - 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\",\"config_src\":\"cloudflare\"}") -fi - -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 "Error: Failed to create tunnel" - echo "$TUNNEL_RESPONSE" | jq . - exit 1 -fi - -echo "✓ Created tunnel: $TUNNEL_NAME (ID: $TUNNEL_ID)" - -# Update .env with tunnel information -echo "CF_TUNNEL_ID=$TUNNEL_ID" >> "$ENV_FILE" -echo "CF_TUNNEL_TOKEN=$TUNNEL_TOKEN" >> "$ENV_FILE" -echo "CF_TUNNEL_NAME=$TUNNEL_NAME" >> "$ENV_FILE" - -# Configure tunnel routes -echo "" -echo "Configuring tunnel routes..." - -# Define services and their configurations -declare -A SERVICES=( - ["dashboard"]="homepage-changemaker:3000" - ["code"]="code-server:8080" - ["listmonk"]="listmonk-app:9000" - ["docs"]="mkdocs:8000" - ["n8n"]="n8n:5678" - ["db"]="nocodb:8080" - ["git"]="gitea-app:3000" -) - -# Configure root domain to mkdocs-site-server -echo "Configuring route for root domain..." -ROOT_CONFIG=$(cat < $service" + # Update and install + sudo apt-get update && sudo apt-get install -y cloudflared - CONFIG=$(cat < /dev/null; then + echo "Failed to install cloudflared. Please install it manually following the instructions at:" + echo "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/" + exit 1 + else + echo "✅ cloudflared installed successfully: $(cloudflared --version | head -n1)" + fi } -EOF -) - - RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/tunnels/$TUNNEL_ID/configurations" \ - -H "Authorization: Bearer $CF_API_TOKEN" \ - -H "Content-Type: application/json" \ - --data "$CONFIG") -done -# Create DNS records -echo "" -echo "Creating DNS records..." +# Authenticate with Cloudflare +authenticate_cloudflare() { + echo "Checking Cloudflare authentication..." + + # Remove --account-tag parameter as it's not supported in this version + if ! cloudflared tunnel list &> /dev/null; then + echo "Not authenticated with Cloudflare. Starting authentication process..." + echo "Log in to Cloudflare in the browser window that opens..." + + # Run login + if ! cloudflared tunnel login; then + echo "Authentication failed. Please try again or authenticate manually using:" + echo "cloudflared tunnel login" + exit 1 + fi + else + echo "✅ Already authenticated with Cloudflare" + fi +} + +# Create or get existing tunnel +setup_tunnel() { + echo "Setting up Cloudflare tunnel..." + + # Check if we have a tunnel ID in the environment + local tunnel_name="changemaker-lite" + local tunnel_id="" + + # Check if tunnel exists by name + echo "Checking for existing tunnel named '$tunnel_name'..." + local tunnel_list_output + # Remove --account-tag parameter as it's not supported in this version + tunnel_list_output=$(cloudflared tunnel list --output json 2>&1) + + # Check if the command was successful + if [ $? -ne 0 ]; then + echo "Error listing tunnels: $tunnel_list_output" + echo "Please ensure you're properly authenticated." + exit 1 + fi + + # Fix for invalid JSON output - try to clean the output if needed + if ! echo "$tunnel_list_output" | jq '.' &>/dev/null; then + echo "Warning: Invalid JSON returned from cloudflared. Attempting to clean output..." + # Try to extract valid JSON portion + tunnel_list_output=$(echo "$tunnel_list_output" | grep -o '\[.*\]' || echo "[]") + fi + + # Now try to extract the tunnel info + local tunnel_info + if echo "$tunnel_list_output" | jq '.' &>/dev/null; then + tunnel_info=$(echo "$tunnel_list_output" | jq -r '.[] | select(.name=="'$tunnel_name'")' 2>/dev/null || echo "") + else + echo "Failed to parse tunnel list. Cannot check for existing tunnels." + tunnel_info="" + fi + + if [ -n "$tunnel_info" ]; then + # Tunnel exists + tunnel_id=$(echo "$tunnel_info" | jq -r '.id' 2>/dev/null) + if [ -n "$tunnel_id" ] && [ "$tunnel_id" != "null" ]; then + echo "✅ Found existing tunnel: $tunnel_name (ID: $tunnel_id)" + + # Update the environment variable if needed + if [ "$CF_TUNNEL_ID" != "$tunnel_id" ]; then + echo "Updating CF_TUNNEL_ID in .env file..." + sed -i "s/CF_TUNNEL_ID=.*/CF_TUNNEL_ID=$tunnel_id/" "$ENV_FILE" + # Reload the variable + export CF_TUNNEL_ID="$tunnel_id" + fi + else + echo "Warning: Found tunnel but couldn't extract ID" + fi + else + # Create new tunnel + echo "Creating new tunnel: $tunnel_name" + local tunnel_create_output + # Remove --account-tag parameter as it's not supported in this version + tunnel_create_output=$(cloudflared tunnel create "$tunnel_name" 2>&1) + echo "Tunnel creation output:" + echo "$tunnel_create_output" + + if [ $? -ne 0 ]; then + echo "Failed to create tunnel. Please check your Cloudflare credentials." + exit 1 + fi + + # Try multiple methods to extract the tunnel ID from output + # Method 1: Try the original regex pattern + tunnel_id=$(echo "$tunnel_create_output" | grep -oP 'Created tunnel changemaker-lite with id \K[a-f0-9-]+' || echo "") + + # Method 2: Look for UUID pattern + if [ -z "$tunnel_id" ]; then + tunnel_id=$(echo "$tunnel_create_output" | grep -oP '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}' || echo "") + fi + + # Method 3: Look for hex string that might be the ID + if [ -z "$tunnel_id" ]; then + tunnel_id=$(echo "$tunnel_create_output" | grep -oP '\b[a-f0-9-]{20,}\b' || echo "") + fi + + # Interactive method as fallback + if [ -z "$tunnel_id" ]; then + echo "Could not automatically extract tunnel ID from output." + echo "Please check the output above and enter the tunnel ID manually:" + read -p "Tunnel ID: " tunnel_id + + if [ -z "$tunnel_id" ]; then + echo "No tunnel ID provided. Please create the tunnel manually and set CF_TUNNEL_ID in the .env file." + exit 1 + fi + fi + + echo "✅ Created new tunnel: $tunnel_name (ID: $tunnel_id)" + + # Update the environment variable + sed -i "s/CF_TUNNEL_ID=.*/CF_TUNNEL_ID=$tunnel_id/" "$ENV_FILE" + export CF_TUNNEL_ID="$tunnel_id" + fi + + # Make sure credentials directory exists + mkdir -p "$TUNNEL_CREDS_DIR" + + # Check if we need to copy the credentials file + local creds_file="$HOME/.cloudflared/$tunnel_id.json" + local target_creds_file="$TUNNEL_CREDS_DIR/$tunnel_id.json" + + if [ -f "$creds_file" ] && [ ! -f "$target_creds_file" ]; then + echo "Copying tunnel credentials to project directory..." + cp "$creds_file" "$target_creds_file" + echo "✅ Credentials copied to $target_creds_file" + elif [ ! -f "$creds_file" ]; then + echo "Warning: Tunnel credentials not found at $creds_file" + echo "Checking for credentials in alternative locations..." + + # Try to find credentials in ~/.cloudflared directory + local found_creds=false + for cred_file in "$HOME/.cloudflared/"*.json; do + if [ -f "$cred_file" ]; then + echo "Found credentials file: $cred_file" + echo "Checking if this file contains credentials for our tunnel..." + + # Check if the file contains the tunnel ID + if grep -q "$tunnel_id" "$cred_file"; then + echo "✅ This appears to be the correct credentials file!" + cp "$cred_file" "$target_creds_file" + echo "✅ Credentials copied to $target_creds_file" + found_creds=true + break + fi + fi + done + + if [ "$found_creds" = false ]; then + echo "Error: Could not find tunnel credentials." + echo "Please locate the credentials file for tunnel $tunnel_id and copy it to:" + echo "$target_creds_file" + echo "" + echo "You can also run this command manually to create a new tunnel with credentials:" + echo "cloudflared tunnel create --account-tag \"$CF_ACCOUNT_ID\" \"$tunnel_name\"" + exit 1 + fi + fi + + # Update tunnel configuration file + update_tunnel_config "$target_creds_file" + + return 0 +} + +# Update tunnel configuration +update_tunnel_config() { + local creds_file="$1" + + echo "Updating tunnel configuration..." + + # Create or update the config file + cat > "$TUNNEL_CONFIG_FILE" << EOL +# Cloudflare Tunnel Configuration for $CF_DOMAIN +# Generated by Changemaker.lite start-production.sh on $(date) + +tunnel: ${CF_TUNNEL_ID} +credentials-file: ${creds_file} +ingress: + - hostname: homepage.${CF_DOMAIN} + service: http://localhost:${HOMEPAGE_PORT:-3010} + - hostname: code.${CF_DOMAIN} + service: http://localhost:${CODE_SERVER_PORT:-8888} + - hostname: listmonk.${CF_DOMAIN} + service: http://localhost:${LISTMONK_PORT:-9000} + - hostname: docs.${CF_DOMAIN} + service: http://localhost:${MKDOCS_PORT:-4000} + - hostname: ${CF_DOMAIN} + service: http://localhost:${MKDOCS_SITE_SERVER_PORT:-4001} + - hostname: n8n.${CF_DOMAIN} + service: http://localhost:${N8N_PORT:-5678} + - hostname: db.${CF_DOMAIN} + service: http://localhost:${NOCODB_PORT:-8090} + - hostname: git.${CF_DOMAIN} + service: http://localhost:${GITEA_WEB_PORT:-3030} + - hostname: map.${CF_DOMAIN} + service: http://localhost:${MAP_PORT:-3000} + - hostname: qr.${CF_DOMAIN} + service: http://localhost:${MINI_QR_PORT:-8089} + - service: http_status:404 +EOL + + echo "✅ Tunnel configuration updated at $TUNNEL_CONFIG_FILE" +} + +# Create systemd service for cloudflared +create_systemd_service() { + echo "Creating systemd service for cloudflared tunnel..." + + local cloudflared_path=$(which cloudflared) + local username=$(whoami) + + # Create service file content + local service_content="[Unit] +Description=Cloudflare Tunnel for Changemaker.lite +After=network.target + +[Service] +User=$username +ExecStart=$cloudflared_path tunnel --config $TUNNEL_CONFIG_FILE run +Restart=always +RestartSec=5 +StartLimitInterval=0 + +[Install] +WantedBy=multi-user.target" + + # Write service file + echo "$service_content" | sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null + + # Reload systemd + sudo systemctl daemon-reload + + # Enable and start the service + sudo systemctl enable $SERVICE_NAME.service + sudo systemctl restart $SERVICE_NAME.service + + # Check service status + sleep 3 + if sudo systemctl is-active --quiet $SERVICE_NAME.service; then + echo "✅ Cloudflared service is running" + else + echo "⚠️ Cloudflared service failed to start. Checking logs..." + sudo systemctl status $SERVICE_NAME.service + echo "Viewing recent logs:" + sudo journalctl -u $SERVICE_NAME.service --no-pager -n 20 + + echo "Please check the configuration and try again." + exit 1 + fi +} # Function to create/update DNS record create_dns_record() { local name=$1 - local content="$TUNNEL_ID.cfargotunnel.com" + local full_name + local content="$CF_TUNNEL_ID.cfargotunnel.com" + + # Determine the full DNS name + if [ "$name" == "@" ]; then + full_name="$CF_DOMAIN" + else + full_name="$name.$CF_DOMAIN" + fi + + echo "Configuring DNS record for $full_name..." # Check if record exists - EXISTING=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$name.$CF_DOMAIN&type=CNAME" \ + EXISTING=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$full_name" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json") - RECORD_ID=$(echo "$EXISTING" | jq -r '.result[0].id') + RECORD_ID=$(echo "$EXISTING" | jq -r '.result[] | select(.name == "'$full_name'") | .id' | head -1) if [ ! -z "$RECORD_ID" ] && [ "$RECORD_ID" != "null" ]; then # Update existing record @@ -242,80 +385,215 @@ create_dns_record() { fi if echo "$RESPONSE" | jq -e '.success' > /dev/null; then - echo "✓ DNS record for $name.$CF_DOMAIN configured" + echo " ✅ DNS record configured" else - echo "✗ Failed to configure DNS record for $name.$CF_DOMAIN" + echo " ❌ Failed to configure DNS record" echo "$RESPONSE" | jq '.errors' fi } -# Create DNS records for all subdomains -for subdomain in "${!SERVICES[@]}"; do - create_dns_record "$subdomain" -done - -# Create root domain record -create_dns_record "@" - # Set up access policies -echo "" -echo "Setting up access policies..." -read -p "Enter admin email for protected services (dashboard, code): " ADMIN_EMAIL +setup_access_policies() { + echo "" + echo "Setting up Cloudflare Access policies..." + read -p "Enter email address for admin access to protected services: " ADMIN_EMAIL -if [[ "$ADMIN_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then - # Create access applications for protected services - PROTECTED_SERVICES=("dashboard" "code") - - for service in "${PROTECTED_SERVICES[@]}"; do - echo "Creating access policy for $service.$CF_DOMAIN..." + if [[ "$ADMIN_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + # Protected services + PROTECTED_SERVICES=("homepage" "code") - # Create access application - APP_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \ - -H "Authorization: Bearer $CF_API_TOKEN" \ - -H "Content-Type: application/json" \ - --data "{ - \"name\": \"$service.$CF_DOMAIN\", - \"domain\": \"$service.$CF_DOMAIN\", - \"type\": \"self_hosted\", - \"session_duration\": \"24h\" - }") - - APP_ID=$(echo "$APP_RESPONSE" | jq -r '.result.id') - - if [ ! -z "$APP_ID" ] && [ "$APP_ID" != "null" ]; then - # Create policy - POLICY_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$APP_ID/policies" \ - -H "Authorization: Bearer $CF_API_TOKEN" \ - -H "Content-Type: application/json" \ - --data "{ - \"name\": \"Email Policy\", - \"decision\": \"allow\", - \"include\": [{ - \"email\": {\"email\": \"$ADMIN_EMAIL\"} - }] - }") + for service in "${PROTECTED_SERVICES[@]}"; do + echo "Creating access policy for $service.$CF_DOMAIN..." - echo "✓ Access policy created for $service.$CF_DOMAIN" + # Check if application already exists + EXISTING_APPS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \ + -H "Authorization: Bearer $CF_API_TOKEN" \ + -H "Content-Type: application/json") + + APP_ID=$(echo "$EXISTING_APPS" | jq -r ".result[] | select(.domain == \"$service.$CF_DOMAIN\") | .id" | head -1) + + if [ -z "$APP_ID" ] || [ "$APP_ID" == "null" ]; then + # Create new access application + APP_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \ + -H "Authorization: Bearer $CF_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data "{ + \"name\": \"$service.$CF_DOMAIN\", + \"domain\": \"$service.$CF_DOMAIN\", + \"type\": \"self_hosted\", + \"session_duration\": \"24h\", + \"auto_redirect_to_identity\": false + }") + + APP_ID=$(echo "$APP_RESPONSE" | jq -r '.result.id') + fi + + if [ ! -z "$APP_ID" ] && [ "$APP_ID" != "null" ]; then + # Create/update policy + POLICY_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$APP_ID/policies" \ + -H "Authorization: Bearer $CF_API_TOKEN" \ + -H "Content-Type: application/json" \ + --data "{ + \"name\": \"Email Policy\", + \"decision\": \"allow\", + \"include\": [{ + \"email\": {\"email\": \"$ADMIN_EMAIL\"} + }] + }") + + if echo "$POLICY_RESPONSE" | jq -e '.success' > /dev/null; then + echo " ✅ Access policy configured" + else + echo " ⚠️ Policy may already exist or update failed" + fi + else + echo " ❌ Failed to create access application" + fi + done + else + echo "Invalid email format. Skipping access policy setup." + echo "You can configure access policies manually in the Cloudflare dashboard." + fi +} + +# Check if services are running +check_services() { + echo "Checking if services are running..." + + local all_running=true + + # Check common ports to see if services are running + if ! nc -z localhost ${HOMEPAGE_PORT:-3010} >/dev/null 2>&1; then + echo "❌ Homepage service not detected on port ${HOMEPAGE_PORT:-3010}" + all_running=false + fi + + if ! nc -z localhost ${CODE_SERVER_PORT:-8888} >/dev/null 2>&1; then + echo "❌ Code Server service not detected on port ${CODE_SERVER_PORT:-8888}" + all_running=false + fi + + if ! nc -z localhost ${LISTMONK_PORT:-9000} >/dev/null 2>&1; then + echo "❌ Listmonk service not detected on port ${LISTMONK_PORT:-9000}" + all_running=false + fi + + # At least check that some services are running + if [ "$all_running" = false ]; then + echo "Warning: Some services may not be running." + read -p "Continue anyway? (y/n): " continue_answer + if [[ ! "$continue_answer" =~ ^[Yy]$ ]]; then + echo "Please start your services with 'docker compose up -d' first." + exit 1 fi - done -else - echo "Invalid email format. Skipping access policy setup." + else + echo "✅ Services appear to be running" + fi +} + +# Add this function to verify Cloudflare API credentials +verify_cloudflare_credentials() { + echo "Verifying Cloudflare API credentials..." + + # Test the API token and Zone ID with a simple request + local TEST_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID" \ + -H "Authorization: Bearer $CF_API_TOKEN" \ + -H "Content-Type: application/json") + + if echo "$TEST_RESPONSE" | jq -e '.success' > /dev/null 2>&1; then + echo "✅ Cloudflare API credentials verified successfully" + return 0 + else + echo "❌ Cloudflare API credentials verification failed" + echo "Error details:" + echo "$TEST_RESPONSE" | jq '.errors' + echo "" + echo "Common issues:" + echo "1. Zone ID may be incorrect - make sure you're using the correct Zone ID from the Cloudflare dashboard" + echo "2. API Token may not have the required permissions - make sure it has Zone:DNS and Zone:Settings permissions" + echo "" + echo "Your current Zone ID: $CF_ZONE_ID" + echo "To continue without configuring DNS records, enter 'continue'. To quit and fix the credentials, enter 'quit'." + read -p "Continue or quit? [continue/quit]: " cf_choice + + if [[ "$cf_choice" == "quit" ]]; then + echo "Exiting. Please update your credentials in .env and try again." + exit 1 + else + echo "Continuing without configuring DNS records..." + return 1 + fi + fi +} + +# Main execution flow +echo "Starting Changemaker.lite production deployment..." + +# Check if services are running +check_services + +# Check if cloudflared is installed, install if needed +check_cloudflared + +# Authenticate with Cloudflare +authenticate_cloudflare + +# Set up tunnel +setup_tunnel + +# Create systemd service +create_systemd_service + +# Verify Cloudflare credentials before proceeding with DNS configuration +CF_CREDS_VALID=true +if ! verify_cloudflare_credentials; then + CF_CREDS_VALID=false + echo "Skipping DNS record creation and access policy setup due to invalid credentials" fi -# Enable cloudflared in docker-compose -echo "" -echo "Enabling cloudflared container..." +# Only proceed with DNS configuration if credentials are valid +if [ "$CF_CREDS_VALID" = true ]; then + # Create/update DNS records + echo "" + echo "Creating/updating DNS records..." -# Uncomment cloudflared service in docker-compose.yml -sed -i '/# cloudflared:/,/# gitea-app/s/^ # / /' "$DOCKER_COMPOSE_FILE" + # Define subdomains + declare -A SUBDOMAINS=( + ["homepage"]="Homepage" + ["code"]="Code Server" + ["listmonk"]="Listmonk" + ["docs"]="Documentation" + ["n8n"]="n8n" + ["db"]="NocoDB" + ["git"]="Gitea" + ["map"]="Map" + ["qr"]="Mini QR" + ) -# Start cloudflared container -docker compose up -d cloudflared + for subdomain in "${!SUBDOMAINS[@]}"; do + create_dns_record "$subdomain" + done + + # Create root domain record + create_dns_record "@" + + echo "" + echo "✅ All DNS records configured" + + # Set up access policies + setup_access_policies +else + echo "" + echo "⚠️ DNS records and access policies were not configured." + echo "To manually configure DNS records, you can use the Cloudflare dashboard." + echo "Add CNAME records for each subdomain pointing to: $CF_TUNNEL_ID.cfargotunnel.com" + echo "" +fi echo "" echo "#############################################################" echo "# " -echo "# Production Setup Complete! " +echo "# Production Deployment Complete! " echo "# " echo "#############################################################" echo "" @@ -328,21 +606,13 @@ echo " - Documentation: https://docs.$CF_DOMAIN" echo " - n8n: https://n8n.$CF_DOMAIN" echo " - NocoDB: https://db.$CF_DOMAIN" echo " - Gitea: https://git.$CF_DOMAIN" +echo " - Map: https://map.$CF_DOMAIN" +echo " - Mini QR: https://qr.$CF_DOMAIN" echo "" -if [ ! -z "$ADMIN_EMAIL" ]; then - echo "Protected services (login required with $ADMIN_EMAIL):" - echo " - Dashboard: https://dashboard.$CF_DOMAIN" - echo " - Code Server: https://code.$CF_DOMAIN" -else - echo "Admin services (no protection configured):" - echo " - Dashboard: https://dashboard.$CF_DOMAIN" - echo " - Code Server: https://code.$CF_DOMAIN" -fi +echo "Protected services (requires login with $ADMIN_EMAIL):" +echo " - Dashboard: https://homepage.$CF_DOMAIN" +echo " - Code Server: https://code.$CF_DOMAIN" echo "" -echo "Note: DNS propagation may take a few minutes." -echo "" -echo "To stop production mode and return to local-only:" -echo " docker compose stop cloudflared" -echo "" -echo "To fully remove production setup:" -echo " docker compose down cloudflared" \ No newline at end of file +echo "Cloudflared service status: sudo systemctl status $SERVICE_NAME" +echo "View tunnel logs: sudo journalctl -u $SERVICE_NAME -f" +echo "" \ No newline at end of file