Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
This commit is contained in:
TIANHE
2026-01-14 23:50:31 +08:00
parent 47efb7e024
commit 6011ba4ce3
4 changed files with 71 additions and 15 deletions
+35 -12
View File
@@ -6,6 +6,7 @@ Supports both multi-user (database) and single-user (legacy) modes.
"""
import os
from flask import Blueprint, request, jsonify, g, redirect
from urllib.parse import urlencode
from app.config.settings import Config
from app.utils.auth import generate_token, login_required, authenticate_legacy
from app.utils.logger import get_logger
@@ -14,6 +15,28 @@ logger = get_logger(__name__)
auth_bp = Blueprint('auth', __name__)
def _build_frontend_login_redirect(frontend_url: str, **params) -> str:
"""
Build a redirect URL to frontend login page for OAuth flows.
Frontend uses Vue Router hash mode (`/#/user/login`), so redirecting to `/user/login`
will 404 on static hosting. Always normalize to `{origin}/#/user/login`.
"""
base = (frontend_url or '').strip().rstrip('/')
if not base:
base = 'http://localhost:8080'
if '/#/' in base:
origin = base.split('/#/', 1)[0].rstrip('/')
elif '#' in base:
origin = base.split('#', 1)[0].rstrip('/')
else:
origin = base
login_url = f"{origin}/#/user/login"
qs = urlencode({k: v for k, v in params.items() if v is not None and v != ''})
return f"{login_url}?{qs}" if qs else login_url
def _is_single_user_mode() -> bool:
"""Check if system is in single-user (legacy) mode"""
@@ -813,22 +836,22 @@ def oauth_google_callback():
frontend_url = oauth.frontend_url
if error:
return redirect(f"{frontend_url}/user/login?oauth_error={error}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error=error))
if not code or not state:
return redirect(f"{frontend_url}/user/login?oauth_error=missing_params")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error='missing_params'))
# Handle callback
success, result = oauth.handle_google_callback(code, state)
if not success:
error_msg = result.get('error', 'unknown_error')
return redirect(f"{frontend_url}/user/login?oauth_error={error_msg}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error=error_msg))
# Get or create user
user_success, user_result = oauth.get_or_create_user_from_oauth(result)
if not user_success:
error_msg = user_result.get('error', 'user_creation_failed')
return redirect(f"{frontend_url}/user/login?oauth_error={error_msg}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error=error_msg))
# Generate token
token = generate_token(
@@ -842,13 +865,13 @@ def oauth_google_callback():
{'provider': 'google'})
# Redirect to frontend with token
return redirect(f"{frontend_url}/user/login?oauth_token={token}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_token=token))
except Exception as e:
logger.error(f"oauth_google_callback error: {e}")
from app.services.oauth_service import get_oauth_service
frontend_url = get_oauth_service().frontend_url
return redirect(f"{frontend_url}/user/login?oauth_error=server_error")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error='server_error'))
@auth_bp.route('/oauth/github', methods=['GET'])
@@ -889,22 +912,22 @@ def oauth_github_callback():
frontend_url = oauth.frontend_url
if error:
return redirect(f"{frontend_url}/user/login?oauth_error={error}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error=error))
if not code or not state:
return redirect(f"{frontend_url}/user/login?oauth_error=missing_params")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error='missing_params'))
# Handle callback
success, result = oauth.handle_github_callback(code, state)
if not success:
error_msg = result.get('error', 'unknown_error')
return redirect(f"{frontend_url}/user/login?oauth_error={error_msg}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error=error_msg))
# Get or create user
user_success, user_result = oauth.get_or_create_user_from_oauth(result)
if not user_success:
error_msg = user_result.get('error', 'user_creation_failed')
return redirect(f"{frontend_url}/user/login?oauth_error={error_msg}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error=error_msg))
# Generate token
token = generate_token(
@@ -918,13 +941,13 @@ def oauth_github_callback():
{'provider': 'github'})
# Redirect to frontend with token
return redirect(f"{frontend_url}/user/login?oauth_token={token}")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_token=token))
except Exception as e:
logger.error(f"oauth_github_callback error: {e}")
from app.services.oauth_service import get_oauth_service
frontend_url = get_oauth_service().frontend_url
return redirect(f"{frontend_url}/user/login?oauth_error=server_error")
return redirect(_build_frontend_login_redirect(frontend_url, oauth_error='server_error'))
# =============================================================================
@@ -431,6 +431,24 @@ class OAuthService:
db.commit()
cur.close()
# Grant registration bonus credits for OAuth-created users
# Keep consistent with email/password registration flows (auth.py).
try:
register_bonus = int(os.getenv('CREDITS_REGISTER_BONUS', '0'))
except (ValueError, TypeError):
register_bonus = 0
if register_bonus > 0:
try:
from app.services.billing_service import get_billing_service
get_billing_service().add_credits(
user_id=user_id,
amount=register_bonus,
action='register_bonus',
remark=f'Registration bonus (OAuth:{provider})'
)
except Exception as e:
logger.warning(f"Failed to grant OAuth registration bonus: {e}")
return True, {
'id': user_id,