refactor: flatten padelnomics/padelnomics/ → repo root

git mv all tracked files from the nested padelnomics/ workspace
directory to the git repo root. Merged .gitignore files.
No code changes — pure path rename.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-02-22 00:44:40 +01:00
parent 5e471567b9
commit 4ae00b35d1
235 changed files with 45 additions and 42 deletions

23
web/scripts/backup.sh Normal file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
set -e
# Padelnomics Manual Backup Script
BACKUP_DIR="./backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DB_PATH="./data/app.db"
mkdir -p "$BACKUP_DIR"
# Create backup using SQLite's backup command
sqlite3 "$DB_PATH" ".backup '$BACKUP_DIR/app_$TIMESTAMP.db'"
# Compress
gzip "$BACKUP_DIR/app_$TIMESTAMP.db"
echo "✅ Backup created: $BACKUP_DIR/app_$TIMESTAMP.db.gz"
# Clean old backups (keep last 7 days)
find "$BACKUP_DIR" -name "*.db.gz" -mtime +7 -delete
echo "🧹 Old backups cleaned"

22
web/scripts/deploy.sh Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -e
# Padelnomics Deployment Script
echo "🚀 Deploying Padelnomics..."
# Pull latest code
git pull origin main
# Build and restart containers
docker compose build
docker compose up -d
# Run migrations
docker compose exec app uv run python -m padelnomics.migrations.migrate
# Health check
sleep 5
curl -f http://localhost:5000/health || exit 1
echo "✅ Deployment complete!"

172
web/scripts/dev_run.sh Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env bash
# Start all Padelnomics dev processes with colored, labeled output.
#
# Usage: ./scripts/dev_run.sh
#
# On each start: resets the DB, runs migrations, seeds data, builds CSS,
# optionally starts ngrok for Paddle webhook forwarding, then starts
# app (port 5000), background worker, and CSS watcher.
# Ctrl-C stops everything cleanly.
set -euo pipefail
cd "$(dirname "$0")/.."
# -- Colors & helpers --------------------------------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
COLOR_APP='\033[0;36m' # cyan
COLOR_WORKER='\033[0;33m' # yellow
COLOR_CSS='\033[0;35m' # magenta
COLOR_NGROK='\033[0;32m' # green
info() { echo -e "${BLUE}==>${NC} ${BOLD}$1${NC}"; }
ok() { echo -e "${GREEN}${NC} $1"; }
warn() { echo -e "${YELLOW} !${NC} $1"; }
fail() { echo -e "${RED}${NC} $1"; exit 1; }
# -- Preflight ---------------------------------------------------------------
if [ ! -f .env ]; then
fail ".env not found. Run ./scripts/dev_setup.sh first."
fi
# Load config from .env (|| true prevents set -e from aborting on empty values)
DATABASE_PATH=$(grep '^DATABASE_PATH=' .env 2>/dev/null | cut -d= -f2- || true)
DATABASE_PATH=${DATABASE_PATH:-data/app.db}
PADDLE_API_KEY=$(grep '^PADDLE_API_KEY=' .env 2>/dev/null | cut -d= -f2- || true)
PADDLE_NOTIFICATION_SETTING_ID=$(grep '^PADDLE_NOTIFICATION_SETTING_ID=' .env 2>/dev/null | cut -d= -f2- || true)
PADDLE_ENVIRONMENT=$(grep '^PADDLE_ENVIRONMENT=' .env 2>/dev/null | cut -d= -f2- || true)
PADDLE_ENVIRONMENT=${PADDLE_ENVIRONMENT:-sandbox}
# -- Preparation -------------------------------------------------------------
info "Resetting database"
rm -f "$DATABASE_PATH"
ok "Removed $DATABASE_PATH"
info "Running migrations"
uv run python -m padelnomics.migrations.migrate
ok "Migrations applied"
info "Seeding development data"
uv run python -m padelnomics.scripts.seed_dev_data
ok "Dev data seeded"
if [ -n "$PADDLE_API_KEY" ]; then
info "Syncing Paddle products to DB"
uv run python -m padelnomics.scripts.setup_paddle --sync
ok "Paddle products synced"
fi
info "Building CSS"
make css-build
ok "CSS built"
# -- Process management ------------------------------------------------------
PIDS=()
cleanup() {
echo ""
echo -e "${BOLD}Stopping all processes...${NC}"
for pid in "${PIDS[@]}"; do
# Kill children first (e.g. make → tailwind), then the process itself
pkill -P "$pid" 2>/dev/null || true
kill "$pid" 2>/dev/null || true
done
wait 2>/dev/null || true
echo "Done."
exit 0
}
trap cleanup SIGINT SIGTERM
# Prefix each line of a command's output with a colored label.
# Uses process substitution so $! is the actual command PID (not the formatter).
run_with_label() {
local color="$1" label="$2"
shift 2
"$@" > >(while IFS= read -r line; do echo -e "${color}[${label}]${NC} ${line}"; done) 2>&1 &
PIDS+=($!)
}
# -- Ngrok tunnel (if Paddle is configured) ----------------------------------
TUNNEL_URL=""
if [ -n "$PADDLE_API_KEY" ] && [ -n "$PADDLE_NOTIFICATION_SETTING_ID" ]; then
if command -v ngrok >/dev/null 2>&1; then
info "Starting ngrok tunnel for Paddle webhooks"
ngrok http 5000 --log=stdout --log-level=warn > /tmp/padelnomics-ngrok.log 2>&1 &
NGROK_PID=$!
PIDS+=($NGROK_PID)
# Wait for ngrok to be ready (up to 10 seconds)
WAIT_SECONDS=10
for i in $(seq 1 $WAIT_SECONDS); do
TUNNEL_URL=$(curl -s http://localhost:4040/api/tunnels 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])" 2>/dev/null) \
&& break
sleep 1
done
if [ -n "$TUNNEL_URL" ]; then
ok "ngrok tunnel: $TUNNEL_URL"
# Update Paddle notification destination with tunnel URL
WEBHOOK_URL="${TUNNEL_URL}/billing/webhook/paddle"
info "Updating Paddle webhook destination → $WEBHOOK_URL"
uv run python -c "
import os
from dotenv import load_dotenv
load_dotenv()
from paddle_billing import Client, Environment, Options
from paddle_billing.Resources.NotificationSettings.Operations import UpdateNotificationSetting
env = Environment.SANDBOX if '${PADDLE_ENVIRONMENT}' == 'sandbox' else Environment.PRODUCTION
paddle = Client('${PADDLE_API_KEY}', options=Options(env))
paddle.notification_settings.update(
'${PADDLE_NOTIFICATION_SETTING_ID}',
UpdateNotificationSetting(destination='${WEBHOOK_URL}'),
)
print(' Updated.')
"
ok "Paddle webhooks → $WEBHOOK_URL"
else
warn "ngrok started but tunnel URL not available — webhooks won't reach localhost"
fi
else
warn "ngrok not installed — Paddle webhooks won't reach localhost"
warn "Install: https://ngrok.com/download (or brew install ngrok)"
fi
elif [ -n "$PADDLE_API_KEY" ]; then
warn "PADDLE_NOTIFICATION_SETTING_ID not set — run setup_paddle to create webhook destination"
fi
# -- Start processes ---------------------------------------------------------
echo ""
echo -e "${BOLD}Starting Padelnomics dev environment${NC}"
echo ""
echo " app: http://localhost:5000"
echo " admin: http://localhost:5000/admin"
echo " login: http://localhost:5000/auth/dev-login?email=dev@localhost"
if [ -n "$TUNNEL_URL" ]; then
echo " tunnel: $TUNNEL_URL"
fi
echo ""
echo "Press Ctrl-C to stop all processes."
echo ""
run_with_label "$COLOR_APP" "app " uv run python -m padelnomics.app
run_with_label "$COLOR_WORKER" "worker" uv run python -m padelnomics.worker
run_with_label "$COLOR_CSS" "css " make css-watch
wait

132
web/scripts/dev_setup.sh Executable file
View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
# Bootstrap a local dev environment for Padelnomics.
#
# Usage: ./scripts/dev_setup.sh
#
# Checks prerequisites, installs deps, creates .env, runs migrations,
# seeds test data, and builds CSS.
set -euo pipefail
cd "$(dirname "$0")/.."
# -- Colors & helpers -------------------------------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
info() { echo -e "${BLUE}==>${NC} ${BOLD}$1${NC}"; }
ok() { echo -e "${GREEN}${NC} $1"; }
warn() { echo -e "${YELLOW} !${NC} $1"; }
fail() { echo -e "${RED}${NC} $1"; exit 1; }
# -- Prerequisites -----------------------------------------------------------
info "Checking prerequisites"
command -v python3 >/dev/null 2>&1 || fail "python3 not found. Install Python 3.12+."
command -v uv >/dev/null 2>&1 || fail "uv not found. Install: https://docs.astral.sh/uv/getting-started/installation/"
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
ok "python3 ${PYTHON_VERSION}"
ok "uv $(uv --version 2>/dev/null | head -1)"
# -- Install dependencies ----------------------------------------------------
info "Installing Python dependencies"
uv sync
ok "Dependencies installed"
# -- .env --------------------------------------------------------------------
if [ -f .env ]; then
warn ".env already exists — skipping creation"
else
info "Creating .env from .env.example"
cp .env.example .env
# Auto-generate SECRET_KEY
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
sed -i "s/^SECRET_KEY=.*/SECRET_KEY=${SECRET_KEY}/" .env
ok "SECRET_KEY generated"
# Prompt for optional keys
echo ""
echo -e "${BOLD}Optional API keys${NC} (press Enter to skip — everything works without them)"
echo ""
read -rp " Paddle API key (for checkout testing): " PADDLE_API_KEY
if [ -n "$PADDLE_API_KEY" ]; then
sed -i "s/^PADDLE_API_KEY=.*/PADDLE_API_KEY=${PADDLE_API_KEY}/" .env
ok "PADDLE_API_KEY set"
read -rp " Paddle client token: " PADDLE_CLIENT_TOKEN
[ -n "$PADDLE_CLIENT_TOKEN" ] && sed -i "s/^PADDLE_CLIENT_TOKEN=.*/PADDLE_CLIENT_TOKEN=${PADDLE_CLIENT_TOKEN}/" .env
fi
read -rp " Resend API key (for email testing): " RESEND_API_KEY
if [ -n "$RESEND_API_KEY" ]; then
sed -i "s/^RESEND_API_KEY=.*/RESEND_API_KEY=${RESEND_API_KEY}/" .env
ok "RESEND_API_KEY set"
fi
echo ""
ok ".env created"
fi
# -- Migrations ---------------------------------------------------------------
info "Running database migrations"
uv run python -m padelnomics.migrations.migrate
ok "Migrations applied"
# -- Seed data ----------------------------------------------------------------
info "Seeding development data"
uv run python -m padelnomics.scripts.seed_dev_data
ok "Dev data seeded"
# -- Paddle setup (optional) --------------------------------------------------
if grep -q '^PADDLE_API_KEY=.\+' .env 2>/dev/null; then
echo ""
read -rp "$(echo -e "${BLUE}==>${NC} Set up Paddle sandbox products? [y/N] ")" SETUP_PADDLE
if [[ "$SETUP_PADDLE" =~ ^[Yy]$ ]]; then
uv run python -m padelnomics.scripts.setup_paddle
ok "Paddle products created"
fi
fi
# -- CSS build ----------------------------------------------------------------
info "Building CSS"
make css-build
ok "CSS built"
# -- Summary ------------------------------------------------------------------
echo ""
echo -e "${GREEN}${BOLD}Setup complete!${NC}"
echo ""
echo " Start all services: ./scripts/dev_run.sh"
echo ""
echo " URLs:"
echo " App: http://localhost:5000"
echo " Dev login: http://localhost:5000/auth/dev-login?email=dev@localhost"
echo " Admin: http://localhost:5000/admin (password: admin)"
echo ""
echo " Seeded accounts:"
echo " dev@localhost — dev user (use dev-login link above)"
echo ""
echo " Email:"
if grep -q '^RESEND_API_KEY=.\+' .env 2>/dev/null; then
echo " Resend configured — emails sent via API"
echo " Test addresses: delivered@resend.dev, bounced@resend.dev"
else
echo " No Resend key — emails print to console"
fi
echo ""

83
web/scripts/smoke-test.sh Executable file
View File

@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# Smoke test: starts the app, hits every route, reports pass/fail.
# Usage: ./scripts/smoke-test.sh
set -euo pipefail
PORT=5099
APP_PID=""
COOKIE_JAR=$(mktemp)
PASS=0
FAIL=0
cleanup() {
[[ -n "$APP_PID" ]] && kill "$APP_PID" 2>/dev/null || true
rm -f "$COOKIE_JAR"
}
trap cleanup EXIT
# --- Start app ---
echo "Starting app on :$PORT ..."
PORT=$PORT uv run python -m padelnomics.app &>/dev/null &
APP_PID=$!
sleep 2
if ! kill -0 "$APP_PID" 2>/dev/null; then
echo "FAIL: App did not start"
exit 1
fi
# --- Helpers ---
check() {
local label="$1" url="$2" expected="${3:-200}" extra="${4:-}"
local code
code=$(curl -s -o /dev/null -w "%{http_code}" $extra "$url")
if [[ "$code" == "$expected" ]]; then
printf " OK %s %s\n" "$code" "$label"
PASS=$((PASS + 1))
else
printf " FAIL %s %s (expected %s)\n" "$code" "$label" "$expected"
FAIL=$((FAIL + 1))
fi
}
# --- Public routes (no auth) ---
echo ""
echo "Public routes:"
check "Landing page" "http://127.0.0.1:$PORT/"
check "Features" "http://127.0.0.1:$PORT/features"
check "About" "http://127.0.0.1:$PORT/about"
check "Terms" "http://127.0.0.1:$PORT/terms"
check "Privacy" "http://127.0.0.1:$PORT/privacy"
check "Sitemap" "http://127.0.0.1:$PORT/sitemap.xml"
check "Pricing" "http://127.0.0.1:$PORT/billing/pricing"
check "Login page" "http://127.0.0.1:$PORT/auth/login"
check "Signup page" "http://127.0.0.1:$PORT/auth/signup"
check "Health" "http://127.0.0.1:$PORT/health"
# --- Auth guards (should redirect when not logged in) ---
echo ""
echo "Auth guards (expect 302):"
check "Planner (no auth)" "http://127.0.0.1:$PORT/planner/" 302
check "Dashboard (no auth)" "http://127.0.0.1:$PORT/dashboard/" 302
check "Suppliers (no auth)" "http://127.0.0.1:$PORT/leads/suppliers" 302
# --- Dev login ---
echo ""
echo "Dev login:"
curl -s -o /dev/null -c "$COOKIE_JAR" -b "$COOKIE_JAR" -L "http://127.0.0.1:$PORT/auth/dev-login?email=test@test.com"
echo " OK Logged in as test@test.com"
# --- Authenticated routes ---
echo ""
echo "Authenticated routes:"
check "Dashboard" "http://127.0.0.1:$PORT/dashboard/" 200 "-b $COOKIE_JAR"
check "Settings" "http://127.0.0.1:$PORT/dashboard/settings" 200 "-b $COOKIE_JAR"
check "Planner" "http://127.0.0.1:$PORT/planner/" 200 "-b $COOKIE_JAR"
check "Suppliers" "http://127.0.0.1:$PORT/leads/suppliers" 200 "-b $COOKIE_JAR"
check "Financing" "http://127.0.0.1:$PORT/leads/financing" 200 "-b $COOKIE_JAR"
# --- Summary ---
echo ""
echo "---"
printf "Passed: %d Failed: %d\n" "$PASS" "$FAIL"
[[ $FAIL -eq 0 ]] && echo "All checks passed." || exit 1