Files
gh-christianlouis-quizzical…/musicround/templates/users/profile.html
T
Christian Krakau-Louis 2f55f898ed Add Spotify integration with improved token management and user interface
- Implemented Spotify OAuth handling in spotify_client_manager.py to ensure valid access tokens for users.
- Created helper functions in spotify_helper.py for refreshing tokens and retrieving user information.
- Developed manage_spotify.html template for connecting and managing Spotify accounts, displaying connection status and token information.
- Added logging for token management processes to enhance debugging and monitoring.
- Introduced a debug client for Spotify interactions to facilitate easier testing and development.
2025-05-27 21:40:37 +02:00

822 lines
43 KiB
HTML

{% extends 'base.html' %}
{% block title %}My Profile{% endblock %}
{% block content %}
<div class="max-w-4xl mx-auto my-8 p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-navy-800">My Profile</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Profile Info -->
<div class="md:col-span-2">
<div class="bg-gray-50 p-4 rounded-lg mb-6">
<h3 class="text-lg font-semibold mb-3 text-navy-700">Account Information</h3>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="text-sm text-gray-600">Username</p>
<p class="font-medium">{{ current_user.username }}</p>
</div>
<div>
<p class="text-sm text-gray-600">Email</p>
<p class="font-medium">{{ current_user.email }}</p>
</div>
<div>
<p class="text-sm text-gray-600">Name</p>
<p class="font-medium">
{% if current_user.first_name or current_user.last_name %}
{{ current_user.first_name }} {{ current_user.last_name }}
{% else %}
<span class="text-gray-400">Not provided</span>
{% endif %}
</p>
</div>
<div>
<p class="text-sm text-gray-600">Member Since</p>
<p class="font-medium">{{ current_user.created_at.strftime('%B %d, %Y') }}</p>
</div>
<!-- Add Admin Status -->
<div>
<p class="text-sm text-gray-600">Admin Status</p>
<p class="font-medium">
{% if current_user.is_admin() %}
<span class="text-green-600">Administrator</span>
<a href="{{ url_for('admin.index') }}" class="ml-2 text-xs text-blue-600 hover:underline">
Admin Dashboard
</a>
{% else %}
<span class="text-gray-400">Regular User</span>
{% if not admin_exists %}
<a href="{{ url_for('users.setup') }}" class="ml-2 text-xs text-blue-600 hover:underline">
Setup Admin
</a>
{% endif %}
{% endif %}
</p>
</div>
<div>
<p class="text-sm text-gray-600">Dropbox Export Folder</p>
<p class="font-medium">
{{ current_user.dropbox_export_path or '/QuizzicalBeats' }}
</p>
</div>
</div>
</div>
<h3 class="text-lg font-semibold mb-3 text-navy-700">Account Actions</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<a href="{{ url_for('users.edit_profile') }}" class="bg-teal-500 hover:bg-teal-600 text-white py-2 px-4 rounded-md text-center transition-colors">
<i class="fas fa-user-edit mr-2"></i> Edit Profile
</a>
<a href="{{ url_for('users.change_password') }}" class="bg-navy-600 hover:bg-navy-700 text-white py-2 px-4 rounded-md text-center transition-colors">
<i class="fas fa-key mr-2"></i> Change Password
</a>
</div>
<!-- Spotify Connection Debug - Only for Admins -->
{% if current_user.is_admin() %}
<div class="mt-6 p-4 border border-gray-300 rounded-lg bg-gray-50">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold text-navy-700">Spotify Connection Debug</h3>
<button id="showSpotifyDebugBtn" class="bg-indigo-500 hover:bg-indigo-600 text-white py-1 px-3 rounded-md text-sm transition-colors">
<i class="fas fa-bug mr-1"></i> Show Details
</button>
</div>
<div class="flex items-center text-sm">
<div class="w-3 h-3 rounded-full mr-2
{% if spotify_status == 'user' %}
bg-green-500
{% elif spotify_status == 'system' %}
bg-yellow-500
{% elif spotify_status == 'bearer' %}
bg-blue-500
{% else %}
bg-red-500
{% endif %}"></div>
<span>
{% if spotify_status == 'user' %}
Using your Spotify account
{% elif spotify_status == 'system' %}
Using system Spotify account
{% elif spotify_status == 'bearer' %}
Using manual bearer token
{% else %}
No Spotify connection active
{% endif %}
</span>
</div>
</div>
<!-- Dropbox Connection Debug - Only for Admins -->
<div class="mt-4 p-4 border border-gray-300 rounded-lg bg-gray-50">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold text-navy-700">Dropbox Connection Debug</h3>
<button id="showDropboxDebugBtn" class="bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded-md text-sm transition-colors">
<i class="fas fa-bug mr-1"></i> Show Details
</button>
</div>
<div class="flex items-center text-sm">
<div class="w-3 h-3 rounded-full mr-2
{% if current_user.dropbox_token and current_user.dropbox_token_expiry and current_user.dropbox_token_expiry > now %}
bg-green-500
{% elif current_user.dropbox_token %}
bg-yellow-500
{% else %}
bg-red-500
{% endif %}"></div>
<span>
{% if current_user.dropbox_token and current_user.dropbox_token_expiry and current_user.dropbox_token_expiry > now %}
Active Dropbox connection
{% elif current_user.dropbox_token %}
Dropbox token expired or expiring soon
{% else %}
No Dropbox connection active
{% endif %}
</span>
</div>
</div>
{% endif %}
</div>
<!-- Connected Services -->
<div>
<h3 class="text-lg font-semibold mb-4 text-navy-700">Connected Services</h3>
<!-- Spotify Connection - Only shown for admins -->
{% if current_user.is_admin() %}
<div class="bg-gray-50 p-4 rounded-lg mb-4 shadow-sm">
<div class="flex items-center mb-3">
<i class="fab fa-spotify text-[#1DB954] text-2xl mr-3"></i>
<div>
<h4 class="font-medium">Spotify</h4>
<p class="text-sm text-gray-600">
{% if current_user.spotify_token %}
Connected
{% if current_user.oauth_id %}
as {{ current_user.oauth_id }}
{% endif %}
{% else %}
Not connected
{% endif %}
</p>
</div>
</div>
<a href="{{ url_for('users.spotify_link') }}" class="inline-block w-full bg-{% if current_user.spotify_token %}gray-200 hover:bg-gray-300{% else %}[#1DB954] hover:bg-[#1ed760]{% endif %} text-{% if current_user.spotify_token %}gray-700{% else %}white{% endif %} text-center py-1 px-3 rounded-md text-sm transition-colors">
{% if current_user.spotify_token %}
Manage Connection
{% else %}
Connect Spotify
{% endif %}
</a>
</div>
{% endif %}
<!-- Dropbox Connection -->
<div class="bg-gray-50 p-4 rounded-lg mb-4 shadow-sm">
<div class="flex items-center mb-3">
<i class="fab fa-dropbox text-[#0061FF] text-2xl mr-3"></i>
<div>
<h4 class="font-medium">Dropbox</h4>
<p class="text-sm text-gray-600">
{% if current_user.dropbox_id %}
Connected
{% if current_user.dropbox_id %}
as {{ current_user.dropbox_id[:10] }}...
{% endif %}
{% else %}
Not connected
{% endif %}
</p>
</div>
</div>
{% if current_user.dropbox_id %}
<div class="grid grid-cols-1 gap-2">
<p class="text-xs text-gray-600">
{% if current_user.dropbox_token_expiry %}
Token expires: {{ current_user.dropbox_token_expiry.strftime('%Y-%m-%d %H:%M') }}
{% endif %}
</p>
<form action="{{ url_for('users.dropbox_disconnect') }}" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="inline-block w-full bg-gray-200 hover:bg-gray-300 text-gray-700 text-center py-1 px-3 rounded-md text-sm transition-colors">
Disconnect Dropbox
</button>
</form>
</div>
{% else %}
<a href="{{ url_for('users.dropbox_auth') }}" class="inline-block w-full bg-[#0061FF] hover:bg-[#0055DF] text-white text-center py-1 px-3 rounded-md text-sm transition-colors">
Connect Dropbox
</a>
{% endif %}
</div>
<!-- Manual Bearer Token Entry - Only for Admins -->
<div class="bg-gray-50 p-4 rounded-lg mb-4 shadow-sm">
<h4 class="font-medium mb-2">Direct Spotify Access</h4>
<p class="text-xs text-gray-600 mb-3">
Enter a Spotify bearer token for direct API access.
This bypasses OAuth and allows immediate access to Spotify features.
</p>
<form action="{{ url_for('users.update_bearer_token') }}" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="mb-2">
<textarea name="bearer_token" rows="2"
placeholder="BQBcQ1foQG0x14axu2kQVz..."
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm text-sm focus:outline-none focus:ring-teal-500 focus:border-teal-500">{{ session.get('access_token', '') }}</textarea>
<p class="mt-1 text-xs text-gray-500">Get a token from <a href="https://developer.spotify.com/console/" target="_blank" class="text-teal-600 hover:underline">Spotify Developer Console</a></p>
</div>
<div class="flex gap-2">
<button type="submit" class="px-4 py-2 bg-teal-600 text-white text-sm font-medium rounded-md hover:bg-teal-700 transition-colors">
Apply Token
</button>
{% if session.get('access_token') %}
<button type="submit" name="clear_token" value="1" class="px-4 py-2 bg-gray-500 text-white text-sm font-medium rounded-md hover:bg-gray-600 transition-colors">
Clear Token
</button>
{% endif %}
</div>
</form>
</div>
<!-- Audio Settings -->
<div class="bg-gray-50 p-4 rounded-lg mb-4 shadow-sm">
<h4 class="font-medium mb-2">Audio Settings</h4>
<p class="text-xs text-gray-600 mb-3">
Customize your audio preferences for quizzes.
</p>
<a href="{{ url_for('users.audio_settings') }}" class="inline-block w-full bg-orange-500 hover:bg-orange-600 text-white text-center py-2 px-4 rounded-md text-sm transition-colors">
Manage Audio Settings
</a>
</div>
<!-- Last Login Info -->
<div class="mt-6">
<h4 class="text-md font-medium mb-2">Last Login</h4>
<p class="text-sm text-gray-600">
{% if current_user.last_login %}
{{ current_user.last_login.strftime('%d %b %Y, %H:%M') }}
{% else %}
<span class="text-gray-400">No login history</span>
{% endif %}
</p>
</div>
</div>
</div>
</div>
<!-- Spotify Debug Modal - Only for Admins -->
{% if current_user.is_admin() %}
<div id="spotifyDebugModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-2xl max-h-[80vh] overflow-y-auto">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-navy-800">Spotify Connection Details</h3>
<button id="closeSpotifyDebugBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<!-- Connection Status -->
<div class="mb-4 p-3 rounded-lg
{% if spotify_status == 'user' %}
bg-green-100 border border-green-300
{% elif spotify_status == 'system' %}
bg-yellow-100 border border-yellow-300
{% elif spotify_status == 'bearer' %}
bg-blue-100 border border-blue-300
{% else %}
bg-red-100 border border-red-300
{% endif %}">
<div class="flex items-center font-semibold mb-2">
<div class="w-4 h-4 rounded-full mr-2
{% if spotify_status == 'user' %}
bg-green-500
{% elif spotify_status == 'system' %}
bg-yellow-500
{% elif spotify_status == 'bearer' %}
bg-blue-500
{% else %}
bg-red-500
{% endif %}"></div>
<span>
{% if spotify_status == 'user' %}
Using your Spotify account
{% elif spotify_status == 'system' %}
Using system Spotify account
{% elif spotify_status == 'bearer' %}
Using manual bearer token
{% else %}
No Spotify credentials available
{% endif %}
</span>
{% if token_source %}
<span class="ml-2 text-xs py-1 px-2 rounded-full bg-gray-200">
Source: {{ token_source }}
</span>
{% endif %}
</div>
<p class="text-sm">
{% if spotify_status == 'user' %}
Your personal Spotify account is connected and authenticated
{% elif spotify_status == 'system' %}
Using the fallback system Spotify account (refresh token from settings)
{% elif spotify_status == 'bearer' %}
Using a manual bearer token from session
{% else %}
No Spotify credentials available
{% endif %}
</p>
{% if token_source %}
<p class="mt-2 text-xs text-gray-600">
<i class="fas fa-info-circle mr-1"></i>
{% if token_source == 'user' %}
This token was automatically generated from your user account refresh token
{% elif token_source == 'system' %}
This token was automatically generated from the system fallback refresh token
{% else %}
Token source: {{ token_source }}
{% endif %}
</p>
{% endif %}
</div>
<!-- Debug Information -->
<div class="space-y-4">
<!-- Personal Account Details -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Your Spotify Account</h4>
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Status:</td>
<td class="py-2">
{% if current_user.spotify_token %}
<span class="text-green-600">Connected</span>
{% else %}
<span class="text-red-600">Not connected</span>
{% endif %}
</td>
</tr>
{% if current_user.spotify_token %}
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Spotify ID:</td>
<td class="py-2">{{ current_user.oauth_id or 'Unknown' }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Token Expiry:</td>
<td class="py-2">
{% if current_user.spotify_token_expiry %}
{% if current_user.spotify_token_expiry > now %}
<span class="text-green-600">{{ current_user.spotify_token_expiry.strftime('%Y-%m-%d %H:%M:%S') }}</span>
{% else %}
<span class="text-red-600">Expired ({{ current_user.spotify_token_expiry.strftime('%Y-%m-%d %H:%M:%S') }})</span>
{% endif %}
{% else %}
Unknown
{% endif %}
</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Has Refresh Token:</td>
<td class="py-2">
{% if current_user.spotify_refresh_token %}
<span class="text-green-600">Yes</span>
{% else %}
<span class="text-red-600">No</span>
{% endif %}
</td>
</tr>
{% endif %}
</table>
</div>
<!-- System Account Details -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">System Spotify Account</h4>
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Has Refresh Token:</td>
<td class="py-2">
{% if system_refresh_token %}
<span class="text-green-600">Yes</span>
{% else %}
<span class="text-red-600">No</span>
{% endif %}
</td>
</tr>
{% if system_refresh_token %}
<tr>
<td class="py-2 font-medium">Token Preview:</td>
<td class="py-2 font-mono">{{ system_refresh_token[:10] }}...{{ system_refresh_token[-5:] }}</td>
</tr>
{% endif %}
</table>
</div>
<!-- Manual Bearer Token -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Manual Bearer Token</h4>
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Status:</td>
<td class="py-2">
{% if session_bearer and token_source == 'manual' %}
<span class="text-green-600">Active manual token</span>
{% elif session_bearer %}
<span class="text-gray-600">Present but not active</span>
{% else %}
<span class="text-gray-600">Not present</span>
{% endif %}
</td>
</tr>
{% if session_bearer %}
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Token Preview:</td>
<td class="py-2 font-mono">{{ session_bearer[:10] }}...{{ session_bearer[-5:] }}</td>
</tr>
{% if token_source == 'manual' and active_token_expiry %}
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Estimated Expiry:</td>
<td class="py-2">
{% if active_token_expiry > now %}
<span class="text-green-600">{{ active_token_expiry.strftime('%Y-%m-%d %H:%M:%S') }}</span>
<span class="text-xs text-gray-500">(Manual tokens typically expire after 1 hour)</span>
{% else %}
<span class="text-red-600">Likely expired ({{ active_token_expiry.strftime('%Y-%m-%d %H:%M:%S') }})</span>
{% endif %}
</td>
</tr>
{% endif %}
{% if active_username and token_source == 'manual' %}
<tr>
<td class="py-2 font-medium">Associated User:</td>
<td class="py-2">
<div class="flex items-center">
{% if active_user_image %}
<img src="{{ active_user_image }}" alt="User" class="w-6 h-6 rounded-full mr-2">
{% endif %}
<span>{{ active_username }} ({{ active_user_id }})</span>
</div>
</td>
</tr>
{% endif %}
{% endif %}
</table>
</div>
<!-- Current Active Token -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Current Active Spotify User</h4>
{% if active_username %}
<div class="flex items-center mb-3">
{% if active_user_image %}
<img src="{{ active_user_image }}" alt="Profile" class="w-10 h-10 rounded-full mr-3">
{% else %}
<div class="w-10 h-10 bg-gray-300 rounded-full flex items-center justify-center mr-3">
<i class="fab fa-spotify text-gray-600"></i>
</div>
{% endif %}
<div>
<p class="font-medium">{{ active_username }}</p>
<p class="text-sm text-gray-600">ID: {{ active_user_id }}</p>
</div>
</div>
{% elif token_source == 'client_credentials' %}
<div class="flex items-center mb-3">
<div class="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center mr-3">
<i class="fab fa-spotify text-purple-600"></i>
</div>
<div>
<p class="font-medium">App Client Credentials</p>
<p class="text-sm text-gray-600">Using application's credentials (limited access)</p>
</div>
</div>
{% else %}
<p class="text-gray-600 italic">No active Spotify user detected</p>
{% endif %}
<div class="mt-3">
<h5 class="text-sm font-medium mb-1">Token Source</h5>
<div class="text-sm">
{% if token_source %}
<span class="px-2 py-1 rounded-full text-xs
{% if token_source == 'user' %}
bg-green-100 text-green-800
{% elif token_source == 'system' %}
bg-yellow-100 text-yellow-800
{% elif token_source == 'client_credentials' %}
bg-purple-100 text-purple-800
{% else %}
bg-blue-100 text-blue-800
{% endif %}">
{{ token_source }}
</span>
{% if token_source == 'client_credentials' and client_token_expiry %}
<span class="text-xs text-gray-600 ml-2">
Expires: {{ client_token_expiry|timestamp_to_datetime|format_datetime }}
</span>
{% endif %}
{% else %}
<span class="text-gray-600 italic">Unknown source</span>
{% endif %}
</div>
</div>
</div>
<!-- Priority Order -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Precedence</h4>
<p class="text-sm mb-2">Connection priority: Manual Bearer → Your Account → Client Credentials</p>
<div class="flex flex-col space-y-2 text-sm">
<div class="flex items-center">
<div class="w-3 h-3 rounded-full
{% if spotify_status == 'bearer' %}bg-blue-500{% else %}bg-gray-300{% endif %}
mr-2"></div>
<span>1. Manual Bearer Token {% if spotify_status == 'bearer' %}(active){% endif %}</span>
</div>
<div class="flex items-center">
<div class="w-3 h-3 rounded-full
{% if spotify_status == 'user' %}bg-green-500{% else %}bg-gray-300{% endif %}
mr-2"></div>
<span>2. User Account {% if spotify_status == 'user' %}(active){% endif %}</span>
</div>
<div class="flex items-center">
<div class="w-3 h-3 rounded-full
{% if spotify_status == 'client_credentials' %}bg-purple-500{% else %}bg-gray-300{% endif %}
mr-2"></div>
<span>3. App Client Credentials {% if spotify_status == 'client_credentials' %}(active){% endif %}</span>
</div>
</div>
</div>
</div>
<div class="mt-6 flex justify-end">
<button id="closeModalBtn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 py-2 px-4 rounded-md">
Close
</button>
</div>
</div>
</div>
</div>
<!-- Dropbox Debug Modal - Only for Admins -->
<div id="dropboxDebugModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-lg w-full max-w-2xl max-h-[80vh] overflow-y-auto">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-navy-800">Dropbox Connection Details</h3>
<button id="closeDropboxDebugBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<!-- Connection Status -->
<div class="mb-4 p-3 rounded-lg
{% if current_user.dropbox_token and current_user.dropbox_token_expiry and current_user.dropbox_token_expiry > now %}
bg-green-100 border border-green-300
{% elif current_user.dropbox_token %}
bg-yellow-100 border border-yellow-300
{% else %}
bg-red-100 border border-red-300
{% endif %}">
<div class="flex items-center font-semibold mb-2">
<div class="w-4 h-4 rounded-full mr-2
{% if current_user.dropbox_token and current_user.dropbox_token_expiry and current_user.dropbox_token_expiry > now %}
bg-green-500
{% elif current_user.dropbox_token %}
bg-yellow-500
{% else %}
bg-red-500
{% endif %}"></div>
<span>
{% if current_user.dropbox_token and current_user.dropbox_token_expiry and current_user.dropbox_token_expiry > now %}
Active Dropbox connection
{% elif current_user.dropbox_token %}
Dropbox token expired or expiring soon
{% else %}
No Dropbox connection
{% endif %}
</span>
</div>
<p class="text-sm">
{% if current_user.dropbox_token and current_user.dropbox_token_expiry and current_user.dropbox_token_expiry > now %}
Your Dropbox account is connected and has a valid access token
{% elif current_user.dropbox_token %}
Your Dropbox account is connected but the token needs to be refreshed
{% else %}
No Dropbox credentials available
{% endif %}
</p>
</div>
<!-- Debug Information -->
<div class="space-y-4">
<!-- Dropbox Account Details -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Your Dropbox Account</h4>
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Status:</td>
<td class="py-2">
{% if current_user.dropbox_id %}
<span class="text-green-600">Connected</span>
{% else %}
<span class="text-red-600">Not connected</span>
{% endif %}
</td>
</tr>
{% if current_user.dropbox_id %}
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">User Info:</td>
<td class="py-2">
<div class="flex items-center">
{% if session.get('dropbox_user_info') and session.get('dropbox_user_info').get('picture') %}
<img src="{{ session.get('dropbox_user_info').get('picture') }}" alt="Profile" class="w-8 h-8 rounded-full mr-2">
{% else %}
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center mr-2">
<i class="fab fa-dropbox text-blue-600"></i>
</div>
{% endif %}
<div>
{% if session.get('dropbox_user_info') %}
<div class="font-medium">{{ session.get('dropbox_user_info').get('name') }}</div>
<div class="text-xs text-gray-500">{{ session.get('dropbox_user_info').get('email') }}</div>
{% else %}
<div class="text-gray-500 italic">User info not available</div>
{% endif %}
</div>
</div>
</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Dropbox ID:</td>
<td class="py-2">{{ current_user.dropbox_id }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Token Expiry:</td>
<td class="py-2">
{% if current_user.dropbox_token_expiry %}
{% if current_user.dropbox_token_expiry > now %}
<span class="text-green-600">{{ current_user.dropbox_token_expiry.strftime('%Y-%m-%d %H:%M:%S') }}</span>
{% else %}
<span class="text-red-600">Expired ({{ current_user.dropbox_token_expiry.strftime('%Y-%m-%d %H:%M:%S') }})</span>
{% endif %}
{% else %}
Unknown
{% endif %}
</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Has Refresh Token:</td>
<td class="py-2">
{% if current_user.dropbox_refresh_token %}
<span class="text-green-600">Yes</span>
{% else %}
<span class="text-red-600">No</span>
{% endif %}
</td>
</tr>
{% endif %}
</table>
</div>
<!-- Dropbox Token Details -->
{% if current_user.dropbox_token %}
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Token Information</h4>
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Access Token Preview:</td>
<td class="py-2 font-mono">
{{ current_user.dropbox_token[:10] }}...{{ current_user.dropbox_token[-5:] if current_user.dropbox_token|length > 15 else '' }}
</td>
</tr>
{% if current_user.dropbox_refresh_token %}
<tr class="border-b border-gray-200">
<td class="py-2 font-medium">Refresh Token Preview:</td>
<td class="py-2 font-mono">
{{ current_user.dropbox_refresh_token[:10] }}...{{ current_user.dropbox_refresh_token[-5:] if current_user.dropbox_refresh_token|length > 15 else '' }}
</td>
</tr>
{% endif %}
<tr>
<td class="py-2 font-medium">Token Status:</td>
<td class="py-2">
{% if current_user.dropbox_token_expiry %}
{% if current_user.dropbox_token_expiry > now %}
<span class="text-green-600">Valid for {{ ((current_user.dropbox_token_expiry - now).total_seconds() / 60)|int }} more minutes</span>
{% else %}
<span class="text-red-600">Expired {{ ((now - current_user.dropbox_token_expiry).total_seconds() / 60)|int }} minutes ago</span>
{% endif %}
{% else %}
<span class="text-gray-600">Unknown expiry time</span>
{% endif %}
</td>
</tr>
</table>
</div>
{% endif %}
<!-- Test Actions -->
<div class="p-3 bg-gray-50 rounded-lg">
<h4 class="font-semibold mb-2">Connection Test</h4>
<p class="text-sm text-gray-600 mb-3">
The following actions can help test your Dropbox connection:
</p>
<div class="space-y-2">
<a href="{{ url_for('users.dropbox_auth') }}" class="inline-block bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded-md text-sm transition-colors">
Reconnect Dropbox
</a>
{% if current_user.dropbox_id %}
<form action="{{ url_for('users.dropbox_disconnect') }}" method="POST" class="inline-block">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="bg-red-500 hover:bg-red-600 text-white py-1 px-3 rounded-md text-sm transition-colors">
Disconnect Dropbox
</button>
</form>
{% endif %}
</div>
</div>
</div>
<div class="mt-6 flex justify-end">
<button id="closeDropboxModalBtn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 py-2 px-4 rounded-md">
Close
</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block scripts %}
{% if current_user.is_admin() %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const modal = document.getElementById('spotifyDebugModal');
const showBtn = document.getElementById('showSpotifyDebugBtn');
const closeBtn = document.getElementById('closeSpotifyDebugBtn');
const closeModalBtn = document.getElementById('closeModalBtn');
// Show modal
showBtn.addEventListener('click', function() {
modal.classList.remove('hidden');
});
// Close modal
closeBtn.addEventListener('click', function() {
modal.classList.add('hidden');
});
closeModalBtn.addEventListener('click', function() {
modal.classList.add('hidden');
});
// Close modal when clicking outside
modal.addEventListener('click', function(event) {
if (event.target === modal) {
modal.classList.add('hidden');
}
});
const dropboxModal = document.getElementById('dropboxDebugModal');
const showDropboxBtn = document.getElementById('showDropboxDebugBtn');
const closeDropboxBtn = document.getElementById('closeDropboxDebugBtn');
const closeDropboxModalBtn = document.getElementById('closeDropboxModalBtn');
// Show Dropbox modal
showDropboxBtn.addEventListener('click', function() {
dropboxModal.classList.remove('hidden');
});
// Close Dropbox modal
closeDropboxBtn.addEventListener('click', function() {
dropboxModal.classList.add('hidden');
});
closeDropboxModalBtn.addEventListener('click', function() {
dropboxModal.classList.add('hidden');
});
// Close Dropbox modal when clicking outside
dropboxModal.addEventListener('click', function(event) {
if (event.target === dropboxModal) {
dropboxModal.classList.add('hidden');
}
});
});
</script>
{% endif %}
{% endblock %}