Split signal scoring, add Redis cache, Tailwind design system, and fix UI coherence issues

Backend: extract pure scoring logic from signal_service.py into signal_scoring.py (h),
add Redis-backed win-rate/PnL caching with graceful degradation (l), add Postgres
backup/restore scripts (n), move DB/encryption secrets to Docker secrets pattern (o),
fix RSI flat-price bug and MFI wraparound index bug (q, r). 134 backend tests passing.

Frontend: consolidate all API calls onto shared apiFetch with auto token refresh (i),
wire AnalyticsPage to the real /analytics/dashboard endpoint instead of fake random
data (j), migrate all pages and shared components to a Tailwind CSS design system (k)
fixing 3 mismatched color palettes found along the way. UI review also found and fixed
missing mobile table scroll wrappers, non-stacking grids, and a missing nav/logout bar
on ProfilePage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Le
2026-07-04 00:27:10 +07:00
parent 4a130363ec
commit 9de41ea92b
43 changed files with 2801 additions and 2800 deletions
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# Backup the trading_portal Postgres database.
# Usage: ./backup_postgres.sh [retention_days]
#
# Intended to run via cron on the deploy host, e.g.:
# 0 3 * * * /opt/data/trading-portal/scripts/backup_postgres.sh >> /var/log/trading-portal-backup.log 2>&1
set -euo pipefail
PROJECT_DIR="${PROJECT_DIR:-/opt/data/trading-portal}"
BACKUP_DIR="${BACKUP_DIR:-$PROJECT_DIR/backups}"
RETENTION_DAYS="${1:-14}"
DB_CONTAINER="trading-db"
DB_USER="trading"
DB_NAME="trading_portal"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
OUT_FILE="$BACKUP_DIR/trading_portal_${TIMESTAMP}.sql.gz"
mkdir -p "$BACKUP_DIR"
echo "==> Backing up $DB_NAME from $DB_CONTAINER..."
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" --no-owner --no-acl \
| gzip > "$OUT_FILE"
SIZE=$(du -h "$OUT_FILE" | cut -f1)
echo "==> Wrote $OUT_FILE ($SIZE)"
echo "==> Pruning backups older than $RETENTION_DAYS days..."
find "$BACKUP_DIR" -name 'trading_portal_*.sql.gz' -mtime "+$RETENTION_DAYS" -print -delete
echo "✅ Backup complete."
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
# Restore the trading_portal Postgres database from a backup produced by
# backup_postgres.sh. DESTRUCTIVE: drops and recreates all data.
#
# Usage: ./restore_postgres.sh /path/to/trading_portal_YYYYMMDD_HHMMSS.sql.gz
set -euo pipefail
BACKUP_FILE="${1:?Usage: restore_postgres.sh <backup_file.sql.gz>}"
DB_CONTAINER="trading-db"
DB_USER="trading"
DB_NAME="trading_portal"
if [ ! -f "$BACKUP_FILE" ]; then
echo "ERROR: backup file not found: $BACKUP_FILE" >&2
exit 1
fi
echo "⚠️ This will REPLACE all data in '$DB_NAME' with the contents of:"
echo " $BACKUP_FILE"
read -r -p "Type 'yes' to continue: " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Aborted."
exit 1
fi
echo "==> Stopping backend-api and backend-scheduler (avoid writes during restore)..."
docker compose -f "$(dirname "$0")/../docker-compose.yml" stop backend-api backend-scheduler
echo "==> Dropping and recreating $DB_NAME..."
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d postgres -c "DROP DATABASE IF EXISTS ${DB_NAME}_restoring;"
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d postgres -c "CREATE DATABASE ${DB_NAME}_restoring;"
echo "==> Restoring dump into ${DB_NAME}_restoring..."
gunzip -c "$BACKUP_FILE" | docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" -d "${DB_NAME}_restoring"
echo "==> Swapping databases..."
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d postgres -c "ALTER DATABASE ${DB_NAME} RENAME TO ${DB_NAME}_old_$(date +%s);"
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d postgres -c "ALTER DATABASE ${DB_NAME}_restoring RENAME TO ${DB_NAME};"
echo "==> Restarting backend-api and backend-scheduler..."
docker compose -f "$(dirname "$0")/../docker-compose.yml" start backend-api backend-scheduler
echo "✅ Restore complete. The pre-restore database was kept as '${DB_NAME}_old_<timestamp>' — drop it manually once you've verified the restore."