findEthics commited on
Commit
385121f
·
1 Parent(s): edabc80

Refactor anonymous chat as default homepage with stateless JWT sessions

Browse files

Major changes:
- Homepage now directly opens anonymous chat interface
- Implemented JWT-based stateless sessions for anonymous users (6-hour expiry)
- Added info banner showing message limits and remaining messages
- Login/register buttons remain accessible in header
- Added cleanup script to reset anonymous data on startup
- Removed choose.html template (no longer needed)

Features:
- Anonymous users land directly on chat without extra clicks
- Clear visual indication of anonymous status and limitations
- Message counter with color-coded warnings as limit approaches
- Stateless operation handles Hugging Face Spaces load balancing
- Automatic cleanup of anonymous sessions on app restart

This fixes session expiration issues on Hugging Face Spaces deployment

app.py CHANGED
@@ -177,22 +177,85 @@ def record_login_attempt(ip_address):
177
  API_URL = app.config['API_URL']
178
  TIMEOUT = app.config['API_TIMEOUT']
179
 
180
- # Anonymous user session management
181
  def generate_anonymous_id():
182
  """Generate a unique anonymous user ID"""
183
  random_string = ''.join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(8))
184
  return f"anon_{random_string}"
185
 
186
- def init_anonymous_session():
187
- """Initialize session for anonymous user"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  session.permanent = True
189
  session['is_anonymous'] = True
190
- session['anonymous_id'] = generate_anonymous_id()
191
  session['anonymous_chat_history'] = []
192
  session['anonymous_message_count'] = 0
193
  session['anonymous_first_message_time'] = None
194
- app.logger.info(f"Initialized anonymous session: {session['anonymous_id']}")
195
- return session['anonymous_id']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
  def get_anonymous_chat_history():
198
  """Get chat history from session for anonymous user"""
@@ -297,16 +360,34 @@ def make_api_request(message, history=None):
297
  @app.route('/')
298
  def index():
299
  """
300
- Main route - show choice page for non-authenticated users
301
  """
302
  if current_user.is_authenticated:
303
- return redirect(url_for('chat_interface'))
 
304
 
305
  # Check if anonymous access is enabled
306
- if app.config.get('ANONYMOUS_ENABLED', True):
307
- return redirect(url_for('choose'))
308
- else:
309
  return redirect(url_for('login'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
311
  @app.route('/register', methods=['GET', 'POST'])
312
  def register():
@@ -463,55 +544,18 @@ def logout():
463
 
464
  return response
465
 
466
- @app.route('/choose')
467
- def choose():
468
- """
469
- Access mode selection page - choose between anonymous or authenticated access
470
- """
471
- # If user is already logged in, redirect to chat
472
- if current_user.is_authenticated:
473
- return redirect(url_for('chat_interface'))
474
-
475
- # Check if anonymous access is enabled
476
- if not app.config.get('ANONYMOUS_ENABLED', True):
477
- return redirect(url_for('login'))
478
-
479
- return render_template('choose.html')
480
 
481
- @app.route('/anonymous')
482
- def anonymous():
483
- """
484
- Anonymous chat interface - no authentication required
485
- """
486
- # If user is authenticated, redirect to authenticated chat
487
- if current_user.is_authenticated:
488
- return redirect(url_for('chat_interface'))
489
-
490
- # Check if anonymous access is enabled
491
- if not app.config.get('ANONYMOUS_ENABLED', True):
492
- flash('Anonymous access is not available. Please login or register.', 'info')
493
- return redirect(url_for('login'))
494
-
495
- # Initialize anonymous session if not already done
496
- if not session.get('is_anonymous'):
497
- init_anonymous_session()
498
-
499
- # Render chat interface in anonymous mode
500
- return render_template('index.html', is_anonymous=True)
501
 
502
  @app.route('/chat')
503
  @hf_login_required
504
  def chat_interface():
505
  """
506
  Protected chat interface - requires authentication
 
507
  """
508
- # Debug session information
509
- app.logger.info(f"Chat interface - Session: {dict(session)}")
510
- app.logger.info(f"Chat interface - Current user authenticated: {current_user.is_authenticated}")
511
- app.logger.info(f"Chat interface - Current user ID: {getattr(current_user, 'id', 'None')}")
512
- app.logger.info(f"Request IP: {request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)}")
513
-
514
- return render_template('index.html')
515
 
516
  @app.route('/api/chat', methods=['POST'])
517
  def chat():
@@ -519,8 +563,28 @@ def chat():
519
  API route to handle chat requests - supports both authenticated and anonymous users
520
  """
521
  try:
522
- # Check if user is anonymous
523
- is_anonymous = session.get('is_anonymous', False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
 
525
  # If not anonymous and not authenticated, require login
526
  if not is_anonymous and not current_user.is_authenticated:
@@ -578,9 +642,19 @@ def chat():
578
  if is_anonymous:
579
  # Save to session for anonymous users
580
  add_anonymous_message(message, response_text)
 
 
 
 
581
  result['history_saved'] = False # Indicate temporary storage
582
  result['user_id'] = session.get('anonymous_id', 'anonymous')
583
  result['is_anonymous'] = True
 
 
 
 
 
 
584
  else:
585
  # Save to database for authenticated users
586
  session_metadata = {
@@ -617,6 +691,23 @@ def chat():
617
  elif 'api' in error_msg.lower():
618
  result['error'] = "The chat service is temporarily unavailable. Please try again in a few moments."
619
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
  return jsonify(result)
621
 
622
  except Exception as e:
 
177
  API_URL = app.config['API_URL']
178
  TIMEOUT = app.config['API_TIMEOUT']
179
 
180
+ # Anonymous user session management with JWT tokens for stateless operation
181
  def generate_anonymous_id():
182
  """Generate a unique anonymous user ID"""
183
  random_string = ''.join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(8))
184
  return f"anon_{random_string}"
185
 
186
+ def create_anonymous_token(anonymous_id, chat_history=None, message_count=0, first_message_time=None):
187
+ """Create a JWT token for anonymous users"""
188
+ payload = {
189
+ 'anonymous_id': anonymous_id,
190
+ 'is_anonymous': True,
191
+ 'exp': datetime.utcnow() + timedelta(hours=6), # 6 hour expiry
192
+ 'iat': datetime.utcnow(),
193
+ 'chat_history': chat_history or [],
194
+ 'message_count': message_count,
195
+ 'first_message_time': first_message_time
196
+ }
197
+ return jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256')
198
+
199
+ def verify_anonymous_token(token):
200
+ """Verify and decode anonymous JWT token"""
201
+ try:
202
+ payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
203
+ if payload.get('is_anonymous'):
204
+ return payload
205
+ return None
206
+ except jwt.ExpiredSignatureError:
207
+ app.logger.info("Anonymous token expired")
208
+ return None
209
+ except jwt.InvalidTokenError:
210
+ app.logger.info("Invalid anonymous token")
211
+ return None
212
+
213
+ def get_or_create_anonymous_session():
214
+ """Get existing anonymous session from token or create new one"""
215
+ token = request.cookies.get('anon_token')
216
+
217
+ if token:
218
+ payload = verify_anonymous_token(token)
219
+ if payload:
220
+ # Restore session from token
221
+ session['is_anonymous'] = True
222
+ session['anonymous_id'] = payload.get('anonymous_id')
223
+ session['anonymous_chat_history'] = payload.get('chat_history', [])
224
+ session['anonymous_message_count'] = payload.get('message_count', 0)
225
+ session['anonymous_first_message_time'] = payload.get('first_message_time')
226
+ app.logger.info(f"Restored anonymous session from token: {payload.get('anonymous_id')}")
227
+ return payload.get('anonymous_id'), token
228
+
229
+ # Create new anonymous session
230
+ anonymous_id = generate_anonymous_id()
231
  session.permanent = True
232
  session['is_anonymous'] = True
233
+ session['anonymous_id'] = anonymous_id
234
  session['anonymous_chat_history'] = []
235
  session['anonymous_message_count'] = 0
236
  session['anonymous_first_message_time'] = None
237
+
238
+ # Create JWT token
239
+ token = create_anonymous_token(anonymous_id)
240
+ app.logger.info(f"Created new anonymous session: {anonymous_id}")
241
+ return anonymous_id, token
242
+
243
+ def update_anonymous_token():
244
+ """Update anonymous JWT token with current session data"""
245
+ if not session.get('is_anonymous'):
246
+ return None
247
+
248
+ # Keep only last 20 messages in token
249
+ chat_history = session.get('anonymous_chat_history', [])
250
+ if len(chat_history) > 20:
251
+ chat_history = chat_history[-20:]
252
+
253
+ return create_anonymous_token(
254
+ anonymous_id=session.get('anonymous_id'),
255
+ chat_history=chat_history,
256
+ message_count=session.get('anonymous_message_count', 0),
257
+ first_message_time=session.get('anonymous_first_message_time')
258
+ )
259
 
260
  def get_anonymous_chat_history():
261
  """Get chat history from session for anonymous user"""
 
360
  @app.route('/')
361
  def index():
362
  """
363
+ Main route - show anonymous chat by default, authenticated chat if logged in
364
  """
365
  if current_user.is_authenticated:
366
+ # Authenticated users get the full chat interface
367
+ return render_template('index.html', is_anonymous=False)
368
 
369
  # Check if anonymous access is enabled
370
+ if not app.config.get('ANONYMOUS_ENABLED', True):
371
+ flash('Please login or register to use the chat.', 'info')
 
372
  return redirect(url_for('login'))
373
+
374
+ # Initialize or restore anonymous session with JWT token
375
+ anonymous_id, token = get_or_create_anonymous_session()
376
+
377
+ # Create response with anonymous chat interface
378
+ response = make_response(render_template('index.html', is_anonymous=True))
379
+
380
+ # Set anonymous JWT token cookie
381
+ response.set_cookie(
382
+ 'anon_token',
383
+ token,
384
+ max_age=6*60*60, # 6 hours
385
+ secure=False, # HF handles HTTPS at proxy level
386
+ httponly=False, # Allow JS access for updates
387
+ samesite=None # Most permissive for cross-origin
388
+ )
389
+
390
+ return response
391
 
392
  @app.route('/register', methods=['GET', 'POST'])
393
  def register():
 
544
 
545
  return response
546
 
547
+ # Choose route removed - no longer needed as homepage shows anonymous chat directly
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
+ # Anonymous route removed - homepage now serves anonymous chat directly
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
 
551
  @app.route('/chat')
552
  @hf_login_required
553
  def chat_interface():
554
  """
555
  Protected chat interface - requires authentication
556
+ Redirects to homepage which handles both anonymous and authenticated chat
557
  """
558
+ return redirect(url_for('index'))
 
 
 
 
 
 
559
 
560
  @app.route('/api/chat', methods=['POST'])
561
  def chat():
 
563
  API route to handle chat requests - supports both authenticated and anonymous users
564
  """
565
  try:
566
+ # Check if user is anonymous - try to restore from token first
567
+ is_anonymous = False
568
+ anonymous_token = None
569
+
570
+ if not current_user.is_authenticated:
571
+ # Try to get anonymous session from token
572
+ token = request.cookies.get('anon_token')
573
+ if token:
574
+ payload = verify_anonymous_token(token)
575
+ if payload:
576
+ # Restore session from token
577
+ session['is_anonymous'] = True
578
+ session['anonymous_id'] = payload.get('anonymous_id')
579
+ session['anonymous_chat_history'] = payload.get('chat_history', [])
580
+ session['anonymous_message_count'] = payload.get('message_count', 0)
581
+ session['anonymous_first_message_time'] = payload.get('first_message_time')
582
+ is_anonymous = True
583
+ anonymous_token = token
584
+
585
+ # If no valid token, check session (fallback)
586
+ if not is_anonymous:
587
+ is_anonymous = session.get('is_anonymous', False)
588
 
589
  # If not anonymous and not authenticated, require login
590
  if not is_anonymous and not current_user.is_authenticated:
 
642
  if is_anonymous:
643
  # Save to session for anonymous users
644
  add_anonymous_message(message, response_text)
645
+
646
+ # Update the anonymous JWT token with new data
647
+ updated_token = update_anonymous_token()
648
+
649
  result['history_saved'] = False # Indicate temporary storage
650
  result['user_id'] = session.get('anonymous_id', 'anonymous')
651
  result['is_anonymous'] = True
652
+ result['message_count'] = session.get('anonymous_message_count', 0)
653
+ result['messages_remaining'] = max(0, app.config['ANONYMOUS_RATE_LIMIT'] - session.get('anonymous_message_count', 0))
654
+
655
+ # Include updated token in response for client to store
656
+ if updated_token:
657
+ result['update_token'] = updated_token
658
  else:
659
  # Save to database for authenticated users
660
  session_metadata = {
 
691
  elif 'api' in error_msg.lower():
692
  result['error'] = "The chat service is temporarily unavailable. Please try again in a few moments."
693
 
694
+ # If we have an updated anonymous token, set it in the response cookie
695
+ if is_anonymous and 'update_token' in result:
696
+ response = make_response(jsonify(result))
697
+ response.set_cookie(
698
+ 'anon_token',
699
+ result['update_token'],
700
+ max_age=6*60*60, # 6 hours
701
+ secure=False, # HF handles HTTPS
702
+ httponly=False, # Allow JS access
703
+ samesite=None
704
+ )
705
+ # Remove token from JSON response to avoid duplication
706
+ del result['update_token']
707
+ response.data = json.dumps(result)
708
+ response.content_type = 'application/json'
709
+ return response
710
+
711
  return jsonify(result)
712
 
713
  except Exception as e:
cleanup_anonymous.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Cleanup script for anonymous session data
4
+ Runs on app startup to clear stale anonymous sessions
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ from datetime import datetime, timedelta
10
+ from pymongo import MongoClient
11
+ import logging
12
+
13
+ # Setup logging
14
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
15
+ logger = logging.getLogger(__name__)
16
+
17
+ def cleanup_anonymous_sessions():
18
+ """Remove all anonymous session data from MongoDB"""
19
+ try:
20
+ # Get MongoDB connection from environment
21
+ mongodb_url = os.environ.get('MONGODB_URL') or os.environ.get('MONGODB_URI')
22
+ if not mongodb_url:
23
+ logger.warning("No MongoDB URL found in environment. Skipping cleanup.")
24
+ return True
25
+
26
+ # Connect to MongoDB
27
+ client = MongoClient(mongodb_url)
28
+ db_name = os.environ.get('MONGODB_DATABASE', 'Atlas')
29
+ db = client[db_name]
30
+
31
+ # Clear anonymous sessions from chat_sessions collection
32
+ # Anonymous sessions have user_id starting with 'anon_' or are older than 6 hours
33
+ if 'chat_sessions' in db.list_collection_names():
34
+ # Remove sessions with anonymous user IDs
35
+ result = db.chat_sessions.delete_many({
36
+ '$or': [
37
+ {'user_id': {'$regex': '^anon_'}},
38
+ {'session_data.anonymous': True}
39
+ ]
40
+ })
41
+ logger.info(f"Removed {result.deleted_count} anonymous chat sessions")
42
+
43
+ # Clear any temporary anonymous user records if they exist
44
+ if 'users' in db.list_collection_names():
45
+ # Remove any accidentally created anonymous user records
46
+ result = db.users.delete_many({
47
+ '$or': [
48
+ {'email': {'$regex': '^anon_'}},
49
+ {'is_anonymous': True}
50
+ ]
51
+ })
52
+ if result.deleted_count > 0:
53
+ logger.info(f"Removed {result.deleted_count} anonymous user records")
54
+
55
+ # Clear expired sessions (older than 24 hours)
56
+ cutoff_time = datetime.utcnow() - timedelta(hours=24)
57
+ if 'sessions' in db.list_collection_names():
58
+ result = db.sessions.delete_many({
59
+ 'expiry': {'$lt': cutoff_time}
60
+ })
61
+ if result.deleted_count > 0:
62
+ logger.info(f"Removed {result.deleted_count} expired sessions")
63
+
64
+ logger.info("Anonymous session cleanup completed successfully")
65
+ return True
66
+
67
+ except Exception as e:
68
+ logger.error(f"Error during anonymous session cleanup: {e}")
69
+ # Don't fail the app startup if cleanup fails
70
+ return False
71
+
72
+ if __name__ == "__main__":
73
+ # Load environment variables if .env exists
74
+ try:
75
+ from dotenv import load_dotenv
76
+ load_dotenv()
77
+ except ImportError:
78
+ pass
79
+
80
+ success = cleanup_anonymous_sessions()
81
+ sys.exit(0 if success else 1)
start.sh CHANGED
@@ -44,6 +44,15 @@ if [ $? -ne 0 ]; then
44
  fi
45
  echo "✅ Database connection successful"
46
 
 
 
 
 
 
 
 
 
 
47
  # Run the application
48
  PORT=${PORT:-7860}
49
  echo "Starting Chatty server on http://localhost:$PORT"
 
44
  fi
45
  echo "✅ Database connection successful"
46
 
47
+ # Clean up anonymous session data on startup
48
+ echo "Cleaning up anonymous session data..."
49
+ python cleanup_anonymous.py
50
+ if [ $? -eq 0 ]; then
51
+ echo "✅ Anonymous session cleanup completed"
52
+ else
53
+ echo "⚠️ Anonymous session cleanup encountered issues (continuing anyway)"
54
+ fi
55
+
56
  # Run the application
57
  PORT=${PORT:-7860}
58
  echo "Starting Chatty server on http://localhost:$PORT"
static/css/style.css CHANGED
@@ -284,6 +284,74 @@ body {
284
  }
285
  }
286
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  /* Loading indicator */
288
  .loading-indicator {
289
  position: fixed;
 
284
  }
285
  }
286
 
287
+ /* Anonymous info banner */
288
+ .anonymous-info-banner {
289
+ background-color: rgb(245, 245, 245);
290
+ color: rgb(100, 100, 100);
291
+ padding: 0.75rem 1rem;
292
+ display: flex;
293
+ align-items: center;
294
+ gap: 0.5rem;
295
+ font-size: 0.9rem;
296
+ border-bottom: 1px solid rgb(220, 220, 220);
297
+ flex-wrap: wrap;
298
+ }
299
+
300
+ .anonymous-info-banner .info-icon {
301
+ font-size: 1.1rem;
302
+ color: rgb(120, 120, 120);
303
+ }
304
+
305
+ .anonymous-info-banner .info-text {
306
+ flex: 1;
307
+ min-width: 200px;
308
+ }
309
+
310
+ .anonymous-info-banner strong {
311
+ color: rgb(80, 80, 80);
312
+ font-weight: 600;
313
+ }
314
+
315
+ .anonymous-info-banner .banner-link {
316
+ color: black;
317
+ text-decoration: underline;
318
+ font-weight: 500;
319
+ }
320
+
321
+ .anonymous-info-banner .banner-link:hover {
322
+ text-decoration: none;
323
+ }
324
+
325
+ .anonymous-info-banner .message-counter {
326
+ margin-left: auto;
327
+ background-color: white;
328
+ padding: 0.25rem 0.75rem;
329
+ border-radius: 1rem;
330
+ border: 1px solid rgb(200, 200, 200);
331
+ font-size: 0.85rem;
332
+ white-space: nowrap;
333
+ }
334
+
335
+ .anonymous-info-banner #messagesUsed {
336
+ font-weight: 600;
337
+ color: black;
338
+ }
339
+
340
+ /* Mobile adjustments for info banner */
341
+ @media (max-width: 768px) {
342
+ .anonymous-info-banner {
343
+ font-size: 0.85rem;
344
+ padding: 0.5rem 0.75rem;
345
+ }
346
+
347
+ .anonymous-info-banner .message-counter {
348
+ margin-left: 0;
349
+ margin-top: 0.5rem;
350
+ width: 100%;
351
+ text-align: center;
352
+ }
353
+ }
354
+
355
  /* Loading indicator */
356
  .loading-indicator {
357
  position: fixed;
static/js/chat.js CHANGED
@@ -27,6 +27,7 @@ class ChatApp {
27
  this.isAnonymous = window.chatConfig && window.chatConfig.isAnonymous;
28
  this.anonymousRateLimit = window.chatConfig ? window.chatConfig.anonymousRateLimit : 0;
29
  this.anonymousId = window.chatConfig ? window.chatConfig.anonymousId : '';
 
30
 
31
  this.init();
32
  }
@@ -144,6 +145,16 @@ class ChatApp {
144
  if (response.user_id) {
145
  this.userId = response.user_id;
146
  }
 
 
 
 
 
 
 
 
 
 
147
  } else {
148
  // Show error message
149
  this.showError(response.error || 'Failed to get response');
@@ -263,8 +274,8 @@ class ChatApp {
263
  // Skip loading history for anonymous users (they start fresh)
264
  if (this.isAnonymous) {
265
  this.historyLoaded = true;
266
- // Show welcome message for anonymous users
267
- this.showAnonymousWelcome();
268
  return;
269
  }
270
 
@@ -481,8 +492,41 @@ class ChatApp {
481
  }, 5000);
482
  }
483
 
484
- showAnonymousWelcome() {
485
- // No welcome message for minimal UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  }
487
  }
488
 
 
27
  this.isAnonymous = window.chatConfig && window.chatConfig.isAnonymous;
28
  this.anonymousRateLimit = window.chatConfig ? window.chatConfig.anonymousRateLimit : 0;
29
  this.anonymousId = window.chatConfig ? window.chatConfig.anonymousId : '';
30
+ this.messageCount = 0; // Track message count for anonymous users
31
 
32
  this.init();
33
  }
 
145
  if (response.user_id) {
146
  this.userId = response.user_id;
147
  }
148
+
149
+ // Update message counter for anonymous users
150
+ if (this.isAnonymous && response.message_count !== undefined) {
151
+ this.updateMessageCounter(response.message_count);
152
+
153
+ // Show warning if approaching limit
154
+ if (response.messages_remaining !== undefined && response.messages_remaining <= 3 && response.messages_remaining > 0) {
155
+ this.showRateLimitWarning(response.messages_remaining);
156
+ }
157
+ }
158
  } else {
159
  // Show error message
160
  this.showError(response.error || 'Failed to get response');
 
274
  // Skip loading history for anonymous users (they start fresh)
275
  if (this.isAnonymous) {
276
  this.historyLoaded = true;
277
+ // Initialize message counter for anonymous users
278
+ this.updateMessageCounter(0);
279
  return;
280
  }
281
 
 
492
  }, 5000);
493
  }
494
 
495
+ updateMessageCounter(count) {
496
+ if (!this.isAnonymous) return;
497
+
498
+ this.messageCount = count;
499
+ const messagesUsedEl = document.getElementById('messagesUsed');
500
+ const messageCounterEl = document.getElementById('messageCounter');
501
+
502
+ if (messagesUsedEl) {
503
+ messagesUsedEl.textContent = count;
504
+ }
505
+
506
+ // Show warning colors as approaching limit
507
+ if (messageCounterEl) {
508
+ if (count >= this.anonymousRateLimit) {
509
+ messageCounterEl.style.backgroundColor = '#ffcccc';
510
+ messageCounterEl.style.borderColor = '#ff6666';
511
+ } else if (count >= this.anonymousRateLimit * 0.8) {
512
+ messageCounterEl.style.backgroundColor = '#fff5cc';
513
+ messageCounterEl.style.borderColor = '#ffcc66';
514
+ } else {
515
+ messageCounterEl.style.backgroundColor = 'white';
516
+ messageCounterEl.style.borderColor = 'rgb(200, 200, 200)';
517
+ }
518
+ }
519
+ }
520
+
521
+ showRateLimitWarning(remaining) {
522
+ const warningDiv = document.createElement('div');
523
+ warningDiv.className = 'system-message warning';
524
+ warningDiv.innerHTML = `
525
+ <strong>⚠️ Rate limit approaching:</strong> You have ${remaining} message${remaining === 1 ? '' : 's'} remaining this hour.
526
+ <a href="/login">Login</a> or <a href="/register">Register</a> for unlimited messages.
527
+ `;
528
+ this.chatMessages.appendChild(warningDiv);
529
+ this.scrollToBottom();
530
  }
531
  }
532
 
templates/choose.html DELETED
@@ -1,26 +0,0 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Chatty{% endblock %}
4
-
5
- {% block content %}
6
- <div class="choose-container">
7
- <div class="choose-wrapper">
8
- <h1 class="choose-title">Chatty</h1>
9
-
10
- <div class="choose-options">
11
- <a href="{{ url_for('anonymous') }}" class="choose-button">
12
- Chat Anonymously
13
- </a>
14
-
15
- <div class="auth-buttons">
16
- <a href="{{ url_for('login') }}" class="choose-button">
17
- Login
18
- </a>
19
- <a href="{{ url_for('register') }}" class="choose-button">
20
- Register
21
- </a>
22
- </div>
23
- </div>
24
- </div>
25
- </div>
26
- {% endblock %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/index.html CHANGED
@@ -4,6 +4,19 @@
4
 
5
  {% block content %}
6
  <div class="chat-container">
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  <div class="chat-messages" id="chatMessages">
8
  <!-- Messages will be dynamically added here -->
9
  </div>
 
4
 
5
  {% block content %}
6
  <div class="chat-container">
7
+ {% if is_anonymous %}
8
+ <div class="anonymous-info-banner">
9
+ <span class="info-icon">ℹ️</span>
10
+ <span class="info-text">
11
+ You're chatting anonymously. Limited to <strong>{{ config.ANONYMOUS_RATE_LIMIT }}</strong> messages per hour.
12
+ <a href="{{ url_for('login') }}" class="banner-link">Login</a> or
13
+ <a href="{{ url_for('register') }}" class="banner-link">Register</a> to save your chat history and remove limits.
14
+ </span>
15
+ <span class="message-counter" id="messageCounter">
16
+ <span id="messagesUsed">0</span>/{{ config.ANONYMOUS_RATE_LIMIT }} messages used
17
+ </span>
18
+ </div>
19
+ {% endif %}
20
  <div class="chat-messages" id="chatMessages">
21
  <!-- Messages will be dynamically added here -->
22
  </div>