diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py
index 1ec2ea24f..0a6b2f093 100644
--- a/apps/assets/forms/asset.py
+++ b/apps/assets/forms/asset.py
@@ -41,9 +41,6 @@ class AssetCreateForm(OrgModelForm):
'nodes': _("Node"),
}
help_texts = {
- 'hostname': '* required',
- 'ip': '* required',
- 'port': '* required',
'admin_user': _(
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
@@ -80,10 +77,6 @@ class AssetUpdateForm(OrgModelForm):
'nodes': _("Node"),
}
help_texts = {
- 'hostname': '* required',
- 'ip': '* required',
- 'port': '* required',
- 'cluster': '* required',
'admin_user': _(
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
@@ -95,7 +88,7 @@ class AssetUpdateForm(OrgModelForm):
class AssetBulkUpdateForm(OrgModelForm):
assets = forms.ModelMultipleChoiceField(
- required=True, help_text='* required',
+ required=True,
label=_('Select assets'), queryset=Asset.objects.all(),
widget=forms.SelectMultiple(
attrs={
diff --git a/apps/assets/forms/domain.py b/apps/assets/forms/domain.py
index 3c733bcc4..635796c97 100644
--- a/apps/assets/forms/domain.py
+++ b/apps/assets/forms/domain.py
@@ -61,7 +61,3 @@ class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm):
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}
- help_texts = {
- 'name': '* required',
- 'username': '* required',
- }
diff --git a/apps/assets/forms/user.py b/apps/assets/forms/user.py
index 81138eab5..575d3e59c 100644
--- a/apps/assets/forms/user.py
+++ b/apps/assets/forms/user.py
@@ -80,10 +80,6 @@ class AdminUserForm(PasswordAndKeyAuthForm):
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}
- help_texts = {
- 'name': '* required',
- 'username': '* required',
- }
class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
@@ -150,7 +146,6 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
}),
}
help_texts = {
- 'name': '* required',
'auto_push': _('Auto push system user to asset'),
'priority': _('1-100, High level will be using login asset as default, '
'if user was granted more than 2 system user'),
diff --git a/apps/common/models.py b/apps/common/models.py
index cb97b8988..012cd7351 100644
--- a/apps/common/models.py
+++ b/apps/common/models.py
@@ -45,10 +45,10 @@ class Setting(models.Model):
def cleaned_value(self):
try:
value = self.value
- if not isinstance(value, (str, bytes)):
- return value
if self.encrypted:
value = signer.unsign(value)
+ if not value:
+ return None
value = json.loads(value)
return value
except json.JSONDecodeError:
diff --git a/apps/common/templates/common/command_storage_create.html b/apps/common/templates/common/command_storage_create.html
index a3a83f2e7..671b11c94 100644
--- a/apps/common/templates/common/command_storage_create.html
+++ b/apps/common/templates/common/command_storage_create.html
@@ -41,7 +41,6 @@
diff --git a/apps/common/templates/common/replay_storage_create.html b/apps/common/templates/common/replay_storage_create.html
index 8b2ab8596..af56c210d 100644
--- a/apps/common/templates/common/replay_storage_create.html
+++ b/apps/common/templates/common/replay_storage_create.html
@@ -44,7 +44,6 @@
diff --git a/apps/common/tree.py b/apps/common/tree.py
index 6b57a3db4..13756a35d 100644
--- a/apps/common/tree.py
+++ b/apps/common/tree.py
@@ -49,7 +49,9 @@ class TreeNode:
return False
elif not self.isParent and other.isParent:
return True
- return self.id > other.id
+ if self.pId != other.pId:
+ return self.pId > other.pId
+ return self.name > other.name
def __eq__(self, other):
return self.id == other.id
diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py
index 8f6a1213f..61fd84249 100644
--- a/apps/jumpserver/conf.py
+++ b/apps/jumpserver/conf.py
@@ -316,7 +316,7 @@ defaults = {
'EMAIL_SUFFIX': 'jumpserver.org',
'TERMINAL_PASSWORD_AUTH': True,
'TERMINAL_PUBLIC_KEY_AUTH': True,
- 'TERMINAL_HEARTBEAT_INTERVAL': 30,
+ 'TERMINAL_HEARTBEAT_INTERVAL': 20,
'TERMINAL_ASSET_LIST_SORT_BY': 'hostname',
'TERMINAL_ASSET_LIST_PAGE_SIZE': 'auto',
'TERMINAL_SESSION_KEEP_DURATION': 9999,
diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py
index 167de3180..ca3d5afa3 100644
--- a/apps/jumpserver/settings.py
+++ b/apps/jumpserver/settings.py
@@ -499,8 +499,9 @@ BOOTSTRAP3 = {
# Field class to use in horizontal forms
'horizontal_field_class': 'col-md-9',
# Set placeholder attributes to label if no placeholder is provided
- 'set_placeholder': True,
+ 'set_placeholder': False,
'success_css_class': '',
+ 'required_css_class': 'required',
}
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION
diff --git a/apps/ops/models/adhoc.py b/apps/ops/models/adhoc.py
index 3e119c98f..cedbbb4ff 100644
--- a/apps/ops/models/adhoc.py
+++ b/apps/ops/models/adhoc.py
@@ -168,7 +168,10 @@ class AdHoc(models.Model):
@property
def tasks(self):
- return json.loads(self._tasks)
+ try:
+ return json.loads(self._tasks)
+ except:
+ return []
@tasks.setter
def tasks(self, item):
diff --git a/apps/ops/templates/ops/adhoc_detail.html b/apps/ops/templates/ops/adhoc_detail.html
index abbb16c04..8b340b808 100644
--- a/apps/ops/templates/ops/adhoc_detail.html
+++ b/apps/ops/templates/ops/adhoc_detail.html
@@ -186,6 +186,19 @@
- {% include 'users/_user_update_pk_modal.html' %}
+{% endblock %}
+{% block custom_foot_js %}
+
{% endblock %}
diff --git a/apps/ops/templates/ops/adhoc_history_detail.html b/apps/ops/templates/ops/adhoc_history_detail.html
index b8a48d4e5..5091470d5 100644
--- a/apps/ops/templates/ops/adhoc_history_detail.html
+++ b/apps/ops/templates/ops/adhoc_history_detail.html
@@ -19,7 +19,7 @@
{% trans 'Run history detail' %}
- {% trans 'Output' %}
+ {% trans 'Output' %}
@@ -141,4 +141,14 @@
{% include 'users/_user_update_pk_modal.html' %}
{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
diff --git a/apps/ops/templates/ops/task_adhoc.html b/apps/ops/templates/ops/task_adhoc.html
index 82c25e667..34e0797e6 100644
--- a/apps/ops/templates/ops/task_adhoc.html
+++ b/apps/ops/templates/ops/task_adhoc.html
@@ -25,7 +25,7 @@
{% trans 'Run history' %}
- {% trans 'Last run output' %}
+ {% trans 'Last run output' %}
@@ -78,52 +78,60 @@
{% endblock %}
{% block custom_foot_js %}
-
+ $(td).html(cellData);
+ }},
+ {targets: 2, createdCell: function (td, cellData, rowData) {
+ var dataLength = cellData.length;
+ $(td).html(dataLength);
+ }},
+ {targets: 4, createdCell: function (td, cellData) {
+ if (!cellData) {
+ $(td).html("Admin")
+ } else {
+ $(td).html(cellData)
+ }
+ }},
+ {targets: 5, createdCell: function (td, cellData, rowData) {
+ if (!cellData) {
+ $(td).html("")
+ } else {
+ $(td).html(cellData.user)
+ }
+ }},
+ {targets: 6, createdCell: function (td, cellData) {
+ var d = new Date(cellData);
+ $(td).html(d.toLocaleString())
+ }},
+ {targets: 7, createdCell: function (td, cellData, rowData) {
+ var detail_btn = '{% trans "Detail" %}'.replace('{{ DEFAULT_PK }}', cellData);
+ if (cellData) {
+ $(td).html(detail_btn);
+ }
+ }}
+ ],
+ ajax_url: '{% url "api-ops:adhoc-list" %}?task={{ object.pk }}',
+ columns: [{data: function(){return ""}}, {data: "short_id" }, {data: "hosts"}, {data: "pattern"},
+ {data: "run_as"}, {data: "become"}, {data: "date_created"}, {data: "id"}]
+ };
+ jumpserver.initDataTable(options);
+}).on('click', '.celery-task-log', function () {
+ var history_pk = "{{ object.latest_history.pk }}";
+ if (!history_pk) {
+ alert("没有运行历史");
+ return
+ }
+ var url = '{% url 'ops:celery-task-log' pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', history_pk);
+ window.open(url, '', 'width=800,height=600,left=400,top=400')
+})
+
{% endblock %}
diff --git a/apps/ops/templates/ops/task_detail.html b/apps/ops/templates/ops/task_detail.html
index d8f9520be..bbeaad660 100644
--- a/apps/ops/templates/ops/task_detail.html
+++ b/apps/ops/templates/ops/task_detail.html
@@ -26,7 +26,7 @@
{% trans 'Run history' %}
- {% trans 'Last run output' %}
+ {% trans 'Last run output' %}
@@ -165,4 +165,19 @@
{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
diff --git a/apps/ops/templates/ops/task_history.html b/apps/ops/templates/ops/task_history.html
index 184987766..9a63acc6a 100644
--- a/apps/ops/templates/ops/task_history.html
+++ b/apps/ops/templates/ops/task_history.html
@@ -25,7 +25,7 @@
{% trans 'Run history' %}
- {% trans 'Last run output' %}
+ {% trans 'Last run output' %}
@@ -148,6 +148,14 @@ function initTable() {
$(document).ready(function () {
initTable();
+}).on('click', '.celery-task-log', function () {
+ var history_pk = "{{ object.latest_history.pk }}";
+ if (!history_pk) {
+ alert("没有运行历史");
+ return
+ }
+ var url = '{% url 'ops:celery-task-log' pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', history_pk);
+ window.open(url, '', 'width=800,height=600,left=400,top=400')
})
diff --git a/apps/perms/utils.py b/apps/perms/utils.py
index ef8e3d09a..45ec6e039 100644
--- a/apps/perms/utils.py
+++ b/apps/perms/utils.py
@@ -18,8 +18,7 @@ class GenerateTree:
"asset_instance": set("system_user")
}
"""
- self.__all_nodes = Node.objects.all()
- self.__node_asset_map = defaultdict(set)
+ self.__all_nodes = list(Node.objects.all())
self.nodes = defaultdict(dict)
def add_asset(self, asset, system_users):
diff --git a/apps/static/css/jumpserver.css b/apps/static/css/jumpserver.css
index 2a6f06e8c..c979678e0 100644
--- a/apps/static/css/jumpserver.css
+++ b/apps/static/css/jumpserver.css
@@ -438,4 +438,7 @@ div.dataTables_wrapper div.dataTables_filter {
white-space: nowrap;
}
-
+.form-group .required .control-label:after {
+ content:"*";
+ color:red;
+}
diff --git a/apps/users/forms.py b/apps/users/forms.py
index fb492412f..521bc13ae 100644
--- a/apps/users/forms.py
+++ b/apps/users/forms.py
@@ -63,11 +63,6 @@ class UserCreateUpdateForm(OrgModelForm):
'username', 'name', 'email', 'groups', 'wechat',
'phone', 'role', 'date_expired', 'comment', 'otp_level'
]
- help_texts = {
- 'username': '* required',
- 'name': '* required',
- 'email': '* required',
- }
widgets = {
'otp_level': forms.RadioSelect(),
'groups': forms.SelectMultiple(
@@ -141,11 +136,6 @@ class UserProfileForm(forms.ModelForm):
'username', 'name', 'email',
'wechat', 'phone',
]
- help_texts = {
- 'username': '* required',
- 'name': '* required',
- 'email': '* required',
- }
UserProfileForm.verbose_name = _("Profile")
@@ -263,7 +253,6 @@ UserPublicKeyForm.verbose_name = _("Public key")
class UserBulkUpdateForm(OrgModelForm):
users = forms.ModelMultipleChoiceField(
required=True,
- help_text='* required',
label=_('Select users'),
queryset=User.objects.all(),
widget=forms.SelectMultiple(
@@ -346,9 +335,6 @@ class UserGroupForm(OrgModelForm):
fields = [
'name', 'users', 'comment',
]
- help_texts = {
- 'name': '* required'
- }
class FileForm(forms.Form):
diff --git a/config_example.py b/config_example.py
index e37df23b0..5f5301f0e 100644
--- a/config_example.py
+++ b/config_example.py
@@ -78,7 +78,7 @@ class Config:
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
# REDIS_PASSWORD = ''
- # REDIS_DB_CELERY_BROKER = 3
+ # REDIS_DB_CELERY = 3
# REDIS_DB_CACHE = 4
# Use OpenID authorization
diff --git a/requirements/deb_requirements.txt b/requirements/deb_requirements.txt
index 07b3441c4..f32a217e6 100644
--- a/requirements/deb_requirements.txt
+++ b/requirements/deb_requirements.txt
@@ -1 +1 @@
-libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite libkrb5-dev sshpass
+libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite libkrb5-dev sshpass libmysqlclient-dev