diff --git a/README.md b/README.md index b351885..5f2db8c 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Railway is the primary deployment platform for staging. It provides built-in log 1. In your Railway project, click **"+ New"** → **"GitHub Repo"** (or **"Empty Service"**) 2. If using GitHub: - Connect your GitHub account - - Select the `lottery-be` repository + - Select the `honey-be` repository - Railway will automatically detect it's a Java/Maven project 3. If using Empty Service: - Click **"Empty Service"** diff --git a/nginx-testforapp-test-base.conf b/nginx-testforapp-test-base.conf new file mode 100644 index 0000000..95eb4e7 --- /dev/null +++ b/nginx-testforapp-test-base.conf @@ -0,0 +1,242 @@ +# Two frontends on the same backend: +# - Frontend 1: /opt/app/frontend/dist -> https://testforapp.website/ (build with base: '/') +# - Frontend 2: /opt/app/frontend/test-dist -> https://testforapp.website/test/ (build with base: '/test/') + +upstream backend { + server 127.0.0.1:8082; + server 127.0.0.1:8080 backup; + keepalive 500; +} + +map $http_referer $is_phpmyadmin_request { + ~*18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905 1; + default 0; +} + +# HTTPS server +server { + + server_name testforapp.website; + + listen [::]:443 ssl ipv6only=on; # managed by Certbot + listen 443 ssl; # managed by Certbot + ssl_certificate /etc/letsencrypt/live/testforapp.website/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/testforapp.website/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot + + ssl_stapling on; + ssl_stapling_verify on; + resolver 8.8.8.8 8.8.4.4 valid=300s; + resolver_timeout 5s; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()"; + + root /opt/app/frontend/dist; + index index.html; + +# --- Frontend 2 at /test/ (from test-dist) --- + location = /test { + return 301 /test/; + } + location ^~ /test/ { + alias /opt/app/frontend/test-dist/; + index index.html; + try_files $uri $uri/ /test/index.html; + expires 0; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header Content-Security-Policy "frame-ancestors 'self' https://web.telegram.org https://webk.telegram.org https://webz.telegram.org https://t.me telegram.org;" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()"; + } + +# Admin Panel - root path (redirects to trailing slash) +location = /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa { + return 301 /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/; +} + +location ~ ^/dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/(assets/.+)$ { + alias /opt/app/admin-panel/$1; + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; +} + +location /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/ { + alias /opt/app/admin-panel/; + index index.html; + try_files $uri $uri/ /dfab0676b6cb6b257370fb5743d8ddac42ab8153c2661072e8ef2717a10fcfaa/index.html; + expires 0; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; +} + +location /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905 { + proxy_pass http://127.0.0.1:8081/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + gzip off; + proxy_set_header Accept-Encoding ""; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffering on; + proxy_buffer_size 8k; + proxy_buffers 16 8k; + proxy_busy_buffers_size 16k; + proxy_cookie_path / /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/; + sub_filter 'action="index.php?route=/' 'action="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php?route=/'; + sub_filter 'action="index.php' 'action="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; + sub_filter 'action="/index.php' 'action="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; + sub_filter 'Location: /index.php' 'Location: /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; + sub_filter 'Location: index.php' 'Location: /18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; + sub_filter 'href="index.php' 'href="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; + sub_filter 'href="/index.php' 'href="/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/index.php'; + sub_filter_once off; + sub_filter_types text/html; +} + +location ~ ^/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/(themes|js|favicon\.ico|libraries|templates|setup|config|tmp|index\.php) { + rewrite ^/18cac693dfdec1c7bd3080d3451d0545746081e2335c241cec5d39ff75822905/(.*)$ /$1 break; + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffering off; +} + +location ~ ^/(themes|js|favicon\.ico|libraries|templates|setup|config|tmp|index\.php) { + if ($is_phpmyadmin_request = 0) { + return 404; + } + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffering off; +} + + location /videos/ { + root /usr/share/nginx/html; + add_header Cache-Control "public, max-age=31536000, immutable"; + add_header Access-Control-Allow-Origin "*"; + } + + location ^~ /images/ { + root /usr/share/nginx/html; + add_header Cache-Control "public, max-age=31536000, immutable"; + add_header Access-Control-Allow-Origin "*"; + autoindex off; + } + + # Frontend 1: static assets at root (from dist) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Frontend 1: SPA at / (from dist) + location / { + try_files $uri $uri/ /index.html; + expires 0; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header Content-Security-Policy "frame-ancestors 'self' https://web.telegram.org https://webk.telegram.org https://webz.telegram.org https://t.me telegram.org;" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()"; + } + + location ^~ /avatars/ { + alias /opt/app/data/avatars/; + expires 24h; + add_header Cache-Control "public, must-revalidate"; + access_log off; + location ~ \.(php|html)$ { + deny all; + } + } + + location /api/telegram/webhook/ { + access_log off; + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 30s; + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + } + + location /api/ { + limit_req zone=api_limit burst=200 nodelay; + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + location = /phpmyadmin { + return 404; + } + + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } +} + +server { + if ($host = testforapp.website) { + return 301 https://$host$request_uri; + } + listen 80; + listen [::]:80; + server_name testforapp.website; + return 404; +} diff --git a/scripts/backup-database.sh b/scripts/backup-database.sh index d0b2bb5..604636c 100644 --- a/scripts/backup-database.sh +++ b/scripts/backup-database.sh @@ -11,8 +11,8 @@ # # Prerequisites: # 1. SSH key-based authentication to backup VPS (5.45.77.77) -# 2. Database password accessible via /run/secrets/lottery-config.properties -# 3. Docker container 'lottery-mysql' running +# 2. Database password accessible via /run/secrets/honey-config.properties +# 3. Docker container 'honey-mysql' running # # Backup location on backup VPS: /raid/backup/acc_260182/ @@ -22,9 +22,9 @@ set -euo pipefail BACKUP_VPS_HOST="5.45.77.77" BACKUP_VPS_USER="acc_260182" # User account on backup VPS BACKUP_VPS_PATH="/raid/backup/acc_260182" -MYSQL_CONTAINER="lottery-mysql" +MYSQL_CONTAINER="honey-mysql" MYSQL_DATABASE="lottery_db" -SECRET_FILE="/run/secrets/lottery-config.properties" +SECRET_FILE="/run/secrets/honey-config.properties" BACKUP_DIR="/opt/app/backups" KEEP_LOCAL=false COMPRESS=true diff --git a/scripts/create-secret-file-from-template.sh b/scripts/create-secret-file-from-template.sh index e1dd53a..51c1287 100644 --- a/scripts/create-secret-file-from-template.sh +++ b/scripts/create-secret-file-from-template.sh @@ -3,8 +3,8 @@ # Script to create secret file from template # Usage: ./create-secret-file-from-template.sh /path/to/template /path/to/output -TEMPLATE_FILE="${1:-lottery-config.properties.template}" -OUTPUT_FILE="${2:-/run/secrets/lottery-config.properties}" +TEMPLATE_FILE="${1:-honey-config.properties.template}" +OUTPUT_FILE="${2:-/run/secrets/honey-config.properties}" OUTPUT_DIR=$(dirname "$OUTPUT_FILE") # Check if template exists diff --git a/scripts/create-secret-file.sh b/scripts/create-secret-file.sh index 3d2f23f..aaa6167 100644 --- a/scripts/create-secret-file.sh +++ b/scripts/create-secret-file.sh @@ -3,7 +3,7 @@ # Create secret file from environment variables for testing ConfigLoader # This simulates the mounted secret file approach used in Inferno -SECRET_FILE="/run/secrets/lottery-config.properties" +SECRET_FILE="/run/secrets/honey-config.properties" SECRET_DIR="/run/secrets" # Create directory if it doesn't exist diff --git a/scripts/diagnose-backup-permissions.sh b/scripts/diagnose-backup-permissions.sh index 5fddf7b..71c5966 100644 --- a/scripts/diagnose-backup-permissions.sh +++ b/scripts/diagnose-backup-permissions.sh @@ -2,7 +2,7 @@ # Diagnostic script for backup-database.sh permission issues # Run this on your VPS to identify the root cause -SCRIPT="/opt/app/backend/lottery-be/scripts/backup-database.sh" +SCRIPT="/opt/app/backend/honey-be/scripts/backup-database.sh" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -167,7 +167,7 @@ echo "" # 14. Secret file check echo "14. Checking secret file:" -SECRET_FILE="/run/secrets/lottery-config.properties" +SECRET_FILE="/run/secrets/honey-config.properties" if [ -f "$SECRET_FILE" ]; then echo -e " ${GREEN}✅ Secret file exists${NC}" if [ -r "$SECRET_FILE" ]; then diff --git a/scripts/restore-database.sh b/scripts/restore-database.sh index 767ca23..675286f 100644 --- a/scripts/restore-database.sh +++ b/scripts/restore-database.sh @@ -13,16 +13,16 @@ # ./scripts/restore-database.sh 5.45.77.77:/raid/backup/acc_260182/lottery_db_backup_20240101_120000.sql.gz # # Prerequisites: -# 1. Database password accessible via /run/secrets/lottery-config.properties -# 2. Docker container 'lottery-mysql' running +# 1. Database password accessible via /run/secrets/honey-config.properties +# 2. Docker container 'honey-mysql' running # 3. Database will be DROPPED and RECREATED (all data will be lost!) set -euo pipefail # Configuration -MYSQL_CONTAINER="lottery-mysql" +MYSQL_CONTAINER="honey-mysql" MYSQL_DATABASE="lottery_db" -SECRET_FILE="/run/secrets/lottery-config.properties" +SECRET_FILE="/run/secrets/honey-config.properties" BACKUP_VPS_USER="acc_260182" # User account on backup VPS # Colors for output @@ -179,5 +179,5 @@ fi log "✅ Database restore completed!" warn "⚠️ Remember to restart the backend container if it's running:" -warn " docker restart lottery-backend" +warn " docker restart honey-backend" diff --git a/scripts/setup-logging.sh b/scripts/setup-logging.sh index 0e5b021..f0b3ebd 100644 --- a/scripts/setup-logging.sh +++ b/scripts/setup-logging.sh @@ -9,8 +9,8 @@ set -e if [ -d "/opt/app/backend" ]; then CONFIG_DIR="/opt/app/backend/config" LOG_DIR="/opt/app/logs" -elif [ -d "/opt/app/backend/lottery-be" ]; then - CONFIG_DIR="/opt/app/backend/lottery-be/config" +elif [ -d "/opt/app/backend/honey-be" ]; then + CONFIG_DIR="/opt/app/backend/honey-be/config" LOG_DIR="/opt/app/logs" else # Try to find from current directory @@ -38,9 +38,9 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then # Try multiple locations for JAR file JAR_PATH="" - for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")" "$(dirname "$(dirname "$CONFIG_DIR")")"; do + for search_path in "/opt/app/backend" "/opt/app/backend/honey-be" "$(dirname "$CONFIG_DIR")" "$(dirname "$(dirname "$CONFIG_DIR")")"; do if [ -d "$search_path" ]; then - found_jar=$(find "$search_path" -name "lottery-be-*.jar" -type f 2>/dev/null | head -n 1) + found_jar=$(find "$search_path" -name "honey-be-*.jar" -type f 2>/dev/null | head -n 1) if [ -n "$found_jar" ]; then JAR_PATH="$found_jar" break @@ -50,7 +50,7 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then # Try to find in target directory if [ -z "$JAR_PATH" ]; then - for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")"; do + for search_path in "/opt/app/backend" "/opt/app/backend/honey-be" "$(dirname "$CONFIG_DIR")"; do if [ -d "$search_path/target" ]; then found_jar=$(find "$search_path/target" -name "*.jar" -type f | head -n 1) if [ -n "$found_jar" ]; then @@ -64,7 +64,7 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then if [ -z "$JAR_PATH" ]; then echo "Warning: JAR file not found. Trying to copy from source..." # If JAR not found, copy from source (if available) - for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")"; do + for search_path in "/opt/app/backend" "/opt/app/backend/honey-be" "$(dirname "$CONFIG_DIR")"; do if [ -f "$search_path/src/main/resources/logback-spring.xml" ]; then cp "$search_path/src/main/resources/logback-spring.xml" "$CONFIG_DIR/logback-spring.xml" echo "Copied from source: $search_path/src/main/resources/logback-spring.xml" @@ -84,7 +84,7 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then unzip -p "$JAR_PATH" logback-spring.xml > "$CONFIG_DIR/logback-spring.xml" 2>/dev/null || { echo "Warning: Could not extract from JAR. Trying to copy from source..." # Try copying from source - for search_path in "/opt/app/backend" "/opt/app/backend/lottery-be" "$(dirname "$CONFIG_DIR")"; do + for search_path in "/opt/app/backend" "/opt/app/backend/honey-be" "$(dirname "$CONFIG_DIR")"; do if [ -f "$search_path/src/main/resources/logback-spring.xml" ]; then cp "$search_path/src/main/resources/logback-spring.xml" "$CONFIG_DIR/logback-spring.xml" break diff --git a/src/main/java/com/honey/honey/controller/TelegramWebhookController.java b/src/main/java/com/honey/honey/controller/TelegramWebhookController.java index 878f994..9885b26 100644 --- a/src/main/java/com/honey/honey/controller/TelegramWebhookController.java +++ b/src/main/java/com/honey/honey/controller/TelegramWebhookController.java @@ -4,6 +4,7 @@ import com.honey.honey.config.TelegramProperties; import com.honey.honey.dto.TelegramApiResponse; import com.honey.honey.dto.PaymentWebhookRequest; import com.honey.honey.model.UserA; +import com.honey.honey.service.FeatureSwitchService; import com.honey.honey.service.PaymentService; import com.honey.honey.service.TelegramBotApiService; import com.honey.honey.service.UserService; @@ -56,6 +57,8 @@ import org.springframework.core.io.ByteArrayResource; @RequiredArgsConstructor public class TelegramWebhookController { + private static final String MINI_APP_URL = "https://testforapp.website/test/auth"; + @Value("${app.telegram-webhook.token:}") private String expectedWebhookToken; @@ -64,6 +67,7 @@ public class TelegramWebhookController { private final TelegramProperties telegramProperties; private final LocalizationService localizationService; private final TelegramBotApiService telegramBotApiService; + private final FeatureSwitchService featureSwitchService; private final ObjectMapper objectMapper = new ObjectMapper(); /** @@ -326,32 +330,27 @@ public class TelegramWebhookController { } Locale locale = LocaleConfig.languageCodeToLocale(languageCode != null ? languageCode : "EN"); ReplyKeyboardMarkup replyKeyboard = buildReplyKeyboard(locale); - // Create inline keyboard with only START SPINNING button - InlineKeyboardMarkup inlineKeyboard = new InlineKeyboardMarkup(); - List> inlineRows = new ArrayList<>(); - List inlineRow = new ArrayList<>(); - - InlineKeyboardButton startInlineButton = new InlineKeyboardButton(); - // Add arrows on both sides like in the reference app (right arrow on left, left arrow on right) - String startSpinningButtonText = localizationService.getMessage(locale, "bot.button.startSpinningInline"); - startInlineButton.setText(startSpinningButtonText); - // Use WebAppInfo to open mini app instead of regular URL - WebAppInfo webAppInfo = new WebAppInfo(); - webAppInfo.setUrl("https://win-spin.live/auth"); - startInlineButton.setWebApp(webAppInfo); - inlineRow.add(startInlineButton); - inlineRows.add(inlineRow); - - inlineKeyboard.setKeyboard(inlineRows); - - // Send first message with GIF animation and reply keyboard - // Note: Telegram doesn't allow both inline and reply keyboards in the same message String firstMessage = localizationService.getMessage(locale, "bot.welcome.firstMessage"); sendAnimationWithReplyKeyboard(chatId, firstMessage, replyKeyboard); - - // Send second message with inline button (START SPINNING) + String welcomeText = localizationService.getMessage(locale, "bot.welcome.message"); - sendMessage(chatId, welcomeText, inlineKeyboard); + if (featureSwitchService.isStartGameButtonEnabled()) { + InlineKeyboardMarkup inlineKeyboard = new InlineKeyboardMarkup(); + List> inlineRows = new ArrayList<>(); + List inlineRow = new ArrayList<>(); + InlineKeyboardButton startInlineButton = new InlineKeyboardButton(); + String startSpinningButtonText = localizationService.getMessage(locale, "bot.button.startSpinningInline"); + startInlineButton.setText(startSpinningButtonText); + WebAppInfo webAppInfo = new WebAppInfo(); + webAppInfo.setUrl(MINI_APP_URL); + startInlineButton.setWebApp(webAppInfo); + inlineRow.add(startInlineButton); + inlineRows.add(inlineRow); + inlineKeyboard.setKeyboard(inlineRows); + sendMessage(chatId, welcomeText, inlineKeyboard); + } else { + sendMessage(chatId, welcomeText, null); + } } /** @@ -359,24 +358,22 @@ public class TelegramWebhookController { */ private void sendStartSpinningMessage(Long chatId, Locale locale) { String message = localizationService.getMessage(locale, "bot.message.startSpinning"); - + if (!featureSwitchService.isStartGameButtonEnabled()) { + sendMessage(chatId, message, null); + return; + } InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup(); List> rows = new ArrayList<>(); List row = new ArrayList<>(); - InlineKeyboardButton button = new InlineKeyboardButton(); - // Add arrows on both sides like in the reference app (right arrow on left, left arrow on right) String startSpinningButtonText = localizationService.getMessage(locale, "bot.button.startSpinningInline"); button.setText(startSpinningButtonText); - // Use WebAppInfo to open mini app instead of regular URL WebAppInfo webAppInfo = new WebAppInfo(); - webAppInfo.setUrl("https://win-spin.live/auth"); + webAppInfo.setUrl(MINI_APP_URL); button.setWebApp(webAppInfo); row.add(button); rows.add(row); - keyboard.setKeyboard(rows); - sendMessage(chatId, message, keyboard); } diff --git a/src/main/java/com/honey/honey/service/FeatureSwitchService.java b/src/main/java/com/honey/honey/service/FeatureSwitchService.java index 78ab2b0..835ac70 100644 --- a/src/main/java/com/honey/honey/service/FeatureSwitchService.java +++ b/src/main/java/com/honey/honey/service/FeatureSwitchService.java @@ -22,6 +22,8 @@ public class FeatureSwitchService { public static final String PROMOTIONS_ENABLED = "promotions_enabled"; /** When enabled, send manual_pay=1 for all crypto payouts. When disabled, only for users who completed 50 or 100 referrals (first withdrawal). Default true. */ public static final String MANUAL_PAY_FOR_ALL_PAYOUTS = "manual_pay_for_all_payouts"; + /** When enabled, "Start Game" inline button is sent (welcome and start-spinning messages). When disabled, the button is not sent. Default true. */ + public static final String START_GAME_BUTTON_ENABLED = "start_game_button_enabled"; private final FeatureSwitchRepository featureSwitchRepository; @@ -85,6 +87,16 @@ public class FeatureSwitchService { .orElse(true); } + /** + * Returns whether the "Start Game" inline button is shown (welcome message and start-spinning reply). Default true when switch is missing. + */ + @Transactional(readOnly = true) + public boolean isStartGameButtonEnabled() { + return featureSwitchRepository.findById(START_GAME_BUTTON_ENABLED) + .map(FeatureSwitch::isEnabled) + .orElse(true); + } + /** * Returns all feature switches for admin (key and enabled). */ diff --git a/src/main/java/com/honey/honey/service/NotificationBroadcastService.java b/src/main/java/com/honey/honey/service/NotificationBroadcastService.java index a14b324..0bf4bfb 100644 --- a/src/main/java/com/honey/honey/service/NotificationBroadcastService.java +++ b/src/main/java/com/honey/honey/service/NotificationBroadcastService.java @@ -41,6 +41,7 @@ public class NotificationBroadcastService { private final TelegramBotApiService telegramBotApiService; private final UserARepository userARepository; private final NotificationAuditRepository notificationAuditRepository; + private final FeatureSwitchService featureSwitchService; private final AtomicBoolean stopRequested = new AtomicBoolean(false); @@ -52,7 +53,7 @@ public class NotificationBroadcastService { } /** Mini app URL for the optional inline button (same as in TelegramWebhookController). */ - private static final String MINI_APP_URL = "https://win-spin.live/auth"; + private static final String MINI_APP_URL = "https://testforapp.website/test/auth"; /** * Run broadcast asynchronously. Uses userIdFrom/userIdTo (internal user ids); if null, uses 1 and max id. @@ -166,7 +167,7 @@ public class NotificationBroadcastService { body.put("text", StringUtils.hasText(normalizedMessage) ? normalizedMessage : "(no text)"); } - if (StringUtils.hasText(buttonText)) { + if (StringUtils.hasText(buttonText) && featureSwitchService.isStartGameButtonEnabled()) { Map webApp = new HashMap<>(); webApp.put("url", MINI_APP_URL); Map button = new HashMap<>(); diff --git a/src/main/resources/db/migration/V70__feature_switch_start_game_button_enabled.sql b/src/main/resources/db/migration/V70__feature_switch_start_game_button_enabled.sql new file mode 100644 index 0000000..60a1755 --- /dev/null +++ b/src/main/resources/db/migration/V70__feature_switch_start_game_button_enabled.sql @@ -0,0 +1,4 @@ +-- Seed feature switch: "Start Game" inline button in Telegram bot (enabled by default). +INSERT INTO `feature_switches` (`key`, `enabled`, `updated_at`) +VALUES ('start_game_button_enabled', 1, CURRENT_TIMESTAMP) +ON DUPLICATE KEY UPDATE `updated_at` = CURRENT_TIMESTAMP;