diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 9569722..e97dc83 100644 Binary files a/routes/__pycache__/main.cpython-313.pyc and b/routes/__pycache__/main.cpython-313.pyc differ diff --git a/routes/main.py b/routes/main.py index ff28ce8..bc8c8ce 100644 --- a/routes/main.py +++ b/routes/main.py @@ -503,6 +503,93 @@ def init_routes(main_bp): db.session.rollback() return jsonify({'error': str(e)}), 400 + @main_bp.route('/instances//detail') + @login_required + @require_password_change + def instance_detail(instance_id): + if not os.environ.get('MASTER', 'false').lower() == 'true': + flash('This page is only available in master instances.', 'error') + return redirect(url_for('main.dashboard')) + + instance = Instance.query.get_or_404(instance_id) + + # Check instance status + status_info = check_instance_status(instance) + instance.status = status_info['status'] + instance.status_details = status_info['details'] + + # Fetch company name from instance settings + try: + if instance.connection_token: + # First get JWT token + jwt_response = requests.post( + f"{instance.main_url.rstrip('/')}/api/admin/management-token", + headers={ + 'X-API-Key': instance.connection_token, + 'Accept': 'application/json' + }, + timeout=5 + ) + if jwt_response.status_code == 200: + jwt_data = jwt_response.json() + jwt_token = jwt_data.get('token') + + if jwt_token: + # Then fetch settings with JWT token + response = requests.get( + f"{instance.main_url.rstrip('/')}/api/admin/settings", + headers={ + 'Authorization': f'Bearer {jwt_token}', + 'Accept': 'application/json' + }, + timeout=5 + ) + if response.status_code == 200: + data = response.json() + if 'company_name' in data: + instance.company = data['company_name'] + db.session.commit() + except Exception as e: + current_app.logger.error(f"Error fetching instance settings: {str(e)}") + + return render_template('main/instance_detail.html', instance=instance) + + @main_bp.route('/instances//auth-status') + @login_required + @require_password_change + def instance_auth_status(instance_id): + if not os.environ.get('MASTER', 'false').lower() == 'true': + return jsonify({'error': 'Unauthorized'}), 403 + + instance = Instance.query.get_or_404(instance_id) + + # Check if instance has a connection token + has_token = bool(instance.connection_token) + + # If there's a token, verify it's still valid + is_valid = False + if has_token: + try: + # Try to get a JWT token using the connection token + response = requests.post( + f"{instance.main_url.rstrip('/')}/api/admin/management-token", + headers={ + 'X-API-Key': instance.connection_token, + 'Accept': 'application/json' + }, + timeout=5 + ) + is_valid = response.status_code == 200 + except Exception as e: + current_app.logger.error(f"Error verifying token: {str(e)}") + is_valid = False + + return jsonify({ + 'authenticated': has_token and is_valid, + 'has_token': has_token, + 'is_valid': is_valid + }) + UPLOAD_FOLDER = '/app/uploads/profile_pics' if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) @@ -1401,227 +1488,4 @@ def init_routes(main_bp): headers={ 'Content-Disposition': f'attachment; filename=event_log_{datetime.utcnow().strftime("%Y%m%d_%H%M%S")}.csv' } - ) - - @main_bp.route('/settings/email-templates/', methods=['PUT']) - @login_required - def update_email_template(template_id): - if not current_user.is_admin: - return jsonify({'error': 'Unauthorized'}), 403 - - template = EmailTemplate.query.get_or_404(template_id) - - data = request.get_json() - if not data: - return jsonify({'error': 'No data provided'}), 400 - - template.subject = data.get('subject', template.subject) - template.body = data.get('body', template.body) - - try: - db.session.commit() - - # Log the template update - log_event( - event_type='settings_update', - details={ - 'user_id': current_user.id, - 'user_name': f"{current_user.username} {current_user.last_name}", - 'update_type': 'email_template', - 'template_id': template.id, - 'template_name': template.name, - 'changes': { - 'subject': template.subject, - 'body': template.body - } - } - ) - db.session.commit() - - return jsonify({ - 'message': 'Template updated successfully', - 'template': { - 'id': template.id, - 'name': template.name, - 'subject': template.subject, - 'body': template.body - } - }) - except Exception as e: - db.session.rollback() - return jsonify({'error': str(e)}), 500 - - @main_bp.route('/settings/mails') - @login_required - def mails(): - if not current_user.is_admin: - flash('You do not have permission to access settings.', 'error') - return redirect(url_for('main.index')) - - # Get filter parameters - status = request.args.get('status', '') - date_range = request.args.get('date_range', '7d') - user_id = request.args.get('user_id', '') - template_id = request.args.get('template_id', '') - page = request.args.get('page', 1, type=int) - per_page = 10 - - # Build query - query = Mail.query - - # Apply filters - if status: - query = query.filter_by(status=status) - if user_id: - query = query.filter_by(recipient=user_id) - if template_id: - query = query.filter_by(template_id=template_id) - if date_range: - if date_range == '24h': - cutoff = datetime.utcnow() - timedelta(hours=24) - elif date_range == '7d': - cutoff = datetime.utcnow() - timedelta(days=7) - elif date_range == '30d': - cutoff = datetime.utcnow() - timedelta(days=30) - else: - cutoff = None - if cutoff: - query = query.filter(Mail.created_at >= cutoff) - - # Get paginated results - mails = query.order_by(Mail.created_at.desc()).paginate(page=page, per_page=per_page) - total_pages = mails.pages - current_page = mails.page - - # Get all users for the filter dropdown - users = User.query.order_by(User.username).all() - - # Get all email templates - email_templates = EmailTemplate.query.filter_by(is_active=True).all() - - # Check if this is an AJAX request - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': - return render_template('settings/tabs/mails.html', - mails=mails, - total_pages=total_pages, - current_page=page, - status=status, - date_range=date_range, - user_id=user_id, - template_id=template_id, - users=users, - email_templates=email_templates, - csrf_token=generate_csrf()) - - # For full page requests, render the full settings page - site_settings = SiteSettings.get_settings() - company_form = CompanySettingsForm() - - return render_template('settings/settings.html', - primary_color=site_settings.primary_color, - secondary_color=site_settings.secondary_color, - active_tab='mails', - site_settings=site_settings, - mails=mails, - total_pages=total_pages, - current_page=page, - status=status, - date_range=date_range, - user_id=user_id, - template_id=template_id, - users=users, - email_templates=email_templates, - form=company_form, - csrf_token=generate_csrf()) - - @main_bp.route('/settings/mails/') - @login_required - def get_mail_details(mail_id): - if not current_user.is_admin: - return jsonify({'error': 'Unauthorized'}), 403 - - mail = Mail.query.get_or_404(mail_id) - return jsonify({ - 'id': mail.id, - 'recipient': mail.recipient, - 'subject': mail.subject, - 'body': mail.body, - 'status': mail.status, - 'created_at': mail.created_at.isoformat(), - 'sent_at': mail.sent_at.isoformat() if mail.sent_at else None, - 'template': { - 'id': mail.template.id, - 'name': mail.template.name - } if mail.template else None - }) - - @main_bp.route('/settings/mails/download') - @login_required - def download_mails(): - if not current_user.is_admin: - flash('Only administrators can download mail logs.', 'error') - return redirect(url_for('main.dashboard')) - - # Get filter parameters - status = request.args.get('status') - date_range = request.args.get('date_range', '7d') - user_id = request.args.get('user_id') - - # Calculate date range - end_date = datetime.utcnow() - if date_range == '24h': - start_date = end_date - timedelta(days=1) - elif date_range == '7d': - start_date = end_date - timedelta(days=7) - elif date_range == '30d': - start_date = end_date - timedelta(days=30) - else: - start_date = None - - # Build query - query = Mail.query - - if status: - query = query.filter_by(status=status) - if start_date: - query = query.filter(Mail.created_at >= start_date) - if user_id: - query = query.filter(Mail.recipient == User.query.get(user_id).email) - - # Get all mails - mails = query.order_by(Mail.created_at.desc()).all() - - # Create CSV - output = StringIO() - writer = csv.writer(output) - - # Write header - writer.writerow([ - 'Created At', - 'Recipient', - 'Subject', - 'Status', - 'Template', - 'Sent At' - ]) - - # Write data - for mail in mails: - writer.writerow([ - mail.created_at.strftime('%Y-%m-%d %H:%M:%S'), - mail.recipient, - mail.subject, - mail.status, - mail.template.name if mail.template else '-', - mail.sent_at.strftime('%Y-%m-%d %H:%M:%S') if mail.sent_at else '-' - ]) - - output.seek(0) - - return Response( - output, - mimetype='text/csv', - headers={ - 'Content-Disposition': f'attachment; filename=mail_log_{datetime.utcnow().strftime("%Y%m%d_%H%M%S")}.csv' - } ) \ No newline at end of file diff --git a/templates/main/instance_detail.html b/templates/main/instance_detail.html new file mode 100644 index 0000000..7c6c964 --- /dev/null +++ b/templates/main/instance_detail.html @@ -0,0 +1,173 @@ +{% extends "common/base.html" %} +{% from "components/header.html" import header %} + +{% block title %}{{ instance.name }} - DocuPulse{% endblock %} + +{% block content %} +{{ header( + title="Instance Details", + description=instance.name + " for " + instance.company, + icon="fa-server", + buttons=[ + { + 'text': 'Back to Instances', + 'url': '/instances', + 'icon': 'fa-arrow-left', + 'class': 'btn-secondary' + } + ] +) }} + +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
Activity Status
+

+ + {{ instance.status|title }} + + {% if instance.status_details %} + {{ instance.status_details }} + {% endif %} +

+
+
+
+
+
+ + +
+
+
+
+
+
+ +
+
+
+
Authentication Status
+

+ + {{ 'Authenticated' if instance.connection_token else 'Not Authenticated' }} + + {% if not instance.connection_token %} + This instance needs to be authenticated + {% endif %} +

+
+
+
+
+
+
+ + +
+
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/templates/main/instances.html b/templates/main/instances.html index 294677b..dc68d1e 100644 --- a/templates/main/instances.html +++ b/templates/main/instances.html @@ -92,6 +92,9 @@ +