mirror of https://github.com/jumpserver/jumpserver
Merge branch 'dev' of github.com:jumpserver/jumpserver into dev
commit
068a280350
|
@ -15,8 +15,6 @@ class BaseForm(forms.Form):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
value = getattr(settings, name, None)
|
value = getattr(settings, name, None)
|
||||||
# django_value = getattr(settings, name) if hasattr(settings, name) else None
|
|
||||||
|
|
||||||
if value is None: # and django_value is None:
|
if value is None: # and django_value is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -24,8 +22,6 @@ class BaseForm(forms.Form):
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
value = json.dumps(value)
|
value = json.dumps(value)
|
||||||
initial_value = value
|
initial_value = value
|
||||||
# elif django_value is False or django_value:
|
|
||||||
# initial_value = django_value
|
|
||||||
else:
|
else:
|
||||||
initial_value = ''
|
initial_value = ''
|
||||||
field.initial = initial_value
|
field.initial = initial_value
|
||||||
|
@ -157,6 +153,11 @@ class TerminalSettingForm(BaseForm):
|
||||||
TERMINAL_ASSET_LIST_PAGE_SIZE = forms.ChoiceField(
|
TERMINAL_ASSET_LIST_PAGE_SIZE = forms.ChoiceField(
|
||||||
choices=PAGE_SIZE_CHOICES, initial='auto', label=_("List page size"),
|
choices=PAGE_SIZE_CHOICES, initial='auto', label=_("List page size"),
|
||||||
)
|
)
|
||||||
|
TERMINAL_SESSION_KEEP_DURATION = forms.IntegerField(
|
||||||
|
label=_("Session keep duration"),
|
||||||
|
help_text=_("Units: days, Session, record, command will be delete "
|
||||||
|
"if more than duration, only in database")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TerminalCommandStorage(BaseForm):
|
class TerminalCommandStorage(BaseForm):
|
||||||
|
|
|
@ -26,21 +26,20 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs):
|
||||||
def refresh_all_settings_on_django_ready(sender, **kwargs):
|
def refresh_all_settings_on_django_ready(sender, **kwargs):
|
||||||
logger.debug("Receive django ready signal")
|
logger.debug("Receive django ready signal")
|
||||||
logger.debug(" - fresh all settings")
|
logger.debug(" - fresh all settings")
|
||||||
CACHE_KEY_PREFIX = '_SETTING_'
|
cache_key_prefix = '_SETTING_'
|
||||||
|
|
||||||
def monkey_patch_getattr(self, name):
|
def monkey_patch_getattr(self, name):
|
||||||
key = CACHE_KEY_PREFIX + name
|
key = cache_key_prefix + name
|
||||||
cached = cache.get(key)
|
cached = cache.get(key)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
return cached
|
return cached
|
||||||
if self._wrapped is empty:
|
if self._wrapped is empty:
|
||||||
self._setup(name)
|
self._setup(name)
|
||||||
val = getattr(self._wrapped, name)
|
val = getattr(self._wrapped, name)
|
||||||
# self.__dict__[name] = val # Never set it
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def monkey_patch_setattr(self, name, value):
|
def monkey_patch_setattr(self, name, value):
|
||||||
key = CACHE_KEY_PREFIX + name
|
key = cache_key_prefix + name
|
||||||
cache.set(key, value, None)
|
cache.set(key, value, None)
|
||||||
if name == '_wrapped':
|
if name == '_wrapped':
|
||||||
self.__dict__.clear()
|
self.__dict__.clear()
|
||||||
|
@ -51,7 +50,7 @@ def refresh_all_settings_on_django_ready(sender, **kwargs):
|
||||||
def monkey_patch_delattr(self, name):
|
def monkey_patch_delattr(self, name):
|
||||||
super(LazySettings, self).__delattr__(name)
|
super(LazySettings, self).__delattr__(name)
|
||||||
self.__dict__.pop(name, None)
|
self.__dict__.pop(name, None)
|
||||||
key = CACHE_KEY_PREFIX + name
|
key = cache_key_prefix + name
|
||||||
cache.delete(key)
|
cache.delete(key)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -318,6 +318,7 @@ defaults = {
|
||||||
'TERMINAL_HEARTBEAT_INTERVAL': 5,
|
'TERMINAL_HEARTBEAT_INTERVAL': 5,
|
||||||
'TERMINAL_ASSET_LIST_SORT_BY': 'hostname',
|
'TERMINAL_ASSET_LIST_SORT_BY': 'hostname',
|
||||||
'TERMINAL_ASSET_LIST_PAGE_SIZE': 'auto',
|
'TERMINAL_ASSET_LIST_PAGE_SIZE': 'auto',
|
||||||
|
'TERMINAL_SESSION_KEEP_DURATION': 9999,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -467,6 +467,7 @@ DEFAULT_TERMINAL_REPLAY_STORAGE = {
|
||||||
TERMINAL_REPLAY_STORAGE = {
|
TERMINAL_REPLAY_STORAGE = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SECURITY_MFA_AUTH = False
|
SECURITY_MFA_AUTH = False
|
||||||
SECURITY_LOGIN_LIMIT_COUNT = 7
|
SECURITY_LOGIN_LIMIT_COUNT = 7
|
||||||
SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute
|
SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute
|
||||||
|
@ -490,6 +491,7 @@ TERMINAL_PUBLIC_KEY_AUTH = CONFIG.TERMINAL_PUBLIC_KEY_AUTH
|
||||||
TERMINAL_HEARTBEAT_INTERVAL = CONFIG.TERMINAL_HEARTBEAT_INTERVAL
|
TERMINAL_HEARTBEAT_INTERVAL = CONFIG.TERMINAL_HEARTBEAT_INTERVAL
|
||||||
TERMINAL_ASSET_LIST_SORT_BY = CONFIG.TERMINAL_ASSET_LIST_SORT_BY
|
TERMINAL_ASSET_LIST_SORT_BY = CONFIG.TERMINAL_ASSET_LIST_SORT_BY
|
||||||
TERMINAL_ASSET_LIST_PAGE_SIZE = CONFIG.TERMINAL_ASSET_LIST_PAGE_SIZE
|
TERMINAL_ASSET_LIST_PAGE_SIZE = CONFIG.TERMINAL_ASSET_LIST_PAGE_SIZE
|
||||||
|
TERMINAL_SESSION_KEEP_DURATION = CONFIG.TERMINAL_SESSION_KEEP_DURATION
|
||||||
|
|
||||||
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
|
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
|
||||||
BOOTSTRAP3 = {
|
BOOTSTRAP3 = {
|
||||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Jumpserver 0.3.3\n"
|
"Project-Id-Version: Jumpserver 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-12-17 20:06+0800\n"
|
"POT-Creation-Date: 2018-12-18 10:13+0800\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
||||||
|
@ -1976,47 +1976,57 @@ msgstr "资产列表排序"
|
||||||
|
|
||||||
#: common/forms.py:158
|
#: common/forms.py:158
|
||||||
msgid "List page size"
|
msgid "List page size"
|
||||||
msgstr "资产列表页面大小"
|
msgstr "资产分页每页数量"
|
||||||
|
|
||||||
#: common/forms.py:170
|
#: common/forms.py:161
|
||||||
|
msgid "Session keep duration"
|
||||||
|
msgstr "会话保留时长"
|
||||||
|
|
||||||
|
#: common/forms.py:162
|
||||||
|
msgid ""
|
||||||
|
"Units: days, Session, record, command will be delete if more than duration, "
|
||||||
|
"only in database"
|
||||||
|
msgstr "单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不受影响)"
|
||||||
|
|
||||||
|
#: common/forms.py:175
|
||||||
msgid "MFA Secondary certification"
|
msgid "MFA Secondary certification"
|
||||||
msgstr "MFA 二次认证"
|
msgstr "MFA 二次认证"
|
||||||
|
|
||||||
#: common/forms.py:172
|
#: common/forms.py:177
|
||||||
msgid ""
|
msgid ""
|
||||||
"After opening, the user login must use MFA secondary authentication (valid "
|
"After opening, the user login must use MFA secondary authentication (valid "
|
||||||
"for all users, including administrators)"
|
"for all users, including administrators)"
|
||||||
msgstr "开启后,用户登录必须使用MFA二次认证(对所有用户有效,包括管理员)"
|
msgstr "开启后,用户登录必须使用MFA二次认证(对所有用户有效,包括管理员)"
|
||||||
|
|
||||||
#: common/forms.py:179
|
#: common/forms.py:184
|
||||||
msgid "Limit the number of login failures"
|
msgid "Limit the number of login failures"
|
||||||
msgstr "限制登录失败次数"
|
msgstr "限制登录失败次数"
|
||||||
|
|
||||||
#: common/forms.py:184
|
#: common/forms.py:189
|
||||||
msgid "No logon interval"
|
msgid "No logon interval"
|
||||||
msgstr "禁止登录时间间隔"
|
msgstr "禁止登录时间间隔"
|
||||||
|
|
||||||
#: common/forms.py:186
|
#: common/forms.py:191
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tip: (unit/minute) if the user has failed to log in for a limited number of "
|
"Tip: (unit/minute) if the user has failed to log in for a limited number of "
|
||||||
"times, no login is allowed during this time interval."
|
"times, no login is allowed during this time interval."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"提示:(单位:分)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
|
"提示:(单位:分)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
|
||||||
|
|
||||||
#: common/forms.py:193
|
#: common/forms.py:198
|
||||||
msgid "Connection max idle time"
|
msgid "Connection max idle time"
|
||||||
msgstr "SSH最大空闲时间"
|
msgstr "SSH最大空闲时间"
|
||||||
|
|
||||||
#: common/forms.py:195
|
#: common/forms.py:200
|
||||||
msgid ""
|
msgid ""
|
||||||
"If idle time more than it, disconnect connection(only ssh now) Unit: minute"
|
"If idle time more than it, disconnect connection(only ssh now) Unit: minute"
|
||||||
msgstr "提示:(单位:分)如果超过该配置没有操作,连接会被断开(仅ssh)"
|
msgstr "提示:(单位:分)如果超过该配置没有操作,连接会被断开(仅ssh)"
|
||||||
|
|
||||||
#: common/forms.py:201
|
#: common/forms.py:206
|
||||||
msgid "Password expiration time"
|
msgid "Password expiration time"
|
||||||
msgstr "密码过期时间"
|
msgstr "密码过期时间"
|
||||||
|
|
||||||
#: common/forms.py:204
|
#: common/forms.py:209
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tip: (unit: day) If the user does not update the password during the time, "
|
"Tip: (unit: day) If the user does not update the password during the time, "
|
||||||
"the user password will expire failure;The password expiration reminder mail "
|
"the user password will expire failure;The password expiration reminder mail "
|
||||||
|
@ -2026,45 +2036,45 @@ msgstr ""
|
||||||
"提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期"
|
"提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期"
|
||||||
"提醒邮件将在密码过期前5天内由系统(每天)自动发送给用户"
|
"提醒邮件将在密码过期前5天内由系统(每天)自动发送给用户"
|
||||||
|
|
||||||
#: common/forms.py:213
|
#: common/forms.py:218
|
||||||
msgid "Password minimum length"
|
msgid "Password minimum length"
|
||||||
msgstr "密码最小长度 "
|
msgstr "密码最小长度 "
|
||||||
|
|
||||||
#: common/forms.py:219
|
#: common/forms.py:224
|
||||||
msgid "Must contain capital letters"
|
msgid "Must contain capital letters"
|
||||||
msgstr "必须包含大写字母"
|
msgstr "必须包含大写字母"
|
||||||
|
|
||||||
#: common/forms.py:221
|
#: common/forms.py:226
|
||||||
msgid ""
|
msgid ""
|
||||||
"After opening, the user password changes and resets must contain uppercase "
|
"After opening, the user password changes and resets must contain uppercase "
|
||||||
"letters"
|
"letters"
|
||||||
msgstr "开启后,用户密码修改、重置必须包含大写字母"
|
msgstr "开启后,用户密码修改、重置必须包含大写字母"
|
||||||
|
|
||||||
#: common/forms.py:227
|
#: common/forms.py:232
|
||||||
msgid "Must contain lowercase letters"
|
msgid "Must contain lowercase letters"
|
||||||
msgstr "必须包含小写字母"
|
msgstr "必须包含小写字母"
|
||||||
|
|
||||||
#: common/forms.py:228
|
#: common/forms.py:233
|
||||||
msgid ""
|
msgid ""
|
||||||
"After opening, the user password changes and resets must contain lowercase "
|
"After opening, the user password changes and resets must contain lowercase "
|
||||||
"letters"
|
"letters"
|
||||||
msgstr "开启后,用户密码修改、重置必须包含小写字母"
|
msgstr "开启后,用户密码修改、重置必须包含小写字母"
|
||||||
|
|
||||||
#: common/forms.py:234
|
#: common/forms.py:239
|
||||||
msgid "Must contain numeric characters"
|
msgid "Must contain numeric characters"
|
||||||
msgstr "必须包含数字字符"
|
msgstr "必须包含数字字符"
|
||||||
|
|
||||||
#: common/forms.py:235
|
#: common/forms.py:240
|
||||||
msgid ""
|
msgid ""
|
||||||
"After opening, the user password changes and resets must contain numeric "
|
"After opening, the user password changes and resets must contain numeric "
|
||||||
"characters"
|
"characters"
|
||||||
msgstr "开启后,用户密码修改、重置必须包含数字字符"
|
msgstr "开启后,用户密码修改、重置必须包含数字字符"
|
||||||
|
|
||||||
#: common/forms.py:241
|
#: common/forms.py:246
|
||||||
msgid "Must contain special characters"
|
msgid "Must contain special characters"
|
||||||
msgstr "必须包含特殊字符"
|
msgstr "必须包含特殊字符"
|
||||||
|
|
||||||
#: common/forms.py:242
|
#: common/forms.py:247
|
||||||
msgid ""
|
msgid ""
|
||||||
"After opening, the user password changes and resets must contain special "
|
"After opening, the user password changes and resets must contain special "
|
||||||
"characters"
|
"characters"
|
||||||
|
|
|
@ -94,44 +94,15 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
||||||
serializer_class = serializers.ReplaySerializer
|
serializer_class = serializers.ReplaySerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
session = None
|
session = None
|
||||||
upload_to = 'replay' # 仅添加到本地存储中
|
|
||||||
|
|
||||||
def get_session_path(self, version=2):
|
|
||||||
"""
|
|
||||||
获取session日志的文件路径
|
|
||||||
:param version: 原来后缀是 .gz,为了统一新版本改为 .replay.gz
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
suffix = '.replay.gz'
|
|
||||||
if version == 1:
|
|
||||||
suffix = '.gz'
|
|
||||||
date = self.session.date_start.strftime('%Y-%m-%d')
|
|
||||||
return os.path.join(date, str(self.session.id) + suffix)
|
|
||||||
|
|
||||||
def get_local_path(self, version=2):
|
|
||||||
session_path = self.get_session_path(version=version)
|
|
||||||
if version == 2:
|
|
||||||
local_path = os.path.join(self.upload_to, session_path)
|
|
||||||
else:
|
|
||||||
local_path = session_path
|
|
||||||
return local_path
|
|
||||||
|
|
||||||
def save_to_storage(self, f):
|
|
||||||
local_path = self.get_local_path()
|
|
||||||
try:
|
|
||||||
name = default_storage.save(local_path, f)
|
|
||||||
return name, None
|
|
||||||
except OSError as e:
|
|
||||||
return None, e
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
session_id = kwargs.get('pk')
|
session_id = kwargs.get('pk')
|
||||||
self.session = get_object_or_404(Session, id=session_id)
|
session = get_object_or_404(Session, id=session_id)
|
||||||
serializer = self.serializer_class(data=request.data)
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
file = serializer.validated_data['file']
|
file = serializer.validated_data['file']
|
||||||
name, err = self.save_to_storage(file)
|
name, err = session.save_to_storage(file)
|
||||||
if not name:
|
if not name:
|
||||||
msg = "Failed save replay `{}`: {}".format(session_id, err)
|
msg = "Failed save replay `{}`: {}".format(session_id, err)
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
|
@ -145,7 +116,7 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
session_id = kwargs.get('pk')
|
session_id = kwargs.get('pk')
|
||||||
self.session = get_object_or_404(Session, id=session_id)
|
session = get_object_or_404(Session, id=session_id)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'type': 'guacamole' if self.session.protocol == 'rdp' else 'json',
|
'type': 'guacamole' if self.session.protocol == 'rdp' else 'json',
|
||||||
|
@ -153,9 +124,9 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
||||||
}
|
}
|
||||||
|
|
||||||
# 新版本和老版本的文件后缀不同
|
# 新版本和老版本的文件后缀不同
|
||||||
session_path = self.get_session_path() # 存在外部存储上的路径
|
session_path = session.get_rel_replay_path() # 存在外部存储上的路径
|
||||||
local_path = self.get_local_path()
|
local_path = session.get_local_path()
|
||||||
local_path_v1 = self.get_local_path(version=1)
|
local_path_v1 = session.get_local_path(version=1)
|
||||||
|
|
||||||
# 去default storage中查找
|
# 去default storage中查找
|
||||||
for _local_path in (local_path, local_path_v1, session_path):
|
for _local_path in (local_path, local_path_v1, session_path):
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.files.storage import default_storage
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from orgs.mixins import OrgModelMixin
|
from orgs.mixins import OrgModelMixin
|
||||||
|
@ -148,6 +150,36 @@ class Session(OrgModelMixin):
|
||||||
date_start = models.DateTimeField(verbose_name=_("Date start"), db_index=True, default=timezone.now)
|
date_start = models.DateTimeField(verbose_name=_("Date start"), db_index=True, default=timezone.now)
|
||||||
date_end = models.DateTimeField(verbose_name=_("Date end"), null=True)
|
date_end = models.DateTimeField(verbose_name=_("Date end"), null=True)
|
||||||
|
|
||||||
|
upload_to = 'replay'
|
||||||
|
|
||||||
|
def get_rel_replay_path(self, version=2):
|
||||||
|
"""
|
||||||
|
获取session日志的文件路径
|
||||||
|
:param version: 原来后缀是 .gz,为了统一新版本改为 .replay.gz
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
suffix = '.replay.gz'
|
||||||
|
if version == 1:
|
||||||
|
suffix = '.gz'
|
||||||
|
date = self.date_start.strftime('%Y-%m-%d')
|
||||||
|
return os.path.join(date, str(self.id) + suffix)
|
||||||
|
|
||||||
|
def get_local_path(self, version=2):
|
||||||
|
rel_path = self.get_rel_replay_path(version=version)
|
||||||
|
if version == 2:
|
||||||
|
local_path = os.path.join(self.upload_to, rel_path)
|
||||||
|
else:
|
||||||
|
local_path = rel_path
|
||||||
|
return local_path
|
||||||
|
|
||||||
|
def save_to_storage(self, f):
|
||||||
|
local_path = self.get_local_path()
|
||||||
|
try:
|
||||||
|
name = default_storage.save(local_path, f)
|
||||||
|
return name, None
|
||||||
|
except OSError as e:
|
||||||
|
return None, e
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "terminal_session"
|
db_table = "terminal_session"
|
||||||
ordering = ["-date_start"]
|
ordering = ["-date_start"]
|
||||||
|
|
|
@ -4,15 +4,20 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
from celery.utils.log import get_task_logger
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.files.storage import default_storage
|
||||||
|
|
||||||
|
|
||||||
from ops.celery.utils import register_as_period_task, after_app_ready_start, \
|
from ops.celery.utils import register_as_period_task, after_app_ready_start, \
|
||||||
after_app_shutdown_clean
|
after_app_shutdown_clean
|
||||||
from .models import Status, Session
|
from .models import Status, Session, Command
|
||||||
|
|
||||||
|
|
||||||
CACHE_REFRESH_INTERVAL = 10
|
CACHE_REFRESH_INTERVAL = 10
|
||||||
RUNNING = False
|
RUNNING = False
|
||||||
|
logger = get_task_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
|
@ -34,3 +39,28 @@ def clean_orphan_session():
|
||||||
if not session.terminal or not session.terminal.is_active:
|
if not session.terminal or not session.terminal.is_active:
|
||||||
session.is_finished = True
|
session.is_finished = True
|
||||||
session.save()
|
session.save()
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
@register_as_period_task(interval=3600*24)
|
||||||
|
@after_app_ready_start
|
||||||
|
@after_app_shutdown_clean
|
||||||
|
def clean_expired_session_period():
|
||||||
|
logger.info("Start clean expired session record, commands and replay")
|
||||||
|
days = settings.TERMINAL_SESSION_KEEP_DURATION
|
||||||
|
dt = timezone.now() - timezone.timedelta(days=days)
|
||||||
|
expired_sessions = Session.objects.filter(date_start__lt=dt)
|
||||||
|
for session in expired_sessions:
|
||||||
|
logger.info("Clean session: {}".format(session.id))
|
||||||
|
Command.objects.filter(session=str(session.id)).delete()
|
||||||
|
# 删除录像文件
|
||||||
|
session_path = session.get_rel_replay_path()
|
||||||
|
local_path = session.get_local_path()
|
||||||
|
local_path_v1 = session.get_local_path(version=1)
|
||||||
|
|
||||||
|
# 去default storage中查找
|
||||||
|
for _local_path in (local_path, local_path_v1, session_path):
|
||||||
|
if default_storage.exists(_local_path):
|
||||||
|
default_storage.delete(_local_path)
|
||||||
|
# 删除session记录
|
||||||
|
session.delete()
|
||||||
|
|
Loading…
Reference in New Issue