jumpserver/apps/reports/views.py

131 lines
4.7 KiB
Python

import io
import urllib.parse
from urllib.parse import urlparse
from django.http import JsonResponse
from django.utils import timezone
from django.conf import settings
from django.http import FileResponse, HttpResponseBadRequest
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from playwright.sync_api import sync_playwright
from django.core.mail import EmailMultiAlternatives
from pdf2image import convert_from_bytes
import base64
from io import BytesIO
charts_map = {
"UserActivity": {
"title": "用户活动",
"path": "/ui/#/reports/users/user-activity"
}
}
def export_chart_to_pdf(chart_name, sessionid, request=None):
chart_info = charts_map.get(chart_name)
if not chart_info:
return None, None
if request:
url = request.build_absolute_uri(urllib.parse.unquote(chart_info['path']))
else:
url = urllib.parse.unquote(chart_info['path'])
if settings.DEBUG_DEV:
url = url.replace(":8080", ":9528")
print("Url: ", url)
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(viewport={"width": 1000, "height": 800})
# 设置 sessionid cookie
parsed_url = urlparse(url)
context.add_cookies([
{
'name': settings.SESSION_COOKIE_NAME,
'value': sessionid,
'domain': parsed_url.hostname,
'path': '/',
'httpOnly': True,
'secure': False, # 如有 https 可改 True
}
])
page = context.new_page()
try:
page.goto(url, wait_until='networkidle')
pdf_bytes = page.pdf(format="A4", landscape=True,
margin={"top": "35px", "bottom": "30px", "left": "20px", "right": "20px"})
except Exception as e:
print(f'Playwright error: {e}')
pdf_bytes = None
finally:
browser.close()
return pdf_bytes, chart_info['title']
@method_decorator(csrf_exempt, name='dispatch')
class ExportPdfView(View):
def get(self, request):
chart_name = request.GET.get('chart')
return self._handle_export(request, chart_name)
def post(self, request):
chart_name = request.POST.get('chart')
return self._handle_export(request, chart_name)
def _handle_export(self, request, chart_name):
if not chart_name:
return HttpResponseBadRequest('Missing chart parameter')
sessionid = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
if not sessionid:
return HttpResponseBadRequest('No sessionid found in cookies')
pdf_bytes, title = export_chart_to_pdf(chart_name, sessionid, request=request)
if not pdf_bytes:
return HttpResponseBadRequest('Failed to generate PDF')
filename = f"{title}-{timezone.now().strftime('%Y%m%d%H%M%S')}.pdf"
response = FileResponse(io.BytesIO(pdf_bytes), as_attachment=True, filename=filename,
content_type='application/pdf')
return response
class SendMailView(View):
def get(self, request):
chart_name = request.GET.get('chart')
email = "ibuler@qq.com"
if not chart_name or not email:
return HttpResponseBadRequest('Missing chart or email parameter')
sessionid = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
if not sessionid:
return HttpResponseBadRequest('No sessionid found in cookies')
# 1. 生成 PDF
pdf_bytes, title = export_chart_to_pdf(chart_name, sessionid, request=request)
if not pdf_bytes:
return HttpResponseBadRequest('Failed to generate PDF')
# 2. PDF 转图片
images = convert_from_bytes(pdf_bytes, dpi=200)
# 3. 图片转 base64
img_tags = []
for img in images:
buffer = BytesIO()
img.save(buffer, format="PNG")
encoded = base64.b64encode(buffer.getvalue()).decode("utf-8")
img_tags.append(f'<img src="data:image/png;base64,{encoded}" style="width:100%; max-width:800px;" />')
html_content = "<br/>".join(img_tags)
# 4. 发送邮件
subject = f"{title} 报表"
from_email = settings.EMAIL_HOST_USER
to = [email]
msg = EmailMultiAlternatives(subject, '', from_email, to)
msg.attach_alternative(html_content, "text/html")
filename = f"{title}-{timezone.now().strftime('%Y%m%d%H%M%S')}.pdf"
msg.attach(filename, pdf_bytes, "application/pdf")
msg.send()
return JsonResponse({"message": "邮件发送成功"})