vps setup
This commit is contained in:
@@ -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"**
|
||||
|
||||
242
nginx-testforapp-test-base.conf
Normal file
242
nginx-testforapp-test-base.conf
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<List<InlineKeyboardButton>> inlineRows = new ArrayList<>();
|
||||
List<InlineKeyboardButton> 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");
|
||||
if (featureSwitchService.isStartGameButtonEnabled()) {
|
||||
InlineKeyboardMarkup inlineKeyboard = new InlineKeyboardMarkup();
|
||||
List<List<InlineKeyboardButton>> inlineRows = new ArrayList<>();
|
||||
List<InlineKeyboardButton> 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<List<InlineKeyboardButton>> rows = new ArrayList<>();
|
||||
List<InlineKeyboardButton> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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<String, Object> webApp = new HashMap<>();
|
||||
webApp.put("url", MINI_APP_URL);
|
||||
Map<String, Object> button = new HashMap<>();
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user