mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-22 20:12:56 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb7a3ea053 | ||
|
|
0499a7265a | ||
|
|
cd80fbcdbb | ||
|
|
5814b833ed | ||
|
|
cda0b9c90a | ||
|
|
609bba569e | ||
|
|
77c7f8fb54 | ||
|
|
f65290ef38 | ||
|
|
de594aebd5 | ||
|
|
2be81c0322 | ||
|
|
e902fccd39 | ||
|
|
61d162312f | ||
|
|
30a3cd2911 | ||
|
|
2dfe9337a2 | ||
|
|
c188696328 | ||
|
|
f8fac06e1b |
@@ -4,10 +4,10 @@
|
|||||||
#欢迎使用Jumpserver
|
#欢迎使用Jumpserver
|
||||||
**Jumpserver** 是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能。基于ssh协议来管理,客户端无需安装agent。
|
**Jumpserver** 是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能。基于ssh协议来管理,客户端无需安装agent。
|
||||||
支持常见系统:
|
支持常见系统:
|
||||||
1. redhat centos
|
1. CentOS, RedHat, Fedora, Amazon Linux
|
||||||
2. debian
|
2. Debian
|
||||||
3. suse ubuntu
|
3. SUSE, Ubuntu
|
||||||
4. freebsd
|
4. FreeBSD
|
||||||
5. 其他ssh协议硬件设备
|
5. 其他ssh协议硬件设备
|
||||||
|
|
||||||
###截图:
|
###截图:
|
||||||
|
|||||||
154
connect.py
154
connect.py
@@ -436,8 +436,11 @@ class Nav(object):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.search_result = {}
|
self.search_result = None
|
||||||
self.user_perm = {}
|
self.user_perm = get_group_user_perm(self.user)
|
||||||
|
self.perm_assets = sorted(self.user_perm.get('asset', []).keys(),
|
||||||
|
key=lambda x: [int(num) for num in x.ip.split('.') if num.isdigit()])
|
||||||
|
self.perm_asset_groups = self.user_perm.get('asset_group', [])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def print_nav():
|
def print_nav():
|
||||||
@@ -460,46 +463,83 @@ class Nav(object):
|
|||||||
"""
|
"""
|
||||||
print textwrap.dedent(msg)
|
print textwrap.dedent(msg)
|
||||||
|
|
||||||
def search(self, str_r=''):
|
def get_asset_group_member(self, str_r):
|
||||||
gid_pattern = re.compile(r'^g\d+$')
|
gid_pattern = re.compile(r'^g\d+$')
|
||||||
# 获取用户授权的所有主机信息
|
|
||||||
if not self.user_perm:
|
|
||||||
self.user_perm = get_group_user_perm(self.user)
|
|
||||||
user_asset_all = self.user_perm.get('asset').keys()
|
|
||||||
# 搜索结果保存
|
|
||||||
user_asset_search = []
|
|
||||||
if str_r:
|
|
||||||
# 资产组组id匹配
|
|
||||||
if gid_pattern.match(str_r):
|
|
||||||
gid = int(str_r.lstrip('g'))
|
|
||||||
# 获取资产组包含的资产
|
|
||||||
asset_group = get_object(AssetGroup, id=gid)
|
|
||||||
if asset_group:
|
|
||||||
user_asset_search = asset_group.asset_set.all()
|
|
||||||
else:
|
|
||||||
color_print('没有该资产组或没有权限')
|
|
||||||
return
|
|
||||||
|
|
||||||
|
if gid_pattern.match(str_r):
|
||||||
|
gid = int(str_r.lstrip('g'))
|
||||||
|
# 获取资产组包含的资产
|
||||||
|
asset_group = get_object(AssetGroup, id=gid)
|
||||||
|
if asset_group:
|
||||||
|
self.search_result = list(asset_group.asset_set.all())
|
||||||
else:
|
else:
|
||||||
|
color_print('没有该资产组或没有权限')
|
||||||
|
return
|
||||||
|
|
||||||
|
def search(self, str_r=''):
|
||||||
|
# 搜索结果保存
|
||||||
|
if str_r:
|
||||||
|
try:
|
||||||
|
id_ = int(str_r)
|
||||||
|
if id_ < len(self.search_result):
|
||||||
|
self.search_result = [self.search_result[id_]]
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
except (ValueError, TypeError):
|
||||||
# 匹配 ip, hostname, 备注
|
# 匹配 ip, hostname, 备注
|
||||||
for asset in user_asset_all:
|
self.search_result = [asset for asset in self.perm_assets if str_r in str(asset.ip)
|
||||||
if str_r in asset.ip or str_r in str(asset.hostname) or str_r in str(asset.comment):
|
or str_r in str(asset.hostname) or str_r in str(asset.comment)]
|
||||||
user_asset_search.append(asset)
|
|
||||||
else:
|
else:
|
||||||
# 如果没有输入就展现所有
|
# 如果没有输入就展现所有
|
||||||
user_asset_search = user_asset_all
|
self.search_result = self.perm_assets
|
||||||
|
|
||||||
self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search))
|
def print_search_result(self):
|
||||||
color_print('[%-3s] %-12s %-15s %-5s %-10s %s' % ('ID', '主机名', 'IP', '端口', '系统用户', '备注'), 'title')
|
color_print('[%-3s] %-12s %-15s %-5s %-10s %s' % ('ID', '主机名', 'IP', '端口', '系统用户', '备注'), 'title')
|
||||||
for index, asset in self.search_result.items():
|
if hasattr(self.search_result, '__iter__'):
|
||||||
# 获取该资产信息
|
for index, asset in enumerate(self.search_result):
|
||||||
asset_info = get_asset_info(asset)
|
# 获取该资产信息
|
||||||
# 获取该资产包含的角色
|
asset_info = get_asset_info(asset)
|
||||||
role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')]
|
# 获取该资产包含的角色
|
||||||
print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
|
role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')]
|
||||||
role, asset.comment)
|
print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'),
|
||||||
|
role, asset.comment)
|
||||||
print
|
print
|
||||||
|
|
||||||
|
def try_connect(self):
|
||||||
|
try:
|
||||||
|
asset = self.search_result[0]
|
||||||
|
roles = list(self.user_perm.get('asset').get(asset).get('role'))
|
||||||
|
if len(roles) == 1:
|
||||||
|
role = roles[0]
|
||||||
|
elif len(roles) > 1:
|
||||||
|
print "\033[32m[ID] 系统用户\033[0m"
|
||||||
|
for index, role in enumerate(roles):
|
||||||
|
print "[%-2s] %s" % (index, role.name)
|
||||||
|
print
|
||||||
|
print "授权系统用户超过1个,请输入ID, q退出"
|
||||||
|
try:
|
||||||
|
role_index = raw_input("\033[1;32mID>:\033[0m ").strip()
|
||||||
|
if role_index == 'q':
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
role = roles[int(role_index)]
|
||||||
|
except IndexError:
|
||||||
|
color_print('请输入正确ID', 'red')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
color_print('没有映射用户', 'red')
|
||||||
|
return
|
||||||
|
|
||||||
|
ssh_tty = SshTty(login_user, asset, role)
|
||||||
|
print('Connecting %s ...' % asset.hostname)
|
||||||
|
ssh_tty.connect()
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
color_print('请输入正确ID', 'red')
|
||||||
|
except ServerError, e:
|
||||||
|
color_print(e, 'red')
|
||||||
|
|
||||||
def print_asset_group(self):
|
def print_asset_group(self):
|
||||||
"""
|
"""
|
||||||
打印用户授权的资产组
|
打印用户授权的资产组
|
||||||
@@ -515,9 +555,6 @@ class Nav(object):
|
|||||||
批量执行命令
|
批量执行命令
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
if not self.user_perm:
|
|
||||||
self.user_perm = get_group_user_perm(self.user)
|
|
||||||
|
|
||||||
roles = self.user_perm.get('role').keys()
|
roles = self.user_perm.get('role').keys()
|
||||||
if len(roles) > 1: # 授权角色数大于1
|
if len(roles) > 1: # 授权角色数大于1
|
||||||
color_print('[%-2s] %-15s' % ('ID', '系统用户'), 'info')
|
color_print('[%-2s] %-15s' % ('ID', '系统用户'), 'info')
|
||||||
@@ -587,8 +624,6 @@ class Nav(object):
|
|||||||
|
|
||||||
def upload(self):
|
def upload(self):
|
||||||
while True:
|
while True:
|
||||||
if not self.user_perm:
|
|
||||||
self.user_perm = get_group_user_perm(self.user)
|
|
||||||
try:
|
try:
|
||||||
print "进入批量上传模式"
|
print "进入批量上传模式"
|
||||||
print "请输入主机名或ansible支持的pattern, 多个主机:分隔 q退出"
|
print "请输入主机名或ansible支持的pattern, 多个主机:分隔 q退出"
|
||||||
@@ -640,8 +675,6 @@ class Nav(object):
|
|||||||
|
|
||||||
def download(self):
|
def download(self):
|
||||||
while True:
|
while True:
|
||||||
if not self.user_perm:
|
|
||||||
self.user_perm = get_group_user_perm(self.user)
|
|
||||||
try:
|
try:
|
||||||
print "进入批量下载模式"
|
print "进入批量下载模式"
|
||||||
print "请输入主机名或ansible支持的pattern, 多个主机:分隔,q退出"
|
print "请输入主机名或ansible支持的pattern, 多个主机:分隔,q退出"
|
||||||
@@ -723,9 +756,14 @@ def main():
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if option in ['P', 'p', '\n', '']:
|
if option in ['P', 'p', '\n', '']:
|
||||||
nav.search()
|
nav.search()
|
||||||
|
nav.print_search_result()
|
||||||
continue
|
continue
|
||||||
if option.startswith('/') or gid_pattern.match(option):
|
if option.startswith('/'):
|
||||||
nav.search(option.lstrip('/'))
|
nav.search(option.lstrip('/'))
|
||||||
|
nav.print_search_result()
|
||||||
|
elif gid_pattern.match(option):
|
||||||
|
nav.get_asset_group_member(str_r=option)
|
||||||
|
nav.print_search_result()
|
||||||
elif option in ['G', 'g']:
|
elif option in ['G', 'g']:
|
||||||
nav.print_asset_group()
|
nav.print_asset_group()
|
||||||
continue
|
continue
|
||||||
@@ -741,36 +779,12 @@ def main():
|
|||||||
elif option in ['Q', 'q', 'exit']:
|
elif option in ['Q', 'q', 'exit']:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
else:
|
else:
|
||||||
try:
|
nav.search(option)
|
||||||
asset = nav.search_result[int(option)]
|
if len(nav.search_result) == 1:
|
||||||
roles = nav.user_perm.get('asset').get(asset).get('role')
|
nav.try_connect()
|
||||||
if len(roles) > 1:
|
else:
|
||||||
role_check = dict(zip(range(len(roles)), roles))
|
nav.print_search_result()
|
||||||
print "\033[32m[ID] 系统用户\033[0m"
|
|
||||||
for index, role in role_check.items():
|
|
||||||
print "[%-2s] %s" % (index, role.name)
|
|
||||||
print
|
|
||||||
print "授权系统用户超过1个,请输入ID, q退出"
|
|
||||||
try:
|
|
||||||
role_index = raw_input("\033[1;32mID>:\033[0m ").strip()
|
|
||||||
if role_index == 'q':
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
role = role_check[int(role_index)]
|
|
||||||
except IndexError:
|
|
||||||
color_print('请输入正确ID', 'red')
|
|
||||||
continue
|
|
||||||
elif len(roles) == 1:
|
|
||||||
role = list(roles)[0]
|
|
||||||
else:
|
|
||||||
color_print('没有映射用户', 'red')
|
|
||||||
continue
|
|
||||||
ssh_tty = SshTty(login_user, asset, role)
|
|
||||||
ssh_tty.connect()
|
|
||||||
except (KeyError, ValueError):
|
|
||||||
color_print('请输入正确ID', 'red')
|
|
||||||
except ServerError, e:
|
|
||||||
color_print(e, 'red')
|
|
||||||
except IndexError, e:
|
except IndexError, e:
|
||||||
color_print(e)
|
color_print(e)
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|||||||
@@ -80,12 +80,12 @@ class PreSetup(object):
|
|||||||
self.ip = ''
|
self.ip = ''
|
||||||
self.key = ''.join(random.choice(string.ascii_lowercase + string.digits) \
|
self.key = ''.join(random.choice(string.ascii_lowercase + string.digits) \
|
||||||
for _ in range(16))
|
for _ in range(16))
|
||||||
self.dist = platform.dist()[0].lower()
|
self.dist = platform.linux_distribution(supported_dists=['system'])[0].lower()
|
||||||
self.version = platform.dist()[1]
|
self.version = platform.linux_distribution(supported_dists=['system'])[1]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _is_redhat(self):
|
def _is_redhat(self):
|
||||||
if self.dist == "centos" or self.dist == "redhat" or self.dist == "fedora":
|
if self.dist == "centos" or self.dist == "redhat" or self.dist == "fedora" or self.dist == "amazon linux ami":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -105,7 +105,7 @@ class PreSetup(object):
|
|||||||
|
|
||||||
def check_platform(self):
|
def check_platform(self):
|
||||||
if not (self._is_redhat or self._is_ubuntu):
|
if not (self._is_redhat or self._is_ubuntu):
|
||||||
print(u"支持的平台: CentOS, RedHat, Fedora, Debian, Ubuntu, 暂不支持其他平台安装.")
|
print(u"支持的平台: CentOS, RedHat, Fedora, Debian, Ubuntu, Amazon Linux, 暂不支持其他平台安装.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -233,7 +233,9 @@ class ExecHandler(tornado.websocket.WebSocketHandler):
|
|||||||
def open(self):
|
def open(self):
|
||||||
logger.debug('Websocket: Open exec request')
|
logger.debug('Websocket: Open exec request')
|
||||||
role_name = self.get_argument('role', 'sb')
|
role_name = self.get_argument('role', 'sb')
|
||||||
self.remote_ip = self.request.remote_ip
|
self.remote_ip = self.request.headers.get("X-Real-IP")
|
||||||
|
if not self.remote_ip:
|
||||||
|
self.remote_ip = self.request.remote_ip
|
||||||
logger.debug('Web执行命令: 请求系统用户 %s' % role_name)
|
logger.debug('Web执行命令: 请求系统用户 %s' % role_name)
|
||||||
self.role = get_object(PermRole, name=role_name)
|
self.role = get_object(PermRole, name=role_name)
|
||||||
self.perm = get_group_user_perm(self.user)
|
self.perm = get_group_user_perm(self.user)
|
||||||
@@ -362,6 +364,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if 'resize' in jsondata.get('data'):
|
if 'resize' in jsondata.get('data'):
|
||||||
|
self.termlog.write(jsondata)
|
||||||
self.channel.resize_pty(
|
self.channel.resize_pty(
|
||||||
jsondata.get('data').get('resize').get('cols', 80),
|
jsondata.get('data').get('resize').get('cols', 80),
|
||||||
jsondata.get('data').get('resize').get('rows', 24)
|
jsondata.get('data').get('resize').get('rows', 24)
|
||||||
|
|||||||
@@ -56,7 +56,12 @@ NgApp.controller('TerminalRecordCtrl', function ($scope, $http) {
|
|||||||
document.getElementById("beforeScrubberText").innerHTML = buildTimeString(time);
|
document.getElementById("beforeScrubberText").innerHTML = buildTimeString(time);
|
||||||
for (; pos < timelist.length; pos++) {
|
for (; pos < timelist.length; pos++) {
|
||||||
if (timelist[pos] * 1000 <= time) {
|
if (timelist[pos] * 1000 <= time) {
|
||||||
term.write(data[timelist[pos]]);
|
try{
|
||||||
|
var findResize = JSON.parse(data[timelist[pos]])['reszie'];
|
||||||
|
term.resize(findResize['cols'], findResize['rows'])
|
||||||
|
} catch (err) {
|
||||||
|
term.write(data[timelist[pos]]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ WSSHClient.prototype.connect = function (options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this._connection.onmessage = function (evt) {
|
this._connection.onmessage = function (evt) {
|
||||||
try{
|
try {
|
||||||
options.onData(evt.data);
|
options.onData(evt.data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var data = JSON.parse(evt.data.toString());
|
var data = JSON.parse(evt.data.toString());
|
||||||
@@ -55,6 +55,15 @@ WSSHClient.prototype.send = function (data) {
|
|||||||
|
|
||||||
function openTerminal(options) {
|
function openTerminal(options) {
|
||||||
var client = new WSSHClient();
|
var client = new WSSHClient();
|
||||||
|
var rowHeight, colWidth;
|
||||||
|
try {
|
||||||
|
rowHeight = localStorage.getItem('term-row');
|
||||||
|
colWidth = localStorage.getItem('term-col');
|
||||||
|
} catch (err) {
|
||||||
|
rowHeight = 24;
|
||||||
|
colWidth = 80
|
||||||
|
}
|
||||||
|
|
||||||
var term = new Terminal({
|
var term = new Terminal({
|
||||||
rows: rowHeight,
|
rows: rowHeight,
|
||||||
cols: colWidth,
|
cols: colWidth,
|
||||||
@@ -66,7 +75,7 @@ function openTerminal(options) {
|
|||||||
client.send(data)
|
client.send(data)
|
||||||
});
|
});
|
||||||
$('.terminal').detach().appendTo('#term');
|
$('.terminal').detach().appendTo('#term');
|
||||||
term.resize(80, 24);
|
//term.resize(colWidth, rowHeight);
|
||||||
term.write('Connecting...');
|
term.write('Connecting...');
|
||||||
client.connect($.extend(options, {
|
client.connect($.extend(options, {
|
||||||
onError: function (error) {
|
onError: function (error) {
|
||||||
@@ -74,6 +83,7 @@ function openTerminal(options) {
|
|||||||
},
|
},
|
||||||
onConnect: function () {
|
onConnect: function () {
|
||||||
// Erase our connecting message
|
// Erase our connecting message
|
||||||
|
client.send({'resize': {'rows': rowHeight, 'cols': colWidth}});
|
||||||
term.write('\r');
|
term.write('\r');
|
||||||
},
|
},
|
||||||
onClose: function () {
|
onClose: function () {
|
||||||
@@ -83,20 +93,20 @@ function openTerminal(options) {
|
|||||||
term.write(data);
|
term.write(data);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
rowHeight = 0.0 + 1.00 * $('.terminal').height() / 24;
|
//rowHeight = 0.0 + 1.00 * $('.terminal').height() / 24;
|
||||||
colWidth = 0.0 + 1.00 * $('.terminal').width() / 80;
|
//colWidth = 0.0 + 1.00 * $('.terminal').width() / 80;
|
||||||
return {'term': term, 'client': client};
|
return {'term': term, 'client': client};
|
||||||
}
|
}
|
||||||
|
|
||||||
function resize() {
|
//function resize() {
|
||||||
$('.terminal').css('width', window.innerWidth - 25);
|
// $('.terminal').css('width', window.innerWidth - 25);
|
||||||
console.log(window.innerWidth);
|
// console.log(window.innerWidth);
|
||||||
console.log(window.innerWidth - 10);
|
// console.log(window.innerWidth - 10);
|
||||||
var rows = Math.floor(window.innerHeight / rowHeight) - 2;
|
// var rows = Math.floor(window.innerHeight / rowHeight) - 2;
|
||||||
var cols = Math.floor(window.innerWidth / colWidth) - 1;
|
// var cols = Math.floor(window.innerWidth / colWidth) - 1;
|
||||||
|
//
|
||||||
return {rows: rows, cols: cols};
|
// return {rows: rows, cols: cols};
|
||||||
}
|
//}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var options = {};
|
var options = {};
|
||||||
@@ -112,5 +122,21 @@ $(document).ready(function () {
|
|||||||
// term_client.client.send({'resize': {'rows': geom.rows, 'cols': geom.cols}});
|
// term_client.client.send({'resize': {'rows': geom.rows, 'cols': geom.cols}});
|
||||||
// $('#ssh').show();
|
// $('#ssh').show();
|
||||||
//}
|
//}
|
||||||
|
try {
|
||||||
|
$('#term-row')[0].value = localStorage.getItem('term-row');
|
||||||
|
$('#term-col')[0].value = localStorage.getItem('term-col');
|
||||||
|
} catch (err) {
|
||||||
|
$('#term-row')[0].value = 24;
|
||||||
|
$('#term-col')[0].value = 80;
|
||||||
|
}
|
||||||
|
$('#col-row').click(function () {
|
||||||
|
var col = $('#term-col').val();
|
||||||
|
var row = $('#term-row').val();
|
||||||
|
localStorage.setItem('term-col', col);
|
||||||
|
localStorage.setItem('term-row', row);
|
||||||
|
term_client.term.resize(col, row);
|
||||||
|
term_client.client.send({'resize': {'rows': row, 'cols': col}});
|
||||||
|
$('#ssh').show();
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -173,8 +173,14 @@
|
|||||||
$('.terminal').detach().appendTo('#term');
|
$('.terminal').detach().appendTo('#term');
|
||||||
$('.terminal').show();
|
$('.terminal').show();
|
||||||
socket.onmessage = function(evt){
|
socket.onmessage = function(evt){
|
||||||
term.write(evt.data);
|
try {
|
||||||
}}, 1000);
|
var findResize = JSON.parse(evt.data)['resize'];
|
||||||
|
term.resize(findResize['cols'], findResize['rows'])
|
||||||
|
} catch (err) {
|
||||||
|
term.write(evt.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
return tag[0];
|
return tag[0];
|
||||||
} ,
|
} ,
|
||||||
|
|||||||
@@ -1,43 +1,57 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Jumpserver Web Terminal: {{ hostname }}</title>
|
<title>Jumpserver Web Terminal: {{ hostname }}</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
padding-bottom: 40px;
|
padding-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal {
|
.terminal {
|
||||||
border: #000 solid 5px;
|
border: #000 solid 5px;
|
||||||
font-family: "Monaco", "Microsoft Yahei", "DejaVu Sans Mono", "Liberation Mono", monospace;
|
font-family: "Monaco", "Microsoft Yahei", "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #f0f0f0;
|
color: #f0f0f0;
|
||||||
background: #000;
|
background: #000;
|
||||||
width: 600px;
|
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
|
||||||
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
|
white-space: nowrap;
|
||||||
white-space: nowrap;
|
display: inline-block;
|
||||||
display: inline-block;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.reverse-video {
|
.reverse-video {
|
||||||
color: #000;
|
color: #000;
|
||||||
background: #f0f0f0;
|
background: #f0f0f0;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
.clock {
|
||||||
<div class="container">
|
line-height: 1;
|
||||||
<div id="term">
|
margin: 0 auto;
|
||||||
</div>
|
border: 1px solid #ffffff;
|
||||||
</div>
|
color: #fff;
|
||||||
|
background-color: #ffffff;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
<script type="application/javascript" src="/static/js/jquery-2.1.1.js">
|
<body>
|
||||||
</script>
|
<div class="container">
|
||||||
<script type="application/javascript" src="/static/js/term.js">
|
<div id="term">
|
||||||
</script>
|
</div>
|
||||||
<script type="application/javascript" src="/static/js/webterminal.js"></script>
|
</div>
|
||||||
</body>
|
<div class="clock">
|
||||||
|
<input type="number" min="80" placeholder="col" id="term-col"/>
|
||||||
|
<input type="number" min="24" placeholder="row" id="term-row"/>
|
||||||
|
<button id="col-row">修改窗口大小</button>
|
||||||
|
</div>
|
||||||
|
<script type="application/javascript" src="/static/js/jquery-2.1.1.js">
|
||||||
|
</script>
|
||||||
|
<script type="application/javascript" src="/static/js/term.js">
|
||||||
|
</script>
|
||||||
|
<script type="application/javascript" src="/static/js/webterminal.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
timely: 2,
|
timely: 2,
|
||||||
theme: "yellow_right_effect",
|
theme: "yellow_right_effect",
|
||||||
rules: {
|
rules: {
|
||||||
check_name: [/^\w{2,20}$/, '大小写字母数字和下划线,2-20位'],
|
check_name: [/^(\w|\-){2,20}$/, '大小写字母、数字、中划线和下划线,2-20位'],
|
||||||
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
check_port: [/^\d{1,5}$/, '端口号不正确'],
|
||||||
either: function(){
|
either: function(){
|
||||||
return $('#password').val() == ''
|
return $('#password').val() == ''
|
||||||
|
|||||||
Reference in New Issue
Block a user