[Feature] 添加 会话管理/历史会话/下载 api (#4093)

pull/4095/head
xinwen 2020-06-10 17:34:56 +08:00 committed by GitHub
parent 0452d53c3f
commit 9ea98bf2b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 3 deletions

View File

@ -1 +1,13 @@
from .csv import * from rest_framework import renderers
from .csv import *
class PassthroughRenderer(renderers.BaseRenderer):
"""
Return data as-is. View should supply a Response.
"""
media_type = ''
format = ''
def render(self, data, accepted_media_type=None, renderer_context=None):
return data

View File

@ -1,15 +1,25 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.utils.translation import ugettext as _ import os
import tarfile
from django.shortcuts import get_object_or_404, reverse from django.shortcuts import get_object_or_404, reverse
from django.utils.translation import ugettext as _
from django.utils.encoding import escape_uri_path
from django.http import FileResponse, HttpResponse
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from rest_framework import viewsets, views from rest_framework import viewsets, views
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.decorators import action
from common.utils import model_to_json
from .. import utils
from common.const.http import GET
from common.utils import is_uuid, get_logger, get_object_or_none from common.utils import is_uuid, get_logger, get_object_or_none
from common.mixins.api import AsyncApiMixin from common.mixins.api import AsyncApiMixin
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsAppUser from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsAppUser
from common.drf.filters import DatetimeRangeFilter from common.drf.filters import DatetimeRangeFilter
from common.drf.renders import PassthroughRenderer
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from orgs.utils import tmp_to_root_org, tmp_to_org from orgs.utils import tmp_to_root_org, tmp_to_org
from users.models import User from users.models import User
@ -41,6 +51,44 @@ class SessionViewSet(OrgBulkModelViewSet):
] ]
extra_filter_backends = [DatetimeRangeFilter] extra_filter_backends = [DatetimeRangeFilter]
@staticmethod
def prepare_offline_file(session, local_path):
replay_path = default_storage.path(local_path)
current_dir = os.getcwd()
dir_path = os.path.dirname(replay_path)
replay_filename = os.path.basename(replay_path)
meta_filename = '{}.json'.format(session.id)
offline_filename = '{}.tar'.format(session.id)
os.chdir(dir_path)
with open(meta_filename, 'wt') as f:
f.write(model_to_json(session))
with tarfile.open(offline_filename, 'w') as f:
f.add(replay_filename)
f.add(meta_filename)
file = open(offline_filename, 'rb')
os.chdir(current_dir)
return file
@action(methods=[GET], detail=True, renderer_classes=(PassthroughRenderer,), url_path='replay/download', url_name='replay-download')
def download(self, request, *args, **kwargs):
session = self.get_object()
local_path, url = utils.get_session_replay_url(session)
if local_path is None:
error = url
return HttpResponse(error)
file = self.prepare_offline_file(session, local_path)
response = FileResponse(file)
response['Content-Type'] = 'application/octet-stream'
# 这里要注意哦网上查到的方法都是response['Content-Disposition']='attachment;filename="filename.py"',
# 但是如果文件名是英文名没问题如果文件名包含中文下载下来的文件名会被改为url中的path。
filename = escape_uri_path('{}.tar'.format(session.id))
disposition = "attachment; filename*=UTF-8''{}".format(filename)
response["Content-Disposition"] = disposition
return response
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset) queryset = super().filter_queryset(queryset)
# 解决guacamole更新session时并发导致幽灵会话的问题 # 解决guacamole更新session时并发导致幽灵会话的问题
@ -95,7 +143,7 @@ class SessionReplayViewSet(AsyncApiMixin, viewsets.ViewSet):
if session.protocol in ('rdp', 'vnc'): if session.protocol in ('rdp', 'vnc'):
tp = 'guacamole' tp = 'guacamole'
download_url = reverse('terminal:session-replay-download', kwargs={'pk': session.id}) download_url = reverse('api-terminal:session-replay-download', kwargs={'pk': session.id})
data = { data = {
'type': tp, 'src': url, 'type': tp, 'src': url,
'user': session.user, 'asset': session.asset, 'user': session.user, 'asset': session.asset,