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"**)
|
1. In your Railway project, click **"+ New"** → **"GitHub Repo"** (or **"Empty Service"**)
|
||||||
2. If using GitHub:
|
2. If using GitHub:
|
||||||
- Connect your GitHub account
|
- Connect your GitHub account
|
||||||
- Select the `lottery-be` repository
|
- Select the `honey-be` repository
|
||||||
- Railway will automatically detect it's a Java/Maven project
|
- Railway will automatically detect it's a Java/Maven project
|
||||||
3. If using Empty Service:
|
3. If using Empty Service:
|
||||||
- Click **"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:
|
# Prerequisites:
|
||||||
# 1. SSH key-based authentication to backup VPS (5.45.77.77)
|
# 1. SSH key-based authentication to backup VPS (5.45.77.77)
|
||||||
# 2. Database password accessible via /run/secrets/lottery-config.properties
|
# 2. Database password accessible via /run/secrets/honey-config.properties
|
||||||
# 3. Docker container 'lottery-mysql' running
|
# 3. Docker container 'honey-mysql' running
|
||||||
#
|
#
|
||||||
# Backup location on backup VPS: /raid/backup/acc_260182/
|
# 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_HOST="5.45.77.77"
|
||||||
BACKUP_VPS_USER="acc_260182" # User account on backup VPS
|
BACKUP_VPS_USER="acc_260182" # User account on backup VPS
|
||||||
BACKUP_VPS_PATH="/raid/backup/acc_260182"
|
BACKUP_VPS_PATH="/raid/backup/acc_260182"
|
||||||
MYSQL_CONTAINER="lottery-mysql"
|
MYSQL_CONTAINER="honey-mysql"
|
||||||
MYSQL_DATABASE="lottery_db"
|
MYSQL_DATABASE="lottery_db"
|
||||||
SECRET_FILE="/run/secrets/lottery-config.properties"
|
SECRET_FILE="/run/secrets/honey-config.properties"
|
||||||
BACKUP_DIR="/opt/app/backups"
|
BACKUP_DIR="/opt/app/backups"
|
||||||
KEEP_LOCAL=false
|
KEEP_LOCAL=false
|
||||||
COMPRESS=true
|
COMPRESS=true
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
# Script to create secret file from template
|
# Script to create secret file from template
|
||||||
# Usage: ./create-secret-file-from-template.sh /path/to/template /path/to/output
|
# Usage: ./create-secret-file-from-template.sh /path/to/template /path/to/output
|
||||||
|
|
||||||
TEMPLATE_FILE="${1:-lottery-config.properties.template}"
|
TEMPLATE_FILE="${1:-honey-config.properties.template}"
|
||||||
OUTPUT_FILE="${2:-/run/secrets/lottery-config.properties}"
|
OUTPUT_FILE="${2:-/run/secrets/honey-config.properties}"
|
||||||
OUTPUT_DIR=$(dirname "$OUTPUT_FILE")
|
OUTPUT_DIR=$(dirname "$OUTPUT_FILE")
|
||||||
|
|
||||||
# Check if template exists
|
# Check if template exists
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Create secret file from environment variables for testing ConfigLoader
|
# Create secret file from environment variables for testing ConfigLoader
|
||||||
# This simulates the mounted secret file approach used in Inferno
|
# 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"
|
SECRET_DIR="/run/secrets"
|
||||||
|
|
||||||
# Create directory if it doesn't exist
|
# Create directory if it doesn't exist
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Diagnostic script for backup-database.sh permission issues
|
# Diagnostic script for backup-database.sh permission issues
|
||||||
# Run this on your VPS to identify the root cause
|
# 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'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
@@ -167,7 +167,7 @@ echo ""
|
|||||||
|
|
||||||
# 14. Secret file check
|
# 14. Secret file check
|
||||||
echo "14. Checking secret file:"
|
echo "14. Checking secret file:"
|
||||||
SECRET_FILE="/run/secrets/lottery-config.properties"
|
SECRET_FILE="/run/secrets/honey-config.properties"
|
||||||
if [ -f "$SECRET_FILE" ]; then
|
if [ -f "$SECRET_FILE" ]; then
|
||||||
echo -e " ${GREEN}✅ Secret file exists${NC}"
|
echo -e " ${GREEN}✅ Secret file exists${NC}"
|
||||||
if [ -r "$SECRET_FILE" ]; then
|
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
|
# ./scripts/restore-database.sh 5.45.77.77:/raid/backup/acc_260182/lottery_db_backup_20240101_120000.sql.gz
|
||||||
#
|
#
|
||||||
# Prerequisites:
|
# Prerequisites:
|
||||||
# 1. Database password accessible via /run/secrets/lottery-config.properties
|
# 1. Database password accessible via /run/secrets/honey-config.properties
|
||||||
# 2. Docker container 'lottery-mysql' running
|
# 2. Docker container 'honey-mysql' running
|
||||||
# 3. Database will be DROPPED and RECREATED (all data will be lost!)
|
# 3. Database will be DROPPED and RECREATED (all data will be lost!)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
MYSQL_CONTAINER="lottery-mysql"
|
MYSQL_CONTAINER="honey-mysql"
|
||||||
MYSQL_DATABASE="lottery_db"
|
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
|
BACKUP_VPS_USER="acc_260182" # User account on backup VPS
|
||||||
|
|
||||||
# Colors for output
|
# Colors for output
|
||||||
@@ -179,5 +179,5 @@ fi
|
|||||||
|
|
||||||
log "✅ Database restore completed!"
|
log "✅ Database restore completed!"
|
||||||
warn "⚠️ Remember to restart the backend container if it's running:"
|
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
|
if [ -d "/opt/app/backend" ]; then
|
||||||
CONFIG_DIR="/opt/app/backend/config"
|
CONFIG_DIR="/opt/app/backend/config"
|
||||||
LOG_DIR="/opt/app/logs"
|
LOG_DIR="/opt/app/logs"
|
||||||
elif [ -d "/opt/app/backend/lottery-be" ]; then
|
elif [ -d "/opt/app/backend/honey-be" ]; then
|
||||||
CONFIG_DIR="/opt/app/backend/lottery-be/config"
|
CONFIG_DIR="/opt/app/backend/honey-be/config"
|
||||||
LOG_DIR="/opt/app/logs"
|
LOG_DIR="/opt/app/logs"
|
||||||
else
|
else
|
||||||
# Try to find from current directory
|
# Try to find from current directory
|
||||||
@@ -38,9 +38,9 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then
|
|||||||
|
|
||||||
# Try multiple locations for JAR file
|
# Try multiple locations for JAR file
|
||||||
JAR_PATH=""
|
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
|
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
|
if [ -n "$found_jar" ]; then
|
||||||
JAR_PATH="$found_jar"
|
JAR_PATH="$found_jar"
|
||||||
break
|
break
|
||||||
@@ -50,7 +50,7 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then
|
|||||||
|
|
||||||
# Try to find in target directory
|
# Try to find in target directory
|
||||||
if [ -z "$JAR_PATH" ]; then
|
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
|
if [ -d "$search_path/target" ]; then
|
||||||
found_jar=$(find "$search_path/target" -name "*.jar" -type f | head -n 1)
|
found_jar=$(find "$search_path/target" -name "*.jar" -type f | head -n 1)
|
||||||
if [ -n "$found_jar" ]; then
|
if [ -n "$found_jar" ]; then
|
||||||
@@ -64,7 +64,7 @@ if [ ! -f "$CONFIG_DIR/logback-spring.xml" ]; then
|
|||||||
if [ -z "$JAR_PATH" ]; then
|
if [ -z "$JAR_PATH" ]; then
|
||||||
echo "Warning: JAR file not found. Trying to copy from source..."
|
echo "Warning: JAR file not found. Trying to copy from source..."
|
||||||
# If JAR not found, copy from source (if available)
|
# 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
|
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"
|
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"
|
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 || {
|
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..."
|
echo "Warning: Could not extract from JAR. Trying to copy from source..."
|
||||||
# Try copying 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
|
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"
|
cp "$search_path/src/main/resources/logback-spring.xml" "$CONFIG_DIR/logback-spring.xml"
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.honey.honey.config.TelegramProperties;
|
|||||||
import com.honey.honey.dto.TelegramApiResponse;
|
import com.honey.honey.dto.TelegramApiResponse;
|
||||||
import com.honey.honey.dto.PaymentWebhookRequest;
|
import com.honey.honey.dto.PaymentWebhookRequest;
|
||||||
import com.honey.honey.model.UserA;
|
import com.honey.honey.model.UserA;
|
||||||
|
import com.honey.honey.service.FeatureSwitchService;
|
||||||
import com.honey.honey.service.PaymentService;
|
import com.honey.honey.service.PaymentService;
|
||||||
import com.honey.honey.service.TelegramBotApiService;
|
import com.honey.honey.service.TelegramBotApiService;
|
||||||
import com.honey.honey.service.UserService;
|
import com.honey.honey.service.UserService;
|
||||||
@@ -56,6 +57,8 @@ import org.springframework.core.io.ByteArrayResource;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class TelegramWebhookController {
|
public class TelegramWebhookController {
|
||||||
|
|
||||||
|
private static final String MINI_APP_URL = "https://testforapp.website/test/auth";
|
||||||
|
|
||||||
@Value("${app.telegram-webhook.token:}")
|
@Value("${app.telegram-webhook.token:}")
|
||||||
private String expectedWebhookToken;
|
private String expectedWebhookToken;
|
||||||
|
|
||||||
@@ -64,6 +67,7 @@ public class TelegramWebhookController {
|
|||||||
private final TelegramProperties telegramProperties;
|
private final TelegramProperties telegramProperties;
|
||||||
private final LocalizationService localizationService;
|
private final LocalizationService localizationService;
|
||||||
private final TelegramBotApiService telegramBotApiService;
|
private final TelegramBotApiService telegramBotApiService;
|
||||||
|
private final FeatureSwitchService featureSwitchService;
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,32 +330,27 @@ public class TelegramWebhookController {
|
|||||||
}
|
}
|
||||||
Locale locale = LocaleConfig.languageCodeToLocale(languageCode != null ? languageCode : "EN");
|
Locale locale = LocaleConfig.languageCodeToLocale(languageCode != null ? languageCode : "EN");
|
||||||
ReplyKeyboardMarkup replyKeyboard = buildReplyKeyboard(locale);
|
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");
|
String firstMessage = localizationService.getMessage(locale, "bot.welcome.firstMessage");
|
||||||
sendAnimationWithReplyKeyboard(chatId, firstMessage, replyKeyboard);
|
sendAnimationWithReplyKeyboard(chatId, firstMessage, replyKeyboard);
|
||||||
|
|
||||||
// Send second message with inline button (START SPINNING)
|
|
||||||
String welcomeText = localizationService.getMessage(locale, "bot.welcome.message");
|
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);
|
sendMessage(chatId, welcomeText, inlineKeyboard);
|
||||||
|
} else {
|
||||||
|
sendMessage(chatId, welcomeText, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -359,24 +358,22 @@ public class TelegramWebhookController {
|
|||||||
*/
|
*/
|
||||||
private void sendStartSpinningMessage(Long chatId, Locale locale) {
|
private void sendStartSpinningMessage(Long chatId, Locale locale) {
|
||||||
String message = localizationService.getMessage(locale, "bot.message.startSpinning");
|
String message = localizationService.getMessage(locale, "bot.message.startSpinning");
|
||||||
|
if (!featureSwitchService.isStartGameButtonEnabled()) {
|
||||||
|
sendMessage(chatId, message, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup();
|
InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup();
|
||||||
List<List<InlineKeyboardButton>> rows = new ArrayList<>();
|
List<List<InlineKeyboardButton>> rows = new ArrayList<>();
|
||||||
List<InlineKeyboardButton> row = new ArrayList<>();
|
List<InlineKeyboardButton> row = new ArrayList<>();
|
||||||
|
|
||||||
InlineKeyboardButton button = new InlineKeyboardButton();
|
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");
|
String startSpinningButtonText = localizationService.getMessage(locale, "bot.button.startSpinningInline");
|
||||||
button.setText(startSpinningButtonText);
|
button.setText(startSpinningButtonText);
|
||||||
// Use WebAppInfo to open mini app instead of regular URL
|
|
||||||
WebAppInfo webAppInfo = new WebAppInfo();
|
WebAppInfo webAppInfo = new WebAppInfo();
|
||||||
webAppInfo.setUrl("https://win-spin.live/auth");
|
webAppInfo.setUrl(MINI_APP_URL);
|
||||||
button.setWebApp(webAppInfo);
|
button.setWebApp(webAppInfo);
|
||||||
row.add(button);
|
row.add(button);
|
||||||
rows.add(row);
|
rows.add(row);
|
||||||
|
|
||||||
keyboard.setKeyboard(rows);
|
keyboard.setKeyboard(rows);
|
||||||
|
|
||||||
sendMessage(chatId, message, keyboard);
|
sendMessage(chatId, message, keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public class FeatureSwitchService {
|
|||||||
public static final String PROMOTIONS_ENABLED = "promotions_enabled";
|
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. */
|
/** 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";
|
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;
|
private final FeatureSwitchRepository featureSwitchRepository;
|
||||||
|
|
||||||
@@ -85,6 +87,16 @@ public class FeatureSwitchService {
|
|||||||
.orElse(true);
|
.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).
|
* Returns all feature switches for admin (key and enabled).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class NotificationBroadcastService {
|
|||||||
private final TelegramBotApiService telegramBotApiService;
|
private final TelegramBotApiService telegramBotApiService;
|
||||||
private final UserARepository userARepository;
|
private final UserARepository userARepository;
|
||||||
private final NotificationAuditRepository notificationAuditRepository;
|
private final NotificationAuditRepository notificationAuditRepository;
|
||||||
|
private final FeatureSwitchService featureSwitchService;
|
||||||
|
|
||||||
private final AtomicBoolean stopRequested = new AtomicBoolean(false);
|
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). */
|
/** 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.
|
* 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)");
|
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<>();
|
Map<String, Object> webApp = new HashMap<>();
|
||||||
webApp.put("url", MINI_APP_URL);
|
webApp.put("url", MINI_APP_URL);
|
||||||
Map<String, Object> button = new HashMap<>();
|
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