Unverified Commit cd4a486f authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #31 from jumpserver/dev

优化Luna左侧树,使用websocket等
parents fc19819e a0bb32eb
cn.json
\ No newline at end of file
......@@ -4,802 +4,907 @@ from flask import Flask, send_from_directory, render_template, request, jsonify,
from flask_socketio import SocketIO, Namespace, emit, join_room, leave_room
import paramiko
import uuid
import eventlet
from threading import Lock
from flask import Flask, request, current_app, redirect
app = Flask(__name__, template_folder='dist')
socketio = None
thread = None
thread_lock = Lock()
class SSHws(Namespace):
def __init__(self, *args, **kwargs):
self.clients = dict()
super().__init__(*args, **kwargs)
def ssh_with_password(self, connection):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("127.0.0.1", 22, "liuzheng", "liuzheng")
self.clients[request.sid]["chan"][connection] = ssh.invoke_shell(term='xterm',
width=self.clients[request.sid]["cols"],
height=self.clients[request.sid]["rows"])
# self.socketio.start_background_task(self.send_data, self.clients[request.sid]["chan"])
# self.chan.settimeout(0.1)
self.socketio.start_background_task(self.sent_data, self, self.clients[request.sid]["chan"][connection],
self.clients[request.sid]["room"],
connection, request.sid)
def sent_data(self, s, chan, room, connection, sid):
while True:
if connection not in s.clients[sid]["chan"].keys():
return
try:
data = chan.recv(2048).decode('utf-8', 'replace')
s.emit(event='data', data={"data": data, "room": connection}, room=room)
except RuntimeError:
print(room, connection)
def send_data(self, s):
# Todo: 这里涉及到并发优化
while True:
for sid in self.clients:
if self.clients[sid]["chan"]:
for room, chan in self.clients[sid]["chan"]:
try:
data = chan.recv(2048).decode('utf-8', 'replace')
s.emit(event='data', data={"data": data, "room": room}, room=self.clients[sid]["room"])
except RuntimeError:
print(self.clients)
def on_connect(self):
self.clients[request.sid] = {
"cols": int(request.cookies.get('cols', 80)),
"rows": int(request.cookies.get('rows', 24)),
"room": str(uuid.uuid4()),
"chan": dict()
}
print(request.sid)
join_room(self.clients[request.sid]["room"])
# self.socketio.start_background_task(self.send_data, self)
def on_data(self, message):
self.clients[request.sid]["chan"][message["room"]].send(message["data"])
def on_host(self, message):
connection = str(uuid.uuid4())
self.emit('room', {'room': connection, 'secret': message['secret']})
self.ssh_with_password(connection)
def on_resize(self, message):
self.clients[request.sid]["cols"] = message.get('cols', 80)
self.clients[request.sid]["rows"] = message.get('rows', 24)
for room in self.clients[request.sid]["chan"]:
self.clients[request.sid]["chan"][room].resize_pty(width=self.clients[request.sid]["cols"],
height=self.clients[request.sid]["rows"], width_pixels=1,
height_pixels=1)
def on_room(self, sessionid):
if sessionid not in self.clients.keys():
self.emit('error', "no such session", room=self.clients[request.sid]["room"])
else:
self.emit('room', self.clients[sessionid]["room"], room=self.clients[request.sid]["room"])
def on_join(self, room):
self.leave_room(room=self.clients[request.sid]["room"])
join_room(room)
def on_token(self, token):
self.on_host(token)
def on_disconnect(self):
print("disconnect")
for connection in self.clients[request.sid]["chan"]:
self.clients[request.sid]["chan"][connection].close()
del self.clients[request.sid]["chan"]
pass
def on_leave(self):
leave_room(self.room)
def on_logout(self, connection):
print("logout", connection)
if connection:
self.clients[request.sid]["chan"][connection].close()
del self.clients[request.sid]["chan"][connection]
def __init__(self, *args, **kwargs):
"""
:param args:
:param kwargs:
self.connections = {
"request_sid": {
"room_id": {
"id": room_id,
"proxy": None,
"client": None,
"forwarder": None,
"request": None,
"cols": 80,
"rows": 24
},
...
},
...
}
"""
super().__init__(*args, **kwargs)
self.connections = dict()
def ssh_with_password(self, ws, room_id):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.244.176", 22, "root", "redhat123")
width, height = self.get_win_size()
chan = ssh.invoke_shell(term='xterm', width=width, height=height)
# self.socketio.start_background_task(self.send_data, self.clients[request.sid]["chan"])
# self.chan.settimeout(0.1)
return chan
def proxy(self, ws, chan, room_id):
# eventlet.monkey_patch(thread=True, select=True, socket=True)
while True:
data = chan.recv(1024)
socketio.sleep(1)
if len(data) == 0:
break
ws.emit(event="data", data={"data": data.decode(), "room": room_id}, room=room_id, namespace='/ssh')
def new_connection(self):
self.connections[request.sid] = dict()
def new_room(self):
room_id = str(uuid.uuid4())
room = {
"id": room_id,
"proxy": None,
"client": None,
"forwarder": None,
"request": self.ssh_with_password(self, room_id),
"cols": 80,
"rows": 24
}
self.connections[request.sid][room_id] = room
return room
@staticmethod
def get_win_size():
cols_request = request.cookies.get('cols')
rows_request = request.cookies.get('rows')
if cols_request and cols_request.isdigit():
cols = int(cols_request)
else:
cols = 80
if rows_request and rows_request.isdigit():
rows = int(rows_request)
else:
rows = 24
return cols, rows
def on_connect(self):
print("On connect event trigger")
self.new_connection()
# self.connections[request.sid] = connection
# self.rooms[connection['room']] = {
# "admin": request.sid,
# "member": [],
# "rw": []
# }
# join_room(connection['room'])
def on_host(self, message):
# 此处获取主机的信息
print("On host event trigger")
asset_id = message.get('uuid', None)
user_id = message.get('userid', None)
secret = message.get('secret', None)
room = self.new_room()
self.emit('room', {'room': room["id"], 'secret': secret})
print("Join room: {}".format(room["id"]))
join_room(room["id"])
if not asset_id or not user_id:
# self.on_connect()
return
global thread
if thread is None:
thread = self.socketio.start_background_task(
self.proxy, self, room["request"], room["id"]
)
def on_data(self, message):
"""
收到浏览器请求
:param message: {"data": "xxx", "room": "xxx"}
:return:
"""
room_id = message.get('room')
room = self.connections.get(request.sid, {}).get(room_id)
if not room:
return
room["request"].send(message['data'])
def on_token(self, message):
# 此处获取token含有的主机的信息
print("On token trigger")
token = message.get('token', None)
secret = message.get('secret', None)
room = self.new_room()
self.emit('room', {'room': room["id"], 'secret': secret})
if not token or not secret:
print("Token or secret is None")
self.emit('data', {'data': "\nOperation not permitted!",
'room': room["id"]})
self.emit('disconnect')
return None
info = self.app.service.get_token_asset(token)
print(info)
if not info:
print("Token info is None")
self.emit('data', {'data': "\nOperation not permitted!",
'room': room["id"]})
self.emit('disconnect')
return None
user_id = info.get('user', None)
self.current_user = self.app.service.get_user_profile(user_id)
room["request"].user = self.current_user
print(self.current_user)
self.on_host({
'secret': secret,
'uuid': info['asset'],
'userid': info['system_user'],
})
def on_resize(self, message):
cols, rows = message.get('cols', None), message.get('rows', None)
print("On resize event trigger: {}*{}".format(cols, rows))
rooms = self.connections.get(request.sid)
if not rooms:
return
room_tmp = list(rooms.values())[0]
if (room_tmp["cols"], room_tmp["rows"]) != (cols, rows):
for room in rooms.values():
room["request"].resize_pty(width=cols, height=rows)
# room["request"].change_size_event.set()
# room.update({"cols": cols, "rows": rows})
# def on_room(self, session_id):
# print("On room event trigger")
# if session_id not in self.connections.keys():
# self.emit(
# 'error', "no such session",
# room=self.connections[request.sid]["room"]
# )
# else:
# self.emit(
# 'room', self.connections[session_id]["room"],
# room=self.connections[request.sid]["room"]
# )
#
# def on_join(self, room):
# print("On join room event trigger")
# self.on_leave(self.connections[request.id]["room"])
# self.connections[request.sid]["room"] = room
# self.rooms[room]["member"].append(request.sid)
# join_room(room=room)
#
# def on_leave(self, room):
# print("On leave room event trigger")
# if self.rooms[room]["admin"] == request.sid:
# self.emit("data", "\nAdmin leave", room=room)
# del self.rooms[room]
# leave_room(room=room)
def on_disconnect(self):
print("On disconnect event trigger")
# self.on_leave(self.clients[request.sid]["room"])
for room in self.connections.get(request.sid, {}):
self.on_logout(room["id"])
del self.connections[request.sid]
def on_logout(self, room_id):
print("On logout event trigger")
room = self.connections.get(request.sid, {}).get(room_id)
if room:
room["proxy"].close()
del self.connections[request.sid][room_id]
del room
@app.route('/luna/<path:path>')
def send_js(path):
return send_from_directory('dist', path)
return send_from_directory('dist', path)
@app.route('/api/perms/v1/user/nodes-assets/')
def asset_groups_assets():
assets = [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"name": "新节点 12",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_granted": [
{
assets = [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"name": "新节点 12",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_granted": [
{
"id": "1600ed6d-e3b6-434c-a960-c5bb818806b6",
"hostname": "windows1",
"ip": "10.1.10.178",
"port": 3389,
"system_users_granted": [
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
],
"is_active": True,
"system_users_join": "Administrator",
"os": "",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Windows",
"comment": ""
},
{
},
{
"id": "b952a481-a624-467e-b97f-9435155f0d53",
"hostname": "testserver",
"ip": "10.1.10.192",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "CentOS",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
"id": "ad594b10-9f64-4913-b7b1-135fe63561d1",
},
{
"id": "9fcd7a09-a171-4cb7-b2f9-a025754f8635",
"hostname": "ali-windows",
"ip": "47.104.206.228",
"port": 3389,
"system_users_granted": [
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
],
"is_active": True,
"system_users_join": "Administrator",
"os": "",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Windows",
"comment": ""
},
{
},
{
"id": "b6f16269-d02a-4055-9cd8-460fa10b1540",
"hostname": "test-vm3",
"ip": "172.19.185.8",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "",
"domain": "8789580f-b5ca-4478-b6d3-d0dafc4c48e8",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
},
{
"id": "27e50edc-52d9-41ef-8c9e-1bff9d1628b2",
"hostname": "test-vm2",
"ip": "172.19.185.7",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "",
"domain": "8789580f-b5ca-4478-b6d3-d0dafc4c48e8",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
},
{
"id": "9ef36bb3-1bed-455f-be09-3770d3f4bf97",
"hostname": "test-vm1",
"ip": "172.19.185.6",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "",
"domain": "8789580f-b5ca-4478-b6d3-d0dafc4c48e8",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
}
],
"assets_amount": 6
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"name": "网域测试",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_granted": [
{
}
],
"assets_amount": 6
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"name": "网域测试",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_granted": [
{
"id": "1600ed6d-e3b6-434c-a960-c5bb818806b6",
"hostname": "windows1",
"ip": "10.1.10.178",
"port": 3389,
"system_users_granted": [
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
],
"is_active": True,
"system_users_join": "Administrator",
"os": "",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Windows",
"comment": ""
},
{
},
{
"id": "b952a481-a624-467e-b97f-9435155f0d53",
"hostname": "testserver",
"ip": "10.1.10.192",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "CentOS",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
},
{
"id": "b6f16269-d02a-4055-9cd8-460fa10b1540",
"hostname": "test-vm3",
"ip": "172.19.185.8",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "",
"domain": "8789580f-b5ca-4478-b6d3-d0dafc4c48e8",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
},
{
"id": "27e50edc-52d9-41ef-8c9e-1bff9d1628b2",
"hostname": "test-vm2",
"ip": "172.19.185.7",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "",
"domain": "8789580f-b5ca-4478-b6d3-d0dafc4c48e8",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
},
{
"id": "9ef36bb3-1bed-455f-be09-3770d3f4bf97",
"hostname": "test-vm1",
"ip": "172.19.185.6",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "",
"domain": "8789580f-b5ca-4478-b6d3-d0dafc4c48e8",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
}
],
"assets_amount": 5
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"name": "Fit2cloud",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_granted": [
{
}
],
"assets_amount": 5
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"name": "Fit2cloud",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_granted": [
{
"id": "b952a481-a624-467e-b97f-9435155f0d53",
"hostname": "testserver",
"ip": "10.1.10.192",
"port": 22,
"system_users_granted": [
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
{
"id": "7e326f71-aee5-4688-8cc1-717919470a09",
"name": "root",
"username": "root",
"priority": 10,
"protocol": "ssh",
"comment": ""
},
{
"id": "17f384f4-683d-4944-a38d-db73608b92a9",
"name": "zbh-test",
"username": "zbh",
"priority": 10,
"protocol": "ssh",
"comment": ""
}
],
"is_active": True,
"system_users_join": "root, zbh",
"os": "CentOS",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"key": "0:11",
"value": "网域测试",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Linux",
"comment": ""
},
{
},
{
"id": "ad594b10-9f64-4913-b7b1-135fe63561d1",
"hostname": "ali-windows",
"ip": "47.104.206.228",
"port": 3389,
"system_users_granted": [
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
{
"id": "8763b81a-bb5e-484a-abca-10514c7bb185",
"name": "组织1-部门1-Administrator",
"username": "Administrator",
"priority": 10,
"protocol": "rdp",
"comment": ""
}
],
"is_active": True,
"system_users_join": "Administrator",
"os": "",
"domain": "",
"nodes": [
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
{
"id": "67f92d6c-0f91-4d20-a0e4-ac83b7dd02b6",
"key": "0:11:77",
"value": "新节点 12",
"parent": "9c83d432-a353-4a4e-9fd9-be27a5851c2d",
"assets_amount": 6,
"is_asset": False
},
{
"id": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"key": "0",
"value": "Fit2cloud",
"parent": "be9d9c3a-68d0-40ec-887c-5815d68e2f2c",
"assets_amount": 6,
"is_asset": False
}
],
"platform": "Windows",
"comment": ""
}
],
"assets_amount": 2
}
]
return jsonify(assets)
}
],
"assets_amount": 2
}
]
return jsonify(assets)
@app.route('/api/users/v1/profile/')
def user_profile():
assets = {
"id": "4fc67feb-9efa-4e7b-94b0-b73356a87b2e",
"username": "admin",
"name": "Administrator",
"email": "admin@mycomany.com",
"is_active": True,
"is_superuser": True,
"role": "Administrator",
"groups": [
"Default"
],
"wechat": "",
"phone": 13888888888,
"comment": "",
"date_expired": "2087-12-16 07:41:35"
}
return jsonify(assets)
assets = {
"id": "4fc67feb-9efa-4e7b-94b0-b73356a87b2e",
"username": "admin",
"name": "Administrator",
"email": "admin@mycomany.com",
"is_active": True,
"is_superuser": True,
"role": "Administrator",
"groups": [
"Default"
],
"wechat": "",
"phone": 13888888888,
"comment": "",
"date_expired": "2087-12-16 07:41:35"
}
return jsonify(assets)
@app.route('/api/terminal/v1/sessions/test/replay/')
def replay():
return redirect("http://jps.ilz.me/media/2017-12-24/ec87a486-0344-4f12-b27a-620321944f7f.gz")
return redirect("http://jps.ilz.me/media/2017-12-24/ec87a486-0344-4f12-b27a-620321944f7f.gz")
@app.route('/api/terminal/v2/sessions/<pk>/replay/')
def get_session_replay(pk):
# return jsonify({
# 'type': 'guacamole',
# 'src': 'http://localhost/media/2018-05-07/5c205f0a-b5ae-405a-9444-c0d59262ec29.gz',
# 'status': 'DONE'
# })
return jsonify({
'type': 'json',
'src': 'http://localhost/media/2018-05-02/dbd5302d-7861-4810-b555-5fe71e26ccc3.gz',
'status': 'DONE',
})
@app.route('/luna/i18n/<i18n>')
def i18n(i18n):
return send_file('./i18n/' + i18n)
return send_file('./i18n/' + i18n)
def read_file(filename, charset='utf-8'):
with open(filename, 'r') as f:
return f.read().decode(charset)
with open(filename, 'rb') as f:
return f.read().decode(charset)
if __name__ == '__main__':
socketio = SocketIO(app)
socketio.on_namespace(SSHws('/ssh'))
socketio.run(app)
async_mode = 'threading'
socketio = SocketIO(app, async_mode=async_mode)
socketio.on_namespace(SSHws('/ssh'))
socketio.run(app, debug=True)
{
"name": "luna",
"version": "1.0.0",
"version": "1.3.0",
"license": "GPLv3",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build --environment prod --base-href=/luna/ --deploy '/luna/'",
"build": "ng build --environment prod --aot --prod --base-href=/luna/ --deploy '/luna/'",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.0",
"@angular/cdk": "^5.2.0",
"@angular/animations": "^5.2.10",
"@angular/cdk": "^5.2.5",
"@angular/common": "5.2.0",
"@angular/compiler": "5.2.0",
"@angular/core": "5.2.0",
"@angular/flex-layout": "^5.0.0-beta.14",
"@angular/forms": "5.2.0",
"@angular/http": "5.2.0",
"@angular/material": "^5.1.1",
"@angular/material": "^5.2.5",
"@angular/platform-browser": "5.2.0",
"@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0",
"@swimlane/ngx-datatable": "^11.1.7",
"@swimlane/ngx-ui": "^20.2.0",
"ajv": "^6.2.1",
"animate.css": "^3.5.2",
"@swimlane/ngx-datatable": "^11.3.2",
"@swimlane/ngx-ui": "^20.2.1",
"ajv": "^6.5.0",
"animate.css": "^3.6.1",
"body-parser": "^1.18.2",
"bootstrap": "^4.0.0-beta.3",
"bootstrap": "^4.1.1",
"clipboard": "^1.7.1",
"compass-mixins": "^0.12.10",
"core-js": "2.5.3",
"directory-encoder": "^0.9.2",
"elfinder": "^2.1.33",
"elfinder": "git+https://github.com/Studio-42/elFinder.git#2.1.33",
"filetree-css": "^1.0.0",
"font-awesome": "4.7.0",
"guacamole-common-js": "^0.9.14-b",
......@@ -44,42 +43,43 @@
"jquery-slimscroll": "^1.3.8",
"jquery-sparkline": "^2.4.0",
"jvectormap": "1.2.2",
"lodash": "^4.17.5",
"lodash": "^4.17.10",
"material-design-icons": "^3.0.1",
"materialize-css": "^0.100.2",
"metismenu": "^2.5.0",
"metismenu": "^2.7.7",
"mstsc.js": "^0.2.4",
"ng2-charts": "^1.5.0",
"ngx-bootstrap": "^1.6.6",
"ngx-cookie-service": "^1.0.10",
"ngx-layer": "0.0.4",
"ngx-logger": "^1.1.2",
"ngx-logger": "^2.2.4",
"ngx-perfect-scrollbar": "5.2.0",
"ngx-progressbar": "^2.1.1",
"npm-font-open-sans": "^1.1.0",
"peity": "^3.2.1",
"peity": "^3.3.0",
"popper.js": "1.12.9",
"requirejs": "^2.3.5",
"roboto-fontface": "^0.8.0",
"rxjs": "5.5.6",
"sass-math": "^1.0.0",
"socket.io": "^2.0.3",
"socket.io": "^2.1.0",
"socket.io-client": "^2.1.0",
"ssh-keygen": "^0.4.1",
"tether": "^1.4.0",
"tether": "^1.4.4",
"tslib": "^1.9.0",
"uuid-js": "^0.7.5",
"xterm": "^2.9.2",
"xterm": "^3.3.0",
"zone.js": "0.8.20"
},
"devDependencies": {
"@angular-devkit/core": "^0.4.2",
"@angular-devkit/schematics": "^0.4.2",
"@angular/cli": "^1.7.3",
"@angular-devkit/core": "^0.4.9",
"@angular-devkit/schematics": "^0.4.9",
"@angular/cli": "^1.7.4",
"@angular/compiler-cli": "5.2.0",
"@angular/language-service": "5.2.0",
"@types/jasmine": "2.8.4",
"@types/jasminewd2": "~2.0.2",
"@types/xterm": "^3.0.0",
"codelyzer": "4.0.2",
"jasmine-core": "2.8.0",
"jasmine-spec-reporter": "4.2.1",
......
@import "~@swimlane/ngx-ui/release/components/tree/tree.component";
@import "~@swimlane/ngx-ui/release/components/icon/icon.component";
.ztree {
height: 100%;
......
......@@ -32,7 +32,12 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
hiddenNodes: any;
onCzTreeOnClick(event, treeId, treeNode, clickFlag) {
this.Connect(treeNode);
if (treeNode.isParent) {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
zTreeObj.expandNode(treeNode);
} else {
this.Connect(treeNode);
}
}
constructor(private _appService: AppService,
......@@ -58,51 +63,36 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
draw() {
const nodes = {};
const assets = {};
this.Data.forEach((v, i) => {
if (!nodes[v['id']]) {
nodes[v['id']] = true;
this.Data.forEach(node => {
if (!nodes[node['id']]) {
nodes[node['id']] = true;
this.nodes.push({
'id': v['id'],
'key': v['key'],
'name': v['name'],
'value': v['value'],
'pId': v['parent'],
'assets_amount': v['assets_amount'],
'id': node['id'],
'key': node['key'],
'name': node['name'],
'value': node['value'],
'pId': node['parent'],
'assets_amount': node['assets_amount'],
'isParent': true,
'open': v['key'] === '0'
'open': node['key'] === '0'
});
}
v['assets_granted'].forEach((vv, ii) => {
vv['nodes'].forEach((vvv, iii) => {
if (!nodes[vvv['id']]) {
this.nodes.push({
'id': vvv['id'],
'key': vvv['key'],
'name': vvv['value'],
'value': vvv['value'],
'pId': vvv['parent'],
'assets_amount': vvv['assets_amount'],
'isParent': true,
'open': vvv['key'] === '0'
});
nodes[vvv['id']] = true;
}
if (!assets[vv['id'] + '@' + vvv['id']]) {
this.nodes.push({
'id': vv['id'],
'name': vv['hostname'],
'value': vv['hostname'],
'system_users_granted': vv['system_users_granted'],
'platform': vv['platform'],
'comment': vv['comment'],
'isParent': false,
'pId': vvv['id'],
'iconSkin': vv['platform'].toLowerCase()
});
assets[vv['id'] + '@' + vvv['id']] = true;
}
});
node['assets_granted'].forEach(asset => {
if (!assets[asset['id']]) {
this.nodes.push({
'id': asset['id'],
'name': asset['hostname'],
'value': asset['hostname'],
'system_users_granted': asset['system_users_granted'],
'platform': asset['platform'],
'comment': asset['comment'],
'isParent': false,
'pId': node['id'],
'iconSkin': asset['platform'].toLowerCase()
});
assets[asset['id'] + '@' + node['id']] = true;
}
});
});
this.nodes.sort(function(node1, node2) {
......@@ -143,6 +133,8 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
} else if (host.system_users_granted.length === 1) {
user = host.system_users_granted[0];
this.login(host, user);
} else {
alert('该主机没有授权登录用户');
}
}
......@@ -184,10 +176,10 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
filter() {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
const _keywords = $('#keyword').val();
zTreeObj.showNodes(this.hiddenNodes);
function filterFunc(node) {
const _keywords = $('#keyword').val();
if (node.isParent || node.name.indexOf(_keywords) !== -1) {
return false;
}
......@@ -195,9 +187,12 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
this.hiddenNodes = zTreeObj.getNodesByFilter(filterFunc);
zTreeObj.hideNodes(this.hiddenNodes);
zTreeObj.expandAll(true);
if (_keywords) {
zTreeObj.expandAll(true);
} else {
zTreeObj.expandAll(false);
}
}
}
......
......@@ -13,7 +13,6 @@ import {DataStore, i18n} from '../../globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
// import * as layer from 'layui-layer/src/layer.js';
declare let layer: any;
@Component({
......@@ -66,6 +65,9 @@ export class ElementNavComponent implements OnInit {
break;
}
case'Disconnect': {
if (!confirm('断开当前连接? (RDP暂不支持)')) {
break;
}
switch (NavList.List[NavList.Active].type) {
case 'ssh': {
ControlComponent.TerminalDisconnect(NavList.Active);
......@@ -83,6 +85,9 @@ export class ElementNavComponent implements OnInit {
break;
}
case'DisconnectAll': {
if (!confirm('断开所有连接? (RDP暂不支持)')) {
break;
}
ControlComponent.DisconnectAll();
break;
}
......@@ -255,12 +260,8 @@ export class ElementNavComponent implements OnInit {
'href': '',
'name': 'Share session (read/write)',
'disable': true
},
{
'id': 'Settings',
'click': 'Settings',
'name': 'Settings'
}]
}
]
}, {
'id': 'Help',
'name': 'Help',
......
import {AfterViewInit, Component, Input, OnInit } from '@angular/core';
import * as io from 'socket.io-client';
// import {ws} from '../../globals';
import * as Terminal from 'xterm/dist/xterm';
import {Terminal} from 'xterm';
import {NavList} from '../../pages/control/control/control.component';
import {UUIDService} from '../../app.service';
import {CookieService} from 'ngx-cookie-service';
import {TermWS} from '../../globals';
const ws = io.connect('/ssh');
const ws = TermWS;
@Component({
selector: 'elements-ssh-term',
......@@ -21,17 +21,22 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit {
term: Terminal;
secret: string;
constructor(private _uuid: UUIDService) {
constructor(private _uuid: UUIDService, private _cookie: CookieService) {
}
ngOnInit() {
this.secret = this._uuid.gen();
this.term = new Terminal({
// cols: 80,
// rows: 24,
useStyle: true,
screenKeys: true,
fontFamily: '"Monaco", "Consolas", "monospace"',
fontSize: 14,
rightClickSelectsWord: true,
theme: {
background: '#1f1b1b'
}
});
const rowInit = parseInt(this._cookie.get('rows') || '24', 10);
const colsInit = parseInt(this._cookie.get('cols') || '80', 10);
this.term.resize(colsInit, rowInit);
}
ngAfterViewInit() {
......@@ -57,34 +62,35 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit {
});
ws.on('data', data => {
if (data['room'] === NavList.List[that.index].room) {
const view = NavList.List[that.index];
if (view && data['room'] === view.room) {
that.term.write(data['data']);
}
});
ws.on('disconnect', () => {
that.disconnect();
that.close();
});
ws.on('logout', (data) => {
if (data['room'] === NavList.List[that.index].room) {
NavList.List[that.index].connected = false;
// this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
});
ws.on('room', data => {
console.log('Compile secret: ', data['secret'], this.secret);
if (data['secret'] === this.secret) {
console.log('Set room: ', data['room']);
NavList.List[that.index].room = data['room'];
console.log('get', that.index, 'room: ', NavList.List[that.index].room);
}
});
}
disconnect() {
NavList.List[this.index].connected = false;
// this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
ws.emit('logout', NavList.List[this.index].room);
close() {
const view = NavList.List[this.index];
if (view) {
NavList.List[this.index].connected = false;
ws.emit('logout', NavList.List[this.index].room);
}
}
active() {
......
......@@ -17,18 +17,18 @@ div {
*padding: 15px 0 15px 15px;;
}
div.terminal div span {
min-width: 12px;
}
/*div.terminal div span {*/
/*min-width: 12px;*/
/*}*/
.terminal div {
user-select: text;
}
/*.terminal div {*/
/*user-select: text;*/
/*}*/
/*.terminal, .terminal .xterm-viewport {*/
/*background-color: inherit;*/
/*}*/
.terminal .xterm-rows {
background-color: #1f1b1b;
}
/*.terminal .xterm-rows {*/
/*background-color: #1f1b1b;*/
/*}*/
<div style="padding: 15px">
<div style='padding:15px 0 15px 15px'>
<div #term (mouseenter)="active()"></div>
</div>
import {AfterViewInit, Component, Input, Output, OnInit, ViewChild, EventEmitter} from '@angular/core';
import {ElementRef} from '@angular/core';
import * as Terminal from 'xterm/dist/xterm';
// import { Terminal } from 'xterm';
import * as $ from 'jquery/dist/jquery.min.js';
import {Terminal} from 'xterm';
import {fit} from 'xterm/lib/addons/fit/fit';
import {Observable} from 'rxjs/Rx';
import {CookieService} from 'ngx-cookie-service';
import * as $ from 'jquery/dist/jquery.min.js';
import 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import {NavList} from '../../pages/control/control/control.component';
@Component({
selector: 'elements-term',
templateUrl: './term.component.html',
......@@ -17,11 +21,9 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
@ViewChild('term') el: ElementRef;
@Input() term: Terminal;
@Output() winSizeChangeTrigger = new EventEmitter<Array<number>>();
col = 80;
row = 24;
winSizeChange$: Observable<any>;
constructor() {
constructor(private _cookie: CookieService) {
}
ngOnInit() {
......@@ -30,29 +32,37 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
.distinctUntilChanged();
this.winSizeChange$
.subscribe(() => this.resizeTerm());
.subscribe(() => {
if (NavList.List[NavList.Active].type === 'ssh') {
this.resizeTerm();
}
});
}
ngAfterViewInit() {
this.term.open(this.el.nativeElement, true);
this.term.open(this.el.nativeElement);
this.resizeTerm();
}
getWinSize() {
const activeEle = $('#winContainer');
const markerEle = $('#marker');
const cols = Math.floor(activeEle.width() / markerEle.width() * 6) - 6;
const rows = Math.floor(activeEle.height() / markerEle.height()) - 1;
return [cols, rows];
}
resizeTerm() {
let contentElement = $('.window.active');
if (contentElement.length === 0) {
contentElement = $('body');
// fit(this.term);
const size = this.getWinSize();
if (isNaN(size[0])) {
fit(this.term);
} else {
this.term.resize(size[0], size[1]);
this.winSizeChangeTrigger.emit([this.term.cols, this.term.rows]);
}
const markerElement = $('#marker');
const col = Math.floor((contentElement.width() - 30) / markerElement.width() * 6) - 1;
const row = Math.floor((contentElement.height() - 30) / markerElement.height());
this.col = col > 80 ? col : 80;
this.row = row > 24 ? row : 24;
console.log('Box size: ', contentElement.width(), '*', contentElement.height());
console.log('Mark size: ', markerElement.width(), '*', markerElement.height());
console.log('Resize term size: ', this.col, this.row);
this.term.resize(this.col, this.row);
this.winSizeChangeTrigger.emit([this.col, this.row]);
this._cookie.set('cols', this.term.cols.toString(), 0, '/', document.domain);
this._cookie.set('rows', this.term.rows.toString(), 0, '/', document.domain);
}
active() {
......
'use strict';
import * as terminal from 'xterm/dist/xterm';
import * as io from 'socket.io-client';
export function Terminal(xargs: any) {
return terminal(xargs);
}
import {Terminal} from 'xterm';
export const TermWS = io.connect('/ssh');
export let term: {
term: any;
col: number;
row: number;
} = {
term: Terminal({
cols: 80,
rows: 24,
useStyle: true,
screenKeys: true,
scrollback: 10
}),
col: 80,
row: 24,
};
export const sep = '/';
export let Video: {
id: string,
......@@ -153,4 +134,3 @@ export let wsEvent: {
};
export const i18n = new Map();
......@@ -26,14 +26,12 @@ export class PagesConnectComponent implements OnInit {
this.system = this._appService.getQueryString('system');
this.token = this._appService.getQueryString('token');
jQuery('body').css('background-color', 'black');
this.userid = this._localStorage.get('user-' + this.token);
this.authToken = this._localStorage.get('authToken-' + this.token);
this.base = this._localStorage.get('base-' + this.token);
jQuery('body').css('background-color', '#1f1b1b');
if (this.system === 'windows') {
if (!this.userid) {
this._http.get_user_id_from_token(this.token)
.subscribe(
......
......@@ -2,7 +2,7 @@
<div fxFlex="1 1 20%" minBasis="100px" maxBasis="800px" fxFlexFill ngxSplitArea *ngIf="DataStore.leftbarshow">
<pages-control-cleftbar></pages-control-cleftbar>
</div>
<div fxFlex="0" ngxSplitHandle></div>
<div fxFlex="0" ngxSplitHandle [style.display]="activeViewIsRdp() ? 'none' : 'block'" (mouseup)="dragSplitBtn($event)"></div>
<div fxFlex="1 1 80%" ngxSplitArea class="content">
<pages-control-control></pages-control-control>
</div>
......
......@@ -6,8 +6,8 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService} from '../../app.service';
import {DataStore, User} from '../../globals';
import {NavList} from './control/control.component';
@Component({
selector: 'pages-control',
......@@ -21,6 +21,14 @@ export class PagesControlComponent implements OnInit {
constructor() {
}
activeViewIsRdp() {
return NavList.List[NavList.Active].type === 'rdp';
}
dragSplitBtn(evt) {
window.dispatchEvent(new Event('resize'));
}
ngOnInit() {
}
......
......@@ -2,7 +2,7 @@
<div fxFlex="0 0 30px" class="search">
<pages-control-nav></pages-control-nav>
</div>
<div fxFlex="0 0 calc(100%-35px)">
<div fxFlex="0 0 calc(100%-35px)" id="winContainer">
<div class="window" *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'active':i==NavList.Active}" style="height: 100%">
<elements-ssh-term [index]="i"
......
......@@ -69,7 +69,6 @@ export class ControlComponent implements OnInit {
}
static DisconnectAll() {
alert('DisconnectAll');
for (let i = 0; i < NavList.List.length; i++) {
ControlComponent.TerminalDisconnect(i);
}
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { IleftbarComponent } from './ileftbar.component';
describe('IleftbarComponent', () => {
let component: IleftbarComponent;
let fixture: ComponentFixture<IleftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ IleftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IleftbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 主页的左边栏
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService, LogService} from '../../../app.service';
@Component({
selector: 'pages-ileftbar',
templateUrl: './ileftbar.component.html',
styleUrls: ['./ileftbar.component.css']
})
export class IleftbarComponent implements OnInit {
constructor(private _appService: AppService,
private _logger: LogService) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {IleftbarComponent} from './ileftbar.component';
@NgModule({
imports: [
CommonModule
],
declarations: [
IleftbarComponent
]
})
export class IleftbarModule {
}
......@@ -8,7 +8,6 @@ import {PagesReplayComponent} from './replay/replay.component';
import {PagesNotFoundComponent} from './not-found/not-found.component';
import {PagesLoginComponent} from './login/login.component';
import {CleftbarComponent} from './control/cleftbar/cleftbar.component';
import {Mp4Component} from './replay/mp4/mp4.component';
import {JsonComponent} from './replay/json/json.component';
import {ControlComponent} from './control/control/control.component';
import {PagesControlNavComponent} from './control/control/controlnav/nav.component';
......@@ -24,7 +23,7 @@ export const PagesComponents = [
CleftbarComponent,
PagesIndexComponent,
PagesMonitorComponent,
PagesReplayComponent, Mp4Component, JsonComponent,
PagesReplayComponent, JsonComponent,
// PagesSettingComponent,
PagesNotFoundComponent,
PagesLoginComponent,
......
......@@ -16,7 +16,7 @@
<div class="seek-notification">
<p>
Seek in progress...
<button id="cancel-seek" class="btn" (click)="cancelSeek()">Cancel</button>
<button id="cancel-seek" class="btn" (click)="cancelSeek($event)">Cancel</button>
</p>
</div>
</div>
......
......@@ -3,19 +3,19 @@ import * as Guacamole from 'guacamole-common-js/dist/guacamole-common';
import { Replay } from '../replay.model';
function zeroPad(num, minLength) {
let str = num.toString();
// Add leading zeroes until string is long enough
while (str.length < minLength) {
str = '0' + str;
}
return str;
let str = num.toString();
// Add leading zeroes until string is long enough
while (str.length < minLength) {
str = '0' + str;
}
return str;
}
function formatTime(millis) {
const totalSeconds = Math.floor(millis / 1000);
const seconds = totalSeconds % 60;
const minutes = Math.floor(totalSeconds / 60);
return zeroPad(minutes, 2) + ':' + zeroPad(seconds, 2);
function formatTime(millis: number) {
const totalSeconds = Math.floor(millis / 1000);
const seconds = totalSeconds % 60;
const minutes = Math.floor(totalSeconds / 60);
return zeroPad(minutes, 2) + ':' + zeroPad(seconds, 2);
}
@Component({
......@@ -25,7 +25,7 @@ function formatTime(millis) {
})
export class ReplayGuacamoleComponent implements OnInit {
isPlaying = false;
recording: Guacamole.SessionRecording;
recording: any;
playerRef: any;
displayRef: any;
max = 100;
......@@ -64,7 +64,7 @@ export class ReplayGuacamoleComponent implements OnInit {
initRecording() {
const that = this;
this.recording.connect();
this.recording.connect('');
this.recording.onplay = function() {
that.isPlaying = true;
};
......
import {Component, Input, OnInit} from '@angular/core';
import * as Terminal from 'xterm/dist/xterm';
import {Terminal} from 'xterm';
import {HttpService, LogService} from '../../../app.service';
import {Replay} from '../replay.model';
......@@ -25,7 +25,14 @@ export class JsonComponent implements OnInit {
constructor(private _http: HttpService) {}
ngOnInit() {
this.term = new Terminal();
this.term = new Terminal({
fontFamily: '"Monaco", "Consolas", "monospace"',
fontSize: 14,
rightClickSelectsWord: true,
theme: {
background: '#1f1b1b'
}
});
if (this.replay.src !== 'READY') {
this._http.get_replay_data(this.replay.src)
.subscribe(
......
<div [ngStyle]="{'width.px': 800,'height.px': 600}">
<video controls>
<source [src]="replay.src" type="video/mp4">
Your browser does not support the video tag.
<br>
您的浏览器不支持,请升级
</video>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Mp4Component } from './mp4.component';
describe('Mp4Component', () => {
let component: Mp4Component;
let fixture: ComponentFixture<Mp4Component>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ Mp4Component ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(Mp4Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, Input, OnInit} from '@angular/core';
import {Replay} from '../replay.model';
@Component({
selector: 'app-replay-mp4',
templateUrl: './mp4.component.html',
styleUrls: ['./mp4.component.css']
})
export class Mp4Component implements OnInit {
@Input() replay: Replay;
constructor() {
}
ngOnInit() {
if (this.replay.height === 0) {
this.replay.height = 600;
}
if (this.replay.width === 0) {
this.replay.width = 800;
}
}
}
......@@ -27,31 +27,14 @@ export class PagesReplayComponent implements OnInit {
this._http.get_replay_json(token)
.subscribe(
data => {
// this.replay = data.json() as Replay;
this.replay.type = data['type'];
this.replay.src = data['src'];
this.replay.id = data['id'];
},
err => {
this._http.get_replay(token);
alert('没找到录像文件');
}
// err => {
// this._http.get_replay(token)
// .subscribe(
// data => {
// this.replay.type = 'json';
// this.replay.json = data;
// this.replay.src = 'READY';
// this.replay.timelist = Object.keys(this.replay.json).map(Number);
// this.replay.timelist = this.replay.timelist.sort(function (a, b) {
// return a - b;
// });
// this.replay.totalTime = this.replay.timelist[this.replay.timelist.length - 1] * 1000;
//
// }, err2 => {
// alert('无法下载');
// this._logger.error(err2);
// },
// );
// }
);
}
}
export const environment = {
production: true
};
export const version = '1.3.0-{{BUILD_NUMBER}} GPLv2.';
\ No newline at end of file
// export const version = '1.3.0-{{BUILD_NUMBER}} GPLv2.';
// export const version = '1.3.0-101 GPLv2.';
export const version = '1.3.0-{{BUILD_NUMBER}} GPLv2.';
......@@ -10,7 +10,7 @@
</head>
<body>
<app-root></app-root>
<span id="marker" style="display: none">marker</span>
<span id="marker" style="display: none;font-size: 14px">marker</span>
</body>
</html>
......@@ -94,6 +94,6 @@ $material-design-icons-font-path: '~/material-design-icons/iconfont/';
button.icon-split-handle.ngx-split-button {
height: 100%;
border: 1px solid #3a3333;
opacity: 0.5;
//border: 1px solid #3a3333;
opacity: 0.0;
}
......@@ -27,27 +27,49 @@ app-root {
color: white;
}
.terminal {
font-family: 'Monaco', 'Consolas', 'monospace' !important;
font-size: 13px;
white-space: nowrap;
/*display: inline-block;*/
/*background-color: #1f1b1b;*/
}
/*.terminal {*/
/*font-family: 'Monaco', 'Consolas', 'monospace' !important;*/
/*font-size: 13px;*/
/*white-space: nowrap;*/
/*!*display: inline-block;*!*/
/*!*background-color: #1f1b1b;*!*/
/*}*/
.terminal div {
user-select: text;
}
/*.terminal div {*/
/*user-select: text;*/
/*}*/
.terminal .xterm-rows {
background-color: #1f1b1b;
/*margin: 15px;*/
}
/*.terminal .xterm-rows {*/
/*background-color: #1f1b1b;*/
/*!*margin: 15px;*!*/
/*}*/
/*.xterm-rows {*/
/*padding: 15px;*/
/*}*/
/*.xterm .composition-view {*/
/*!* TODO: Composition position got messed up somewhere *!*/
/*background: #1f1b1b !important;*/
/*color: #FFF;*/
/*display: none;*/
/*position: absolute;*/
/*white-space: nowrap;*/
/*z-index: 1;*/
/*}*/
/*.xterm .xterm-viewport {*/
/*!* On OS X this is required in order for the scroll bar to appear fully opaque *!*/
/*background: #1f1b1b !important;*/
/*overflow-y: scroll;*/
/*cursor: default;*/
/*position: absolute;*/
/*right: 0;*/
/*left: 0;*/
/*top: 0;*/
/*bottom: 0;*/
/*}*/
.terminal .xterm-viewport {
background-color: #1f1b1b;
overflow: auto;
......
......@@ -6,6 +6,6 @@ npm run-script build
rm -fr luna*
mv dist luna
cp -R i18n/ luna/
cp -R i18n luna/
tar czf luna.tar.gz luna
md5 luna.tar.gz
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