mirror of https://github.com/jumpserver/jumpserver
perf: basic finished
parent
c7f91e14e5
commit
cf241bf9d6
|
@ -43,6 +43,7 @@ api_v1 = resource_api + [
|
|||
app_view_patterns = [
|
||||
path('auth/', include('authentication.urls.view_urls'), name='auth'),
|
||||
path('ops/', include('ops.urls.view_urls'), name='ops'),
|
||||
path('reports/', include('reports.urls.view_urls'), name='reports'),
|
||||
path('tickets/', include('tickets.urls.view_urls'), name='tickets'),
|
||||
path('common/', include('common.urls.view_urls'), name='common'),
|
||||
re_path(r'flower/(?P<path>.*)', views.celery_flower_view, name='flower-view'),
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
from __future__ import unicode_literals
|
||||
from django.urls import path
|
||||
|
||||
from .. import views
|
||||
|
||||
__all__ = ["urlpatterns"]
|
||||
|
||||
app_name = "reports"
|
||||
|
||||
urlpatterns = [
|
||||
# Resource Task url
|
||||
path('export-pdf/', views.ExportPdfView.as_view(), name='export-pdf'),
|
||||
path('send-mail/', views.SendMailView.as_view(), name='send-mail'),
|
||||
]
|
|
@ -1,3 +1,130 @@
|
|||
from django.shortcuts import render
|
||||
import io
|
||||
import urllib.parse
|
||||
from urllib.parse import urlparse
|
||||
|
||||
# Create your views here.
|
||||
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": "邮件发送成功"})
|
||||
|
|
|
@ -152,6 +152,8 @@ dependencies = [
|
|||
'botocore==1.31.9',
|
||||
's3transfer==0.6.1',
|
||||
'xmlsec==1.3.14',
|
||||
'playwright==4.12.2',
|
||||
'pdf2image==1.17.0'
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
|
|
@ -5,16 +5,13 @@ PROJECT_DIR=$(dirname "$BASE_DIR")
|
|||
echo "1. 安装依赖"
|
||||
brew install libtiff libjpeg webp little-cms2 openssl gettext git \
|
||||
git-lfs libxml2 libxmlsec1 pkg-config postgresql freetds openssl \
|
||||
libffi freerdp
|
||||
libffi freerdp poppler
|
||||
pip install daphne==4.0.0 channels channels-redis
|
||||
|
||||
echo "2. 下载 IP 数据库"
|
||||
ip_db_path="${PROJECT_DIR}/apps/common/utils/geoip/GeoLite2-City.mmdb"
|
||||
wget "https://download.jumpserver.org/files/GeoLite2-City.mmdb" -O "${ip_db_path}"
|
||||
|
||||
echo "3. 安装依赖的插件"
|
||||
git lfs install
|
||||
|
||||
if ! uname -a | grep 'ARM64' &> /dev/null;then
|
||||
exit 0
|
||||
fi
|
||||
|
|
Loading…
Reference in New Issue