Commit 1bd73ac0 authored by liuzheng712's avatar liuzheng712

publish

parent 29432a53
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "my-app"
"name": "WebTerminal"
},
"apps": [
{
......@@ -19,9 +19,26 @@
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"../node_modules/xterm/dist/xterm.css",
"../node_modules/layui-layer/dist/theme/default/layer.css",
"../node_modules/animate.css/animate.min.css",
"./assets/css/style.css",
"styles.css"
],
"scripts": [],
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
"../node_modules/tether/dist/js/tether.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js",
"../node_modules/xterm/dist/xterm.js",
"../node_modules/layui-layer/dist/layer.js",
"../node_modules/socket.io-client/dist/socket.io.js",
"./assets/js/mstsc.js",
"./assets/js/keyboard.js",
"./assets/js/rle.js",
"./assets/js/client.js",
"./assets/js/canvas.js"
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
......
......@@ -41,3 +41,5 @@ yarn-error.log
# System Files
.DS_Store
Thumbs.db
publish.sh
This diff is collapsed.
......@@ -26,3 +26,28 @@ Before running the tests make sure you are serving the app via `ng serve`.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
## Running On Your Server
Untar the luna release tar file, and put it under the folder you like.
Nginx config:
```
location /luna/ {
index index.html;
alias /path/of/your/luna/;
}
location ^~ /ws/ {
proxy_pass http://localhost:3000/ws/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support (nginx 1.4)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
```
/**
* Created by liuzheng on 7/17/16.
*/
var server = {};
var http = require('http');
var express = require('express');
var io = require('socket.io');
var pty = require('pty.js');
// var terminal = require('term.js');
var socket;
var term;
var buff = [];
server.run = function (options) {
// create shell process
term = pty.fork(
process.env.SHELL || 'sh',
[],
{
name: require('fs').existsSync('/usr/share/terminfo/x/xterm-256color')
? 'xterm-256color'
: 'xterm',
cols: 80,
rows: 24,
cwd: process.env.HOME
}
);
//
// // store term's output into buffer or emit through socket
term.on('data', function (data) {
return !socket ? buff.push(data) : socket.emit('data', data);
});
// console.log('Created shell with pty master/slave pair (master: %d, pid: %d)', term.fd, term.pid);
var app = express();
var server = http.createServer(app);
var apis = express.Router();
app.use("/", express.static(__dirname + '/dist/')); // 创建服务端
// app.use("/socket.io/", express.static(__dirname + '/api/')); // 创建服务端
// let term.js handle req/res
// app.use(terminal.middleware());
apis.route('/browser')
.post(function (req, res) {
// console.log(req);
// res.string('');
res.json({verified: true, csrf: "liuzheng"})
});
apis.route('/checklogin')
.post(function (req, res) {
res.json({logined: true, id: 1, username: "liuzheng", name: "liuzheng"})
})
.get(function (req, res) {
res.json({logined: true})
});
apis.route('/nav')
.get(function (req, res) {
res.json([{
"id": "File",
"name": "Server",
"children": [
{
"id": "NewConnection",
"href": "Aaaa",
"name": "New connection",
"disable": true
},
{
"id": "Connect",
"href": "Aaaa",
"name": "Connect",
"disable": true
},
{
"id": "Disconnect",
"click": "Disconnect",
"name": "Disconnect"
},
{
"id": "DisconnectAll",
"click": "DisconnectAll",
"name": "Disconnect all"
},
{
"id": "Duplicate",
"href": "Aaaa",
"name": "Duplicate",
"disable": true
},
{
"id": "Upload",
"href": "Aaaa",
"name": "Upload",
"disable": true
},
{
"id": "Download",
"href": "Aaaa",
"name": "Download",
"disable": true
},
{
"id": " Search",
"href": "Aaaa",
"name": "Search",
"disable": true
},
{
"id": "Reload",
"click": "ReloadLeftbar",
"name": "Reload"
}
]
}, {
"id": "View",
"name": "View",
"children": [
{
"id": "HindLeftManager",
"click": "HideLeft",
"name": "Hind left manager"
},
{
"id": "SplitVertical",
"href": "Aaaa",
"name": "Split vertical",
"disable": true
},
{
"id": "CommandBar",
"href": "Aaaa",
"name": "Command bar",
"disable": true
},
{
"id": "ShareSession",
"href": "Aaaa",
"name": "Share session (read/write)",
"disable": true
},
{
"id": "Language",
"href": "Aaaa",
"name": "Language",
"disable": true
}]
}, {
"id": "Help",
"name": "Help",
"children": [
{
"id": "EnterLicense",
"click": "EnterLicense",
"name": "Enter License"
},
{
"id": "Website",
"click": "Website",
"name": "Website"
},
{
"id": "BBS",
"click": "BBS",
"name": "BBS"
}]
}])
});
apis.route('/perms/v1/user/my/asset-groups-assets/')
.get(function (req, res) {
res.json([
{
"id": 0,
"name": "ungrouped",
"assets": []
},
{
"id": 1,
"name": "Default",
"comment": "Default asset group",
"assets": [
{
"id": 2,
"hostname": "192.168.1.6",
"ip": "192.168.2.6",
"port": 22,
"system_users": [
{
"id": 1,
"name": "web",
"username": "web",
"protocol": "ssh",
"auth_method": "P",
"auto_push": true
}
]
},
{
"id": 4,
"hostname": "testserver123",
"ip": "123.57.183.135",
"port": 8022,
"system_users": [
{
"id": 1,
"name": "web",
"username": "web",
"protocol": "ssh",
"auth_method": "P",
"auto_push": true
}
]
}
]
},
{
"id": 4,
"name": "java",
"comment": "",
"assets": [
{
"id": 2,
"hostname": "192.168.1.6",
"ip": "192.168.2.6",
"port": 22,
"system_users": [
{
"id": 1,
"name": "web",
"username": "web",
"protocol": "ssh",
"auth_method": "P",
"auto_push": true
}
]
}
]
},
{
"id": 3,
"name": "数据库",
"comment": "",
"assets": [
{
"id": 2,
"hostname": "192.168.1.6",
"ip": "192.168.2.6",
"port": 22,
"system_users": [
{
"id": 1,
"name": "web",
"username": "web",
"protocol": "ssh",
"auth_method": "P",
"auto_push": true
}
]
}
]
},
{
"id": 2,
"name": "运维组",
"comment": "",
"assets": [
{
"id": 2,
"hostname": "192.168.1.6",
"ip": "192.168.2.6",
"port": 22,
"system_users": [
{
"id": 1,
"name": "web",
"username": "web",
"protocol": "ssh",
"auth_method": "P",
"auto_push": true
}
]
}
]
}
])
});
app.use("/api", apis);
// let server listen on the port
options = options || {};
server.listen(options.port || 3000);
// let socket.io handle sockets
io = io.listen(server, {log: false});
io.sockets.on('connection', function (s) {
// when connect, store the socket
socket = s;
// handme incoming data (client -> server)
socket.on('data', function (data) {
term.write(data);
});
socket.on('resize', function (data) {
term.resize(data[0], data[1]);
console.log(data)
});
// handle connection lost
socket.on('disconnect', function () {
socket = null;
});
// send buffer data to client
while (buff.length) {
socket.emit('data', buff.shift());
}
});
};
server.run({port: 3000});
console.log('Please open your browser with http://127.0.0.1:3000');
This diff is collapsed.
{
"name": "my-app",
"version": "0.0.0",
"license": "MIT",
"name": "luna",
"version": "0.1.0",
"license": "GPLv3",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build -prod --base-href=/luna/ --deploy '/luna/'",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
......@@ -21,18 +21,32 @@
"@angular/platform-browser": "^4.2.4",
"@angular/platform-browser-dynamic": "^4.2.4",
"@angular/router": "^4.2.4",
"angular2-logger": "^0.6.0",
"animate.css": "^3.5.2",
"body-parser": "^1.18.2",
"bootstrap": "^4.0.0-alpha.6",
"clipboard": "^1.7.1",
"core-js": "^2.4.1",
"directory-encoder": "^0.9.2",
"filetree-css": "^1.0.0",
"handlebars": "^4.0.11",
"jquery": "^3.2.1",
"layui-layer": "git+https://github.com/jumpserver/layer.git",
"ng2-cookies": "^1.0.12",
"rxjs": "^5.4.2",
"socket.io": "^2.0.3",
"ssh-keygen": "^0.4.1",
"tether": "^1.4.0",
"xterm": "^2.9.2",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.3.2",
"@angular/cli": "1.5.2",
"@angular/compiler-cli": "^4.2.4",
"@angular/language-service": "^4.2.4",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "~3.1.1",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
......@@ -42,6 +56,7 @@
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"pty.js": "^0.3.1",
"ts-node": "~3.2.0",
"tslint": "~5.3.2",
"typescript": "~2.3.3"
......
{
"/api": {
"target": "http://localhost:3000",
"secure": false
},
"/socket.io/": {
"target": "http://127.0.0.1:3000",
"secure": false
},
"/rdp/socket.io/": {
"target": "http://localhost:9250",
"pathRewrite": {
"^/rdp": ""
},
"secure": false
}
}
<div class="footer fixed">
<div class="pull-right">
Version <strong>0.4</strong> GPL.
</div>
<div>
<strong>Copyright</strong> Jumpserver.org Team &copy; 2014-2018
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* footer
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import {AppService, DataStore, User} from '../../app.service';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css'],
providers: [AppService]
})
export class FooterComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
}
}
.loginColumns {
max-width: 800px;
margin: 0px auto;
padding: 100px 20px 20px;
}
p {
font-size: 13px;
}
<div class="container loginColumns animated fadeInDown">
<div class="row justify-content-md-center">
<div class="col col-md-6">
<h2 class="font-bold">欢迎使用Jumpserver开源跳板机</h2>
<p>
Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理
</p>
<p>
我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求
</p>
<p>
专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力
</p>
<p>
<small>永远年轻,永远热泪盈眶 stay foolish stay hungry</small>
</p>
</div>
<div class="col col-md-6">
<div class="ibox-content">
<div><img src="/assets/imgs/logo.png" width="82" height="82"> <span class="font-bold text-center"
style="font-size: 32px; font-family: inherit">登录</span>
</div>
<form class="m-t" #f="ngForm" (ngSubmit)="onSubmit(f)">
<p class="red-fonts">* Please enter a correct Username and password. Note that both fields may be
case-sensitive.</p>
<div class="form-group">
<input type="text" class="form-control" id="username" name="username" placeholder="Username" required
autofocus pattern="^\w+$" [(ngModel)]="User.username">
</div>
<div class="form-group">
<input type="password" class="form-control" id="password" name="password" placeholder="Password"
[(ngModel)]="User.password" required (ngEnter)="onSubmit(f)">
</div>
<!--<div>-->
<!--<img src="/captcha/image/c6fc850ce5a0c4d12e56e0ed2f02b55d1659a83a/" alt="captcha" class="captcha"><input-->
<!--id="id_captcha_0" name="captcha_0" type="hidden" value="c6fc850ce5a0c4d12e56e0ed2f02b55d1659a83a">-->
<!--<div class="row">-->
<!--<div class="col-sm-6">-->
<!--<input autocomplete="off" id="id_captcha_1" class="form-control" name="captcha_1" placeholder="Captcha"-->
<!--type="text">-->
<!--</div>-->
<!--</div>-->
<!--<br>-->
<!--</div>-->
<button type="submit" class="btn btn-primary block full-width m-b">Login</button>
<a href="/users/password/forgot">
<small>Forgot password?</small>
</a>
<p class="text-muted text-center">
</p>
</form>
<p class="m-t">
</p>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-6">
Copyright Jumpserver.org
</div>
<div class="col-md-6 text-right">
<small>© 2014-2017</small>
</div>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore, HttpService, User} from '../../app.service';
import {NgForm} from '@angular/forms';
import {Router} from '@angular/router';
declare let jQuery: any;
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
providers: [AppService]
})
export class LoginComponent implements OnInit {
DataStore = DataStore;
User = User;
loginBotton = 'login to your account';
constructor(private _appService: AppService,
private _http: HttpService,
private _router: Router,
private _logger: Logger) {
this._logger.log('login.ts:LoginComponent');
DataStore.NavShow = false;
}
onSubmit(f: NgForm) {
if (f.valid) {
this.login();
} else {
this._logger.error("the form not valid")
}
}
login() {
this._logger.log('service.ts:AppService,login');
DataStore.error['login'] = '';
this._logger.log(User);
if (User.username.length > 0 && User.password.length > 6 && User.password.length < 100) {
this._http.post('/api/checklogin', JSON.stringify(User)).map(res => res.json())
.subscribe(
data => {
User.logined = data.logined;
User.name = data.name;
User.username = data.username;
User.logined = data.logined;
},
err => {
this._logger.error(err);
User.logined = false;
this._router.navigate(['login']);
DataStore.error['login'] = '后端错误,请重试';
return '后端错误,请重试';
},
() => {
if (User.logined) {
if (jQuery.isEmptyObject(DataStore.Path)) {
this._router.navigate(['welcome']);
} else {
this._router.navigate([DataStore.Path['name'], DataStore.Path['res']]);
}
} else {
this._router.navigate(['login']);
DataStore.error['login'] = '请检查用户名和密码';
return '请检查用户名和密码';
}
// jQuery('angular2').show();
});
} else {
DataStore.error['login'] = '请检查用户名和密码';
return '请检查用户名和密码';
}
}
ngOnInit() {
jQuery('#form').fadeIn('slow');
// this._router.navigate(['login']);
// jQuery('nav').hide();
}
}
.nav {
display: block;
margin-top: 2px;
height: 30px
}
.nav ul {
list-style-type: none;
line-height: 24px;
}
.nav li {
display: inline-block;
}
.nav a {
color: #f0f0f1;
text-decoration: none;
padding: 6px 15px 6px 15px;
}
.nav a:hover {
color: #fff;
cursor: pointer
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown:hover {
background-color: #2d2828;
}
.dropdown-content {
display: none;
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;
}
.dropdown:hover .dropdown-content {
display: block;
}
.nav .dropdown-content li {
float: left;
display: flex;
}
.nav .dropdown-content li a {
padding: 6px 14px 6px 25px;
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;
}
.nav .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;
}
<div class="nav">
<ul>
<li><a [routerLink]="['Index']"><img src="assets/imgs/logo.png" height="26px"/></a>
</li>
<li *ngFor="let v of DataStore.Nav" [ngClass]="{'dropdown': v.children}">
<a>{{v.name}}</a>
<ul [ngClass]="{'dropdown-content': v.children}">
<li *ngFor="let vv of v.children" [ngClass]="{'disabled': vv.disable}">
<a *ngIf="vv.href" [routerLink]="[vv.href]">{{vv.name}}</a>
<a id="{{vv.id}}" *ngIf="vv.click" (click)="click(vv.click)">{{vv.name}}</a>
</li>
</ul>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NavComponent } from './nav.component';
describe('NavComponent', () => {
let component: NavComponent;
let fixture: ComponentFixture<NavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NavComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore, 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';
declare let layer: any;
declare let jQuery: any;
@Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css'],
})
export class NavComponent implements OnInit {
DataStore = DataStore;
constructor(private _appService: AppService,
private _http: HttpService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
this.getnav()
}
ngOnInit() {
}
click(event) {
this._logger.debug('nav.ts:NavComponent,click', event);
switch (event) {
case "ReloadLeftbar": {
CleftbarComponent.Reload();
break
}
case "HideLeft": {
CleftbarComponent.Hide();
break
}
case "ShowLeft": {
CleftbarComponent.Show();
break
}
case "Copy": {
// this._appService.copy();
break
}
case"Disconnect": {
switch (NavList.List[NavList.Active].type) {
case "ssh": {
SshComponent.TerminalDisconnect(NavList.List[NavList.Active]);
break
}
case "rdp": {
RdpComponent.Disconnect(NavList.List[NavList.Active]);
break
}
default: {
//statements;
break;
}
}
break
}
case"DisconnectAll": {
SshComponent.TerminalDisconnectAll();
RdpComponent.DisconnectAll();
break
}
case "Website": {
window.open('http://www.jumpserver.org');
break
}
case "BBS": {
window.open('http://bbs.jumpserver.org');
break
}
case "EnterLicense": {
this.EnterLicense();
break
}
default: {
break
}
}
}
EnterLicense() {
layer.prompt({
formType: 2,
maxlength: 500,
title: 'Please Input Code',
scrollbar: false,
area: ['400px', '300px'],
moveOut: true,
moveType: 1
}, function (value, index) {
DataStore.socket.emit('key', value);
// layer.msg(value); //得到value
layer.close(index);
});
}
getnav() {
this._logger.log('getnav');
// this._http.get('/api/nav')
// .map(res => res.json())
// .subscribe(response => {
// DataStore.Nav = response;
// });
DataStore.Nav = [{
"id": "File",
"name": "Server",
"children": [
{
"id": "NewConnection",
"href": "",
"name": "New connection",
"disable": true
},
{
"id": "Connect",
"click": "Connect",
"name": "Connect",
"disable": true
},
{
"id": "Disconnect",
"click": "Disconnect",
"name": "Disconnect"
},
{
"id": "DisconnectAll",
"click": "DisconnectAll",
"name": "Disconnect all"
},
{
"id": "Duplicate",
"href": "",
"name": "Duplicate",
"disable": true
},
{
"id": "Upload",
"href": "",
"name": "Upload",
"disable": true
},
{
"id": "Download",
"href": "",
"name": "Download",
"disable": true
},
{
"id": " Search",
"href": "",
"name": "Search",
"disable": true
},
{
"id": "Reload",
"click": "ReloadLeftbar",
"name": "Reload"
}
]
}, {
"id": "View",
"name": "View",
"children": [
{
"id": "HindLeftManager",
"click": "HideLeft",
"name": "Hind left manager"
},
{
"id": "SplitVertical",
"href": "",
"name": "Split vertical",
"disable": true
},
{
"id": "CommandBar",
"href": "",
"name": "Command bar",
"disable": true
},
{
"id": "ShareSession",
"href": "",
"name": "Share session (read/write)",
"disable": true
},
{
"id": "Language",
"href": "",
"name": "Language",
"disable": true
}]
}, {
"id": "Help",
"name": "Help",
"children": [
{
"id": "EnterLicense",
"click": "EnterLicense",
"name": "Enter License"
},
{
"id": "Website",
"click": "Website",
"name": "Website"
},
{
"id": "BBS",
"click": "BBS",
"name": "BBS"
}]
}]
}
static Hide() {
jQuery("app-nav").hide()
}
Connect() {
layer.prompt({
formType: 2,
maxlength: 500,
title: 'Please Input Code',
scrollbar: false,
area: ['400px', '300px'],
moveOut: true,
moveType: 1
}, function (value, index) {
DataStore.socket.emit('key', value);
// layer.msg(value); //得到value
layer.close(index);
});
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotFoundComponent } from './not-found.component';
describe('NotFoundComponent', () => {
let component: NotFoundComponent;
let fixture: ComponentFixture<NotFoundComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NotFoundComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {NavComponent} from '../nav/nav.component'
@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.css']
})
export class NotFoundComponent implements OnInit {
constructor() {
}
ngOnInit() {
NavComponent.Hide()
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupComponent } from './popup.component';
describe('PopupComponent', () => {
let component: PopupComponent;
let fixture: ComponentFixture<PopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-popup',
templateUrl: './popup.component.html',
styleUrls: ['./popup.component.css']
})
export class PopupComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
/**
* filetree.css
* */
:root {
font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}
.filetree input[type="checkbox"] {
display: none;
}
.filetree ul {
height: 0;
overflow: hidden;
}
.filetree > li input:checked ~ ul {
height: auto;
}
.filetree li {
list-style: none;
cursor: pointer;
color: #ffffff;
}
.filetree label {
padding-left: 33px;
line-height: 33px;
display: inline-block;
}
/**
* Icon
* */
.filetree label {
background-image: url('./icon.png');
background-repeat: no-repeat;
}
.filetree input[type="checkbox"] + label {
background-position: 0 0;
width: 100%;
height: 33px;
}
.filetree input[type="checkbox"]:checked + label {
background-position: 0 -33px;
width: 100%;
height: 30px;
}
@media screen and (-webkit-min-device-pixel-ratio: 1.0), screen and (min--moz-device-pixel-ratio: 1.0), screen and (-o-min-device-pixel-ratio: 100/100), screen and (min-device-pixel-ratio: 1.0), screen and (min-resolution: 1.0dppx) {
filetree label {
background-image: url('./icon.png');
-webkit-background-size: 33px 63px;
-moz-background-size: 33px 63px;
background-size: 33px 63px;
}
}
.search {
border-left-width: 0;
border-bottom: gold 2px inset;
}
.left-search {
padding-left: 14px;
width: 100%;
border: none;
height: 28px;
background: #2f2a2a;
}
<div id="sidebar">
<div class="search">
<input class="left-search" placeholder=" Search ..." maxlength="2048" name="q" autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" autofocus [(ngModel)]="q" (keyup.enter)="Search(q)">
</div>
<ul class="filetree">
<li *ngFor="let hostGroup of HostGroups | SearchFilter: q; let i = index ">
<input type="checkbox" id="hostgroup-{{i}}">
<label for="hostgroup-{{i}}">{{hostGroup.name}}</label>
<ul>
<li *ngFor="let host of hostGroup.assets | SearchFilter: q" (click)="Connect(host)">{{host.hostname}}</li>
</ul>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CleftbarComponent } from './cleftbar.component';
describe('CleftbarComponent', () => {
let component: CleftbarComponent;
let fixture: ComponentFixture<CleftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CleftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CleftbarComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore, HttpService} from '../../app.service';
import {SshComponent} from '../control/ssh/ssh.component';
import {RdpComponent} from '../control/rdp/rdp.component';
import {SearchComponent} from "../search/search.component";
declare let layer: any;
declare let jQuery: any;
export class HostGroup {
name: string;
id: number;
children: Array<Host>;
}
export class Host {
name: string;
uuid: string;
type: string;
}
@Component({
selector: 'app-cleftbar',
templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.css'],
providers: [SshComponent, RdpComponent, SearchComponent]
})
export class CleftbarComponent implements OnInit {
DataStore = DataStore;
HostGroups: Array<HostGroup>;
q: string;
static Reload() {
}
static Hide() {
DataStore.leftbarshow = false;
DataStore.Nav.map(function (value, i) {
for (var ii in value["children"]) {
if (DataStore.Nav[i]["children"][ii]["id"] === "HindLeftManager") {
DataStore.Nav[i]["children"][ii] = {
"id": "ShowLeftManager",
"click": "ShowLeft",
"name": "Show left manager"
}
}
}
})
}
static Show() {
DataStore.leftbarshow = true;
DataStore.Nav.map(function (value, i) {
for (var ii in value["children"]) {
if (DataStore.Nav[i]["children"][ii]["id"] === "ShowLeftManager") {
DataStore.Nav[i]["children"][ii] = {
"id": "HindLeftManager",
"click": "HideLeft",
"name": "Hind left manager"
}
}
}
})
}
constructor(private _appService: AppService,
private _term: SshComponent,
private _rdp: RdpComponent,
private _http: HttpService,
private _search: SearchComponent,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
this._http.get('/api/perms/v1/user/my/asset-groups-assets/')
.map(res => res.json())
.subscribe(response => {
this.HostGroups = response;
});
}
Connect(host) {
console.log(host);
let username: string;
if (host.system_users.length > 1) {
let options = "";
for (let u of host.system_users) {
options += "<option value='" + u.username + "'>" + u.username + "</option>"
}
layer.open({
title: 'Please Choose a User',
scrollbar: false,
moveOut: true,
moveType: 1,
btn: ["确定", "取消"],
content: "<select id='selectuser'>" + options + "</select>",
yes: function (index, layero) {
username = jQuery("#selectuser").val();
layer.close(index);
},
btn2: function (index, layero) {
},
cancel: function () {
//右上角关闭回调
//return false 开启该代码可禁止点击该按钮关闭
}
});
} else if (host.system_users.length === 1) {
username = host.system_users[0].username
}
if (username === "") {
return
}
if (host.type === 'ssh') {
jQuery("app-ssh").show();
jQuery("app-rdp").hide();
this._term.TerminalConnect(host, username);
} else {
jQuery("app-ssh").hide();
jQuery("app-rdp").show();
this._rdp.Connect(host, username);
}
}
Search(q) {
this._search.Search(q)
}
}
<app-controlnav></app-controlnav>
<app-ssh></app-ssh>
<app-rdp></app-rdp>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlComponent } from './control.component';
describe('ControlComponent', () => {
let component: ControlComponent;
let fixture: ComponentFixture<ControlComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面
*
* 主管已连接的主机标签卡,各连接方式的web展现(WebTerminal、RDP、VNC等)
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
export class Term {
machine: string;
socket: any;
term: any;
}
export class Rdp {
machine: string;
token: string;
client: any;
}
export class View {
nick: string;
type: string;
edit: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
Rdp: Rdp;
Term: Term;
}
export let NavList: {
List: Array<View>;
Active: number;
} = {
List: [new View()],
Active: 0,
};
@Component({
selector: 'app-control',
templateUrl: './control.component.html',
styleUrls: ['./control.component.css']
})
export class ControlComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}
#tabs {
height: 30px;
width: 100%
}
#tabs ul li.disconnected {
background-color: darkgray;
}
#tabs ul li.hidden {
display: none;
}
#tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
overflow-y: hidden;
overflow-x: auto;
width: 100%;
display: inline-flex;
position: absolute;
}
#tabs ul li {
display: inline-table;
width: 150px;
height: 30px;
position: relative;
box-sizing: content-box;
}
#tabs ul li.active {
box-sizing: border-box;
border-bottom: 3px solid #7f3f98 !important;
}
#tabs ul li span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: gray;
font-family: 'Roboto', sans-serif;
font-size: 13px;
text-decoration: none;
padding: 8px 20px 6px 15px;
cursor: default;
width: 115px;
height: 21px;
}
#tabs ul li a.close {
font-family: 'Roboto', sans-serif;
font-size: 13px;
position: absolute;
color: gray;
top: 0;
right: 5px;
cursor: pointer;
line-height: 26px;
display: inline-block;
}
#tabs ul li.active a {
color: white;
}
#tabs ul li.active span {
padding: 5px 20px 4px 15px;
color: white;
height: 18px;
}
#tabs ul li input {
font-family: 'Roboto', sans-serif;
font-size: 13px;
width: 115px;
border: none;
background-color: inherit;
color: white;
padding: 5px 20px 4px 15px;
height: 18px;
}
<div id="tabs">
<ul>
<li *ngFor="let m of NavList.List;let i = index"
[ngClass]="{'active':i==NavList.Active,'disconnected':!m.connected, 'hidden': m.closed != false}"
id="termnav-{{i}}" (click)="setActive(i)">
<span *ngIf="!m.edit" (dblclick)="m.edit=true;setActive(i)">{{m.nick}}</span>
<input *ngIf="m.edit" [(ngModel)]="m.nick" autofocus (blur)="m.edit=false" (keyup.enter)="m.edit=false"/>
<a class="close" (click)="close(m,i)">&times;</a>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlnavComponent } from './controlnav.component';
describe('ControlnavComponent', () => {
let component: ControlnavComponent;
let fixture: ComponentFixture<ControlnavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlnavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlnavComponent);
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 {NavList} from '../control.component'
import {SshComponent} from '../ssh/ssh.component'
import {RdpComponent} from '../rdp/rdp.component'
declare let jQuery: any;
@Component({
selector: 'app-controlnav',
templateUrl: './controlnav.component.html',
styleUrls: ['./controlnav.component.css'],
})
export class ControlnavComponent implements OnInit {
setActive = ControlnavComponent.setActive;
NavList = NavList;
constructor() {
}
ngOnInit() {
}
static checkActive(index) {
let len = NavList.List.length;
if (len == 1) {
// 唯一一个
NavList.Active = 0;
} else if (len - 1 == index) {
// 删了最后一个
NavList.Active = len - 2;
} else {
NavList.Active = index;
}
ControlnavComponent.setActive(NavList.Active)
}
static setActive(index) {
for (let m in NavList.List) {
NavList.List[m].hide = true;
}
NavList.List[index].hide = false;
NavList.Active = index;
if (NavList.List[index].type === "ssh") {
jQuery("app-ssh").show();
jQuery("app-rdp").hide()
} else if (NavList.List[index].type === 'rdp') {
jQuery("app-ssh").hide();
jQuery("app-rdp").show();
}
}
close(host, index) {
if (host.type === 'rdp') {
RdpComponent.Disconnect(host)
} else if (host.type === 'ssh') {
SshComponent.TerminalDisconnect(host)
}
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} from '../control.component';
declare let Mstsc: any;
@Component({
selector: 'app-rdp',
templateUrl: './rdp.component.html',
styleUrls: ['./rdp.component.css']
})
export class RdpComponent implements OnInit {
NavList = NavList;
constructor() {
}
ngOnInit() {
}
Connect(host, username) {
let id = NavList.List.length - 1;
let 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 = Mstsc.client.create(Mstsc.$("canvas-" + id));
NavList.List[id].Rdp.client.connect(host.token, "rdp/socket.io");
NavList.List.push(new View());
for (let m in NavList.List) {
NavList.List[m].hide = true;
}
NavList.List[id].hide = false;
NavList.Active = id;
}
static Disconnect(host) {
host.connected = false;
// document.getElementById("templatesrc").remove();
}
static DisconnectAll() {
}
}
#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;
width: 100%;
height: 100%;
background-color: black;
}
#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;
}
#liuzheng {
position: fixed;
top: 0;
left: 0;
z-index: -1;
font-size: 11px !important;
padding-bottom: 16px !important;
font-family: 'Monaco', iosevka !important;
}
<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>
<span id="liuzheng">liuzheng</span>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SshComponent } from './ssh.component';
describe('SshComponent', () => {
let component: SshComponent;
let fixture: ComponentFixture<SshComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SshComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SshComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* WebTerminal
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import * as io from 'socket.io-client';
import {Cookie} from 'ng2-cookies/ng2-cookies';
declare let jQuery: any;
declare let Terminal: any;
import {AppService, DataStore} from '../../../app.service';
import {NavList, View, Term} from '../control.component';
@Component({
selector: 'app-ssh',
templateUrl: './ssh.component.html',
styleUrls: ['./ssh.component.css']
})
export class SshComponent implements OnInit {
NavList = NavList;
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('SshComponent.ts:SshComponent');
}
ngOnInit() {
}
TerminalConnect(host,username) {
let socket = io.connect();
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);
let 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.uuid;
NavList.List[id].Term.socket = socket;
NavList.List[id].Term.term = new Terminal({
cols: cols,
rows: rows,
useStyle: true,
screenKeys: true,
});
NavList.List.push(new View());
for (let m in NavList.List) {
NavList.List[m].hide = true;
}
NavList.List[id].hide = false;
NavList.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('login', "root");
socket.emit('machine', host.uuid);
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 () {
this.TerminalDisconnect(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'));
}
if (Cookie.get('cols')) {
cols = parseInt(Cookie.get('cols'));
}
if (col < 80) col = 80;
if (row < 24) row = 24;
if (cols == col && row == rows) {
} else {
for (let tid in NavList.List) {
if (NavList.List[tid].connected) {
NavList.List[tid].Term.socket.emit('resize', [col, row]);
NavList.List[tid].Term.term.resize(col, row);
}
}
Cookie.set('cols', String(col), 99, '/', document.domain);
Cookie.set('rows', String(row), 99, '/', document.domain);
}
};
jQuery(window).resize();
});
}
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 in NavList.List) {
SshComponent.TerminalDisconnect(i);
// TermStore.term[i]["connected"] = false;
// TermStore.term[i]["socket"].destroy();
// TermStore.term[i]["term"].write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
}
}
div, term-leftbar, term-body {
height: 100%;
width: 100%;
padding: 0;
}
div {
background-color: black;
margin: 0;
}
app-cleftbar {
padding: 0;
background: #2f2a2a;
color: #d6cbcb;
}
app-control {
padding: 0;
}
<div class="container-fluid row">
<app-cleftbar class="col-md-2" *ngIf="DataStore.leftbarshow"></app-cleftbar>
<app-control [ngClass]="{'col-md-10':DataStore.leftbarshow,'col-md-12':!DataStore.leftbarshow}"></app-control>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlPageComponent } from './controlpage.component';
describe('ControlPageComponent', () => {
let component: ControlPageComponent;
let fixture: ComponentFixture<ControlPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlPageComponent);
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, DataStore, User} from '../app.service';
@Component({
selector: 'app-controllpage',
templateUrl: './controlpage.component.html',
styleUrls: ['./controlpage.component.css'],
providers: [AppService]
})
export class ControlPageComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor() {
}
ngOnInit() {
}
}
.left-search {
padding-left: 14px;
width: 100%;
border: none;
}
<input class="left-search" placeholder=" Search ..." maxlength="2048" name="q" autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" autofocus [(ngModel)]="q" (keyup.enter)="Search(q)"
(ngModelChange)="modelChange($event)">
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SearchComponent } from './search.component';
describe('SearchComponent', () => {
let component: SearchComponent;
let fixture: ComponentFixture<SearchComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SearchComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面的搜索框
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnChanges, Input, Pipe, PipeTransform} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import {AppService, DataStore, HttpService} from '../../app.service';
export let Q: string = '';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnChanges {
q: string;
@Input() input;
searchrequest: any;
constructor(private _appService: AppService,
private _http: HttpService,
private _logger: Logger) {
this._logger.log('LeftbarComponent.ts:SearchBar');
}
ngOnChanges(changes) {
Q = changes.input.currentValue;
}
modelChange($event) {
this.Search(Q)
}
public Search(q) {
if (this.searchrequest) {
this.searchrequest.unsubscribe();
}
this.searchrequest = this._http.get('/api/search?q=' + q)
.map(res => res.json())
.subscribe(
data => {
this._logger.log(data);
},
err => {
this._logger.error(err);
},
() => {
}
);
this._logger.log(q)
}
}
@Pipe({name: 'SearchFilter'})
export class SearchFilter implements PipeTransform {
transform(value: any, input: string) {
if (input) {
input = input.toLowerCase();
return value.filter(function (el: any) {
// ToDo: search with a simple SQL like language, and a bug search a group's hosts
return JSON.stringify(el).toLowerCase().indexOf(input) > -1;
})
}
return value;
}
}
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore} from '../../app.service';
@Component({
selector: 'app-ileftbar',
templateUrl: './ileftbar.component.html',
styleUrls: ['./ileftbar.component.css']
})
export class IleftbarComponent implements OnInit {
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
}
}
<div class="container" *ngIf="User.logined">
<div class="row row-offcanvas row-offcanvas-right">
<div class="col-12 col-md-9">
<p class="float-right hidden-md-up">
<button type="button" class="btn btn-primary btn-sm" data-toggle="offcanvas">Toggle nav</button>
</p>
<div class="jumbotron">
<h1>Hello, world! {{User.username}}</h1>
<p>This is an example to show the potential of an offcanvas layout pattern in Bootstrap. Try some
responsive-range viewport sizes to see it in action.</p>
</div>
<div class="row">
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
</div><!--/row-->
</div><!--/span-->
<div class="col-6 col-md-3 sidebar-offcanvas" id="sidebar">
<div class="list-group">
<a href="#" class="list-group-item active">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
</div>
</div><!--/span-->
</div><!--/row-->
<hr>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { IndexPageComponent } from './index-page.component';
describe('IndexPageComponent', () => {
let component: IndexPageComponent;
let fixture: ComponentFixture<IndexPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ IndexPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IndexPageComponent);
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, User} from '../app.service';
@Component({
selector: 'app-index-page',
templateUrl: './index-page.component.html',
styleUrls: ['./index-page.component.css'],
providers: [AppService]
})
export class IndexPageComponent implements OnInit {
User = User;
constructor() {
}
ngOnInit() {
}
}
/**
* app路由
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {environment} from '../environments/environment';
import {IndexPageComponent} from './IndexPage/index-page.component';
import {NotFoundComponent} from './BasicPage/not-found/not-found.component';
import {LoginComponent} from './BasicPage/login/login.component';
import {ControlPageComponent} from './ControlPage/controlpage.component';
import {RdppageComponent} from "./rdppage/rdppage.component";
import {TermpageComponent} from "./termpage/termpage.component";
const appRoutes: Routes = [
{path: 'users/login', component: LoginComponent},
{path: 'rdp/:token', component: RdppageComponent},
{path: 'term/:token', component: TermpageComponent},
{path: '', component: ControlPageComponent},
{path: '**', component: NotFoundComponent}
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{enableTracing: !environment.production} // <-- debugging purposes only
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {
}
app-nav {
font-family: 'Roboto', sans-serif;
font-size: 13px;
font-weight: 300;
position: absolute;
display: inline-block;
top: 0;
left: 0;
height: 30px;
width: 100%;
color: #323a4e;
background-color: #463e3e;
background-repeat: no-repeat;
background-position: 100% 50%;
z-index: 5;
transition: background 0.2s ease-out;
}
nav {
height: 30px;
width: 100%;
}
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{title}}!
</h1>
<img width="300" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo=">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" href="https://blog.angular.io//">Angular blog</a></h2>
</li>
</ul>
<app-nav *ngIf="DataStore.NavShow"></app-nav>
<nav *ngIf="DataStore.NavShow"></nav>
<router-outlet></router-outlet>
import { Component } from '@angular/core';
/**
* 控制主页
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component} from '@angular/core';
import {AppService, HttpService, DataStore} from './app.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
styleUrls: ['./app.component.css'],
providers: [AppService, HttpService],
// directives: [LeftbarComponent, TermComponent]
})
export class AppComponent {
title = 'app';
DataStore = DataStore;
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
/**
* Created by liuzheng on 2017/8/30.
*/
/**
* app 模块
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms'; // <-- NgModel lives here
import {Logger, Options, Level as LoggerLevel} from 'angular2-logger/core';
import {HttpModule} from '@angular/http';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {NavComponent} from './BasicPage/nav/nav.component';
import {LoginComponent} from './BasicPage/login/login.component';
import {FooterComponent} from './BasicPage/footer/footer.component';
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';
import {PopupComponent} from './BasicPage/popup/popup.component';
import { RdppageComponent } from './rdppage/rdppage.component';
import { TermpageComponent } from './termpage/termpage.component';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
BrowserModule,
FormsModule,
AppRoutingModule,
HttpModule,
],
declarations: [
AppComponent,
NavComponent,
LoginComponent,
FooterComponent,
RdpComponent,
SshComponent,
SearchComponent,
SearchFilter,
IleftbarComponent,
CleftbarComponent,
ControlComponent,
ControlnavComponent,
ControlPageComponent,
IndexPageComponent,
NotFoundComponent,
PopupComponent,
RdppageComponent,
TermpageComponent,
// HeroListComponent,
// CrisisListComponent,
],
providers: [],
bootstrap: [AppComponent]
bootstrap: [AppComponent],
providers: [
{provide: Options, useValue: {store: false, level: LoggerLevel.WARN}},
Logger
]
})
export class AppModule { }
export class AppModule {
}
/**
* 后台控制
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Injectable, OnInit} from '@angular/core';
import {Http, RequestOptionsArgs, Headers} from '@angular/http';
import {Router} from '@angular/router';
import {Cookie} from 'ng2-cookies/ng2-cookies';
import {Logger} from 'angular2-logger/core';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
declare let jQuery: any;
// declare var Clipboard: any;
import * as io from 'socket.io-client';
export class Group {
id: number;
name: string;
membercount: number;
comment: string;
}
export let User: {
id: number;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
is_active: boolean;
date_joined: string;
last_login: string;
groups: Array<Group>;
logined: boolean;
} = {
id: 0,
name: 'nobody',
username: '',
password: '',
phone: '',
avatar: '',
role: '',
email: '',
is_active: false,
date_joined: '',
last_login: '',
groups: [],
logined: false,
};
export let DataStore: {
socket: any;
Nav: Array<{}>;
NavShow: boolean;
Path: {};
error: {};
msg: {};
loglevel: number;
leftbarshow: boolean;
windowsize: Array<number>;
} = {
socket: io.connect(),
Nav: [{}],
NavShow: true,
Path: {},
error: {},
msg: {},
loglevel: 0,
leftbarshow: true,
windowsize: [],
};
export let CSRF: string = '';
export let Browser: {
userAgent: string;
appCodeName: string;
appName: string;
appVersion: string;
language: string;
platform: string;
product: string;
productSub: string;
vendor: string;
} = {
userAgent: navigator.userAgent,
appCodeName: navigator.appCodeName,
appName: navigator.appName,
appVersion: navigator.appVersion,
language: navigator.language,
platform: navigator.platform,
product: navigator.product,
productSub: navigator.productSub,
vendor: navigator.vendor,
};
export class wsEvent {
event: string;
data: any;
}
@Injectable()
export class HttpService {
headers = new Headers();
constructor(private _http: Http) {
}
// request(url: string | Request, options?: RequestOptionsArgs) {
// if (options == null) {
// options = {};
// }
// options.headers = this.headers;
// return this._http.request(url, options)
// }
get(url: string, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.get(url, options)
}
post(url: string, body: any, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.post(url, body, options)
}
put(url: string, body: any, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.put(url, body, options)
}
delete(url: string, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.delete(url, options)
}
patch(url: string, body: any, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.patch(url, body, options)
}
head(url: string, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.head(url, options)
}
options(url: string, options?: RequestOptionsArgs) {
if (options == null) {
options = {};
}
options.headers = this.headers;
return this._http.options(url, options)
}
}
@Injectable()
export class AppService implements OnInit {
// user:User = user ;
constructor(private _http: HttpService,
private _router: Router,
private _logger: Logger) {
if (Cookie.get('loglevel')) {
// 0.- Level.OFF
// 1.- Level.ERROR
// 2.- Level.WARN
// 3.- Level.INFO
// 4.- Level.DEBUG
// 5.- Level.LOG
this._logger.level = parseInt(Cookie.get('loglevel'), 10);
// this._logger.debug('Your debug stuff');
// this._logger.info('An info');
// this._logger.warn('Take care ');
// this._logger.error('Too late !');
// this._logger.log('log !');
} else {
Cookie.set('loglevel', '0', 99, '/', document.domain);
// this._logger.level = parseInt(Cookie.getCookie('loglevel'));
this._logger.level = 0;
}
// this.checklogin();
}
ngOnInit() {
}
checklogin() {
this._logger.log('service.ts:AppService,checklogin');
if (DataStore.Path) {
if (DataStore.Path['name'] === 'FOF' || DataStore.Path['name'] === 'Forgot') {
} else {
if (User.logined) {
if (document.location.pathname === '/login') {
this._router.navigate(['']);
} else {
this._router.navigate([document.location.pathname]);
}
// jQuery('angular2').show();
} else {
this.browser();
this._http.get('/api/checklogin')
.map(res => res.json())
.subscribe(
data => {
User.name = data.name;
User.username = data.username;
User.logined = data.logined;
this._logger.debug(User);
},
err => {
this._logger.error(err);
User.logined = false;
this._router.navigate(['login']);
},
() => {
if (User.logined) {
if (document.location.pathname === '/login') {
this._router.navigate(['']);
} else {
this._router.navigate([document.location.pathname]);
}
} else {
this._router.navigate(['login']);
}
// jQuery('angular2').show();
}
);
}
}
} else {
this._router.navigate(['FOF']);
// jQuery('angular2').show();
}
}
browser() {
this._http.post('/api/browser', JSON.stringify(Browser)).map(res => res.json()).subscribe()
}
//
//
// HideLeft() {
// DataStore.leftbarhide = true;
//
// DataStore.Nav.map(function (value, i) {
// for (var ii in value['children']) {
// if (DataStore.Nav[i]['children'][ii]['id'] === 'HindLeftManager') {
// DataStore.Nav[i]['children'][ii] = {
// 'id': 'ShowLeftManager',
// 'click': 'ShowLeft',
// 'name': 'Show left manager'
// };
// }
// }
// });
//
// }
//
// ShowLeft() {
// DataStore.leftbarhide = false;
//
// DataStore.Nav.map(function (value, i) {
// for (var ii in value['children']) {
// if (DataStore.Nav[i]['children'][ii]['id'] === 'ShowLeftManager') {
// DataStore.Nav[i]['children'][ii] = {
// 'id': 'HindLeftManager',
// 'click': 'HideLeft',
// 'name': 'Hind left manager'
// };
// }
// }
// });
//
//
// }
//
// setMyinfo(user:User) {
// // Update data store
// this._dataStore.user = user;
// this._logger.log("service.ts:AppService,setMyinfo");
// this._logger.debug(user);
// // Push the new list of todos into the Observable stream
// // this._dataObserver.next(user);
// // this.myinfo$ = new Observable(observer => this._dataObserver = observer).share()
// }
//
// getMyinfo() {
// this._logger.log('service.ts:AppService,getMyinfo');
// return this.http.get('/api/userprofile')
// .map(res => res.json())
// .subscribe(response => {
// DataStore.user = response;
// // this._logger.warn(this._dataStore.user);
// // this._logger.warn(DataStore.user)
// });
// }
//
// getUser(id: string) {
// this._logger.log('service.ts:AppService,getUser');
// return this.http.get('/api/userprofile')
// .map(res => res.json());
// }
//
// gettest() {
// this._logger.log('service.ts:AppService,gettest');
// this.http.get('/api/userprofile')
// .map(res => res.json())
// .subscribe(res => {
// return res;
// });
// }
//
// getGrouplist() {
// this._logger.log('service.ts:AppService,getGrouplist');
// return this.http.get('/api/grouplist')
// .map(res => res.json());
// }
//
// getUserlist(id: string) {
// this._logger.log('service.ts:AppService,getUserlist');
// if (id)
// return this.http.get('/api/userlist/' + id)
// .map(res => res.json());
// else
// return this.http.get('/api/userlist')
// .map(res => res.json());
// }
//
// delGroup(id) {
//
// }
//
//
// copy() {
// var clipboard = new Clipboard('#Copy');
//
// clipboard.on('success', function (e) {
// console.info('Action:', e.action);
// console.info('Text:', e.text);
// console.info('Trigger:', e.trigger);
//
// e.clearSelection();
// });
// console.log('ffff');
// console.log(window.getSelection().toString());
//
// var copy = new Clipboard('#Copy', {
// text: function () {
// return window.getSelection().toString();
// }
// });
// copy.on('success', function (e) {
// layer.alert('Lucky Copyed!');
// });
//
// }
}
<canvas id="canvas"></canvas>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RdppageComponent } from './rdppage.component';
describe('RdppageComponent', () => {
let component: RdppageComponent;
let fixture: ComponentFixture<RdppageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RdppageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RdppageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
/**
* RDP页面
*
* @date 2017-11-24
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {DataStore} from "../app.service";
declare let Mstsc: any;
@Component({
selector: 'app-rdppage',
templateUrl: './rdppage.component.html',
styleUrls: ['./rdppage.component.css']
})
export class RdppageComponent implements OnInit {
constructor(private activatedRoute: ActivatedRoute) {
DataStore.NavShow = false;
}
ngOnInit() {
let token: string;
this.activatedRoute.params.subscribe((params: Params) => {
token = params['token'];
});
let canvas = Mstsc.$("canvas");
canvas.style.display = 'inline';
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let client = Mstsc.client.create(Mstsc.$("canvas"));
client.connect(token, "socket.io");
}
}
#term {
width: 100%;
height: 100%;
}
#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;
width: 100%;
height: 100%;
background-color: black;
}
#term .terminal div span {
min-width: 12px;
}
#liuzheng {
position: fixed;
top: 0;
left: 0;
z-index: -1;
font-size: 11px !important;
padding-bottom: 16px !important;
font-family: 'Monaco', iosevka !important;
}
<span id="liuzheng">liuzheng</span>
<div id="term"></div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TermpageComponent } from './termpage.component';
describe('TermpageComponent', () => {
let component: TermpageComponent;
let fixture: ComponentFixture<TermpageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TermpageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TermpageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {DataStore} from "../app.service";
import * as io from 'socket.io-client';
declare let jQuery: any;
declare let Terminal: any;
@Component({
selector: 'app-termpage',
templateUrl: './termpage.component.html',
styleUrls: ['./termpage.component.css']
})
export class TermpageComponent implements OnInit {
constructor(private activatedRoute: ActivatedRoute) {
DataStore.NavShow = false;
}
ngOnInit() {
let token: string;
this.activatedRoute.params.subscribe((params: Params) => {
token = params['token'];
});
let socket = io.connect('/ssh');
let term = new Terminal({
cols: '80',
rows: '24',
useStyle: true,
screenKeys: true,
});
term.open(document.getElementById('term' ), true);
socket.on('connect', function () {
socket.emit('token', token);
term.on('data', function (data) {
socket.emit('data', data);
});
socket.on('data', function (data) {
term.write(data);
});
socket.on('disconnect', function () {
term.destroy();
});
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 (col < 80) col = 80;
if (row < 24) row = 24;
if (cols == col && row == rows) {
} else {
socket.emit('resize', [col, row]);
term.resize(col, row);
}
};
// jQuery(window).resize();
});
}
}
/*
* Copyright (c) 2015 Sylvain Peyrefitte
*
* This file is part of mstsc.js.
*
* mstsc.js is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function() {
/**
* decompress bitmap from RLE algorithm
* @param bitmap {object} bitmap object of bitmap event of node-rdpjs
*/
function decompress (bitmap) {
var fName = null;
switch (bitmap.bitsPerPixel) {
case 15:
fName = 'bitmap_decompress_15';
break;
case 16:
fName = 'bitmap_decompress_16';
break;
case 24:
fName = 'bitmap_decompress_24';
break;
case 32:
fName = 'bitmap_decompress_32';
break;
default:
throw 'invalid bitmap data format';
}
var input = new Uint8Array(bitmap.data);
var inputPtr = Module._malloc(input.length);
var inputHeap = new Uint8Array(Module.HEAPU8.buffer, inputPtr, input.length);
inputHeap.set(input);
var output_width = bitmap.destRight - bitmap.destLeft + 1;
var output_height = bitmap.destBottom - bitmap.destTop + 1;
var ouputSize = output_width * output_height * 4;
var outputPtr = Module._malloc(ouputSize);
var outputHeap = new Uint8Array(Module.HEAPU8.buffer, outputPtr, ouputSize);
var res = Module.ccall(fName,
'number',
['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
[outputHeap.byteOffset, output_width, output_height, bitmap.width, bitmap.height, inputHeap.byteOffset, input.length]
);
var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize);
Module._free(inputPtr);
Module._free(outputPtr);
return { width : output_width, height : output_height, data : output };
}
/**
* Un compress bitmap are reverse in y axis
*/
function reverse (bitmap) {
return { width : bitmap.width, height : bitmap.height, data : new Uint8ClampedArray(bitmap.data) };
}
/**
* Canvas renderer
* @param canvas {canvas} use for rendering
*/
function Canvas(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
}
Canvas.prototype = {
/**
* update canvas with new bitmap
* @param bitmap {object}
*/
update : function (bitmap) {
var output = null;
if (bitmap.isCompress) {
output = decompress(bitmap);
}
else {
output = reverse(bitmap);
}
// use image data to use asm.js
var imageData = this.ctx.createImageData(output.width, output.height);
imageData.data.set(output.data);
this.ctx.putImageData(imageData, bitmap.destLeft, bitmap.destTop);
}
};
/**
* Module export
*/
Mstsc.Canvas = {
create : function (canvas) {
return new Canvas(canvas);
}
}
})();
/*
* Copyright (c) 2015 Sylvain Peyrefitte
*
* This file is part of mstsc.js.
*
* mstsc.js is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function () {
/**
* Mouse button mapping
* @param button {integer} client button number
*/
function mouseButtonMap(button) {
switch (button) {
case 0:
return 1;
case 2:
return 2;
default:
return 0;
}
}
/**
* Mstsc client
* Input client connection (mouse and keyboard)
* bitmap processing
* @param canvas {canvas} rendering element
*/
function Client(canvas) {
this.canvas = canvas;
// create renderer
this.render = new Mstsc.Canvas.create(this.canvas);
this.socket = null;
this.activeSession = false;
this.install();
}
Client.prototype = {
install: function () {
var self = this;
// bind mouse move event
this.canvas.addEventListener('mousemove', function (e) {
if (!self.socket) return;
var offset = Mstsc.elementOffset(self.canvas);
self.socket.emit('mouse', e.clientX - offset.left, e.clientY - offset.top, 0, false);
e.preventDefault || !self.activeSession();
return false;
});
this.canvas.addEventListener('mousedown', function (e) {
if (!self.socket) return;
var offset = Mstsc.elementOffset(self.canvas);
self.socket.emit('mouse', e.clientX - offset.left, e.clientY - offset.top, mouseButtonMap(e.button), true);
e.preventDefault();
return false;
});
this.canvas.addEventListener('mouseup', function (e) {
if (!self.socket || !self.activeSession) return;
var offset = Mstsc.elementOffset(self.canvas);
self.socket.emit('mouse', e.clientX - offset.left, e.clientY - offset.top, mouseButtonMap(e.button), false);
e.preventDefault();
return false;
});
this.canvas.addEventListener('contextmenu', function (e) {
if (!self.socket || !self.activeSession) return;
var offset = Mstsc.elementOffset(self.canvas);
self.socket.emit('mouse', e.clientX - offset.left, e.clientY - offset.top, mouseButtonMap(e.button), false);
e.preventDefault();
return false;
});
this.canvas.addEventListener('DOMMouseScroll', function (e) {
if (!self.socket || !self.activeSession) return;
var isHorizontal = false;
var delta = e.detail;
var step = Math.round(Math.abs(delta) * 15 / 8);
var offset = Mstsc.elementOffset(self.canvas);
self.socket.emit('wheel', e.clientX - offset.left, e.clientY - offset.top, step, delta > 0, isHorizontal);
e.preventDefault();
return false;
});
this.canvas.addEventListener('mousewheel', function (e) {
if (!self.socket || !self.activeSession) return;
var isHorizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY);
var delta = isHorizontal ? e.deltaX : e.deltaY;
var step = Math.round(Math.abs(delta) * 15 / 8);
var offset = Mstsc.elementOffset(self.canvas);
self.socket.emit('wheel', e.clientX - offset.left, e.clientY - offset.top, step, delta > 0, isHorizontal);
e.preventDefault();
return false;
});
// bind keyboard event
window.addEventListener('keydown', function (e) {
if (!self.socket || !self.activeSession) return;
self.socket.emit('scancode', Mstsc.scancode(e), true);
e.preventDefault();
return false;
});
window.addEventListener('keyup', function (e) {
if (!self.socket || !self.activeSession) return;
self.socket.emit('scancode', Mstsc.scancode(e), false);
e.preventDefault();
return false;
});
return this;
},
/**
* connect
* @param ip {string} ip target for rdp
* @param domain {string} microsoft domain
* @param username {string} session username
* @param password {string} session password
* @param next {function} asynchrone end callback
*/
connect: function (token, socket) {
// compute socket.io path (cozy cloud integration)
var parts = document.location.pathname.split('/')
, base = parts.slice(0, parts.length - 1).join('/') + '/'
, path = base + socket;
// start connection
var self = this;
this.socket = io(window.location.protocol + "//" + window.location.host, {"path": path}).on('rdp-connect', function () {
// this event can be occured twice (RDP protocol stack artefact)
console.log('[mstsc.js] connected');
self.activeSession = true;
}).on('rdp-bitmap', function (bitmap) {
console.log('[mstsc.js] bitmap update bpp : ' + bitmap.bitsPerPixel);
self.render.update(bitmap);
}).on('rdp-close', function () {
// next(null);
console.log('[mstsc.js] close');
self.activeSession = false;
}).on('rdp-error', function (err) {
// next(err);
alert(err.code);
console.log('[mstsc.js] error : ' + err.code + '(' + err.message + ')');
self.activeSession = false;
});
if (token != null) {
// emit infos event
this.socket.emit('infos', {
token: token,
screen: {
width: this.canvas.width,
height: this.canvas.height
},
locale: Mstsc.locale()
});
} else {
alert("No privilege!!!")
}
}
};
Mstsc.client = {
create: function (canvas) {
return new Client(canvas);
}
}
})();
This diff is collapsed.
/*
* Copyright (c) 2015 Sylvain Peyrefitte
*
* This file is part of mstsc.js.
*
* mstsc.j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function() {
/**
* Use for domain declaration
*/
Mstsc = function () {
};
Mstsc.prototype = {
// shortcut
$ : function (id) {
return document.getElementById(id);
},
/**
* Compute screen offset for a target element
* @param el {DOM element}
* @return {top : {integer}, left {integer}}
*/
elementOffset : function (el) {
var x = 0;
var y = 0;
while (el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop )) {
x += el.offsetLeft - el.scrollLeft;
y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: y, left: x };
},
/**
* Try to detect browser
* @returns {String} [firefox|chrome|ie]
*/
browser : function () {
if (typeof InstallTrigger !== 'undefined') {
return 'firefox';
}
if (!!window.chrome) {
return 'chrome';
}
if (!!document.docuemntMode) {
return 'ie';
}
return null;
},
/**
* Try to detect language
* @returns
*/
locale : function () {
return window.navigator.userLanguage || window.navigator.language;
}
}
})();
this.Mstsc = new Mstsc();
This diff is collapsed.
src/favicon.ico

5.3 KB | W: | H:

src/favicon.ico

1.59 KB | W: | H:

src/favicon.ico
src/favicon.ico
src/favicon.ico
src/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -2,13 +2,13 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
<base href="/">
<title>JumpServer</title>
<base href="/luna">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<app-root></app-root>
</body>
</html>
/* You can add global styles to this file, and also import other style files */
/*
* Style tweaks
* --------------------------------------------------
*/
@font-face {
font-family: iosevka;
src: url('assets/fonts/iosevka-term-ss07-1.13.3/woff2/iosevka-term-ss07-light.woff2') format("woff2");
font-weight: normal;
}
html,
body {
overflow-x: hidden; /* Prevent scroll on narrow devices */
height: 100%;
width: 100%;
background-color: rgb(243, 243, 244);
}
app-root {
height: 100%;
}
#!/bin/bash
# coding: utf-8
# Copyright (c) 2017
# Gmail:liuzheng712
#
set -ex
git checkout publish && \
git pull github dev --rebase && \
git merge master -m "publish" && \
git reset --soft HEAD^ && \
git commit -m "publish" && \
git push github publish:dev && \
echo "success"
git checkout master
git pull github dev --commit && git push origin master
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