findEthics commited on
Commit
6ec566b
·
1 Parent(s): dcffe87

Add anonymous chat access feature

Browse files

- Allow users to chat without creating an account
- Add choice page for selecting access mode (anonymous vs authenticated)
- Implement session-based chat storage for anonymous users
- Add rate limiting (20 messages/hour) for anonymous users
- Add message length limits (2000 chars) for anonymous users
- Update UI to show anonymous user indicators and session warnings
- Maintain backward compatibility with existing authenticated flow

Files changed (10) hide show
  1. .gitignore +67 -0
  2. CLAUDE.md +193 -0
  3. app.py +163 -36
  4. config.py +8 -0
  5. static/css/style.css +223 -0
  6. static/js/chat.js +44 -8
  7. task_list.md +140 -1
  8. templates/base.html +4 -0
  9. templates/choose.html +75 -0
  10. templates/index.html +17 -0
.gitignore ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claude
2
+ .claude
3
+ .swarm
4
+ .claude-flow/metrics
5
+ .kiro
6
+
7
+ # Environment variables
8
+ .env
9
+ .env.local
10
+ .env.production
11
+
12
+ # Python
13
+ __pycache__/
14
+ *.py[cod]
15
+ *$py.class
16
+ *.so
17
+ .Python
18
+ build/
19
+ develop-eggs/
20
+ dist/
21
+ downloads/
22
+ eggs/
23
+ .eggs/
24
+ lib/
25
+ lib64/
26
+ parts/
27
+ sdist/
28
+ var/
29
+ wheels/
30
+ *.egg-info/
31
+ .installed.cfg
32
+ *.egg
33
+ MANIFEST
34
+
35
+ # Virtual environments
36
+ atlas_env/
37
+ venv/
38
+ env/
39
+ ENV/
40
+
41
+ # IDE
42
+ .vscode/
43
+ .idea/
44
+ *.swp
45
+ *.swo
46
+
47
+ # OS
48
+ .DS_Store
49
+ Thumbs.db
50
+
51
+ # Logs
52
+ *.log
53
+
54
+ # Test files (exclude from root, but allow in tests/ folder)
55
+ /test_*.py
56
+ /debug_*.py
57
+ /validate_*.py
58
+ /deployment_check.py
59
+
60
+ # MongoDB local data
61
+ *.json
62
+ *.bson
63
+
64
+ # API Keys (backup protection)
65
+ *.key
66
+ *.pem
67
+ api_keys.txt
CLAUDE.md ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Chatty - Ethics Chat Application Context
2
+
3
+ ## Project Overview
4
+ Chatty is a secure web-based chat interface that connects to the findEthics-Atlas API for ethical discussions and guidance. Originally a Gradio chatbot template, it has been transformed into a full Flask web application with user authentication, chat history persistence, and deployment on Hugging Face Spaces.
5
+
6
+ ## Architecture
7
+
8
+ ### Tech Stack
9
+ - **Backend**: Flask 3.0.0 (Python web framework)
10
+ - **Database**: MongoDB Atlas (user accounts & chat history)
11
+ - **Authentication**: Flask-Login with JWT tokens for stateless auth
12
+ - **Security**: CSRF protection (disabled for HF Spaces), rate limiting, secure password hashing (Werkzeug PBKDF2)
13
+ - **Frontend**: HTML, CSS (minimal design), JavaScript (vanilla ES6)
14
+ - **Deployment**: Hugging Face Spaces (primary), supports Docker and traditional servers
15
+ - **API Integration**: findEthics-Atlas API at https://findEthics-Atlas.hf.space/chat
16
+
17
+ ### Project Structure
18
+ ```
19
+ Chatty/
20
+ ├── app.py # Main Flask application with routes and JWT auth
21
+ ├── config.py # Environment-based configuration management
22
+ ├── auth.py # Authentication utilities & Flask-Login setup
23
+ ├── models.py # MongoDB models (User, ChatSession)
24
+ ├── database.py # MongoDB connection and collection utilities
25
+ ├── deploy.py # Deployment tools and configuration helpers
26
+ ├── requirements.txt # Python dependencies
27
+ ├── start.sh # Application startup script
28
+ ├── templates/ # HTML templates
29
+ │ ├── base.html # Base template with navigation
30
+ │ ├── login.html # Login form
31
+ │ ├── register.html # Registration form
32
+ │ ├── index.html # Main chat interface
33
+ │ └── errors/ # Error page templates (400, 401, 429, 500)
34
+ ├── static/ # Static assets
35
+ │ ├── css/
36
+ │ │ ├── style.css # Main stylesheet
37
+ │ │ └── auth.css # Authentication form styles
38
+ │ └── js/
39
+ │ └── chat.js # Frontend chat functionality
40
+ ├── Test/ # Test suite
41
+ └── Documentation/
42
+ ├── README.md # Main documentation with HF Spaces YAML header
43
+ ├── HUGGINGFACE_DEPLOYMENT_CHECKLIST.md
44
+ ├── PRODUCTION_DEPLOYMENT.md
45
+ └── instructions.md # Original requirements
46
+ ```
47
+
48
+ ## Key Features
49
+ 1. **User Authentication**: Secure registration/login with email validation
50
+ 2. **Personalized Chat History**: Each user's conversations saved in MongoDB
51
+ 3. **Stateless JWT Auth**: For Hugging Face Spaces compatibility
52
+ 4. **Real-time Chat**: Instant messaging with ethics AI
53
+ 5. **Security Features**: Rate limiting, secure sessions, password hashing
54
+ 6. **Responsive Design**: Mobile-friendly minimal UI
55
+
56
+ ## API Integration
57
+
58
+ ### findEthics-Atlas API
59
+ - **Endpoint**: https://findEthics-Atlas.hf.space/chat
60
+ - **Method**: POST
61
+ - **Request Format**:
62
+ ```json
63
+ {
64
+ "prompt": "user message",
65
+ "history": []
66
+ }
67
+ ```
68
+ - **Response Format**:
69
+ ```json
70
+ {
71
+ "response": "AI response text",
72
+ "search_results": []
73
+ }
74
+ ```
75
+
76
+ ## Database Schema
77
+
78
+ ### Users Collection
79
+ ```javascript
80
+ {
81
+ _id: ObjectId,
82
+ email: String (lowercase, unique),
83
+ password_hash: String (Werkzeug PBKDF2),
84
+ created_at: DateTime,
85
+ is_active: Boolean
86
+ }
87
+ ```
88
+
89
+ ### Chat Sessions Collection
90
+ ```javascript
91
+ {
92
+ _id: ObjectId,
93
+ user_id: ObjectId (reference to Users),
94
+ message: String,
95
+ response: String,
96
+ timestamp: DateTime,
97
+ session_data: Object (metadata)
98
+ }
99
+ ```
100
+
101
+ ## Recent Updates
102
+ - **Stateless JWT Authentication**: Implemented for Hugging Face Spaces compatibility
103
+ - **Session Persistence Fixes**: Resolved load balancing issues on HF Spaces
104
+ - **CSRF Protection**: Disabled for HF Spaces deployment (iframe/proxy limitations)
105
+ - **Cookie Configuration**: Adjusted for HF Spaces environment
106
+
107
+ ## Deployment Configuration
108
+
109
+ ### Hugging Face Spaces
110
+ - **FLASK_ENV**: huggingface
111
+ - **Port**: 7860
112
+ - **CSRF**: Disabled (WTF_CSRF_ENABLED=false)
113
+ - **Session Cookies**: Secure=false (for HF proxy)
114
+ - **JWT Tokens**: 48-hour expiry for stateless auth
115
+
116
+ ### Environment Variables
117
+ Required:
118
+ - `SECRET_KEY`: 64-character secure key
119
+ - `MONGODB_URL`: MongoDB Atlas connection string
120
+ - `MONGODB_DATABASE`: Atlas
121
+
122
+ Security (HF Spaces optimized):
123
+ - `SESSION_COOKIE_SECURE`: false
124
+ - `WTF_CSRF_ENABLED`: false
125
+ - `MAX_LOGIN_ATTEMPTS`: 3
126
+ - `RATE_LIMIT_WINDOW`: 1800
127
+
128
+ ## Security Considerations
129
+ 1. **Password Security**: PBKDF2 hashing via Werkzeug
130
+ 2. **Rate Limiting**: Login attempt limiting (5 attempts default)
131
+ 3. **JWT Tokens**: Stateless authentication for distributed deployment
132
+ 4. **Input Validation**: Server and client-side validation
133
+ 5. **HTTPS**: Provided by Hugging Face Spaces
134
+
135
+ ## Testing
136
+ - **Test Suite**: Located in Test/ directory
137
+ - **Coverage**: Unit tests, integration tests, auth flow tests
138
+ - **Run Tests**: `python Test/run_tests.py`
139
+
140
+ ## Development Workflow
141
+
142
+ ### Local Development
143
+ ```bash
144
+ # Setup virtual environment
145
+ python -m venv atlas_env
146
+ source atlas_env/bin/activate
147
+
148
+ # Install dependencies
149
+ pip install -r requirements.txt
150
+
151
+ # Configure environment
152
+ python deploy.py setup-dev
153
+
154
+ # Run application
155
+ ./start.sh
156
+ ```
157
+
158
+ ### Production Deployment
159
+ ```bash
160
+ # Check production readiness
161
+ python deploy.py check-prod
162
+
163
+ # Generate secure key
164
+ python deploy.py gen-secret
165
+
166
+ # Deploy to HF Spaces
167
+ git push origin main
168
+ ```
169
+
170
+ ## Important Notes
171
+ 1. **CSRF Protection**: Disabled for HF Spaces due to iframe limitations
172
+ 2. **Session Management**: Uses JWT tokens for stateless operation
173
+ 3. **Database**: Requires MongoDB Atlas with proper network access
174
+ 4. **API Dependency**: Relies on findEthics-Atlas API availability
175
+ 5. **Minimal UI**: Intentionally basic design per requirements
176
+
177
+ ## Common Issues & Solutions
178
+ 1. **Session Persistence**: Resolved with JWT tokens
179
+ 2. **CSRF Errors on HF**: Disabled CSRF for HF environment
180
+ 3. **Database Connection**: Ensure MongoDB Atlas allows HF IP ranges
181
+ 4. **Cookie Issues**: Configured for HF Spaces proxy environment
182
+
183
+ ## Maintenance Tasks
184
+ - Monitor MongoDB Atlas usage and performance
185
+ - Check API endpoint availability
186
+ - Review authentication logs for security
187
+ - Update dependencies regularly
188
+ - Backup database periodically
189
+
190
+ ## Contact & Support
191
+ - Deployment issues: Check HUGGINGFACE_DEPLOYMENT_CHECKLIST.md
192
+ - Production setup: See PRODUCTION_DEPLOYMENT.md
193
+ - Test failures: Review Test/TEST_SUMMARY.md
app.py CHANGED
@@ -20,6 +20,8 @@ from config import get_config, validate_environment
20
  from auth import init_login_manager, authenticate_user, create_user_account
21
  from models import User, ChatSession
22
  from functools import wraps
 
 
23
 
24
  # Validate configuration before starting
25
  if not validate_environment():
@@ -175,6 +177,66 @@ def record_login_attempt(ip_address):
175
  API_URL = app.config['API_URL']
176
  TIMEOUT = app.config['API_TIMEOUT']
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  def make_api_request(message, history=None):
179
  """
180
  Make POST request to the findEthics-Atlas API
@@ -235,11 +297,16 @@ def make_api_request(message, history=None):
235
  @app.route('/')
236
  def index():
237
  """
238
- Main route - redirect to login if not authenticated, otherwise show chat
239
  """
240
  if current_user.is_authenticated:
241
  return redirect(url_for('chat_interface'))
242
- return redirect(url_for('login'))
 
 
 
 
 
243
 
244
  @app.route('/register', methods=['GET', 'POST'])
245
  def register():
@@ -396,6 +463,42 @@ def logout():
396
 
397
  return response
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  @app.route('/chat')
400
  @hf_login_required
401
  def chat_interface():
@@ -411,14 +514,16 @@ def chat_interface():
411
  return render_template('index.html')
412
 
413
  @app.route('/api/chat', methods=['POST'])
414
- @hf_login_required
415
  def chat():
416
  """
417
- Protected API route to handle chat requests
418
  """
419
  try:
420
- # Check if user session is still valid
421
- if not current_user.is_authenticated:
 
 
 
422
  return jsonify({
423
  "success": False,
424
  "error": "Your session has expired. Please log in again.",
@@ -440,47 +545,68 @@ def chat():
440
  "error": "Your message cannot be empty. Please type something."
441
  }), 400
442
 
443
- # Check message length (reasonable limit)
444
- if len(message) > 5000:
 
445
  return jsonify({
446
  "success": False,
447
- "error": "Your message is too long. Please keep it under 5000 characters."
448
  }), 400
449
 
450
- # Get user's chat history for context
451
- user_history = ChatSession.get_session_context(current_user.id, limit=10)
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  # Make request to the external API
454
  result = make_api_request(message, user_history)
455
 
456
- # If successful, save the message and response to database
457
  if result.get('success'):
458
  response_text = result.get('response', '')
459
 
460
- # Save to database with user_id
461
- session_metadata = {
462
- 'timestamp': datetime.utcnow().isoformat(),
463
- 'api_url': API_URL,
464
- 'user_agent': request.headers.get('User-Agent', ''),
465
- 'ip_address': request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)
466
- }
467
-
468
- save_success = ChatSession.save_message(
469
- user_id=current_user.id,
470
- message=message,
471
- response=response_text,
472
- session_metadata=session_metadata
473
- )
474
-
475
- if not save_success:
476
- # Log the error but don't fail the request
477
- app.logger.warning(f"Failed to save chat message for user {current_user.id}")
478
- # Optionally notify user that history wasn't saved
479
- result['history_saved'] = False
480
  else:
481
- result['history_saved'] = True
482
-
483
- result['user_id'] = current_user.id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
  else:
485
  # Enhance error messages from API
486
  error_msg = result.get('error', 'Unknown error occurred')
@@ -494,7 +620,8 @@ def chat():
494
  return jsonify(result)
495
 
496
  except Exception as e:
497
- app.logger.error(f"Chat API error for user {current_user.id if current_user.is_authenticated else 'unknown'}: {str(e)}")
 
498
  return jsonify({
499
  "success": False,
500
  "error": "We're experiencing technical difficulties. Please try again in a few moments."
 
20
  from auth import init_login_manager, authenticate_user, create_user_account
21
  from models import User, ChatSession
22
  from functools import wraps
23
+ import secrets
24
+ import string
25
 
26
  # Validate configuration before starting
27
  if not validate_environment():
 
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"""
199
+ return session.get('anonymous_chat_history', [])
200
+
201
+ def add_anonymous_message(message, response):
202
+ """Add message and response to anonymous session history"""
203
+ if 'anonymous_chat_history' not in session:
204
+ session['anonymous_chat_history'] = []
205
+
206
+ session['anonymous_chat_history'].append({
207
+ 'user': message,
208
+ 'assistant': response
209
+ })
210
+
211
+ # Keep only last 20 messages for anonymous users
212
+ if len(session['anonymous_chat_history']) > 20:
213
+ session['anonymous_chat_history'] = session['anonymous_chat_history'][-20:]
214
+
215
+ def check_anonymous_rate_limit():
216
+ """Check if anonymous user has exceeded rate limit"""
217
+ current_time = time.time()
218
+
219
+ # Initialize rate limit tracking
220
+ if 'anonymous_first_message_time' not in session or session.get('anonymous_first_message_time') is None:
221
+ session['anonymous_first_message_time'] = current_time
222
+ session['anonymous_message_count'] = 0
223
+
224
+ # Check if rate limit window has passed
225
+ time_since_first = current_time - session['anonymous_first_message_time']
226
+ if time_since_first > app.config['ANONYMOUS_RATE_LIMIT_WINDOW']:
227
+ # Reset rate limit window
228
+ session['anonymous_first_message_time'] = current_time
229
+ session['anonymous_message_count'] = 0
230
+
231
+ # Check if limit exceeded
232
+ if session.get('anonymous_message_count', 0) >= app.config['ANONYMOUS_RATE_LIMIT']:
233
+ time_remaining = app.config['ANONYMOUS_RATE_LIMIT_WINDOW'] - time_since_first
234
+ return False, f"Rate limit exceeded. Please wait {int(time_remaining/60)} minutes."
235
+
236
+ # Increment message count
237
+ session['anonymous_message_count'] = session.get('anonymous_message_count', 0) + 1
238
+ return True, None
239
+
240
  def make_api_request(message, history=None):
241
  """
242
  Make POST request to the findEthics-Atlas API
 
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
 
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():
 
514
  return render_template('index.html')
515
 
516
  @app.route('/api/chat', methods=['POST'])
 
517
  def chat():
518
  """
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:
527
  return jsonify({
528
  "success": False,
529
  "error": "Your session has expired. Please log in again.",
 
545
  "error": "Your message cannot be empty. Please type something."
546
  }), 400
547
 
548
+ # Check message length (different limits for anonymous vs authenticated)
549
+ max_length = app.config['ANONYMOUS_MAX_MESSAGE_LENGTH'] if is_anonymous else 5000
550
+ if len(message) > max_length:
551
  return jsonify({
552
  "success": False,
553
+ "error": f"Your message is too long. Please keep it under {max_length} characters."
554
  }), 400
555
 
556
+ # Check rate limit for anonymous users
557
+ if is_anonymous:
558
+ allowed, error_msg = check_anonymous_rate_limit()
559
+ if not allowed:
560
+ return jsonify({
561
+ "success": False,
562
+ "error": error_msg
563
+ }), 429
564
+
565
+ # Get chat history for context
566
+ if is_anonymous:
567
+ user_history = get_anonymous_chat_history()
568
+ else:
569
+ user_history = ChatSession.get_session_context(current_user.id, limit=10)
570
 
571
  # Make request to the external API
572
  result = make_api_request(message, user_history)
573
 
574
+ # If successful, save the message and response
575
  if result.get('success'):
576
  response_text = result.get('response', '')
577
 
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 = {
587
+ 'timestamp': datetime.utcnow().isoformat(),
588
+ 'api_url': API_URL,
589
+ 'user_agent': request.headers.get('User-Agent', ''),
590
+ 'ip_address': request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)
591
+ }
592
+
593
+ save_success = ChatSession.save_message(
594
+ user_id=current_user.id,
595
+ message=message,
596
+ response=response_text,
597
+ session_metadata=session_metadata
598
+ )
599
+
600
+ if not save_success:
601
+ # Log the error but don't fail the request
602
+ app.logger.warning(f"Failed to save chat message for user {current_user.id}")
603
+ # Optionally notify user that history wasn't saved
604
+ result['history_saved'] = False
605
+ else:
606
+ result['history_saved'] = True
607
+
608
+ result['user_id'] = current_user.id
609
+ result['is_anonymous'] = False
610
  else:
611
  # Enhance error messages from API
612
  error_msg = result.get('error', 'Unknown error occurred')
 
620
  return jsonify(result)
621
 
622
  except Exception as e:
623
+ user_identifier = session.get('anonymous_id', 'unknown') if is_anonymous else (current_user.id if current_user.is_authenticated else 'unknown')
624
+ app.logger.error(f"Chat API error for user {user_identifier}: {str(e)}")
625
  return jsonify({
626
  "success": False,
627
  "error": "We're experiencing technical difficulties. Please try again in a few moments."
config.py CHANGED
@@ -39,6 +39,13 @@ class Config:
39
  MAX_LOGIN_ATTEMPTS = int(os.environ.get('MAX_LOGIN_ATTEMPTS', 5))
40
  RATE_LIMIT_WINDOW = int(os.environ.get('RATE_LIMIT_WINDOW', 900)) # 15 minutes
41
 
 
 
 
 
 
 
 
42
  # API Configuration
43
  API_URL = os.environ.get('API_URL', 'https://findEthics-Atlas.hf.space/chat')
44
  API_TIMEOUT = int(os.environ.get('API_TIMEOUT', 30))
@@ -72,6 +79,7 @@ class DevelopmentConfig(Config):
72
  DEBUG = True
73
  SESSION_COOKIE_SECURE = False
74
  WTF_CSRF_SSL_STRICT = False
 
75
  LOG_LEVEL = 'DEBUG'
76
 
77
  class ProductionConfig(Config):
 
39
  MAX_LOGIN_ATTEMPTS = int(os.environ.get('MAX_LOGIN_ATTEMPTS', 5))
40
  RATE_LIMIT_WINDOW = int(os.environ.get('RATE_LIMIT_WINDOW', 900)) # 15 minutes
41
 
42
+ # Anonymous User Configuration
43
+ ANONYMOUS_ENABLED = os.environ.get('ANONYMOUS_ENABLED', 'True').lower() == 'true'
44
+ ANONYMOUS_RATE_LIMIT = int(os.environ.get('ANONYMOUS_RATE_LIMIT', 20)) # messages per hour
45
+ ANONYMOUS_RATE_LIMIT_WINDOW = int(os.environ.get('ANONYMOUS_RATE_LIMIT_WINDOW', 3600)) # 1 hour
46
+ ANONYMOUS_SESSION_TIMEOUT = int(os.environ.get('ANONYMOUS_SESSION_TIMEOUT', 3600)) # 1 hour
47
+ ANONYMOUS_MAX_MESSAGE_LENGTH = int(os.environ.get('ANONYMOUS_MAX_MESSAGE_LENGTH', 2000)) # characters
48
+
49
  # API Configuration
50
  API_URL = os.environ.get('API_URL', 'https://findEthics-Atlas.hf.space/chat')
51
  API_TIMEOUT = int(os.environ.get('API_TIMEOUT', 30))
 
79
  DEBUG = True
80
  SESSION_COOKIE_SECURE = False
81
  WTF_CSRF_SSL_STRICT = False
82
+ WTF_CSRF_ENABLED = False # Disable CSRF for testing
83
  LOG_LEVEL = 'DEBUG'
84
 
85
  class ProductionConfig(Config):
static/css/style.css CHANGED
@@ -542,4 +542,227 @@ uch and interaction improvements */
542
  border: 1px solid #ccc;
543
  page-break-inside: avoid;
544
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  }
 
542
  border: 1px solid #ccc;
543
  page-break-inside: avoid;
544
  }
545
+ }
546
+
547
+ /* Choice Page Styles */
548
+ .choose-container {
549
+ width: 100%;
550
+ min-height: calc(100vh - 60px);
551
+ display: flex;
552
+ align-items: center;
553
+ justify-content: center;
554
+ padding: 2rem;
555
+ background: linear-gradient(135deg, #f5f5f5 0%, #ffffff 100%);
556
+ }
557
+
558
+ .choose-wrapper {
559
+ max-width: 900px;
560
+ width: 100%;
561
+ text-align: center;
562
+ }
563
+
564
+ .choose-title {
565
+ font-size: 2.5rem;
566
+ margin-bottom: 0.5rem;
567
+ color: black;
568
+ }
569
+
570
+ .choose-subtitle {
571
+ font-size: 1.2rem;
572
+ color: #666;
573
+ margin-bottom: 3rem;
574
+ }
575
+
576
+ .choose-options {
577
+ display: grid;
578
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
579
+ gap: 2rem;
580
+ margin-bottom: 2rem;
581
+ }
582
+
583
+ .choose-option {
584
+ background: white;
585
+ border: 2px solid #e5e5e5;
586
+ border-radius: 12px;
587
+ padding: 2rem;
588
+ transition: all 0.3s ease;
589
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
590
+ }
591
+
592
+ .choose-option:hover {
593
+ border-color: #000;
594
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
595
+ }
596
+
597
+ .choose-option h2 {
598
+ margin-bottom: 1.5rem;
599
+ font-size: 1.5rem;
600
+ color: black;
601
+ }
602
+
603
+ .option-features ul {
604
+ list-style: none;
605
+ padding: 0;
606
+ margin-bottom: 1.5rem;
607
+ text-align: left;
608
+ }
609
+
610
+ .option-features li {
611
+ padding: 0.5rem 0;
612
+ padding-left: 1.5rem;
613
+ position: relative;
614
+ }
615
+
616
+ .option-features li:before {
617
+ content: "✓";
618
+ position: absolute;
619
+ left: 0;
620
+ color: #10b981;
621
+ font-weight: bold;
622
+ }
623
+
624
+ .privacy-notice {
625
+ background: #fef3c7;
626
+ border: 1px solid #fbbf24;
627
+ border-radius: 8px;
628
+ padding: 1rem;
629
+ margin-bottom: 1.5rem;
630
+ font-size: 0.9rem;
631
+ text-align: left;
632
+ }
633
+
634
+ .privacy-notice p {
635
+ margin: 0;
636
+ color: #92400e;
637
+ }
638
+
639
+ .choose-button {
640
+ display: inline-block;
641
+ padding: 0.75rem 2rem;
642
+ background: black;
643
+ color: white;
644
+ text-decoration: none;
645
+ border-radius: 8px;
646
+ font-weight: 500;
647
+ transition: all 0.3s ease;
648
+ border: 2px solid black;
649
+ margin: 0.5rem;
650
+ }
651
+
652
+ .choose-button:hover {
653
+ background: white;
654
+ color: black;
655
+ transform: translateY(-2px);
656
+ }
657
+
658
+ .anonymous-button {
659
+ background: #6b7280;
660
+ border-color: #6b7280;
661
+ }
662
+
663
+ .anonymous-button:hover {
664
+ background: white;
665
+ color: #6b7280;
666
+ }
667
+
668
+ .register-button {
669
+ background: #10b981;
670
+ border-color: #10b981;
671
+ }
672
+
673
+ .register-button:hover {
674
+ background: white;
675
+ color: #10b981;
676
+ }
677
+
678
+ .auth-buttons {
679
+ display: flex;
680
+ justify-content: center;
681
+ gap: 1rem;
682
+ flex-wrap: wrap;
683
+ }
684
+
685
+ .choose-footer {
686
+ margin-top: 3rem;
687
+ color: #666;
688
+ font-size: 0.9rem;
689
+ }
690
+
691
+ /* Anonymous User Styles */
692
+ .anonymous-user {
693
+ color: #6b7280;
694
+ font-style: italic;
695
+ }
696
+
697
+ .session-warning {
698
+ background: #fef3c7;
699
+ border-bottom: 2px solid #fbbf24;
700
+ padding: 0.75rem;
701
+ text-align: center;
702
+ color: #92400e;
703
+ }
704
+
705
+ .warning-content {
706
+ max-width: 800px;
707
+ margin: 0 auto;
708
+ }
709
+
710
+ .warning-content a {
711
+ color: #92400e;
712
+ font-weight: bold;
713
+ }
714
+
715
+ .session-expiry-warning {
716
+ position: fixed;
717
+ top: 0;
718
+ left: 0;
719
+ right: 0;
720
+ background: #ef4444;
721
+ color: white;
722
+ padding: 1rem;
723
+ text-align: center;
724
+ z-index: 10000;
725
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
726
+ }
727
+
728
+ .session-expiry-warning button {
729
+ background: white;
730
+ color: #ef4444;
731
+ border: none;
732
+ padding: 0.5rem 1rem;
733
+ margin: 0 0.5rem;
734
+ border-radius: 4px;
735
+ cursor: pointer;
736
+ font-weight: bold;
737
+ }
738
+
739
+ .session-expiry-warning button:hover {
740
+ background: #fee2e2;
741
+ }
742
+
743
+ /* System message styles */
744
+ .message.system .message-content {
745
+ background: #f3f4f6;
746
+ color: #4b5563;
747
+ border: 1px solid #d1d5db;
748
+ font-style: italic;
749
+ }
750
+
751
+ /* Mobile responsive for choice page */
752
+ @media (max-width: 768px) {
753
+ .choose-options {
754
+ grid-template-columns: 1fr;
755
+ }
756
+
757
+ .choose-title {
758
+ font-size: 2rem;
759
+ }
760
+
761
+ .auth-buttons {
762
+ flex-direction: column;
763
+ }
764
+
765
+ .choose-button {
766
+ width: 100%;
767
+ }
768
  }
static/js/chat.js CHANGED
@@ -23,6 +23,11 @@ class ChatApp {
23
  this.historyLoaded = false;
24
  this.sessionWarningShown = false;
25
 
 
 
 
 
 
26
  this.init();
27
  }
28
 
@@ -151,7 +156,11 @@ class ChatApp {
151
  this.handleSessionExpiry();
152
  this.showError('Your session has expired. Please log in again to continue.');
153
  } else if (error.message.includes('429')) {
154
- this.showError('You are sending messages too quickly. Please wait a moment and try again.');
 
 
 
 
155
  } else if (error.message.includes('400')) {
156
  this.showError('There was a problem with your message. Please check it and try again.');
157
  } else if (error.message.includes('500')) {
@@ -251,6 +260,14 @@ class ChatApp {
251
  }
252
 
253
  async loadChatHistory() {
 
 
 
 
 
 
 
 
254
  try {
255
  // Show loading state
256
  this.showHistoryLoading(true);
@@ -407,12 +424,25 @@ class ChatApp {
407
  // Create session expiry warning banner
408
  const warningDiv = document.createElement('div');
409
  warningDiv.className = 'session-expiry-warning';
410
- warningDiv.innerHTML = `
411
- Your session has expired. Please log in again to continue chatting.
412
- <button onclick="window.location.href='/login?next=' + encodeURIComponent(window.location.pathname)">
413
- Login Again
414
- </button>
415
- `;
 
 
 
 
 
 
 
 
 
 
 
 
 
416
 
417
  // Insert at top of page
418
  document.body.insertBefore(warningDiv, document.body.firstChild);
@@ -420,7 +450,7 @@ class ChatApp {
420
  // Disable chat interface
421
  this.messageInput.disabled = true;
422
  this.sendButton.disabled = true;
423
- this.messageInput.placeholder = 'Session expired - please log in again';
424
  }
425
 
426
  showSessionWarning(message) {
@@ -450,6 +480,12 @@ class ChatApp {
450
  }
451
  }, 5000);
452
  }
 
 
 
 
 
 
453
  }
454
 
455
  // Initialize the chat application when the page loads
 
23
  this.historyLoaded = false;
24
  this.sessionWarningShown = false;
25
 
26
+ // Check if anonymous mode
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
  }
33
 
 
156
  this.handleSessionExpiry();
157
  this.showError('Your session has expired. Please log in again to continue.');
158
  } else if (error.message.includes('429')) {
159
+ if (this.isAnonymous) {
160
+ this.showError(`Rate limit exceeded. Anonymous users are limited to ${this.anonymousRateLimit} messages per hour.`);
161
+ } else {
162
+ this.showError('You are sending messages too quickly. Please wait a moment and try again.');
163
+ }
164
  } else if (error.message.includes('400')) {
165
  this.showError('There was a problem with your message. Please check it and try again.');
166
  } else if (error.message.includes('500')) {
 
260
  }
261
 
262
  async loadChatHistory() {
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
+
271
  try {
272
  // Show loading state
273
  this.showHistoryLoading(true);
 
424
  // Create session expiry warning banner
425
  const warningDiv = document.createElement('div');
426
  warningDiv.className = 'session-expiry-warning';
427
+
428
+ if (this.isAnonymous) {
429
+ warningDiv.innerHTML = `
430
+ Your anonymous session has expired.
431
+ <button onclick="window.location.href='/anonymous'">
432
+ Start New Session
433
+ </button>
434
+ <button onclick="window.location.href='/register'">
435
+ Create Account
436
+ </button>
437
+ `;
438
+ } else {
439
+ warningDiv.innerHTML = `
440
+ Your session has expired. Please log in again to continue chatting.
441
+ <button onclick="window.location.href='/login?next=' + encodeURIComponent(window.location.pathname)">
442
+ Login Again
443
+ </button>
444
+ `;
445
+ }
446
 
447
  // Insert at top of page
448
  document.body.insertBefore(warningDiv, document.body.firstChild);
 
450
  // Disable chat interface
451
  this.messageInput.disabled = true;
452
  this.sendButton.disabled = true;
453
+ this.messageInput.placeholder = this.isAnonymous ? 'Session expired - start a new anonymous session' : 'Session expired - please log in again';
454
  }
455
 
456
  showSessionWarning(message) {
 
480
  }
481
  }, 5000);
482
  }
483
+
484
+ showAnonymousWelcome() {
485
+ // Show welcome message for anonymous users
486
+ const welcomeMessage = `Welcome to Anonymous Chat! You can send up to ${this.anonymousRateLimit} messages per hour. Your chat history is temporary and will be lost when you close your browser.`;
487
+ this.addMessage(welcomeMessage, 'system', false);
488
+ }
489
  }
490
 
491
  // Initialize the chat application when the page loads
task_list.md CHANGED
@@ -122,4 +122,143 @@ Transform the current Gradio chatbot template into a web app that uses the API "
122
  - [x] UI is minimal and basic as requested
123
  - [x] Chat functionality works with conversation history
124
  - [x] App can be deployed as a Hugging Face space
125
- - [x] Error handling is implemented for API failures
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  - [x] UI is minimal and basic as requested
123
  - [x] Chat functionality works with conversation history
124
  - [x] App can be deployed as a Hugging Face space
125
+ - [x] Error handling is implemented for API failures
126
+
127
+ ---
128
+
129
+ # Anonymous Chat Access Implementation Tasks
130
+
131
+ ## Overview
132
+ Add anonymous chat functionality alongside existing authenticated access, allowing users to chat without creating an account while maintaining the option for registered users.
133
+
134
+ ## Task List
135
+
136
+ ### Phase 1: Backend Infrastructure
137
+
138
+ - [x] **Task 1: Update config.py with anonymous user settings**
139
+ - Add ANONYMOUS_ENABLED flag
140
+ - Add ANONYMOUS_RATE_LIMIT (messages per hour)
141
+ - Add ANONYMOUS_SESSION_TIMEOUT
142
+ - Add ANONYMOUS_MAX_MESSAGE_LENGTH
143
+ - **Status: COMPLETED**
144
+ - **Assignee: Backend Coder Agent**
145
+
146
+ - [x] **Task 2: Create anonymous session management functions in app.py**
147
+ - Generate anonymous user IDs (e.g., anon_<hash>)
148
+ - Implement session-based chat history storage
149
+ - Add session validation functions
150
+ - **Status: COMPLETED**
151
+ - **Assignee: Backend Coder Agent**
152
+
153
+ - [x] **Task 3: Update app.py index route to show choice page**
154
+ - Modify `/` route to show choice instead of redirect
155
+ - Handle authenticated user redirect
156
+ - **Status: COMPLETED**
157
+ - **Assignee: Backend Coder Agent**
158
+
159
+ - [x] **Task 4: Add /choose route for access mode selection**
160
+ - Create route handler
161
+ - Implement GET method for displaying choice page
162
+ - **Status: COMPLETED**
163
+ - **Assignee: Backend Coder Agent**
164
+
165
+ - [x] **Task 5: Add /anonymous route for anonymous chat interface**
166
+ - Create route for anonymous chat
167
+ - Set up anonymous session
168
+ - Render chat template with anonymous mode
169
+ - **Status: COMPLETED**
170
+ - **Assignee: Backend Coder Agent**
171
+
172
+ - [x] **Task 6: Update /api/chat endpoint to handle anonymous users**
173
+ - Detect anonymous vs authenticated users
174
+ - Handle session-based chat history for anonymous
175
+ - Skip MongoDB saves for anonymous users
176
+ - Implement different rate limiting
177
+ - **Status: COMPLETED**
178
+ - **Assignee: Backend Coder Agent**
179
+
180
+ - [x] **Task 7: Implement rate limiting for anonymous users**
181
+ - Track message count per session
182
+ - Use IP + session combo for tracking
183
+ - Return 429 errors when exceeded
184
+ - **Status: COMPLETED**
185
+ - **Assignee: Backend Coder Agent**
186
+
187
+ ### Phase 2: Frontend Updates
188
+
189
+ - [x] **Task 8: Create choose.html template**
190
+ - Design landing page with two options
191
+ - "Chat Anonymously" button
192
+ - "Login/Register" section
193
+ - Include privacy notice
194
+ - **Status: COMPLETED**
195
+ - **Assignee: Frontend Coder Agent**
196
+
197
+ - [x] **Task 9: Update base.html to handle anonymous users**
198
+ - Conditional navigation for anonymous users
199
+ - Show "Anonymous User" instead of email
200
+ - Different logout/exit behavior
201
+ - **Status: COMPLETED**
202
+ - **Assignee: Frontend Coder Agent**
203
+
204
+ - [x] **Task 10: Update index.html for anonymous mode**
205
+ - Add session warning banner
206
+ - Include "Create Account" prompt
207
+ - Display anonymous user indicator
208
+ - **Status: COMPLETED**
209
+ - **Assignee: Frontend Coder Agent**
210
+
211
+ - [x] **Task 11: Update chat.js for anonymous mode support**
212
+ - Detect anonymous mode from page data
213
+ - Handle session expiry warnings
214
+ - Modify API calls for anonymous users
215
+ - Add upgrade prompts
216
+ - **Status: COMPLETED**
217
+ - **Assignee: Frontend Coder Agent**
218
+
219
+ - [x] **Task 12: Add CSS styles for choice page**
220
+ - Style choice buttons
221
+ - Add anonymous mode indicators
222
+ - Style session warnings
223
+ - **Status: COMPLETED**
224
+ - **Assignee: Frontend Coder Agent**
225
+
226
+ ### Phase 3: Testing & Documentation
227
+
228
+ - [ ] **Task 13: Test anonymous chat functionality**
229
+ - Test anonymous chat flow
230
+ - Verify rate limiting
231
+ - Test session expiry
232
+ - Test switching between modes
233
+ - **Status: PENDING**
234
+ - **Assignee: Testing Agent**
235
+
236
+ - [ ] **Task 14: Update CLAUDE.md with anonymous feature**
237
+ - Document new architecture
238
+ - Add anonymous mode details
239
+ - Update configuration section
240
+ - **Status: PENDING**
241
+ - **Assignee: Documentation Agent**
242
+
243
+ - [ ] **Task 15: Update README.md**
244
+ - Add anonymous mode to features
245
+ - Document usage instructions
246
+ - Add configuration details
247
+ - **Status: PENDING**
248
+ - **Assignee: Documentation Agent**
249
+
250
+ ## Implementation Order
251
+ 1. Backend configuration (Tasks 1-2)
252
+ 2. Core routes (Tasks 3-5)
253
+ 3. API updates (Tasks 6-7)
254
+ 4. Frontend templates (Tasks 8-10)
255
+ 5. JavaScript updates (Task 11)
256
+ 6. Styling (Task 12)
257
+ 7. Testing (Task 13)
258
+ 8. Documentation (Tasks 14-15)
259
+
260
+ ## Notes
261
+ - Maintain backward compatibility with existing authenticated flow
262
+ - Anonymous sessions are temporary and not persisted to database
263
+ - Stricter rate limits for anonymous users to prevent abuse
264
+ - Clear privacy notices about session-only storage
templates/base.html CHANGED
@@ -27,6 +27,10 @@
27
  {% endif %}
28
  <button type="submit" class="nav-button logout-button">Logout</button>
29
  </form>
 
 
 
 
30
  {% else %}
31
  <a href="{{ url_for('login') }}" class="nav-button">Login</a>
32
  <a href="{{ url_for('register') }}" class="nav-button">Register</a>
 
27
  {% endif %}
28
  <button type="submit" class="nav-button logout-button">Logout</button>
29
  </form>
30
+ {% elif is_anonymous %}
31
+ <span class="user-info anonymous-user">Anonymous User</span>
32
+ <a href="{{ url_for('login') }}" class="nav-button">Login</a>
33
+ <a href="{{ url_for('register') }}" class="nav-button">Create Account</a>
34
  {% else %}
35
  <a href="{{ url_for('login') }}" class="nav-button">Login</a>
36
  <a href="{{ url_for('register') }}" class="nav-button">Register</a>
templates/choose.html ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Choose Access Mode - Chatty{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="choose-container">
7
+ <div class="choose-wrapper">
8
+ <h1 class="choose-title">Welcome to Chatty</h1>
9
+ <p class="choose-subtitle">Ethics Chat Application</p>
10
+
11
+ <div class="choose-options">
12
+ <!-- Anonymous Access Option -->
13
+ <div class="choose-option anonymous-option">
14
+ <h2>Chat Anonymously</h2>
15
+ <div class="option-features">
16
+ <ul>
17
+ <li>No registration required</li>
18
+ <li>Start chatting immediately</li>
19
+ <li>Session-based conversation</li>
20
+ <li>Limited to {{ config.ANONYMOUS_RATE_LIMIT }} messages per hour</li>
21
+ </ul>
22
+ </div>
23
+ <div class="privacy-notice">
24
+ <p><strong>Privacy Notice:</strong> Your chat history will be stored temporarily in your browser session and will be lost when you close the browser or after {{ config.ANONYMOUS_SESSION_TIMEOUT // 3600 }} hour(s) of inactivity.</p>
25
+ </div>
26
+ <a href="{{ url_for('anonymous') }}" class="choose-button anonymous-button">
27
+ Start Anonymous Chat
28
+ </a>
29
+ </div>
30
+
31
+ <!-- Authenticated Access Option -->
32
+ <div class="choose-option auth-option">
33
+ <h2>Login or Register</h2>
34
+ <div class="option-features">
35
+ <ul>
36
+ <li>Save your chat history permanently</li>
37
+ <li>Access conversations from any device</li>
38
+ <li>No message limits</li>
39
+ <li>Personalized experience</li>
40
+ </ul>
41
+ </div>
42
+ <div class="auth-buttons">
43
+ <a href="{{ url_for('login') }}" class="choose-button login-button">
44
+ Login
45
+ </a>
46
+ <a href="{{ url_for('register') }}" class="choose-button register-button">
47
+ Create Account
48
+ </a>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="choose-footer">
54
+ <p>By using this service, you agree to our ethical guidelines and responsible AI usage.</p>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ {% endblock %}
59
+
60
+ {% block scripts %}
61
+ <script>
62
+ document.addEventListener('DOMContentLoaded', function() {
63
+ // Add hover effects
64
+ const options = document.querySelectorAll('.choose-option');
65
+ options.forEach(option => {
66
+ option.addEventListener('mouseenter', function() {
67
+ this.style.transform = 'translateY(-5px)';
68
+ });
69
+ option.addEventListener('mouseleave', function() {
70
+ this.style.transform = 'translateY(0)';
71
+ });
72
+ });
73
+ });
74
+ </script>
75
+ {% endblock %}
templates/index.html CHANGED
@@ -4,6 +4,15 @@
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>
@@ -33,5 +42,13 @@
33
  {% endblock %}
34
 
35
  {% block scripts %}
 
 
 
 
 
 
 
 
36
  <script src="{{ url_for('static', filename='js/chat.js') }}"></script>
37
  {% endblock %}
 
4
 
5
  {% block content %}
6
  <div class="chat-container">
7
+ {% if is_anonymous %}
8
+ <div class="session-warning">
9
+ <div class="warning-content">
10
+ <strong>Anonymous Session:</strong> Your chat history is temporary and will be lost when you close your browser.
11
+ <a href="{{ url_for('register') }}">Create an account</a> to save your conversations permanently.
12
+ </div>
13
+ </div>
14
+ {% endif %}
15
+
16
  <div class="chat-messages" id="chatMessages">
17
  <!-- Messages will be dynamically added here -->
18
  </div>
 
42
  {% endblock %}
43
 
44
  {% block scripts %}
45
+ <script>
46
+ // Pass server-side variables to JavaScript
47
+ window.chatConfig = {
48
+ isAnonymous: {{ 'true' if is_anonymous else 'false' }},
49
+ anonymousRateLimit: {{ config.ANONYMOUS_RATE_LIMIT if is_anonymous else 0 }},
50
+ anonymousId: "{{ session.get('anonymous_id', '') if is_anonymous else '' }}"
51
+ };
52
+ </script>
53
  <script src="{{ url_for('static', filename='js/chat.js') }}"></script>
54
  {% endblock %}