Skip to content

Commit 34da7cd

Browse files
committed
chore: detect commercial plan
1 parent cf54e67 commit 34da7cd

File tree

1 file changed

+341
-3
lines changed

1 file changed

+341
-3
lines changed

script/diagnose_appflowy.sh

Lines changed: 341 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,7 @@ run_functional_tests() {
17731773
check_minio_storage
17741774
check_api_endpoints
17751775
check_websocket_endpoint
1776+
check_websocket_connection_simulation
17761777
check_admin_frontend
17771778
check_collaboration_data
17781779
check_ai_service
@@ -1977,9 +1978,9 @@ check_container_errors() {
19771978

19781979
# Try to parse JSON logs for better formatting
19791980
if echo "$line" | grep -q '"timestamp".*"level".*"message"'; then
1980-
local timestamp=$(echo "$line" | grep -oP '"timestamp":"[^"]*"' | cut -d'"' -f4 | cut -c1-19)
1981-
local level=$(echo "$line" | grep -oP '"level":"[^"]*"' | cut -d'"' -f4 | tr '[:lower:]' '[:upper:]')
1982-
local message=$(echo "$line" | grep -oP '"message":"[^"]*"' | cut -d'"' -f4)
1981+
local timestamp=$(echo "$line" | sed -n 's/.*"timestamp":"\([^"]*\)".*/\1/p' | cut -c1-19)
1982+
local level=$(echo "$line" | sed -n 's/.*"level":"\([^"]*\)".*/\1/p' | tr '[:lower:]' '[:upper:]')
1983+
local message=$(echo "$line" | sed -n 's/.*"message":"\([^"]*\)".*/\1/p')
19831984

19841985
if [[ -n "$timestamp" && -n "$level" && -n "$message" ]]; then
19851986
print_error " [$timestamp] $level: $message"
@@ -2369,6 +2370,340 @@ generate_recommendations() {
23692370
fi
23702371
}
23712372

2373+
# ==================== ADDITIONAL DIAGNOSTICS ====================
2374+
2375+
check_plan_limits() {
2376+
print_verbose "Checking self-hosted plan limits..."
2377+
2378+
local compose_cmd=$(get_compose_command)
2379+
2380+
# Get appflowy_cloud container ID
2381+
local container_id=$($compose_cmd ps -q appflowy_cloud 2>/dev/null)
2382+
if [[ -z "$container_id" ]]; then
2383+
print_verbose "AppFlowy Cloud container not found"
2384+
return 0
2385+
fi
2386+
2387+
# Parse logs for "Free plan limits" message
2388+
local logs=$(docker logs --tail 200 "$container_id" 2>&1)
2389+
local plan_limits=$(echo "$logs" | grep -i "Free plan limits" | tail -1)
2390+
2391+
if [[ -n "$plan_limits" ]]; then
2392+
# Extract max_users and max_guests from log line (BSD grep compatible)
2393+
local max_users=$(echo "$plan_limits" | sed -n 's/.*max_users:[[:space:]]*\([0-9][0-9]*\).*/\1/p')
2394+
local max_guests=$(echo "$plan_limits" | sed -n 's/.*max_guests:[[:space:]]*\([0-9][0-9]*\).*/\1/p')
2395+
2396+
# Fallback to "unknown" if extraction failed
2397+
[[ -z "$max_users" ]] && max_users="unknown"
2398+
[[ -z "$max_guests" ]] && max_guests="unknown"
2399+
2400+
if [[ "$max_users" != "unknown" && "$max_guests" != "unknown" ]]; then
2401+
print_warning "Self-Hosted Plan Limits Detected:"
2402+
print_warning " Max Users: $max_users"
2403+
print_warning " Max Guests: $max_guests"
2404+
echo ""
2405+
2406+
if [[ "$max_users" == "1" ]]; then
2407+
print_error "IMPORTANT: Self-hosted version has a 1-user limit"
2408+
print_error " - Only ONE user account can log in (not including admin)"
2409+
print_error " - Admin and user can use different emails"
2410+
print_error " - Additional users will fail to authenticate"
2411+
print_error " - This is a limitation of the self-hosted free plan"
2412+
echo ""
2413+
fi
2414+
2415+
# Try to count existing users (if database accessible)
2416+
if $compose_cmd ps postgres &>/dev/null; then
2417+
local user_count=$($compose_cmd exec -T postgres psql -U postgres -d postgres -tAc "SELECT COUNT(*) FROM af_user WHERE deleted_at IS NULL;" 2>/dev/null || echo "unknown")
2418+
2419+
if [[ "$user_count" != "unknown" && "$user_count" =~ ^[0-9]+$ ]]; then
2420+
print_info "Current user count: $user_count"
2421+
2422+
if [[ "$max_users" != "unknown" && "$max_users" =~ ^[0-9]+$ ]]; then
2423+
if [[ $user_count -ge $max_users ]]; then
2424+
print_error "⚠️ User limit reached! ($user_count/$max_users users)"
2425+
print_error " - New users will not be able to log in"
2426+
print_error " - Consider removing unused accounts"
2427+
else
2428+
print_success "Users: $user_count/$max_users (within limit)"
2429+
fi
2430+
fi
2431+
fi
2432+
fi
2433+
else
2434+
print_verbose "Could not parse plan limits from logs"
2435+
fi
2436+
else
2437+
print_verbose "No plan limits found in logs (container may be using custom configuration)"
2438+
fi
2439+
2440+
return 0
2441+
}
2442+
2443+
check_admin_frontend_errors() {
2444+
print_verbose "Checking Admin Frontend specific errors..."
2445+
2446+
local compose_cmd=$(get_compose_command)
2447+
2448+
# Get admin_frontend container ID
2449+
local container_id=$($compose_cmd ps -q admin_frontend 2>/dev/null)
2450+
if [[ -z "$container_id" ]]; then
2451+
print_verbose "Admin Frontend container not found"
2452+
return 0
2453+
fi
2454+
2455+
# Check container status
2456+
local container_status=$(docker inspect --format='{{.State.Status}}' "$container_id" 2>/dev/null)
2457+
local restart_count=$(docker inspect --format='{{.RestartCount}}' "$container_id" 2>/dev/null)
2458+
2459+
# Get logs
2460+
local logs=$(docker logs --tail 300 "$container_id" 2>&1)
2461+
2462+
# Check for specific error patterns
2463+
local has_errors=false
2464+
2465+
# Pattern 1: EISDIR error (node_modules issue)
2466+
if echo "$logs" | grep -q "EISDIR.*node_modules"; then
2467+
has_errors=true
2468+
print_error "Admin Frontend: EISDIR error detected (node_modules issue)"
2469+
local error_line=$(echo "$logs" | grep "EISDIR.*node_modules" | tail -1 | cut -c1-200)
2470+
print_error " Error: $error_line"
2471+
print_error " Cause: Likely a Docker volume or build cache issue"
2472+
print_error " Fix: Try rebuilding the admin_frontend image:"
2473+
print_error " docker compose build --no-cache admin_frontend"
2474+
print_error " docker compose up -d admin_frontend"
2475+
echo ""
2476+
fi
2477+
2478+
# Pattern 2: Module resolution errors
2479+
if echo "$logs" | grep -qiE "Cannot find module|Module not found|Error: Cannot resolve"; then
2480+
has_errors=true
2481+
print_error "Admin Frontend: Module resolution error detected"
2482+
local error_line=$(echo "$logs" | grep -iE "Cannot find module|Module not found|Error: Cannot resolve" | tail -1 | cut -c1-200)
2483+
print_error " Error: $error_line"
2484+
print_error " Fix: Rebuild with fresh dependencies:"
2485+
print_error " docker compose build --no-cache admin_frontend"
2486+
echo ""
2487+
fi
2488+
2489+
# Pattern 3: Configuration injection failures
2490+
if echo "$logs" | grep -qiE "Failed to inject configuration|Configuration error|APPFLOWY_BASE_URL.*undefined"; then
2491+
has_errors=true
2492+
print_error "Admin Frontend: Configuration injection failed"
2493+
local error_line=$(echo "$logs" | grep -iE "Failed to inject configuration|Configuration error" | tail -1 | cut -c1-200)
2494+
print_error " Error: $error_line"
2495+
print_error " Fix: Verify .env file has correct APPFLOWY_BASE_URL"
2496+
print_error " Check: APPFLOWY_BASE_URL is set and matches your deployment URL"
2497+
echo ""
2498+
fi
2499+
2500+
# Pattern 4: Port binding errors
2501+
if echo "$logs" | grep -qiE "EADDRINUSE.*3000|port.*already in use"; then
2502+
has_errors=true
2503+
print_error "Admin Frontend: Port conflict detected"
2504+
print_error " Error: Port 3000 already in use"
2505+
print_error " Fix: Another service is using the port"
2506+
print_error " Check: docker ps | grep 3000"
2507+
echo ""
2508+
fi
2509+
2510+
# Pattern 5: Bun/Node runtime errors
2511+
if echo "$logs" | grep -qiE "bun.*error|node.*fatal|v8::"; then
2512+
has_errors=true
2513+
local error_line=$(echo "$logs" | grep -iE "bun.*error|node.*fatal|v8::" | tail -1 | cut -c1-200)
2514+
print_error "Admin Frontend: Runtime error"
2515+
print_error " Error: $error_line"
2516+
echo ""
2517+
fi
2518+
2519+
# Check if container is constantly restarting
2520+
if [[ "$restart_count" -gt 3 ]]; then
2521+
print_error "Admin Frontend: Container restarting frequently (count: $restart_count)"
2522+
print_error " Status: $container_status"
2523+
2524+
# Get the last startup attempt
2525+
local startup_logs=$(echo "$logs" | tail -50)
2526+
local last_error=$(echo "$startup_logs" | grep -iE "error|fatal|failed" | tail -3)
2527+
2528+
if [[ -n "$last_error" ]]; then
2529+
print_error " Recent errors:"
2530+
while IFS= read -r line; do
2531+
local clean_line=$(echo "$line" | cut -c1-200)
2532+
print_error " $clean_line"
2533+
done <<< "$last_error"
2534+
fi
2535+
echo ""
2536+
has_errors=true
2537+
fi
2538+
2539+
if [[ "$has_errors" == "false" && "$container_status" == "running" ]]; then
2540+
print_success "Admin Frontend: No specific startup errors detected"
2541+
fi
2542+
2543+
return 0
2544+
}
2545+
2546+
check_websocket_connection_simulation() {
2547+
print_verbose "Simulating WebSocket connection for login hang detection..."
2548+
2549+
if ! load_env_vars; then
2550+
print_warning "WebSocket Simulation: Cannot load .env configuration"
2551+
return 0
2552+
fi
2553+
2554+
local base_url="${APPFLOWY_BASE_URL}"
2555+
local ws_url="${APPFLOWY_WS_BASE_URL:-${APPFLOWY_WEBSOCKET_BASE_URL}}"
2556+
2557+
if [[ -z "$ws_url" ]]; then
2558+
print_warning "WebSocket Simulation: WS URL not configured, skipping connection test"
2559+
return 0
2560+
fi
2561+
2562+
# Extract scheme and check for common misconfigurations
2563+
local ws_scheme=$(extract_url_scheme "$ws_url")
2564+
local base_scheme=$(extract_url_scheme "$base_url")
2565+
2566+
# Critical: Check for HTTPS + WS (not WSS) mismatch
2567+
if [[ "$base_scheme" == "https" && "$ws_scheme" == "ws" ]]; then
2568+
print_error "WebSocket Connection: CRITICAL MISCONFIGURATION DETECTED"
2569+
print_error " Base URL: $base_url (HTTPS)"
2570+
print_error " WebSocket URL: $ws_url (WS)"
2571+
print_error ""
2572+
print_error " ⚠️ This WILL cause login to hang at 100%!"
2573+
print_error " Browsers block insecure WebSocket (ws://) from HTTPS pages"
2574+
print_error ""
2575+
print_error " Fix: Change WS_SCHEME=wss in .env file"
2576+
print_error " Then: docker compose up -d"
2577+
echo ""
2578+
return 1
2579+
fi
2580+
2581+
# Test WebSocket endpoint availability (without auth)
2582+
local ws_test_url
2583+
if [[ "$ws_url" == *"/ws/v2" ]]; then
2584+
# For v2, test with a dummy workspace ID
2585+
ws_test_url="http://localhost/ws/v2/00000000-0000-0000-0000-000000000000"
2586+
else
2587+
# For v1 or other versions
2588+
ws_test_url="http://localhost${ws_url##*${base_url}}"
2589+
fi
2590+
2591+
# Try WebSocket upgrade request
2592+
local response=$(curl -s -o /dev/null -w "%{http_code}" \
2593+
-X GET "$ws_test_url" \
2594+
-H "Upgrade: websocket" \
2595+
-H "Connection: Upgrade" \
2596+
-H "Sec-WebSocket-Version: 13" \
2597+
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
2598+
--max-time 5 2>/dev/null)
2599+
2600+
if [[ "$response" == "401" || "$response" == "403" ]]; then
2601+
print_success "WebSocket Connection: Endpoint reachable (auth required - expected)"
2602+
print_verbose " WebSocket upgrade request returned: $response"
2603+
elif [[ "$response" == "404" ]]; then
2604+
print_error "WebSocket Connection: Endpoint not found (404)"
2605+
print_error " This will cause login to hang after authentication"
2606+
print_error " Fix: Verify nginx WebSocket routing configuration"
2607+
echo ""
2608+
return 1
2609+
elif [[ "$response" == "502" || "$response" == "503" ]]; then
2610+
print_error "WebSocket Connection: Backend unavailable ($response)"
2611+
print_error " AppFlowy Cloud service may not be running properly"
2612+
print_error " Check: docker compose ps appflowy_cloud"
2613+
echo ""
2614+
return 1
2615+
elif [[ "$response" == "000" ]]; then
2616+
print_warning "WebSocket Connection: Connection timeout or refused"
2617+
print_warning " This may indicate nginx or network issues"
2618+
else
2619+
print_verbose "WebSocket Connection: Received HTTP $response"
2620+
fi
2621+
2622+
# Additional check: verify nginx WebSocket proxy configuration exists
2623+
local compose_cmd=$(get_compose_command)
2624+
local nginx_container=$($compose_cmd ps -q nginx 2>/dev/null)
2625+
2626+
if [[ -n "$nginx_container" ]]; then
2627+
local nginx_config=$(docker exec "$nginx_container" cat /etc/nginx/nginx.conf 2>/dev/null || echo "")
2628+
2629+
if [[ -n "$nginx_config" ]]; then
2630+
# Check for WebSocket upgrade headers
2631+
if ! echo "$nginx_config" | grep -q "Upgrade.*\$http_upgrade"; then
2632+
print_warning "WebSocket Connection: Nginx may be missing WebSocket upgrade headers"
2633+
print_warning " This can cause connection failures after login"
2634+
fi
2635+
fi
2636+
fi
2637+
2638+
return 0
2639+
}
2640+
2641+
check_user_auth_flow() {
2642+
print_verbose "Validating user authentication flow..."
2643+
2644+
if ! load_env_vars; then
2645+
return 0
2646+
fi
2647+
2648+
local compose_cmd=$(get_compose_command)
2649+
2650+
# Check if GoTrue is configured for auto-confirm (important for testing)
2651+
local mailer_autoconfirm="${GOTRUE_MAILER_AUTOCONFIRM:-false}"
2652+
2653+
if [[ "$mailer_autoconfirm" == "false" ]]; then
2654+
print_warning "User Auth: Email confirmation required (GOTRUE_MAILER_AUTOCONFIRM=false)"
2655+
print_warning " - New users must confirm email before login"
2656+
print_warning " - Check SMTP settings if emails not arriving"
2657+
print_warning " - For testing, set GOTRUE_MAILER_AUTOCONFIRM=true"
2658+
echo ""
2659+
else
2660+
print_success "User Auth: Auto-confirm enabled (good for testing)"
2661+
fi
2662+
2663+
# Check for OTP configuration
2664+
local enable_otp="${GOTRUE_EXTERNAL_PHONE_ENABLED:-false}"
2665+
if [[ "$enable_otp" == "true" ]]; then
2666+
print_info "User Auth: OTP/Phone authentication enabled"
2667+
fi
2668+
2669+
# Verify GoTrue is accessible
2670+
local gotrue_health=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost/gotrue/health" --max-time 3 2>/dev/null)
2671+
2672+
if [[ "$gotrue_health" == "200" ]]; then
2673+
print_success "User Auth: GoTrue service healthy"
2674+
elif [[ "$gotrue_health" == "404" ]]; then
2675+
print_error "User Auth: GoTrue health endpoint not found"
2676+
print_error " This suggests nginx routing issues"
2677+
print_error " Fix: Verify nginx.conf has /gotrue/* proxy configuration"
2678+
echo ""
2679+
else
2680+
print_warning "User Auth: GoTrue health check returned: $gotrue_health"
2681+
fi
2682+
2683+
# Check for common auth flow issues in logs
2684+
local gotrue_container=$($compose_cmd ps -q gotrue 2>/dev/null)
2685+
if [[ -n "$gotrue_container" ]]; then
2686+
local recent_logs=$(docker logs --tail 100 "$gotrue_container" 2>&1)
2687+
2688+
# Look for email delivery issues
2689+
if echo "$recent_logs" | grep -qiE "failed to send.*email|smtp.*error|mailer.*failed"; then
2690+
print_error "User Auth: Email delivery issues detected in GoTrue logs"
2691+
print_error " Users may not receive confirmation emails"
2692+
print_error " Check SMTP configuration in .env"
2693+
echo ""
2694+
fi
2695+
2696+
# Look for token/JWT issues
2697+
if echo "$recent_logs" | grep -qiE "invalid.*token|jwt.*error|signature.*invalid"; then
2698+
print_error "User Auth: JWT/Token validation issues detected"
2699+
print_error " Verify GOTRUE_JWT_SECRET matches across services"
2700+
echo ""
2701+
fi
2702+
fi
2703+
2704+
return 0
2705+
}
2706+
23722707
# ==================== MAIN EXECUTION ====================
23732708

23742709
main() {
@@ -2414,12 +2749,14 @@ main() {
24142749
check_base_urls
24152750
check_scheme_consistency
24162751
check_gotrue_configuration
2752+
check_user_auth_flow
24172753
check_admin_credentials
24182754
check_smtp_configuration
24192755
check_nginx_websocket_config
24202756
check_production_https_websocket
24212757
check_ssl_certificate
24222758
check_websocket_cors_headers
2759+
check_plan_limits
24232760

24242761
echo ""
24252762

@@ -2452,6 +2789,7 @@ main() {
24522789
fi
24532790

24542791
check_admin_frontend_connectivity
2792+
check_admin_frontend_errors
24552793
check_gotrue_auth_errors
24562794
check_container_errors
24572795
extract_container_crash_summary

0 commit comments

Comments
 (0)