Commit 6db76423 authored by liuzheng712's avatar liuzheng712

Merge branch 'dev' of https://git.coding.net/jumpserver/jumpserver into NormalUserPageLZ

parents cab34668 91b5d039
This diff is collapsed.
...@@ -48,7 +48,8 @@ def log_list(request, offset): ...@@ -48,7 +48,8 @@ def log_list(request, offset):
contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request)
web_monitor_uri = '%s/monitor' % web_socket_host web_monitor_uri = 'ws://%s/monitor' % web_socket_host
web_kill_uri = 'http://%s/kill' % web_socket_host
return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request)) return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request))
...@@ -103,6 +104,10 @@ def log_record(request): ...@@ -103,6 +104,10 @@ def log_record(request):
def web_terminal(request): def web_terminal(request):
web_terminal_uri = '%s/terminal' % web_socket_host #username = get_session.get('username', '')
token = request.COOKIES.get('sessionid')
username = request.user.username
asset_name = '127.0.0.1'
web_terminal_uri = 'ws://%s/terminal?username=%s&asset_name=%s&token=%s' % (web_socket_host, username, asset_name, token)
return render_to_response('jlog/web_terminal.html', locals()) return render_to_response('jlog/web_terminal.html', locals())
...@@ -23,7 +23,7 @@ root_pw = secret234 ...@@ -23,7 +23,7 @@ root_pw = secret234
[websocket] [websocket]
web_socket_host = ws://192.168.244.129:3000 web_socket_host = 192.168.244.129:3000
[mail] [mail]
...@@ -32,4 +32,4 @@ email_host = smtp.exmail.qq.com ...@@ -32,4 +32,4 @@ email_host = smtp.exmail.qq.com
email_port = 25 email_port = 25
email_host_user = noreply@jumpserver.org email_host_user = noreply@jumpserver.org
email_host_password = jumpserver1234 email_host_password = jumpserver1234
email_use_tls = False email_use_tls = True
...@@ -7,6 +7,7 @@ class Setting(models.Model): ...@@ -7,6 +7,7 @@ class Setting(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
default_user = models.CharField(max_length=100, null=True, blank=True) default_user = models.CharField(max_length=100, null=True, blank=True)
default_port = models.IntegerField(max_length=10, null=True, blank=True) default_port = models.IntegerField(max_length=10, null=True, blank=True)
default_password = models.CharField(max_length=100, null=True, blank=True)
default_pri_key_path = models.CharField(max_length=100, null=True, blank=True) default_pri_key_path = models.CharField(max_length=100, null=True, blank=True)
class Meta: class Meta:
......
...@@ -15,7 +15,7 @@ import getpass ...@@ -15,7 +15,7 @@ import getpass
config = ConfigParser.ConfigParser() config = ConfigParser.ConfigParser()
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
config.read(os.path.join(BASE_DIR, 'jumpserver.conf')) config.read(os.path.join(BASE_DIR, 'jumpserver.conf'))
DB_HOST = config.get('db', 'host') DB_HOST = config.get('db', 'host')
...@@ -25,11 +25,13 @@ DB_PASSWORD = config.get('db', 'password') ...@@ -25,11 +25,13 @@ DB_PASSWORD = config.get('db', 'password')
DB_DATABASE = config.get('db', 'database') DB_DATABASE = config.get('db', 'database')
AUTH_USER_MODEL = 'juser.User' AUTH_USER_MODEL = 'juser.User'
# mail config # mail config
MAIL_ENABLE = config.get('mail', 'mail_enable')
EMAIL_HOST = config.get('mail', 'email_host') EMAIL_HOST = config.get('mail', 'email_host')
EMAIL_PORT = config.get('mail', 'email_port') EMAIL_PORT = config.get('mail', 'email_port')
EMAIL_HOST_USER = config.get('mail', 'email_host_user') EMAIL_HOST_USER = config.get('mail', 'email_host_user')
EMAIL_HOST_PASSWORD = config.get('mail', 'email_host_password') EMAIL_HOST_PASSWORD = config.get('mail', 'email_host_password')
EMAIL_USE_TLS = config.getboolean('mail', 'email_use_tls') EMAIL_USE_TLS = config.getboolean('mail', 'email_use_tls')
EMAIL_TIMEOUT = 5
# ======== Log ========== # ======== Log ==========
LOG = False LOG = False
...@@ -41,8 +43,6 @@ KEY = config.get('base', 'key') ...@@ -41,8 +43,6 @@ KEY = config.get('base', 'key')
LOGIN_NAME = getpass.getuser() LOGIN_NAME = getpass.getuser()
# LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') # LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable')
URL = config.get('base', 'url') URL = config.get('base', 'url')
MAIL_ENABLE = config.get('mail', 'mail_enable')
MAIL_FROM = config.get('mail', 'email_host_user')
log_dir = os.path.join(BASE_DIR, 'logs') log_dir = os.path.join(BASE_DIR, 'logs')
log_level = config.get('base', 'log') log_level = config.get('base', 'log')
web_socket_host = config.get('websocket', 'web_socket_host') web_socket_host = config.get('websocket', 'web_socket_host')
......
...@@ -246,26 +246,38 @@ def Logout(request): ...@@ -246,26 +246,38 @@ def Logout(request):
def setting(request): def setting(request):
header_title, path1 = '项目设置', '设置' header_title, path1 = '项目设置', '设置'
setting_r = get_object(Setting, name='default') setting_default = get_object(Setting, name='default')
if request.method == "POST": if request.method == "POST":
setting_raw = request.POST.get('setting', '')
if setting_raw == 'default':
username = request.POST.get('username', '') username = request.POST.get('username', '')
port = request.POST.get('port', '') port = request.POST.get('port', '')
password = request.POST.get('password', '')
private_key = request.POST.get('key', '') private_key = request.POST.get('key', '')
if '' in [username, port, private_key]: if '' in [username, port] and ('' in password or '' in private_key):
return HttpResponse('所填内容不能为空') return HttpResponse('所填内容不能为空, 且密码和私钥填一个')
else: else:
settings = get_object(Setting, id=1)
private_key_path = os.path.join(BASE_DIR, 'keys', 'default', 'default_private_key.pem') private_key_path = os.path.join(BASE_DIR, 'keys', 'default', 'default_private_key.pem')
if private_key:
with open(private_key_path, 'w') as f: with open(private_key_path, 'w') as f:
f.write(private_key) f.write(private_key)
os.chmod(private_key_path, 0600) os.chmod(private_key_path, 0600)
if settings:
if setting_default:
if password != setting_default.default_password:
password_encode = CRYPTOR.encrypt(password)
else:
password_encode = password
Setting.objects.filter(name='default').update(default_user=username, default_port=port, Setting.objects.filter(name='default').update(default_user=username, default_port=port,
default_password=password_encode,
default_pri_key_path=private_key_path) default_pri_key_path=private_key_path)
else: else:
password_encode = CRYPTOR.encrypt(password)
setting_r = Setting(name='default', default_user=username, default_port=port, setting_r = Setting(name='default', default_user=username, default_port=port,
default_password=password_encode,
default_pri_key_path=private_key_path).save() default_pri_key_path=private_key_path).save()
msg = "设置成功" msg = "设置成功"
......
...@@ -5,7 +5,7 @@ from subprocess import call ...@@ -5,7 +5,7 @@ from subprocess import call
from juser.models import AdminGroup from juser.models import AdminGroup
from jumpserver.api import * from jumpserver.api import *
from jumpserver.settings import BASE_DIR from jumpserver.settings import BASE_DIR, EMAIL_HOST_USER as MAIL_FROM
def group_add_user(group, user_id=None, username=None): def group_add_user(group, user_id=None, username=None):
......
...@@ -10,10 +10,11 @@ from django.contrib.auth.decorators import login_required ...@@ -10,10 +10,11 @@ from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
from django.template import RequestContext from django.template import RequestContext
from django.db.models import ObjectDoesNotExist from django.db.models import ObjectDoesNotExist
from jumpserver.settings import MAIL_FROM, MAIL_ENABLE from jumpserver.settings import EMAIL_HOST_USER
from juser.user_api import * from juser.user_api import *
from jperm.perm_api import _public_perm_api, perm_user_api, user_permed from jperm.perm_api import _public_perm_api, perm_user_api, user_permed
MAIL_FROM = EMAIL_HOST_USER
def chg_role(request): def chg_role(request):
role = {'SU': 2, 'GA': 1, 'CU': 0} role = {'SU': 2, 'GA': 1, 'CU': 0}
......
# coding: utf-8 # coding: utf-8
import time import time
import datetime
import json import json
import os import os
import sys import sys
import os.path import os.path
import threading import threading
import urllib
import tornado.ioloop import tornado.ioloop
import tornado.options import tornado.options
...@@ -13,6 +15,7 @@ import tornado.web ...@@ -13,6 +15,7 @@ import tornado.web
import tornado.websocket import tornado.websocket
import tornado.httpserver import tornado.httpserver
import tornado.gen import tornado.gen
import tornado.httpclient
from tornado.websocket import WebSocketClosedError from tornado.websocket import WebSocketClosedError
from tornado.options import define, options from tornado.options import define, options
...@@ -21,9 +24,12 @@ from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE ...@@ -21,9 +24,12 @@ from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE
# from gevent import monkey # from gevent import monkey
# monkey.patch_all() # monkey.patch_all()
# import gevent # import gevent
from gevent.socket import wait_read, wait_write # from gevent.socket import wait_read, wait_write
import struct, fcntl, signal, socket, select, fnmatch
import paramiko import paramiko
from connect import Tty
from connect import TtyLog, Log
try: try:
import simplejson as json import simplejson as json
...@@ -35,6 +41,20 @@ define("port", default=3000, help="run on the given port", type=int) ...@@ -35,6 +41,20 @@ define("port", default=3000, help="run on the given port", type=int)
define("host", default='0.0.0.0', help="run port on", type=str) define("host", default='0.0.0.0', help="run port on", type=str)
def require_auth(func):
def _deco(request, *args, **kwargs):
username = request.get_argument('username', '')
asset_name = request.get_argument('asset_name', '')
token = request.get_argument('token', '')
print username, asset_name, token
client = tornado.httpclient.HTTPClient()
# response = client.fetch('http://some/url') + urllib.urlencode({'username': username,
# 'asset_name': asset_name, 'token': token})
# return request.close()
return func(request, *args, **kwargs)
return _deco
class MyThread(threading.Thread): class MyThread(threading.Thread):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MyThread, self).__init__(*args, **kwargs) super(MyThread, self).__init__(*args, **kwargs)
...@@ -92,6 +112,7 @@ class Application(tornado.web.Application): ...@@ -92,6 +112,7 @@ class Application(tornado.web.Application):
handlers = [ handlers = [
(r'/monitor', MonitorHandler), (r'/monitor', MonitorHandler),
(r'/terminal', WebTerminalHandler), (r'/terminal', WebTerminalHandler),
(r'/kill', WebTerminalKillHandler),
] ]
setting = { setting = {
...@@ -115,6 +136,7 @@ class MonitorHandler(tornado.websocket.WebSocketHandler): ...@@ -115,6 +136,7 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin): def check_origin(self, origin):
return True return True
@require_auth
def open(self): def open(self):
# 获取监控的path # 获取监控的path
self.file_path = self.get_argument('file_path', '') self.file_path = self.get_argument('file_path', '')
...@@ -123,12 +145,6 @@ class MonitorHandler(tornado.websocket.WebSocketHandler): ...@@ -123,12 +145,6 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
MonitorHandler.threads.append(thread) MonitorHandler.threads.append(thread)
self.stream.set_nodelay(True) self.stream.set_nodelay(True)
print len(MonitorHandler.threads), len(MonitorHandler.clients)
def on_message(self, message):
self.write_message('Connect WebSocket Success. <br/>')
# 监控日志,发生变动发向客户端
try: try:
for t in MonitorHandler.threads: for t in MonitorHandler.threads:
if t.is_alive(): if t.is_alive():
...@@ -142,6 +158,12 @@ class MonitorHandler(tornado.websocket.WebSocketHandler): ...@@ -142,6 +158,12 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
MonitorHandler.clients.remove(self) MonitorHandler.clients.remove(self)
MonitorHandler.threads.remove(MonitorHandler.threads[client_index]) MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
print len(MonitorHandler.threads), len(MonitorHandler.clients)
def on_message(self, message):
# 监控日志,发生变动发向客户端
pass
def on_close(self): def on_close(self):
# 客户端主动关闭 # 客户端主动关闭
# self.close() # self.close()
...@@ -152,28 +174,55 @@ class MonitorHandler(tornado.websocket.WebSocketHandler): ...@@ -152,28 +174,55 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
MonitorHandler.threads.remove(MonitorHandler.threads[client_index]) MonitorHandler.threads.remove(MonitorHandler.threads[client_index])
class WebTty(Tty):
def __init__(self, *args, **kwargs):
super(WebTty, self).__init__(*args, **kwargs)
self.login_type = 'web'
self.ws = None
self.input_r = ''
self.input_mode = False
class WebTerminalKillHandler(tornado.web.RequestHandler):
def get(self):
ws_id = self.get_argument('id')
Log.objects.filter(id=ws_id).update(is_finished=True)
for ws in WebTerminalHandler.clients:
print ws.id
if ws.id == int(ws_id):
print "killed"
ws.log.save()
ws.close()
print len(WebTerminalHandler.clients)
class WebTerminalHandler(tornado.websocket.WebSocketHandler): class WebTerminalHandler(tornado.websocket.WebSocketHandler):
tasks = [] tasks = []
clients = []
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.chan = None self.term = None
self.ssh = None self.channel = None
self.log_file_f = None
self.log_time_f = None
self.log = None
self.id = 0
super(WebTerminalHandler, self).__init__(*args, **kwargs) super(WebTerminalHandler, self).__init__(*args, **kwargs)
def check_origin(self, origin): def check_origin(self, origin):
return True return True
@require_auth
def open(self): def open(self):
self.ssh = paramiko.SSHClient() asset_name = self.get_argument('asset_name', '')
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) username = self.get_argument('username', '')
try: token = self.get_argument('token', '')
self.ssh.connect('127.0.0.1', 22, 'root', 'redhat') print asset_name, username, token
except: self.term = WebTty('a', 'b')
self.write_message(json.loads({'data': 'Connect server Error'})) self.term.get_connection()
self.close() self.channel = self.term.ssh.invoke_shell(term='xterm')
WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound))
self.chan = self.ssh.invoke_shell(term='xterm') WebTerminalHandler.clients.append(self)
WebTerminalHandler.tasks.append(threading.Thread(target=self._forward_outbound))
for t in WebTerminalHandler.tasks: for t in WebTerminalHandler.tasks:
if t.is_alive(): if t.is_alive():
...@@ -185,35 +234,56 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler): ...@@ -185,35 +234,56 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
data = json.loads(message) data = json.loads(message)
if not data: if not data:
return return
if 'resize' in data: if data.get('data'):
self.chan.resize_pty( self.term.input_mode = True
data['resize'].get('width', 80), if str(data['data']) in ['\r', '\n', '\r\n']:
data['resize'].get('height', 24)) TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=self.term.remove_control_char(self.term.input_r)).save()
if 'data' in data: self.term.input_r = ''
self.chan.send(data['data']) self.term.input_mode = False
self.channel.send(data['data'])
def on_close(self): def on_close(self):
self.write_message(json.dumps({'data': 'close websocket'})) print 'On_close'
if self in WebTerminalHandler.clients:
WebTerminalHandler.clients.remove(self)
try:
self.log_file_f.write('End time is %s' % datetime.datetime.now())
self.log.is_finished = True
self.log.end_time = datetime.datetime.now()
self.log.save()
self.close()
except AttributeError:
pass
def _forward_outbound(self): def forward_outbound(self):
""" Forward outbound traffic (ssh -> websockets) """ self.log_file_f, self.log_time_f, self.log = self.term.get_log_file()
self.id = self.log.id
try: try:
data = '' data = ''
pre_timestamp = time.time()
while True: while True:
wait_read(self.chan.fileno()) r, w, e = select.select([self.channel, sys.stdin], [], [])
recv = self.chan.recv(1024) if self.channel in r:
recv = self.channel.recv(1024)
if not len(recv): if not len(recv):
return return
data += recv data += recv
try: try:
self.write_message(json.dumps({'data': data})) self.write_message(json.dumps({'data': data}))
now_timestamp = time.time()
self.log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
self.log_file_f.write(data)
pre_timestamp = now_timestamp
self.log_file_f.flush()
self.log_time_f.flush()
if self.term.input_mode and not self.term.is_output(data):
self.term.input_r += data
data = '' data = ''
except UnicodeDecodeError: except UnicodeDecodeError:
pass pass
finally: finally:
self.close() self.close()
if __name__ == '__main__': if __name__ == '__main__':
tornado.options.parse_command_line() tornado.options.parse_command_line()
app = Application() app = Application()
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<div class="col-lg-12"> <div class="col-lg-12">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title"> <div id="ibox-content" class="ibox-title">
<h5> 用户日志详细信息列表 </h5> <h5> 用户日志详细信息列表 <input type="button" id="test_connect" class="btn btn-primary" value="测试连接 web terminal" /> </h5>
<div class="ibox-tools"> <div class="ibox-tools">
<a class="collapse-link"> <a class="collapse-link">
<i class="fa fa-chevron-up"></i> <i class="fa fa-chevron-up"></i>
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
{% ifnotequal session_role_id 0 %} {% ifnotequal session_role_id 0 %}
<td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </a></td> <td class="text-center"><a href="/jlog/history/?id={{ post.id }}" class="log_command"> 命令统计 </a></td>
<td class="text-center"><a class="monitor" file_path="{{ post.log_path }}"> 监控 </a></td> <td class="text-center"><a class="monitor" file_path="{{ post.log_path }}"> 监控 </a></td>
<td class="text-center"><input type="button" id="cut" class="btn btn-danger btn-xs" name="cut" value="阻断" onclick='cut("{{ post.pid }}")' /></td> <td class="text-center"><input type="button" id="cut" class="btn btn-danger btn-xs" name="cut" value="阻断" onclick='cut("{{ post.pid }}", "{{ post.remote_ip }}")' /></td>
{% endifnotequal %} {% endifnotequal %}
<td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s" }} </td> <td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s" }} </td>
</tr> </tr>
...@@ -127,8 +127,20 @@ ...@@ -127,8 +127,20 @@
var file_path = obj.attr('file_path'); var file_path = obj.attr('file_path');
var wsUri = '{{ web_monitor_uri }}'; var wsUri = '{{ web_monitor_uri }}';
var socket = new WebSocket(wsUri + '?file_path=' + file_path); var socket = new WebSocket(wsUri + '?file_path=' + file_path);
var term = new Terminal({
cols: 80,
rows: 24,
screenKeys: false
});
var tag = $('<div id="term" style="height:500px; overflow: auto;background-color: rgba(0, 0, 0, 0);border: none"></div>');
term.open();
term.resize(80, 24);
socket.onopen = function(evt){ socket.onopen = function(evt){
socket.send(file_path) socket.send('hello');
term.write('~.~ Connect WebSocket Success.~.~ \r\n');
}; };
window.onbeforeunload = function(){ window.onbeforeunload = function(){
...@@ -138,29 +150,15 @@ ...@@ -138,29 +150,15 @@
var username = obj.closest('tr').find('#username').text(); var username = obj.closest('tr').find('#username').text();
var ip = obj.closest('tr').find('#ip').text(); var ip = obj.closest('tr').find('#ip').text();
BootstrapDialog.show({message: function(){ BootstrapDialog.show({message: function(){
//服务器端认证 //服务器端认证
{# socket.send('login', {userid:message.id, filename:message.filename,username:username,seed:seed});#} {# socket.send('login', {userid:message.id, filename:message.filename,username:username,seed:seed});#}
var term = new Terminal({
cols: 80,
rows: 24,
screenKeys: false
});
var tag = $('<div id="term" style="height:500px; overflow: auto;background-color: rgba(0, 0, 0, 0);border: none"></div>');
term.open();
term.resize(80, 24);
window.setTimeout(function(){ window.setTimeout(function(){
$('.terminal').detach().appendTo('#term'); $('.terminal').detach().appendTo('#term');
socket.onmessage = function(evt){ socket.onmessage = function(evt){
term.write(evt.data); term.write(evt.data);
}}, 1000); }}, 1000);
tag[0].style.color = "#00FF00";
return tag[0]; return tag[0];
} , } ,
title:'Jumpserver实时监控 '+' 登录用户名: '+'<span class="text-info">'+username+'</span>'+' 登录主机: '+'<span class="text-info">'+ip, title:'Jumpserver实时监控 '+' 登录用户名: '+'<span class="text-info">'+username+'</span>'+' 登录主机: '+'<span class="text-info">'+ip,
...@@ -190,6 +188,10 @@ ...@@ -190,6 +188,10 @@
}}); }});
return false; return false;
}); });
$('#test_connect').click(function(){
window.open('/jlog/web_terminal/?asset_name="hello', '播放', 'height=400, width=600, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
});
}); });
{# function log_search(){#} {# function log_search(){#}
...@@ -204,8 +206,14 @@ ...@@ -204,8 +206,14 @@
{# }#} {# }#}
function cut(num){ function cut(num, host){
var g_url = "/jlog/log_kill/?id="+num; console.log(host);
if (host=='Web'){
var g_url = '{{ web_kill_uri }}' + '?id=' + num;
} else {
g_url = "/jlog/log_kill/?id=" + num;
}
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: g_url, url: g_url,
......
<div class="col-sm-6"> <div class="col-sm-6">
<div class="dataTables_paginate paging_simple_numbers" id="editable_paginate"> <div class="dataTables_paginate paging_simple_numbers" id="editable_paginate">
<ul class="pagination" style="margin-top: 0; float: right"> <ul class="pagination" style="margin-top: 0; float: right">
{% if keyword %}
{% if contacts.has_previous %} {% if contacts.has_previous %}
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="editable_previous"> <li class="paginate_button previous" aria-controls="editable" tabindex="0" id="editable_previous">
<a href="?keyword={{ keyword }}&page={{ contacts.previous_page_number }}">Previous</a> <a class="page" href="?page={{ contacts.previous_page_number }}">Previous</a>
</li> </li>
{% else %} {% else %}
<li class="paginate_button previous disabled" aria-controls="editable" tabindex="0" id="editable_previous"> <li class="paginate_button previous disabled" aria-controls="editable" tabindex="0" id="editable_previous">
<a href="#">Previous</a> <a class="page" href="#">Previous</a>
</li> </li>
{% endif %} {% endif %}
{% ifequal show_first 1 %} {% ifequal show_first 1 %}
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page=1" title="第1页">1...</a></li> <li class="paginate_button" aria-controls="editable" tabindex="0"><a class="page" href="?page=1" title="第1页">1...</a></li>
{% endifequal %} {% endifequal %}
{% for page in page_range %} {% for page in page_range %}
{% ifequal current_page page %} {% ifequal current_page page %}
<li class="paginate_button active" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page={{ page }}" title="第{{ page }}页">{{ page }}</a></li> <li class="paginate_button active" aria-controls="editable" tabindex="0"><a class="page" href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
{% else %} {% else %}
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page={{ page }}" title="第{{ page }}页">{{ page }}</a></li> <li class="paginate_button" aria-controls="editable" tabindex="0"><a class="page" href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
{% endifequal %} {% endifequal %}
{% endfor %} {% endfor %}
{% ifequal show_end 1 %} {% ifequal show_end 1 %}
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?keyword={{ keyword }}&page={{ p.num_pages }}" title="第{{ page }}页">...{{ p.num_pages }}</a></li> <li class="paginate_button" aria-controls="editable" tabindex="0"><a class="page" href="?page={{ p.num_pages }}" title="第{{ page }}页">...{{ p.num_pages }}</a></li>
{% endifequal %} {% endifequal %}
{% if contacts.has_next %} {% if contacts.has_next %}
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="editable_next"> <li class="paginate_button next" aria-controls="editable" tabindex="0" id="editable_next">
<a href="?keyword={{ keyword }}&page={{ contacts.next_page_number }}">Next</a> <a class="page" href="?page={{ contacts.next_page_number }}">Next</a>
</li> </li>
{% else %} {% else %}
<li class="paginate_button next disabled" aria-controls="editable" tabindex="0" id="editable_next"> <li class="paginate_button next disabled" aria-controls="editable" tabindex="0" id="editable_next">
<a href="#">Next</a> <a class="page" href="#">Next</a>
</li> </li>
{% endif %} {% endif %}
{% else %}
{% if contacts.has_previous %}
<li class="paginate_button previous" aria-controls="editable" tabindex="0" id="editable_previous">
<a href="?page={{ contacts.previous_page_number }}">Previous</a>
</li>
{% else %}
<li class="paginate_button previous disabled" aria-controls="editable" tabindex="0" id="editable_previous">
<a href="#">Previous</a>
</li>
{% endif %}
{% ifequal show_first 1 %}
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?page=1" title="第1页">1...</a></li>
{% endifequal %}
{% for page in page_range %}
{% ifequal current_page page %}
<li class="paginate_button active" aria-controls="editable" tabindex="0"><a href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
{% else %}
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?page={{ page }}" title="第{{ page }}页">{{ page }}</a></li>
{% endifequal %}
{% endfor %}
{% ifequal show_end 1 %}
<li class="paginate_button" aria-controls="editable" tabindex="0"><a href="?page={{ p.num_pages }}" title="第{{ page }}页">...{{ p.num_pages }}</a></li>
{% endifequal %}
{% if contacts.has_next %}
<li class="paginate_button next" aria-controls="editable" tabindex="0" id="editable_next">
<a href="?page={{ contacts.next_page_number }}">Next</a>
</li>
{% else %}
<li class="paginate_button next disabled" aria-controls="editable" tabindex="0" id="editable_next">
<a href="#">Next</a>
</li>
{% endif %}
{% endif %}
</ul> </ul>
</div> </div>
</div> </div>
<script>
$(document).ready(function(){
$('.page').click(function(){
var searchStr = location.search;
var old_href = $(this).attr('href').replace('?', '');
var searchArray = searchStr.split('&');
if (searchStr.indexOf('page')){
searchArray.pop();
}
searchArray.push(old_href);
if (searchArray.length > 2){
$(this).attr('href', searchArray.join('&'));
}
})
});
</script>
...@@ -46,20 +46,28 @@ ...@@ -46,20 +46,28 @@
{% endif %} {% endif %}
<div class="form-group"> <div class="form-group">
<label for="username" class="col-sm-2 control-label">默认用户名<span class="red-fonts">*</span></label> <label for="username" class="col-sm-2 control-label">默认用户名<span class="red-fonts">*</span></label>
<input name="setting" value="default" style="display: none">
<div class="col-sm-8"> <div class="col-sm-8">
<input id="username" name="username" placeholder="Username" type="text" value="{{ setting_r.default_user }}" class="form-control"> <input id="username" name="username" placeholder="Username" type="text" value="{{ setting_default.default_user }}" class="form-control">
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<label for="port" class="col-sm-2 control-label">默认ssh端口<span class="red-fonts">*</span></label> <label for="port" class="col-sm-2 control-label">默认ssh端口<span class="red-fonts">*</span></label>
<div class="col-sm-8"> <div class="col-sm-8">
<input id="port" name="port" placeholder="Port" type="text" value="{{ setting_r.default_port }}" class="form-control"> <input id="port" name="port" placeholder="Port" type="text" value="{{ setting_default.default_port }}" class="form-control">
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<label for="key" class="col-sm-2 control-label">默认密钥<span class="red-fonts">*</span></label> <label for="key" class="col-sm-2 control-label">默认密码</label>
<div class="col-sm-8">
<input id="password" name="password" placeholder="Password" type="password" value="{{ setting_default.default_password }}" class="form-control">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="key" class="col-sm-2 control-label">默认密钥</label>
<div class="col-sm-8"> <div class="col-sm-8">
<textarea class="form-control" name="key" placeholder="请复制粘贴私钥(原来的因为安全原因不被显示)" rows="10" style="font-size: 9px;"></textarea> <textarea class="form-control" name="key" placeholder="请复制粘贴私钥(原来的因为安全原因不被显示)" rows="10" style="font-size: 9px;"></textarea>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment