feat: fix the mutil connections bug, but bring some view bugs,

this version needs coco support, the websocket re-use. add guacamole component. framework changed
parent a20d991e
......@@ -13,51 +13,62 @@ class SSHws(Namespace):
self.clients = dict()
super().__init__(*args, **kwargs)
def ssh_with_password(self):
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"] = 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"])
# 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)
def sent_data(self, s, chan, room, connection):
while True:
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:
try:
if self.clients[sid]["chan"]:
data = self.clients[sid]["chan"].recv(2048).decode('utf-8', 'replace')
s.emit(event='data', data=data, room=self.clients[sid]["room"])
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(data)
print(self.clients)
def on_connect(self):
print(request.sid)
self.clients[request.sid] = {
"cols": int(request.cookies.get('cols', 80)),
"rows": int(request.cookies.get('rows', 24)),
"room": str(uuid.uuid4()),
"chan": None
"chan": dict()
}
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):
self.clients[request.sid]["chan"].send(message)
self.clients[request.sid]["chan"][message["room"]].send(message["data"])
def on_host(self, message):
# self.clients[request.sid]["room"] = str(uuid.uuid4())
# self.emit('room', self.clients[request.sid]["chan"]["room"])
# join_room(self.clients[request.sid]["room"])
self.ssh_with_password()
print(message, self.clients[request.sid]["room"])
connection = str(uuid.uuid4())
self.ssh_with_password(connection)
self.emit('room', {'room': connection, 'secret': message['secret']})
def on_resize(self, message):
self.clients[request.sid]["cols"] = message.get('cols', 80)
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_pixels=1)
......
......@@ -14396,6 +14396,11 @@
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
"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": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz",
......
......@@ -59,6 +59,7 @@
"ssh-keygen": "^0.4.1",
"term.js": "0.0.7",
"tether": "^1.4.0",
"uuid-js": "^0.7.5",
"xterm": "^2.9.2",
"zone.js": "^0.8.14"
},
......
......@@ -11,14 +11,13 @@ import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core';
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 {DataStore} from '../../globals';
import {version} from '../../../environments/environment';
import * as jQuery from 'jquery/dist/jquery.min.js';
import * as layer from 'layui-layer/src/layer.js';
import {ElementServerMenuComponent} from '../../elements/server-menu/server-menu.component';
import {NavList, View} from '../control/control.component';
export class HostGroup {
......@@ -37,7 +36,7 @@ export class Host {
selector: 'app-cleftbar',
templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.css'],
providers: [SshComponent, RdpComponent, SearchComponent, ElementServerMenuComponent]
providers: [SearchComponent, ElementServerMenuComponent]
})
export class CleftbarComponent implements OnInit {
DataStore = DataStore;
......@@ -82,8 +81,6 @@ export class CleftbarComponent implements OnInit {
}
constructor(private _appService: AppService,
private _term: SshComponent,
private _rdp: RdpComponent,
private _http: HttpService,
private _search: SearchComponent,
private _logger: Logger,
......@@ -166,15 +163,20 @@ export class CleftbarComponent implements OnInit {
login(host, user) {
if (user) {
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].host = host;
NavList.List[id].user = user;
if (user.protocol === 'ssh') {
jQuery('app-ssh').show();
jQuery('app-rdp').hide();
this._term.TerminalConnect(host, user.id);
NavList.List[id].type = 'ssh';
} else if (user.protocol === 'rdp') {
jQuery('app-ssh').hide();
jQuery('app-rdp').show();
this._rdp.Connect(host, user.id);
NavList.List[id].type = 'rdp';
}
NavList.List.push(new View());
}
// if (host.platform) {
// if (host.platform.toLowerCase() === 'linux') {
......
<app-controlnav></app-controlnav>
<app-ssh></app-ssh>
<!--<app-ssh></app-ssh>-->
<!--<app-rdp></app-rdp>-->
<div *ngFor="let m of NavList.List;let i=index">
<app-element-term [host]="m.host"
[userid]="m.user.id"
[index]="i"
*ngIf="m.type=='ssh'">
</app-element-term>
</div>
......@@ -9,11 +9,11 @@
import {Component, OnInit} from '@angular/core';
export class Term {
machine: string;
socket: any;
term: any;
}
// export class Term {
// machine: string;
// socket: any;
// term: any;
// }
export class Rdp {
machine: string;
......@@ -28,8 +28,10 @@ export class View {
connected: boolean;
hide: boolean;
closed: boolean;
host: any;
user: any;
Rdp: Rdp;
Term: Term;
Term: any;
}
export let NavList: {
......@@ -46,6 +48,8 @@ export let NavList: {
styleUrls: ['./control.component.css']
})
export class ControlComponent implements OnInit {
NavList = NavList;
static active(id) {
for (let i in NavList.List) {
if (id.toString() === i) {
......@@ -58,6 +62,22 @@ export class ControlComponent implements OnInit {
NavList.Active = id;
}
static TerminalDisconnect(id) {
NavList.List[id].connected = false;
NavList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
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() {
}
......
......@@ -8,9 +8,7 @@
*/
import {Component, OnInit} from '@angular/core';
import {NavList} from '../control.component';
import {SshComponent} from '../ssh/ssh.component';
import {RdpComponent} from '../rdp/rdp.component';
import {ControlComponent, NavList} from '../control.component';
import * as jQuery from 'jquery/dist/jquery.min.js';
......@@ -62,9 +60,9 @@ export class ControlnavComponent implements OnInit {
close(host, index) {
if (host.type === 'rdp') {
RdpComponent.Disconnect(host);
ControlComponent.RdpDisconnect(index);
} else if (host.type === 'ssh') {
SshComponent.TerminalDisconnect(host);
ControlComponent.TerminalDisconnect(index);
}
NavList.List.splice(index, 1);
ControlnavComponent.checkActive(index);
......
#rdp {
width: 100%;
height: 100%;
padding: 15px;
}
#rdp > div, iframe {
width: 100%;
height: 100%;
}
iframe {
border: none;
}
<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>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RdpComponent } from './rdp.component';
describe('RdpComponent', () => {
let component: RdpComponent;
let fixture: ComponentFixture<RdpComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RdpComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RdpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 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();
});
}
}
......@@ -25,14 +25,15 @@ import {ElementInteractiveComponent} from './elements/interactive/interactive.co
import {ElementNavComponent} from './elements/nav/nav.component';
import {LoginComponent} from './BasicPage/login/login.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 {ElementGuacamoleComponent} from './elements/guacamole/guacamole.component';
// pages
import {IleftbarComponent} from './IndexPage/ileftbar/ileftbar.component';
import {SearchComponent, SearchFilter} from './ControlPage/search/search.component';
import {CleftbarComponent} from './ControlPage/cleftbar/cleftbar.component';
import {ControlComponent} from './ControlPage/control/control.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 {IndexPageComponent} from './IndexPage/index-page.component';
import {NotFoundComponent} from './BasicPage/not-found/not-found.component';
......@@ -45,8 +46,6 @@ import {UtcDatePipe} from './app.pipe';
import {MonitorPageComponent} from './monitor-page/monitor-page.component';
import {LinuxComponent} from './monitor-page/linux/linux.component';
import {WindowsComponent} from './monitor-page/windows/windows.component';
import {ElementRdpComponent} from './elements/rdp/rdp.component';
import {ElementServerMenuComponent} from './elements/server-menu/server-menu.component';
@NgModule({
imports: [
......@@ -64,9 +63,8 @@ import {ElementServerMenuComponent} from './elements/server-menu/server-menu.com
ElementInteractiveComponent,
ElementRdpComponent,
ElementServerMenuComponent,
ElementGuacamoleComponent,
LoginComponent,
RdpComponent,
SshComponent,
SearchComponent,
SearchFilter,
IleftbarComponent,
......
<div>
<iframe src="{{target}}"></iframe>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SshComponent } from './ssh.component';
import { ElementGuacamoleComponent } from './guacamole.component';
describe('SshComponent', () => {
let component: SshComponent;
let fixture: ComponentFixture<SshComponent>;
describe('ElementGuacamoleComponent', () => {
let component: ElementGuacamoleComponent;
let fixture: ComponentFixture<ElementGuacamoleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SshComponent ]
declarations: [ ElementGuacamoleComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SshComponent);
fixture = TestBed.createComponent(ElementGuacamoleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'app-element-guacamole',
templateUrl: './guacamole.component.html',
styleUrls: ['./guacamole.component.scss']
})
export class ElementGuacamoleComponent implements OnInit {
@Input() target: string;
constructor() {
}
ngOnInit() {
this.target = '/guacamole/?asset_id=' + '&system_user_id=' + '';
}
}
......@@ -10,9 +10,7 @@ import {Logger} from 'angular2-logger/core';
import {AppService, HttpService} from '../../app.service';
import {CleftbarComponent} from '../../ControlPage/cleftbar/cleftbar.component';
import {SshComponent} from '../../ControlPage/control/ssh/ssh.component';
import {RdpComponent} from '../../ControlPage/control/rdp/rdp.component';
import {NavList} from '../../ControlPage/control/control.component';
import {ControlComponent, NavList} from '../../ControlPage/control/control.component';
import {DataStore} from '../../globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
// import * as layer from 'layui-layer/src/layer.js';
......@@ -63,11 +61,11 @@ export class ElementNavComponent implements OnInit {
case'Disconnect': {
switch (NavList.List[NavList.Active].type) {
case 'ssh': {
SshComponent.TerminalDisconnect(NavList.List[NavList.Active]);
ControlComponent.TerminalDisconnect(NavList.Active);
break;
}
case 'rdp': {
RdpComponent.Disconnect(NavList.List[NavList.Active]);
ControlComponent.RdpDisconnect(NavList.Active);
break;
}
default: {
......@@ -78,8 +76,7 @@ export class ElementNavComponent implements OnInit {
break;
}
case'DisconnectAll': {
SshComponent.TerminalDisconnectAll();
RdpComponent.DisconnectAll();
ControlComponent.DisconnectAll();
break;
}
case 'Website': {
......
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
import {ElementRef} from '@angular/core';
import {term} from '../../globals';
import {term, 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 UUID from 'uuid-js/lib/uuid.js';
@Component({
selector: 'app-element-term',
......@@ -9,19 +12,32 @@ import * as jQuery from 'jquery/dist/jquery.min.js';
styleUrls: ['./term.component.css']
})
export class ElementTermComponent implements OnInit, AfterViewInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
@Input() room: string;
@ViewChild('term') el: ElementRef;
secret: string;
constructor() {
}
ngOnInit() {
this.secret = UUID.create()['hex'];
}
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.row = Math.floor(jQuery(this.el.nativeElement).height() / jQuery('#liuzheng').height()) - 5;
}
term.term.open(this.el.nativeElement, true);
const that = this;
window.onresize = function () {
......@@ -34,8 +50,44 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
term.row = 24;
}
term.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();
if (this.host) {
NavList.List[this.index].Term = term.term;
term.term.write('\x1b[31mWelcome to Jumpserver!\x1b[m\r\n');
TermWS.emit('host', {'uuid': this.host.id, 'userid': this.userid, 'secret': this.secret});
term.term.on('data', function (data) {
TermWS.emit('data', {'data': data, 'room': that.room});
});
TermWS.on('data', function (data) {
if (data['room'] === that.room) {
term.term.write(data['data']);
}
});
TermWS.on('disconnect', function () {
that.TerminalDisconnect();
});
TermWS.on('room', function (data) {
if (data['secret'] === that.secret) {
that.room = data['room'];
}
});
}
}
TerminalDisconnect() {
NavList.List[this.index].connected = false;
term.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
}
......@@ -6,6 +6,7 @@ export function Terminal(xargs: any) {
return terminal(xargs);
}
export const TermWS = io.connect('/ssh');
export let term: {
term: any;
col: number;
......
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