Commit 3cb6a1cc authored by zheng liu's avatar zheng liu

Merged in dev (pull request #37)

fix: start on load
parents 045b24dc 28ff57ef
...@@ -13,51 +13,64 @@ class SSHws(Namespace): ...@@ -13,51 +13,64 @@ class SSHws(Namespace):
self.clients = dict() self.clients = dict()
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def ssh_with_password(self): def ssh_with_password(self, connection):
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("127.0.0.1", 22, "liuzheng", "liuzheng") ssh.connect("127.0.0.1", 22, "liuzheng", "liuzheng")
self.clients[request.sid]["chan"] = ssh.invoke_shell(term='xterm', width=self.clients[request.sid]["cols"], self.clients[request.sid]["chan"][connection] = ssh.invoke_shell(term='xterm',
width=self.clients[request.sid]["cols"],
height=self.clients[request.sid]["rows"]) height=self.clients[request.sid]["rows"])
# self.socketio.start_background_task(self.send_data, self.clients[request.sid]["chan"]) # self.socketio.start_background_task(self.send_data, self.clients[request.sid]["chan"])
# self.chan.settimeout(0.1) # 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): def send_data(self, s):
# Todo: 这里涉及到并发优化
while True: while True:
for sid in self.clients: for sid in self.clients:
try:
if self.clients[sid]["chan"]: if self.clients[sid]["chan"]:
data = self.clients[sid]["chan"].recv(2048).decode('utf-8', 'replace') for room, chan in self.clients[sid]["chan"]:
s.emit(event='data', data=data, room=self.clients[sid]["room"]) 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: except RuntimeError:
print(data)
print(self.clients) print(self.clients)
def on_connect(self): def on_connect(self):
print(request.sid)
self.clients[request.sid] = { self.clients[request.sid] = {
"cols": int(request.cookies.get('cols', 80)), "cols": int(request.cookies.get('cols', 80)),
"rows": int(request.cookies.get('rows', 24)), "rows": int(request.cookies.get('rows', 24)),
"room": str(uuid.uuid4()), "room": str(uuid.uuid4()),
"chan": None "chan": dict()
} }
join_room(self.clients[request.sid]["room"]) join_room(self.clients[request.sid]["room"])
self.socketio.start_background_task(self.send_data, self) # self.socketio.start_background_task(self.send_data, self)
def on_data(self, message): def on_data(self, message):
self.clients[request.sid]["chan"].send(message) self.clients[request.sid]["chan"][message["room"]].send(message["data"])
def on_host(self, message): def on_host(self, message):
# self.clients[request.sid]["room"] = str(uuid.uuid4()) connection = str(uuid.uuid4())
# self.emit('room', self.clients[request.sid]["chan"]["room"]) self.emit('room', {'room': connection, 'secret': message['secret']})
# join_room(self.clients[request.sid]["room"]) self.ssh_with_password(connection)
self.ssh_with_password()
print(message, self.clients[request.sid]["room"])
def on_resize(self, message): def on_resize(self, message):
self.clients[request.sid]["cols"] = message.get('cols', 80) self.clients[request.sid]["cols"] = message.get('cols', 80)
self.clients[request.sid]["rows"] = message.get('rows', 24) self.clients[request.sid]["rows"] = message.get('rows', 24)
self.clients[request.sid]["chan"].resize_pty(width=self.clients[request.sid]["rows"], 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=self.clients[request.sid]["rows"], width_pixels=1,
height_pixels=1) height_pixels=1)
...@@ -73,11 +86,20 @@ class SSHws(Namespace): ...@@ -73,11 +86,20 @@ class SSHws(Namespace):
def on_disconnect(self): def on_disconnect(self):
print("disconnect") print("disconnect")
for connection in self.clients[request.sid]["chan"]:
self.clients[request.sid]["chan"][connection].close()
del self.clients[request.sid]["chan"]
pass pass
def on_leave(self): def on_leave(self):
leave_room(self.room) 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]
@app.route('/luna/<path:path>') @app.route('/luna/<path:path>')
def send_js(path): def send_js(path):
......
...@@ -62,6 +62,14 @@ ...@@ -62,6 +62,14 @@
"tslib": "1.7.1" "tslib": "1.7.1"
} }
}, },
"@angular/cdk": {
"version": "2.0.0-beta.12",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-2.0.0-beta.12.tgz",
"integrity": "sha1-OiQ8tiuT9OA5EgunD5ANyeI1Yi4=",
"requires": {
"tslib": "1.7.1"
}
},
"@angular/cli": { "@angular/cli": {
"version": "1.5.2", "version": "1.5.2",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.5.2.tgz",
...@@ -7059,6 +7067,11 @@ ...@@ -7059,6 +7067,11 @@
"resolved": "https://registry.npmjs.org/ngx-bootstrap/-/ngx-bootstrap-1.9.3.tgz", "resolved": "https://registry.npmjs.org/ngx-bootstrap/-/ngx-bootstrap-1.9.3.tgz",
"integrity": "sha512-beoKQGJEFwdg0ctIpGb+vx4PTPkyqHrA5tBAEbnUn7ZlTjjfA6533QYGv3qVoKPDNkkHmLA3lRjWKxEMYepCdg==" "integrity": "sha512-beoKQGJEFwdg0ctIpGb+vx4PTPkyqHrA5tBAEbnUn7ZlTjjfA6533QYGv3qVoKPDNkkHmLA3lRjWKxEMYepCdg=="
}, },
"ngx-contextmenu": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ngx-contextmenu/-/ngx-contextmenu-4.1.1.tgz",
"integrity": "sha512-XSmZxFUV1+sedMwRGvSvzZAAiOCuhb9jtzBNRjoVn5nDFx70IrjalIUiZUsWiDjj0TUMTNpAY8g9+glOdsjnCg=="
},
"no-case": { "no-case": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
...@@ -14383,6 +14396,11 @@ ...@@ -14383,6 +14396,11 @@
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
"dev": true "dev": true
}, },
"uuid-js": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
"integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
},
"uws": { "uws": {
"version": "0.14.5", "version": "0.14.5",
"resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz",
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^4.2.4", "@angular/animations": "^4.2.4",
"@angular/cdk": "^2.0.0-beta.12",
"@angular/common": "^4.2.4", "@angular/common": "^4.2.4",
"@angular/compiler": "^4.2.4", "@angular/compiler": "^4.2.4",
"@angular/core": "^4.2.4", "@angular/core": "^4.2.4",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
"ng2-charts": "^1.5.0", "ng2-charts": "^1.5.0",
"ng2-cookies": "^1.0.12", "ng2-cookies": "^1.0.12",
"ngx-bootstrap": "^1.6.6", "ngx-bootstrap": "^1.6.6",
"ngx-contextmenu": "^4.1.1",
"node": "^9.3.0", "node": "^9.3.0",
"npm": "^5.6.0", "npm": "^5.6.0",
"npm-font-open-sans": "^1.1.0", "npm-font-open-sans": "^1.1.0",
...@@ -57,6 +59,7 @@ ...@@ -57,6 +59,7 @@
"ssh-keygen": "^0.4.1", "ssh-keygen": "^0.4.1",
"term.js": "0.0.7", "term.js": "0.0.7",
"tether": "^1.4.0", "tether": "^1.4.0",
"uuid-js": "^0.7.5",
"xterm": "^2.9.2", "xterm": "^2.9.2",
"zone.js": "^0.8.14" "zone.js": "^0.8.14"
}, },
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<input type="checkbox" id="hostgroup-{{i}}"> <input type="checkbox" id="hostgroup-{{i}}">
<label for="hostgroup-{{i}}">{{hostGroup.name}}</label> <label for="hostgroup-{{i}}">{{hostGroup.name}}</label>
<ul [ngClass]="{'insearch': q }"> <ul [ngClass]="{'insearch': q }">
<li *ngFor="let host of hostGroup.assets_granted | SearchFilter: q" (click)="Connect(host)"> <li *ngFor="let host of hostGroup.assets_granted | SearchFilter: q" (click)="Connect(host)" (contextmenu)="onRightClick($event)">
<i class="fa" [ngClass]="'fa-'+(host.platform||'undefined').toLowerCase()" id="fa-{{i}}"></i> <i class="fa" [ngClass]="'fa-'+(host.platform||'undefined').toLowerCase()" id="fa-{{i}}"></i>
{{host.hostname}} {{host.hostname}}
</li> </li>
...@@ -20,3 +20,4 @@ ...@@ -20,3 +20,4 @@
<div class="footer"> <div class="footer">
Version <strong>{{version}}</strong> Version <strong>{{version}}</strong>
</div> </div>
<!--<app-element-server-menu></app-element-server-menu>-->
...@@ -9,15 +9,15 @@ ...@@ -9,15 +9,15 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core'; import {Logger} from 'angular2-logger/core';
import {AppService, HttpService} from '../../app.service'; import {AppService, HttpService} from '../../app.service';
import {SshComponent} from '../control/ssh/ssh.component';
import {RdpComponent} from '../control/rdp/rdp.component';
import {SearchComponent} from '../search/search.component'; import {SearchComponent} from '../search/search.component';
import {DataStore} from '../../globals'; import {DataStore} from '../../globals';
import {version} from '../../../environments/environment'; import {version} from '../../../environments/environment';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
import * as layer from 'layui-layer/src/layer.js'; import * as layer from 'layui-layer/src/layer.js';
import * as UUID from 'uuid-js/lib/uuid.js';
import {ElementServerMenuComponent} from '../../elements/server-menu/server-menu.component';
import {NavList, View} from '../control/control.component';
export class HostGroup { export class HostGroup {
...@@ -36,13 +36,16 @@ export class Host { ...@@ -36,13 +36,16 @@ export class Host {
selector: 'app-cleftbar', selector: 'app-cleftbar',
templateUrl: './cleftbar.component.html', templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.css'], styleUrls: ['./cleftbar.component.css'],
providers: [SshComponent, RdpComponent, SearchComponent] providers: [SearchComponent, ElementServerMenuComponent]
}) })
export class CleftbarComponent implements OnInit { export class CleftbarComponent implements OnInit {
DataStore = DataStore; DataStore = DataStore;
HostGroups: Array<HostGroup>; HostGroups: Array<HostGroup>;
version = version; version = version;
q: string; q: string;
event: MouseEvent;
clientX = 0;
clientY = 0;
static Reload() { static Reload() {
} }
...@@ -78,11 +81,10 @@ export class CleftbarComponent implements OnInit { ...@@ -78,11 +81,10 @@ export class CleftbarComponent implements OnInit {
} }
constructor(private _appService: AppService, constructor(private _appService: AppService,
private _term: SshComponent,
private _rdp: RdpComponent,
private _http: HttpService, private _http: HttpService,
private _search: SearchComponent, private _search: SearchComponent,
private _logger: Logger) { private _logger: Logger,
private _menu: ElementServerMenuComponent) {
this._logger.log('nav.ts:NavComponent'); this._logger.log('nav.ts:NavComponent');
// this._appService.getnav() // this._appService.getnav()
} }
...@@ -117,14 +119,14 @@ export class CleftbarComponent implements OnInit { ...@@ -117,14 +119,14 @@ export class CleftbarComponent implements OnInit {
Connect(host) { Connect(host) {
// console.log(host); // console.log(host);
let user: any; let user: any;
let options = '';
const that = this; const that = this;
if (host.system_users_granted.length > 1) { if (host.system_users_granted.length > 1) {
let options = '';
user = this.checkPriority(host.system_users_granted); user = this.checkPriority(host.system_users_granted);
if (user) { if (user) {
this.login(host, user); this.login(host, user);
} else { } else {
for (let u of host.system_users_granted) { for (const u of host.system_users_granted) {
options += '<option value="' + u.id + '">' + u.username + '</option>'; options += '<option value="' + u.id + '">' + u.username + '</option>';
} }
layer.open({ layer.open({
...@@ -160,16 +162,21 @@ export class CleftbarComponent implements OnInit { ...@@ -160,16 +162,21 @@ export class CleftbarComponent implements OnInit {
} }
login(host, user) { login(host, user) {
const id = NavList.List.length - 1;
if (user) { if (user) {
NavList.List[id].nick = host.hostname;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].user = user;
if (user.protocol === 'ssh') { if (user.protocol === 'ssh') {
jQuery('app-ssh').show(); NavList.List[id].type = 'ssh';
jQuery('app-rdp').hide();
this._term.TerminalConnect(host, user.id);
} else if (user.protocol === 'rdp') { } else if (user.protocol === 'rdp') {
jQuery('app-ssh').hide(); NavList.List[id].type = 'rdp';
jQuery('app-rdp').show();
this._rdp.Connect(host, user.id);
} }
NavList.List.push(new View());
NavList.Active = id;
} }
// if (host.platform) { // if (host.platform) {
// if (host.platform.toLowerCase() === 'linux') { // if (host.platform.toLowerCase() === 'linux') {
...@@ -191,7 +198,7 @@ export class CleftbarComponent implements OnInit { ...@@ -191,7 +198,7 @@ export class CleftbarComponent implements OnInit {
checkPriority(sysUsers) { checkPriority(sysUsers) {
let priority: number = -1; let priority: number = -1;
let user: any; let user: any;
for (let u of sysUsers) { for (const u of sysUsers) {
if (u.priority > priority) { if (u.priority > priority) {
user = u; user = u;
priority = u.priority; priority = u.priority;
...@@ -206,4 +213,10 @@ export class CleftbarComponent implements OnInit { ...@@ -206,4 +213,10 @@ export class CleftbarComponent implements OnInit {
this._search.Search(q); this._search.Search(q);
} }
onRightClick(event: MouseEvent): void {
this.clientX = event.clientX;
this.clientY = event.clientY;
// console.log(this.clientX, this.clientY);
// this._menu.contextmenu(this.clientY, this.clientX);
}
} }
div, app-element-term, app-element-guacamole {
height: 100%;
}
div {
display: none;
}
.active {
display: block;
}
<app-controlnav></app-controlnav> <app-controlnav></app-controlnav>
<app-ssh></app-ssh> <div *ngFor="let m of NavList.List;let i=index"
<!--<app-rdp></app-rdp>--> [ngClass]="{'active':i==NavList.Active}"
>
<app-element-term [host]="m.host"
[userid]="m.user.id"
[index]="i"
*ngIf="m.type=='ssh'">
</app-element-term>
<app-element-iframe [host]="m.host"
[userid]="m.user.id"
[index]="i"
*ngIf="m.type=='rdp'">
</app-element-iframe>
</div>
...@@ -8,12 +8,13 @@ ...@@ -8,12 +8,13 @@
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {TermWS} from '../../globals';
export class Term { // export class Term {
machine: string; // machine: string;
socket: any; // socket: any;
term: any; // term: any;
} // }
export class Rdp { export class Rdp {
machine: string; machine: string;
...@@ -28,8 +29,11 @@ export class View { ...@@ -28,8 +29,11 @@ export class View {
connected: boolean; connected: boolean;
hide: boolean; hide: boolean;
closed: boolean; closed: boolean;
host: any;
user: any;
room: string;
Rdp: Rdp; Rdp: Rdp;
Term: Term; Term: any;
} }
export let NavList: { export let NavList: {
...@@ -46,6 +50,8 @@ export let NavList: { ...@@ -46,6 +50,8 @@ export let NavList: {
styleUrls: ['./control.component.css'] styleUrls: ['./control.component.css']
}) })
export class ControlComponent implements OnInit { export class ControlComponent implements OnInit {
NavList = NavList;
static active(id) { static active(id) {
for (let i in NavList.List) { for (let i in NavList.List) {
if (id.toString() === i) { if (id.toString() === i) {
...@@ -58,10 +64,32 @@ export class ControlComponent implements OnInit { ...@@ -58,10 +64,32 @@ export class ControlComponent implements OnInit {
NavList.Active = id; NavList.Active = id;
} }
static TerminalDisconnect(id) {
if (NavList.List[id].connected) {
NavList.List[id].connected = false;
NavList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
TermWS.emit('logout', NavList.List[id].room);
}
}
static RdpDisconnect(id) {
NavList.List[id].connected = false;
}
static DisconnectAll() {
alert('DisconnectAll');
for (let i = 0; i < NavList.List.length; i++) {
ControlComponent.TerminalDisconnect(i);
}
}
constructor() { constructor() {
} }
ngOnInit() { ngOnInit() {
} }
// trackByFn(index: number, item: View) {
// return item.id;
// }
} }
...@@ -81,3 +81,19 @@ ...@@ -81,3 +81,19 @@
padding: 5px 20px 4px 15px; padding: 5px 20px 4px 15px;
height: 18px; height: 18px;
} }
/*
* scrollbar
*/
.tabs::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #F5F5F5;
}
.tabs::-webkit-scrollbar {
height: 1px;
}
.tabs::-webkit-scrollbar-thumb {
background-color: #19aa8d;
}
...@@ -8,9 +8,7 @@ ...@@ -8,9 +8,7 @@
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {NavList} from '../control.component'; import {ControlComponent, NavList} from '../control.component';
import {SshComponent} from '../ssh/ssh.component';
import {RdpComponent} from '../rdp/rdp.component';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
...@@ -44,12 +42,8 @@ export class ControlnavComponent implements OnInit { ...@@ -44,12 +42,8 @@ export class ControlnavComponent implements OnInit {
NavList.List[index].hide = false; NavList.List[index].hide = false;
NavList.Active = index; NavList.Active = index;
if (NavList.List[index].type === 'ssh') { if (NavList.List[index].type === 'ssh') {
jQuery('app-ssh').show(); NavList.List[index].Term.focus();
jQuery('app-rdp').hide();
NavList.List[index].Term.term.focus();
} else if (NavList.List[index].type === 'rdp') { } else if (NavList.List[index].type === 'rdp') {
jQuery('app-ssh').hide();
jQuery('app-rdp').show();
} }
} }
...@@ -62,9 +56,9 @@ export class ControlnavComponent implements OnInit { ...@@ -62,9 +56,9 @@ export class ControlnavComponent implements OnInit {
close(host, index) { close(host, index) {
if (host.type === 'rdp') { if (host.type === 'rdp') {
RdpComponent.Disconnect(host); ControlComponent.RdpDisconnect(index);
} else if (host.type === 'ssh') { } else if (host.type === 'ssh') {
SshComponent.TerminalDisconnect(host); ControlComponent.TerminalDisconnect(index);
} }
NavList.List.splice(index, 1); NavList.List.splice(index, 1);
ControlnavComponent.checkActive(index); ControlnavComponent.checkActive(index);
......
<div id="rdp">
<div *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'disconnected':!m.connected,'hidden': m.hide || m.type!='rdp'}" id="rdp-{{i}}">
<canvas id="canvas-{{i}}"></canvas>
</div>
</div>
/**
* RDP页面
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {NavList, View, Rdp, ControlComponent} from '../control.component';
import {Mstsc} from 'mstsc.js/client/js/mstsc.js';
import {Client} from 'mstsc.js/client/js/client.js';
import {Canvas} from 'mstsc.js/client/js/canvas.js';
// declare let Mstsc: any;
@Component({
selector: 'app-rdp',
templateUrl: './rdp.component.html',
styleUrls: ['./rdp.component.css']
})
export class RdpComponent implements OnInit {
NavList = NavList;
static Disconnect(host) {
host.connected = false;
}
static DisconnectAll() {
}
constructor() {
}
ngOnInit() {
}
Connect(host, username) {
const id = NavList.List.length - 1;
const canvas = Mstsc.$('canvas-' + id);
// canvas.style.display = 'inline';
// canvas.width = window.innerWidth;
// canvas.height = window.innerHeight;
NavList.List[id].nick = host.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = 'rdp';
NavList.List[id].Rdp = new Rdp;
NavList.List[id].Rdp.token = host.token;
NavList.List[id].Rdp.machine = host.uuid;
NavList.List[id].Rdp.client = new Client.Client(Mstsc.$('canvas-' + id));
NavList.List[id].Rdp.client.connect(host.token, '/rdp/socket.io');
NavList.List.push(new View());
ControlComponent.active(id);
}
}
#term {
width: 100%;
height: 100%;
padding: 15px;
}
#term > div {
height: 100%;
}
.terminal {
border: #000 solid 5px;
color: #f0f0f0;
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
white-space: nowrap;
display: inline-block;
height: 100%;
}
#term .terminal div span {
min-width: 12px;
}
.reverse-video {
color: #000;
background: #f0f0f0;
}
.termChangBar {
line-height: 1;
margin: 0 auto;
border: 1px solid #ffffff;
color: #fff;
background-color: #ffffff;
position: fixed;
right: 0;
top: 0;
}
.hidden {
display: none;
}
<div id="term">
<div *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'disconnected':!m.connected,'hidden': m.hide || m.type!='ssh'}" id="term-{{i}}">
</div>
</div>
/**
* WebTerminal
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import {Cookie} from 'ng2-cookies/ng2-cookies';
import {AppService} from '../../../app.service';
import {NavList, View, Term, ControlComponent} from '../control.component';
import {Terminal} from '../../../globals';
import * as io from 'socket.io-client';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
selector: 'app-ssh',
templateUrl: './ssh.component.html',
styleUrls: ['./ssh.component.css']
})
export class SshComponent implements OnInit {
NavList = NavList;
static TerminalDisconnect(host) {
host.connected = false;
host.Term.socket.destroy();
host.Term.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
static TerminalDisconnectAll() {
alert('TerminalDisconnectAll');
for (let i = 0; i < NavList.List.length; i++) {
SshComponent.TerminalDisconnect(NavList.List[i]);
}
}
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('SshComponent.ts:SshComponent');
}
ngOnInit() {
}
TerminalConnect(host, userid) {
// console.log(host, userid);
const socket = io.connect('/ssh');
let cols = '80';
let rows = '24';
if (Cookie.get('cols')) {
cols = Cookie.get('cols');
}
if (Cookie.get('rows')) {
rows = Cookie.get('rows');
}
Cookie.set('cols', cols, 99, '/', document.domain);
Cookie.set('rows', rows, 99, '/', document.domain);
const id = NavList.List.length - 1;
NavList.List[id].nick = host.hostname;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = 'ssh';
NavList.List[id].Term = new Term;
NavList.List[id].Term.machine = host.id;
NavList.List[id].Term.socket = socket;
NavList.List[id].Term.term = Terminal({
cols: cols,
rows: rows,
useStyle: true,
screenKeys: true,
});
NavList.List.push(new View());
ControlComponent.active(id);
// TermStore.term[id]['term'].on('title', function (title) {
// document.title = title;
// });
NavList.List[id].Term.term.open(document.getElementById('term-' + id), true);
NavList.List[id].Term.term.write('\x1b[31mWelcome to Jumpserver!\x1b[m\r\n');
socket.on('connect', function () {
socket.emit('host', {'uuid': host.id, 'userid': userid});
NavList.List[id].Term.term.on('data', function (data) {
socket.emit('data', data);
});
socket.on('data', function (data) {
NavList.List[id].Term.term.write(data);
});
socket.on('disconnect', function () {
SshComponent.TerminalDisconnect(NavList.List[id]);
// TermStore.term[id]["term"].destroy();
// TermStore.term[id]["connected"] = false;
});
window.onresize = function () {
let col = Math.floor(jQuery('#term').width() / jQuery('#liuzheng').width() * 8) - 3;
let row = Math.floor(jQuery('#term').height() / jQuery('#liuzheng').height()) - 5;
let rows = 24;
let cols = 80;
if (Cookie.get('rows')) {
rows = parseInt(Cookie.get('rows'), 10);
}
if (Cookie.get('cols')) {
cols = parseInt(Cookie.get('cols'), 10);
}
if (col < 80) {
col = 80;
}
if (row < 24) {
row = 24;
}
if (cols === col && row === rows) {
} else {
for (let _i = 0; _i < NavList.List.length; _i++) {
if (NavList.List[_i].connected) {
NavList.List[_i].Term.socket.emit('resize', {'cols': col, 'rows': row});
NavList.List[_i].Term.term.resize(col, row);
}
}
Cookie.set('cols', String(col), 99, '/', document.domain);
Cookie.set('rows', String(row), 99, '/', document.domain);
}
};
jQuery(window).resize();
});
}
}
...@@ -16,6 +16,7 @@ import {ReplayPageComponent} from './replay-page/replay-page.component'; ...@@ -16,6 +16,7 @@ import {ReplayPageComponent} from './replay-page/replay-page.component';
import {MonitorPageComponent} from './monitor-page/monitor-page.component'; import {MonitorPageComponent} from './monitor-page/monitor-page.component';
import {RdpPageComponent} from './rdp-page/rdp-page.component'; import {RdpPageComponent} from './rdp-page/rdp-page.component';
import {TermPageComponent} from './term-page/term-page.component'; import {TermPageComponent} from './term-page/term-page.component';
import {ElementServerMenuComponent} from './elements/server-menu/server-menu.component';
const appRoutes: Routes = [ const appRoutes: Routes = [
{path: 'users/login', component: LoginComponent}, {path: 'users/login', component: LoginComponent},
...@@ -23,6 +24,7 @@ const appRoutes: Routes = [ ...@@ -23,6 +24,7 @@ const appRoutes: Routes = [
{path: 'term/:token', component: TermPageComponent}, {path: 'term/:token', component: TermPageComponent},
{path: 'replay/:token', component: ReplayPageComponent}, {path: 'replay/:token', component: ReplayPageComponent},
{path: 'monitor/:token', component: MonitorPageComponent}, {path: 'monitor/:token', component: MonitorPageComponent},
{path: 'test', component: ElementServerMenuComponent},
{path: '', component: ControlPageComponent}, {path: '', component: ControlPageComponent},
{path: '**', component: NotFoundComponent} {path: '**', component: NotFoundComponent}
]; ];
......
...@@ -25,14 +25,15 @@ import {ElementInteractiveComponent} from './elements/interactive/interactive.co ...@@ -25,14 +25,15 @@ import {ElementInteractiveComponent} from './elements/interactive/interactive.co
import {ElementNavComponent} from './elements/nav/nav.component'; import {ElementNavComponent} from './elements/nav/nav.component';
import {LoginComponent} from './BasicPage/login/login.component'; import {LoginComponent} from './BasicPage/login/login.component';
import {ElementPopupComponent} from './elements/popup/popup.component'; import {ElementPopupComponent} from './elements/popup/popup.component';
import {ElementRdpComponent} from './elements/rdp/rdp.component';
import {ElementServerMenuComponent} from './elements/server-menu/server-menu.component';
import {ElementIframeComponent} from './elements/iframe/iframe.component';
// pages // pages
import {IleftbarComponent} from './IndexPage/ileftbar/ileftbar.component'; import {IleftbarComponent} from './IndexPage/ileftbar/ileftbar.component';
import {SearchComponent, SearchFilter} from './ControlPage/search/search.component'; import {SearchComponent, SearchFilter} from './ControlPage/search/search.component';
import {CleftbarComponent} from './ControlPage/cleftbar/cleftbar.component'; import {CleftbarComponent} from './ControlPage/cleftbar/cleftbar.component';
import {ControlComponent} from './ControlPage/control/control.component'; import {ControlComponent} from './ControlPage/control/control.component';
import {ControlnavComponent} from './ControlPage/control/controlnav/controlnav.component'; import {ControlnavComponent} from './ControlPage/control/controlnav/controlnav.component';
import {RdpComponent} from './ControlPage/control/rdp/rdp.component';
import {SshComponent} from './ControlPage/control/ssh/ssh.component';
import {ControlPageComponent} from './ControlPage/controlpage.component'; import {ControlPageComponent} from './ControlPage/controlpage.component';
import {IndexPageComponent} from './IndexPage/index-page.component'; import {IndexPageComponent} from './IndexPage/index-page.component';
import {NotFoundComponent} from './BasicPage/not-found/not-found.component'; import {NotFoundComponent} from './BasicPage/not-found/not-found.component';
...@@ -45,7 +46,6 @@ import {UtcDatePipe} from './app.pipe'; ...@@ -45,7 +46,6 @@ import {UtcDatePipe} from './app.pipe';
import {MonitorPageComponent} from './monitor-page/monitor-page.component'; import {MonitorPageComponent} from './monitor-page/monitor-page.component';
import {LinuxComponent} from './monitor-page/linux/linux.component'; import {LinuxComponent} from './monitor-page/linux/linux.component';
import {WindowsComponent} from './monitor-page/windows/windows.component'; import {WindowsComponent} from './monitor-page/windows/windows.component';
import {ElementRdpComponent} from './elements/rdp/rdp.component';
@NgModule({ @NgModule({
imports: [ imports: [
...@@ -62,9 +62,9 @@ import {ElementRdpComponent} from './elements/rdp/rdp.component'; ...@@ -62,9 +62,9 @@ import {ElementRdpComponent} from './elements/rdp/rdp.component';
ElementTermComponent, ElementTermComponent,
ElementInteractiveComponent, ElementInteractiveComponent,
ElementRdpComponent, ElementRdpComponent,
ElementServerMenuComponent,
ElementIframeComponent,
LoginComponent, LoginComponent,
RdpComponent,
SshComponent,
SearchComponent, SearchComponent,
SearchFilter, SearchFilter,
IleftbarComponent, IleftbarComponent,
......
<iframe [src]="trust(target)"></iframe>
#rdp { iframe {
width: 100%;
height: 100%;
padding: 15px;
}
#rdp > div, iframe {
width: 100%; width: 100%;
height: 100%; height: 100%;
}
iframe {
border: none; border: none;
background-color: white;
} }
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SshComponent } from './ssh.component'; import { ElementIframeComponent } from './iframe.component';
describe('SshComponent', () => { describe('ElementIframeComponent', () => {
let component: SshComponent; let component: ElementIframeComponent;
let fixture: ComponentFixture<SshComponent>; let fixture: ComponentFixture<ElementIframeComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ SshComponent ] declarations: [ ElementIframeComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SshComponent); fixture = TestBed.createComponent(ElementIframeComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should be created', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });
import {Component, Input, OnInit} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {NavList} from '../../ControlPage/control/control.component';
@Component({
selector: 'app-element-iframe',
templateUrl: './iframe.component.html',
styleUrls: ['./iframe.component.scss']
})
export class ElementIframeComponent implements OnInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
target: string;
constructor(private sanitizer: DomSanitizer) {
}
ngOnInit() {
this.target = 'https://wx.qq.com/';
}
trust(url) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
Disconnect() {
NavList.List[this.index].connected = false;
}
}
...@@ -10,9 +10,7 @@ import {Logger} from 'angular2-logger/core'; ...@@ -10,9 +10,7 @@ import {Logger} from 'angular2-logger/core';
import {AppService, HttpService} from '../../app.service'; import {AppService, HttpService} from '../../app.service';
import {CleftbarComponent} from '../../ControlPage/cleftbar/cleftbar.component'; import {CleftbarComponent} from '../../ControlPage/cleftbar/cleftbar.component';
import {SshComponent} from '../../ControlPage/control/ssh/ssh.component'; import {ControlComponent, NavList} from '../../ControlPage/control/control.component';
import {RdpComponent} from '../../ControlPage/control/rdp/rdp.component';
import {NavList} from '../../ControlPage/control/control.component';
import {DataStore} from '../../globals'; import {DataStore} from '../../globals';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
// import * as layer from 'layui-layer/src/layer.js'; // import * as layer from 'layui-layer/src/layer.js';
...@@ -63,11 +61,11 @@ export class ElementNavComponent implements OnInit { ...@@ -63,11 +61,11 @@ export class ElementNavComponent implements OnInit {
case'Disconnect': { case'Disconnect': {
switch (NavList.List[NavList.Active].type) { switch (NavList.List[NavList.Active].type) {
case 'ssh': { case 'ssh': {
SshComponent.TerminalDisconnect(NavList.List[NavList.Active]); ControlComponent.TerminalDisconnect(NavList.Active);
break; break;
} }
case 'rdp': { case 'rdp': {
RdpComponent.Disconnect(NavList.List[NavList.Active]); ControlComponent.RdpDisconnect(NavList.Active);
break; break;
} }
default: { default: {
...@@ -78,8 +76,7 @@ export class ElementNavComponent implements OnInit { ...@@ -78,8 +76,7 @@ export class ElementNavComponent implements OnInit {
break; break;
} }
case'DisconnectAll': { case'DisconnectAll': {
SshComponent.TerminalDisconnectAll(); ControlComponent.DisconnectAll();
RdpComponent.DisconnectAll();
break; break;
} }
case 'Website': { case 'Website': {
......
<div class="ES-menu" [ngStyle]="{'top':top,'left':left}">
<ul class="dropdown-content">
<li [ngClass]="m.type" *ngFor="let m of MenuList">
<a (click)="m.action" *ngIf="m.type!='line'">{{m.name}}</a>
</li>
</ul>
</div>
.ES-menu {
display: block;
width: 120px;
background-color: #2f2a2a;
position: absolute;
}
.ES-menu ul {
list-style-type: none;
line-height: 24px;
}
.ES-menu li {
display: inline-block;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown:hover {
background-color: #2d2828;
}
.dropdown-content {
position: absolute;
background-color: black;
color: #c6bcbc;
padding: 4px 0;
z-index: 999;
float: none;
list-style: none;
line-height: normal;
direction: ltr;
width: auto;
top: auto;
left: 0px;
margin-left: 0px;
margin-top: 0px;
min-width: 150px;
}
.ES-menu:hover .dropdown-content {
display: block;
}
.ES-menu .dropdown-content li {
float: left;
display: flex;
}
.ES-menu .dropdown-content li a {
padding: 6px 14px 6px 14px;
white-space: nowrap;
font-family: 'Roboto', sans-serif;
font-size: 13px;
font-weight: 300;
position: relative;
text-decoration: none;
min-width: 150px;
line-height: normal;
}
.ES-menu .dropdown-content li a span {
float: right;
}
.dropdown-content li:hover {
background-color: #3a3333;
color: black;
width: 100%;
}
.dropdown-content li.disabled:hover {
background-color: black;
}
.dropdown-content li.disabled a {
color: #c5babc;
}
.dropdown-content li.disabled a:hover {
cursor: default;
color: #c5babc;
}
.ES-menu a {
color: #f0f0f1;
text-align: center;
text-decoration: none;
padding: 6px 15px 6px 15px;
}
.ES-menu a:hover {
color: #fff;
cursor: pointer
}
.ES-menu .line {
height: 1px;
width: 100%;
background-color: white;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RdpComponent } from './rdp.component'; import { ElementServerMenuComponent } from './server-menu.component';
describe('RdpComponent', () => { describe('ElementServerMenuComponent', () => {
let component: RdpComponent; let component: ElementServerMenuComponent;
let fixture: ComponentFixture<RdpComponent>; let fixture: ComponentFixture<ElementServerMenuComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ RdpComponent ] declarations: [ ElementServerMenuComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(RdpComponent); fixture = TestBed.createComponent(ElementServerMenuComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should be created', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });
import {Component, OnInit} from '@angular/core';
export class Menu {
name: string;
type: string;
action: any;
}
@Component({
selector: 'app-element-server-menu',
templateUrl: './server-menu.component.html',
styleUrls: ['./server-menu.component.scss']
})
export class ElementServerMenuComponent implements OnInit {
MenuList: Array<any>;
top: number;
left: number;
constructor() {
}
ngOnInit() {
const m = new Menu();
const line = new Menu();
m.action = '';
m.name = 'test';
m.type = 'lll';
line.type = 'line';
this.MenuList = [m, m, line, m, m];
}
public contextmenu(top: number, left: number) {
this.top = top;
this.left = left;
}
}
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core'; import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
import {ElementRef} from '@angular/core'; import {ElementRef} from '@angular/core';
import {term} from '../../globals'; import {term, Terminal, TermWS} from '../../globals';
import {Cookie} from 'ng2-cookies/ng2-cookies';
import {NavList} from '../../ControlPage/control/control.component';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
import * as UUID from 'uuid-js/lib/uuid.js';
@Component({ @Component({
selector: 'app-element-term', selector: 'app-element-term',
...@@ -9,20 +12,42 @@ import * as jQuery from 'jquery/dist/jquery.min.js'; ...@@ -9,20 +12,42 @@ import * as jQuery from 'jquery/dist/jquery.min.js';
styleUrls: ['./term.component.css'] styleUrls: ['./term.component.css']
}) })
export class ElementTermComponent implements OnInit, AfterViewInit { export class ElementTermComponent implements OnInit, AfterViewInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
// @Input() room: string;
@ViewChild('term') el: ElementRef; @ViewChild('term') el: ElementRef;
secret: string;
term: any;
constructor() { constructor() {
} }
ngOnInit() { ngOnInit() {
this.secret = UUID.create()['hex'];
this.term = Terminal({
cols: 80,
rows: 24,
useStyle: true,
screenKeys: true,
});
// NavList.List[this.index].room = this.room;
} }
ngAfterViewInit() { ngAfterViewInit() {
if (this.host) {
if (Cookie.get('cols')) {
term.col = parseInt(Cookie.get('cols'), 10);
}
if (Cookie.get('rows')) {
term.row = parseInt(Cookie.get('rows'), 10);
}
} else {
term.col = Math.floor(jQuery(this.el.nativeElement).width() / jQuery('#liuzheng').width() * 8) - 3; term.col = Math.floor(jQuery(this.el.nativeElement).width() / jQuery('#liuzheng').width() * 8) - 3;
term.row = Math.floor(jQuery(this.el.nativeElement).height() / jQuery('#liuzheng').height()) - 5; term.row = Math.floor(jQuery(this.el.nativeElement).height() / jQuery('#liuzheng').height()) - 5;
term.term.open(this.el.nativeElement, true); term.term = this.term;
}
this.term.open(this.el.nativeElement, true);
const that = this; const that = this;
window.onresize = function () { window.onresize = function () {
term.col = Math.floor(jQuery(that.el.nativeElement).width() / jQuery('#liuzheng').width() * 8) - 3; term.col = Math.floor(jQuery(that.el.nativeElement).width() / jQuery('#liuzheng').width() * 8) - 3;
...@@ -33,9 +58,46 @@ export class ElementTermComponent implements OnInit, AfterViewInit { ...@@ -33,9 +58,46 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
if (term.row < 24) { if (term.row < 24) {
term.row = 24; term.row = 24;
} }
term.term.resize(term.col, term.row); that.term.resize(term.col, term.row);
if (that.host) {
Cookie.set('cols', term.col.toString(), 99, '/', document.domain);
Cookie.set('rows', term.row.toString(), 99, '/', document.domain);
TermWS.emit('resize', {'cols': term.col, 'rows': term.row});
}
}; };
jQuery(window).resize(); jQuery(window).resize();
if (this.host) {
NavList.List[this.index].Term = this.term;
this.term.write('\x1b[31mWelcome to Jumpserver!\x1b[m\r\n');
TermWS.emit('host', {'uuid': this.host.id, 'userid': this.userid, 'secret': this.secret});
this.term.on('data', function (data) {
TermWS.emit('data', {'data': data, 'room': NavList.List[that.index].room});
});
TermWS.on('data', function (data) {
if (data['room'] === NavList.List[that.index].room) {
that.term.write(data['data']);
}
});
TermWS.on('disconnect', function () {
that.TerminalDisconnect();
});
TermWS.on('room', function (data) {
if (data['secret'] === that.secret) {
NavList.List[that.index].room = data['room'];
}
});
}
} }
TerminalDisconnect() {
NavList.List[this.index].connected = false;
this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
TermWS.emit('logout', NavList.List[this.index].room);
}
} }
...@@ -6,6 +6,7 @@ export function Terminal(xargs: any) { ...@@ -6,6 +6,7 @@ export function Terminal(xargs: any) {
return terminal(xargs); return terminal(xargs);
} }
export const TermWS = io.connect('/ssh');
export let term: { export let term: {
term: any; term: any;
col: number; col: number;
......
...@@ -21,9 +21,14 @@ export class JsonComponent implements OnInit { ...@@ -21,9 +21,14 @@ export class JsonComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
setTimeout(() => { const that = this;
this.pause(); let r = true;
}, 2000); window.onresize = function () {
if (r) {
that.pause();
r = false;
}
};
} }
setSpeed() { setSpeed() {
......
#!/bin/bash
set -ex
npm run-script build
rm -fr luna*
mv dist 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