Unverified Commit 39f977cb authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #100 from jumpserver/dev_beta

Dev beta
parents 93dc40c3 fd957e28
......@@ -8,6 +8,7 @@
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"static",
"theme/default/",
"favicon.ico"
......@@ -31,8 +32,6 @@
"../node_modules/metismenu/dist/metisMenu.js",
"../node_modules/jquery-sparkline/jquery.sparkline.js",
"../node_modules/tether/dist/js/tether.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js",
"assets/inspinia/inspinia.js",
"assets/slimscroll/jquery.slimscroll.min.js",
"../node_modules/xterm/dist/xterm.js",
"assets/ztree/jquery.ztree.all.min.js",
......
......@@ -9,5 +9,5 @@ RUN npm run-script build
FROM nginx:alpine
COPY --from=stage-build /data/dist /opt/luna/
COPY i18n /opt/luna/i18n
COPY ./src/assets/i18n /opt/luna/i18n
COPY nginx.conf /etc/nginx/conf.d/default.conf
{
"reset": "重置",
"submit": "提交",
"email subject prefix": "邮件主题前缀",
"basic setting": "基本设置",
"email setting": "邮件设置",
"ldap setting": "LDAP设置",
"terminal setting": "终端设置",
"current site url": "当前站点URL",
"user guide url": "用户向导URL",
"user first login update profile done redirect to it": "用户第一次登录,修改profile后重定向到地址",
"server": "服务器",
"view": "视图",
"help": "帮助",
"hide left manager": "隐藏左边栏",
"show left manager": "显示左边栏",
"disconnect all": "断开所有链接",
"disconnect": "断开链接",
"website": "官网",
"search": "搜索",
"settings": "系统设置",
"job center": "作业中心",
"sessions": "会话管理",
"perms": "权限管理",
"assets": "资产管理",
"users": "用户管理",
"dashboard": "仪表盘",
"task": "任务",
"session online": "在线会话",
"session offline": "离线会话",
"commands": "命令记录",
"terminal": "终端管理",
"asset perminssion": "资产授权",
"asset": "资产",
"asset group": "资产组",
"cluster": "集群",
"admin user": "管理用户",
"system user": "系统用户",
"labels": "标签管理",
"user": "用户",
"user group": "用户组",
"login logs": "登陆日志",
"language": "语言选择",
"found": "发现",
"users ": "用户",
"choose a user": "选择一个用户",
"please choose a user": "请选择一个用户",
"cancel": "取消",
"confirm": "确认",
"document": "文档",
"support": "商业支持",
"speed": "速度",
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率",
"set font": "设置字体",
"font": "字体",
"font size": "字体大小",
"full screen": "全屏显示",
"please input password": "请输入密码",
"username": "用户名",
"password": "密码",
"tab list": "窗口列表",
"reconnect": "重新连接",
"are you sure to reconnect it?(rdp not support)": "确定要重新连接吗? (RDP暂不支持)"
}
{
"reset": "重置",
"submit": "提交",
"email subject prefix": "邮件主题前缀",
"basic setting": "基本设置",
"email setting": "邮件设置",
"ldap setting": "LDAP设置",
"terminal setting": "终端设置",
"current site url": "当前站点URL",
"user guide url": "用户向导URL",
"user first login update profile done redirect to it": "用户第一次登录,修改profile后重定向到地址",
"server": "服务器",
"view": "视图",
"help": "帮助",
"hide left manager": "隐藏左边栏",
"show left manager": "显示左边栏",
"disconnect all": "断开所有链接",
"disconnect": "断开链接",
"website": "官网",
"search": "搜索",
"settings": "系统设置",
"job center": "作业中心",
"sessions": "会话管理",
"perms": "权限管理",
"assets": "资产管理",
"users": "用户管理",
"dashboard": "仪表盘",
"task": "任务",
"session online": "在线会话",
"session offline": "离线会话",
"commands": "命令记录",
"terminal": "终端管理",
"asset perminssion": "资产授权",
"asset": "资产",
"asset group": "资产组",
"cluster": "集群",
"admin user": "管理用户",
"system user": "系统用户",
"labels": "标签管理",
"user": "用户",
"user group": "用户组",
"login logs": "登陆日志",
"language": "语言选择",
"found": "发现",
"users ": "用户",
"choose a user": "选择一个用户",
"please choose a user": "请选择一个用户",
"cancel": "取消",
"confirm": "确认",
"document": "文档",
"support": "商业支持",
"speed": "速度",
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率",
"set font": "设置字体",
"font": "字体",
"font size": "字体大小",
"full screen": "全屏显示",
"please input password": "请输入密码",
"username": "用户名",
"password": "密码",
"tab list": "窗口列表",
"reconnect": "重新连接",
"are you sure to reconnect it?(rdp not support)": "确定要重新连接吗? (RDP暂不支持)"
}
This diff is collapsed.
......@@ -12,17 +12,18 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.10",
"@angular/cdk": "^5.2.5",
"@angular/common": "5.2.0",
"@angular/compiler": "5.2.0",
"@angular/core": "5.2.0",
"@angular/forms": "5.2.0",
"@angular/http": "5.2.0",
"@angular/material": "^5.2.5",
"@angular/platform-browser": "5.2.0",
"@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0",
"@angular/animations": "^5.2.11",
"@angular/cdk": "^5.2.4",
"@angular/common": "^5.2.11",
"@angular/compiler": "^5.2.11",
"@angular/core": "^5.2.11",
"@angular/flex-layout": "^5.0.0-beta.15",
"@angular/forms": "^5.2.11",
"@angular/http": "^5.2.11",
"@angular/material": "^5.2.0",
"@angular/platform-browser": "^5.2.11",
"@angular/platform-browser-dynamic": "^5.2.11",
"@angular/router": "^5.2.11",
"@swimlane/ngx-datatable": "^11.3.2",
"@swimlane/ngx-ui": "^20.2.1",
"@types/jquery": "^3.3.6",
......@@ -31,22 +32,20 @@
"ajv": "^6.5.0",
"animate.css": "^3.6.1",
"body-parser": "^1.18.2",
"bootstrap": "^4.1.1",
"bootstrap": "^4.3.1",
"clipboard": "^1.7.1",
"compass-mixins": "^0.12.10",
"core-js": "2.5.3",
"directory-encoder": "^0.9.2",
"elfinder": "git+https://github.com/Studio-42/elFinder.git#2.1.33",
"filetree-css": "^1.0.0",
"font-awesome": "4.7.0",
"guacamole-common-js": "0.9.14-b",
"handlebars": "^4.0.11",
"handlebars": "^4.1.2",
"intl": "1.2.5",
"jquery": "3.2.1",
"jquery": "^3.4.1",
"jquery-slimscroll": "^1.3.8",
"jquery-sparkline": "^2.4.0",
"jvectormap": "1.2.2",
"lodash": "^4.17.10",
"lodash": "^4.17.15",
"material-design-icons": "^3.0.1",
"materialize-css": "^0.100.2",
"metismenu": "^2.7.9",
......@@ -79,7 +78,7 @@
"@angular-devkit/core": "^0.4.9",
"@angular-devkit/schematics": "^0.4.9",
"@angular/cli": "^1.7.4",
"@angular/compiler-cli": "5.2.0",
"@angular/compiler-cli": "^5.2.11",
"@angular/language-service": "5.2.0",
"@types/jasmine": "2.8.4",
"@types/jasminewd2": "~2.0.2",
......
{
"/api": {
"target": "http://127.0.0.1:5001",
"target": "http://127.0.0.1:8080",
"secure": false
},
"/luna/i18n": {
......@@ -8,7 +8,7 @@
"secure": false
},
"/socket.io/": {
"target": "http://127.0.0.1:5001",
"target": "http://127.0.0.1:5000",
"secure": false,
"ws": true
},
......@@ -27,5 +27,13 @@
"^/rdp": ""
},
"secure": false
},
"/guacamole/": {
"target": "http://127.0.0.1:8083",
"secure": false,
"ws": true,
"pathRewrite": {
"^/guacamole": ""
}
}
}
/**
* app 模块
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms'; // <-- NgModel lives here
import {NGXLogger} from 'ngx-logger';
import {HttpClientModule} from '@angular/common/http';
import {AppRouterModule} from './router/router.module';
import {AppComponent} from './pages/app.component';
// service
import {AppService, HttpService, LocalStorageService, LogService, UUIDService} from './app.service';
import {CookieService} from 'ngx-cookie-service';
import {MAT_LABEL_GLOBAL_OPTIONS} from '@angular/material';
// service
import {AppService, HttpService, LocalStorageService, NavService, LogService, UUIDService, TreeFilterService} from './app.service';
import {AppRouterModule} from './router/router.module';
import {Pipes} from './pipes/pipes';
import {AppComponent} from './pages/app.component';
import {PagesComponents} from './pages/pages.component';
import {ElementComponents} from './elements/elements.component';
import {ChangLanWarningDialogComponent, RDPSolutionDialogComponent, FontDialogComponent} from './elements/nav/nav.component';
import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dialog.service';
import {PluginModules} from './plugins/plugins';
import {TestPageComponent} from './test-page/test-page.component';
import {AssetTreeDialogComponent, ManualPasswordDialogComponent} from './elements/asset-tree/asset-tree.component';
import {AssetTreeDialogComponent, ManualPasswordDialogComponent} from './elements/connect/connect.component';
import {SftpComponent} from './elements/sftp/sftp.component';
......@@ -66,13 +55,14 @@ import {SftpComponent} from './elements/sftp/sftp.component';
AppService,
HttpService,
LogService,
NavService,
UUIDService,
LocalStorageService,
DialogService,
CookieService,
TreeFilterService,
NGXLogger,
{provide: MAT_LABEL_GLOBAL_OPTIONS, useValue: {float: 'always'}}
]
})
export class AppModule {
......
This diff is collapsed.
<ul id="ztree" class="ztree"></ul>
<div>
<ul id="assetsTree" class="ztree">
{{ "Loading"|trans }} ...
</ul>
<ul id="remoteAppsTree" class="ztree">
{{ "Loading"|trans }} ...
</ul>
</div>
<div #rMenu *ngIf="isShowRMenu" class="basicContext" [style.top]="pos.top" [style.left]="pos.left">
<table>
......
#ztree .fa {
.tree-refresh .fa {
width: 24px;
height: 24px;
line-height: 24px;
......@@ -35,7 +35,7 @@
user-select: none;
}
.basicContext, .basicContext * {
box-sizing: border-box;
box-sizing: border-box;
}
.basicContextContainer {
......@@ -92,11 +92,11 @@ tr {
}
tr:hover {
background-color: #463e3e;
background-color: #463e3e;
}
.basicContext table {
border-spacing: 0 !important;
border-spacing: 0 !important;
}
.basicContext__data {
......
import {Component, Input, OnInit, Output, Inject, OnDestroy, EventEmitter} from '@angular/core';
import {connectEvt} from '@app/globals';
import {AppService, HttpService, LogService, NavService} from '@app/app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
import {ActivatedRoute} from '@angular/router';
import {SystemUser, TreeNode, Asset} from '@app/model';
import {View} from '@app/model';
import * as jQuery from 'jquery/dist/jquery.min';
declare var $: any;
@Component({
selector: 'elements-connect',
templateUrl: './connect.component.html',
})
export class ElementConnectComponent implements OnInit, OnDestroy {
@Output() onNewView: EventEmitter<View> = new EventEmitter<View>();
pos = {left: '100px', top: '200px'};
hasLoginTo = false;
constructor(private _appSvc: AppService,
public _dialog: MatDialog,
public _logger: LogService,
private activatedRoute: ActivatedRoute,
private _http: HttpService,
) {
}
ngOnInit(): void {
connectEvt.asObservable().subscribe(evt => {
switch (evt.action) {
case 'asset': {
this.Connect(evt.node);
break;
}
case 'sftp': {
this.connectFileManager(evt.node);
break;
}
}
});
}
ngOnDestroy(): void {
connectEvt.unsubscribe();
}
Connect(node: TreeNode) {
switch (node.meta.type) {
case 'asset':
this.connectAsset(node);
break;
case 'remote_app':
this.connectRemoteApp(node);
break;
default:
alert('Unknown type: ' + node.meta.type);
}
}
connectAsset(node: TreeNode) {
const host = node.meta.asset as Asset;
this._http.getMyAssetSystemUsers(host.id).subscribe(systemUsers => {
let user: SystemUser;
if (systemUsers.length > 1) {
// 检查系统用户优先级,获取最高优先级的
user = this.checkPriority(systemUsers);
if (user) {
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
}
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: systemUsers}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of systemUsers) {
if (i.id.toString() === result.toString()) {
user = i;
break;
}
}
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
}
});
} else if (systemUsers.length === 1) {
user = systemUsers[0];
this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
} else {
alert('该主机没有授权登录用户');
}
});
}
connectRemoteApp(node: TreeNode) {
const user = node.meta.user as SystemUser;
return this.manualSetUserAuthLoginIfNeed(node, user, this.loginRemoteApp.bind(this));
}
loginRemoteApp(node: TreeNode, user: SystemUser) {
if (node) {
const view = new View();
view.nick = node.name;
view.connected = true;
view.editable = false;
view.closed = false;
view.remoteApp = node.id;
view.user = user;
view.type = 'rdp';
this.onNewView.emit(view);
}
}
connectFileManager(node: TreeNode) {
const host = node.meta.asset as Asset;
if (host) {
const view = new View();
view.nick = '[FILE] ' + host.hostname;
view.connected = true;
view.editable = false;
view.closed = false;
view.host = host;
view.type = 'sftp';
this.onNewView.emit(view);
// jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
}
connectTerminal(node: TreeNode) {
this.Connect(node);
}
manualSetUserAuthLoginIfNeed(node: any, user: SystemUser, callback) {
if (user.login_mode !== 'manual' || user.protocol !== 'rdp') {
return callback(node, user);
}
user = Object.assign({}, user);
const dialogRef = this._dialog.open(ManualPasswordDialogComponent, {
height: '250px',
width: '500px',
data: {username: user.username}
});
dialogRef.afterClosed().subscribe(result => {
if (!result) {
return;
}
if (result.skip) {
return callback(node, user);
}
user.username = result.username;
user.password = result.password;
return callback(node, user);
});
}
loginAsset(host: Asset, user: SystemUser) {
if (user) {
const view = new View();
view.nick = host.hostname;
view.connected = true;
view.editable = false;
view.closed = false;
view.host = host;
view.user = user;
if (user.protocol === 'ssh' || user.protocol === 'telnet') {
view.type = 'ssh';
} else if (user.protocol === 'rdp' || user.protocol === 'vnc') {
view.type = 'rdp';
}
this.onNewView.emit(view);
}
}
checkPriority(sysUsers: Array<SystemUser>) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
return null;
}
}
return user;
}
}
@Component({
selector: 'elements-asset-tree-dialog',
templateUrl: 'dialog.html',
})
export class AssetTreeDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<AssetTreeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
}
}
@Component({
selector: 'elements-manual-password-dialog',
templateUrl: 'manual-password-dialog.html',
})
export class ManualPasswordDialogComponent implements OnInit {
PasswordControl = new FormControl('', [Validators.required]);
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) {
}
onSkip() {
this.data.skip = true;
this.dialogRef.close(this.data);
}
onSkipAll() {
this.data.skipAll = true;
this.dialogRef.close(this.data);
}
onNoClick() {
this.dialogRef.close();
}
onEnter() {
this.dialogRef.close(this.data);
}
ngOnInit(): void {
}
}
......@@ -11,6 +11,7 @@
</mat-form-field>
<div style="float: right">
<button mat-raised-button (click)="onSkip()" >{{"Skip"|trans}}</button>
<button mat-raised-button (click)="onNoClick()" >{{"Cancel"|trans}}</button>
<button mat-raised-button color="primary" [type]="'submit'" [mat-dialog-close]="data" >{{"Confirm"|trans}}</button>
</div>
......
.tabs {
height: 30px;
overflow-y: hidden;
overflow-x: hidden;
position: relative;
.window {
display: none;
/*padding: 15px;*/
}
.tabs ul li.disconnected {
li.disconnected {
background-color: darkgray;
}
.tabs ul li.hidden {
li.hidden {
display: none;
}
.tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
display: block;
min-width: 100%;
padding-left: 0;
}
.tabs ul li {
li {
display: inline-table;
width: 150px;
height: 30px;
position: relative;
box-sizing: content-box;
float: left;;
background-color: #463a3a66;
border-right: 1px solid #6b6565c2;
border-radius: 2px;
}
.tabs ul li.active {
li.active {
box-sizing: border-box;
border-bottom: 3px solid #19aa8d !important;
border-bottom: 3px solid #19aa8d;
}
.tabs ul li span {
li span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: gray;
color: #f1f0f0;
font-family: 'Roboto', sans-serif;
font-size: 13px;
text-decoration: none;
......@@ -53,7 +45,7 @@
display: block;
}
.tabs ul li a.close {
li a.close {
font-family: 'Roboto', sans-serif;
font-size: 13px;
position: absolute;
......@@ -65,18 +57,18 @@
display: inline-block;
}
.tabs ul li.active a {
li.active a {
color: white;
}
.tabs ul li.active span {
li.active span {
padding-left: 12px;
line-height: 26px;
color: white;
height: 26px;
}
.tabs ul li input {
li input {
font-family: 'Roboto', sans-serif;
font-size: 13px;
width: 120px;
......@@ -87,27 +79,3 @@
height: 26px;
}
/*
* scrollbar
*/
.tabs::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #676a6c;
}
.tabs::-webkit-scrollbar {
height: 2px;
}
.tabs::-webkit-scrollbar-thumb {
background-color: #F5F5F5;
}
.scroll-botton {
font-size: 20px;
float: left;
height: 30px;
overflow: hidden;
background-color: #3a3333;
color: white
}
<li
[ngClass]="{'active': view.active,'disconnected': !view.connected, 'hidden': view.closed != false}"
[id]="view.id" (click)="setActive()" (dblclick)="view.editable=true;setActive()"
>
<span *ngIf="view.nick">{{view.nick | truncatechars:25 }}</span>
<input *ngIf="view.editable" [(ngModel)]="view.nick" (blur)="view.editable=false" (keyup.enter)="view.editable=false" autofocus="true"/>
<a class="close" (click)="close()">&times;</a>
</li>
import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
import {View, ViewAction} from '@app/model';
@Component({
selector: 'elements-content-tab',
templateUrl: './content-tab.component.html',
styleUrls: ['./content-tab.component.css'],
})
export class ElementContentTabComponent implements OnInit {
@Input() view: View;
@Output() onAction: EventEmitter<ViewAction> = new EventEmitter<ViewAction>();
ngOnInit(): void {
}
close() {
const action = new ViewAction(this.view, 'close');
this.onAction.emit(action);
}
setActive() {
const action = new ViewAction(this.view, 'active');
this.onAction.emit(action);
}
}
div, elements-term, elements-guacamole, elements-settings {
div, elements-term, elements-guacamole {
height: 100%;
}
elements-term, elements-guacamole, elements-settings {
elements-term, elements-guacamole {
/*padding-bottom: 30px;*/
}
.window {
display: none;
height: 100%;
/*padding: 15px;*/
}
.active {
display: block;
}
.right-side {
height: 100%;
width: 100%;
background-color: gray;
}
<div class="window" [ngClass]="{'active':view.active}" style="height: 100%">
<elements-ssh-term
[view]="view"
[host]="view.host"
[sysUser]="view.user"
*ngIf="view.type=='ssh'"
>
</elements-ssh-term>
<elements-guacamole
[view]="view"
[host]="view.host"
[sysUser]="view.user"
[remoteAppId]="view.remoteApp"
*ngIf="view.type=='rdp'"
>
</elements-guacamole>
<app-sftp *ngIf="view.type=='sftp'" [host]="view.host"></app-sftp>
</div>
import {Component, OnInit, Input} from '@angular/core';
import {View} from '@app/model';
@Component({
selector: 'elements-content-window',
templateUrl: './content-window.component.html',
styleUrls: ['./content-window.component.css']
})
export class ElementContentViewComponent implements OnInit {
@Input() view: View;
static active() {
// viewList.List.forEach((v, k) => {
// v.hide = id.toString() !== k;
// });
// viewList.Active = id;
}
static TerminalDisconnect(id) {
// if (viewList.List[id].connected) {
// viewList.List[id].connected = false;
// viewList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
// TermWS.emit('logout', viewList.List[id].room);
// }
}
static RdpDisconnect(id) {
// viewList.List[id].connected = false;
}
static DisconnectAll() {
// for (let i = 0; i < viewList.List.length; i++) {
// Todo:
// ContentComponent.TerminalDisconnect(i);
// }
}
constructor() {
}
ngOnInit() {
}
// trackByFn(index: number, item: View) {
// return item.id;
// }
}
<div id="content">
<div>
<div class="scroll-button">
<a class="left" (click)="scrollLeft()"><i class="fa fa-caret-left"></i></a>
<a class="right" (click)="scrollRight()"><i class="fa fa-caret-right"></i></a>
</div>
<div class="tabs" #tabs>
<ul [ngStyle]="{'width':tabsWidth+'px'}">
<elements-content-tab
*ngFor="let view of viewList"
[view]="view" (onAction)="onViewAction($event)">
</elements-content-tab>
</ul>
</div>
</div>
<div id="winContainer">
<elements-content-window *ngFor="let view of viewList" [view]="view" ></elements-content-window>
</div>
</div>
<elements-connect [ngStyle]="{'display': 'none'}" (onNewView)="onNewView($event)" ></elements-connect>
.tabs {
height: 30px;
overflow-y: hidden;
overflow-x: hidden;
position: relative;
}
.tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
//display: block;
display: inline-block;
min-width: 100%;
padding-left: 0;
margin: 0;
}
/*
* scrollbar
*/
.tabs::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #676a6c;
}
.tabs::-webkit-scrollbar {
height: 2px;
}
.tabs::-webkit-scrollbar-thumb {
background-color: #F5F5F5;
}
.scroll-button {
font-size: 20px;
float: left;
height: 30px;
overflow: hidden;
background-color: #3a3333;
color: white;
padding: 0 5px;
}
.scroll-button a.disabled {
color: #676A6D;
cursor: not-allowed;
}
#content {
height: 100%;
}
#winContainer {
height: calc(100% - 30px);
}
//
//.window {
// height: 100%;
//}
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {View, ViewAction} from '@app/model';
@Component({
selector: 'elements-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.scss']
})
export class ElementContentComponent implements OnInit {
@ViewChild('tabs') tabsRef: ElementRef;
viewList: Array<View> = [];
static DisconnectAll() {
}
get tabsWidth() {
return (this.viewList.length + 1) * 151 + 10;
}
constructor() {
}
ngOnInit() {
}
onNewView(view) {
this.scrollToEnd();
setTimeout(() => {
this.viewList.push(view);
this.setViewActive(view);
}, 100);
}
onViewAction(action: ViewAction) {
switch (action.name) {
case 'active': {
this.setViewActive(action.view);
break;
}
case 'close': {
this.closeView(action.view);
break;
}
}
}
setViewActive(view) {
this.viewList.forEach((v, k) => {
v.active = v === view;
});
}
closeView(view) {
let nextActiveView = null;
const index = this.viewList.indexOf(view);
if (view.active) {
// 如果关掉的是最后一个, 存在上一个
if (index === this.viewList.length - 1 && index !== 0) {
nextActiveView = this.viewList[index - 1];
} else if (index < this.viewList.length) {
nextActiveView = this.viewList[index + 1];
}
}
this.viewList.splice(index, 1);
if (nextActiveView) {
this.setViewActive(nextActiveView);
}
}
scrollLeft() {
this.tabsRef.nativeElement.scrollLeft -= 150 * 2;
}
scrollRight() {
this.tabsRef.nativeElement.scrollLeft += 150 * 2;
}
scrollToEnd() {
this.tabsRef.nativeElement.scrollLeft = this.tabsRef.nativeElement.scrollWidth;
}
}
import {Component, Inject, Injectable, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {LogService} from '../../app.service';
import {LogService} from '@app/app.service';
import {FormControl, Validators} from '@angular/forms';
// import * as layer from 'layui-layer/src/layer.js';
......
// Elements
import {ElementTableComponent} from './table/table.component';
import {ElementLeftbarComponent} from './leftbar/leftbar.component';
import {ElementOfooterComponent} from './ofooter/ofooter.component';
import {ElementLeftBarComponent} from './left-bar/left-bar.component';
import {ElementContentComponent} from './content/content.component';
import {ElementContentViewComponent} from './content-window/content-window.component';
import {ElementContentTabComponent} from './content-tab/content-tab.component';
import {ElementAssetTreeComponent} from './asset-tree/asset-tree.component';
import {ElementTreeFilterComponent} from './tree-filter/tree-filter.component';
import {ElementFooterComponent} from './footer/footer.component';
import {ElementTermComponent} from './term/term.component';
import {ElementInteractiveComponent} from './interactive/interactive.component';
......@@ -13,17 +17,22 @@ import {ElementIframeComponent} from './iframe/iframe.component';
import {ElementDialogAlertComponent} from './dialog/dialog.service';
import {ElementGuacamoleComponent} from './guacamole/guacamole.component';
import {ElementSshTermComponent} from './ssh-term/ssh-term.component';
import {AssetTreeDialogComponent, ElementAssetTreeComponent, ManualPasswordDialogComponent} from './asset-tree/asset-tree.component';
import {ElementConnectComponent, AssetTreeDialogComponent, ManualPasswordDialogComponent} from './connect/connect.component';
import {RDPSolutionDialogComponent, FontDialogComponent} from './nav/nav.component';
export const ElementComponents = [
ElementLeftbarComponent,
ElementOfooterComponent,
ElementLeftBarComponent,
ElementContentComponent,
ElementContentTabComponent,
ElementContentViewComponent,
ElementConnectComponent,
ElementTreeFilterComponent,
ElementTableComponent,
ElementFooterComponent,
ElementTermComponent,
ElementInteractiveComponent,
ElementNavComponent, ChangLanWarningDialogComponent,
ElementNavComponent,
ChangLanWarningDialogComponent,
ElementPopupComponent,
ElementRdpComponent,
ElementServerMenuComponent,
......@@ -32,6 +41,7 @@ export const ElementComponents = [
ElementGuacamoleComponent,
ElementAssetTreeComponent,
ElementSshTermComponent,
ElementConnectComponent,
AssetTreeDialogComponent,
ManualPasswordDialogComponent,
RDPSolutionDialogComponent,
......
......@@ -5,9 +5,9 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService, LogService} from '../../app.service';
import {DataStore, User} from '../../globals';
import {version} from '../../../environments/environment';
import {AppService, LogService} from '@app/app.service';
import {DataStore, User} from '@app/globals';
import {version} from '@src/environments/environment';
@Component({
selector: 'elements-footer',
......
<!--<iframe #rdp [src]="trust('https://inews.gtimg.com/newsapp_bt/0/3460971429/1000')" width="100%" height="100%" (mouseenter)="active()"></iframe>-->
<iframe *ngIf="target" #rdp [src]="trust(target)" width="100%" height="100%" (mouseenter)="active()"></iframe>
<div class="rdpIframe">
<iframe #rdpRef *ngIf="target" [src]="trust(target)" width="100%" height="100%" (mouseenter)="active()"></iframe>
</div>
......@@ -2,3 +2,7 @@ iframe {
border: none;
background-color: white;
}
.rdpIframe {
height: 100%;
}
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
import {CookieService} from 'ngx-cookie-service';
import {HttpService, LogService} from '../../app.service';
import {DataStore, User} from '../../globals';
import {HttpService, LogService} from '@app/app.service';
import {DataStore, User} from '@app/globals';
import {DomSanitizer} from '@angular/platform-browser';
import {environment} from '../../../environments/environment';
import {NavList} from '../../pages/control/control/control.component';
import {View} from '@app/model';
@Component({
selector: 'elements-guacamole',
......@@ -12,12 +11,13 @@ import {NavList} from '../../pages/control/control/control.component';
styleUrls: ['./guacamole.component.scss']
})
export class ElementGuacamoleComponent implements OnInit {
@Input() view: View;
@Input() host: any;
@Input() sysUser: any;
@Input() remoteAppId: string;
@Input() target: string;
@Input() index: number;
@ViewChild('rdp') el: ElementRef;
@ViewChild('rdpRef') el: ElementRef;
registered = false;
constructor(private sanitizer: DomSanitizer,
......@@ -29,15 +29,14 @@ export class ElementGuacamoleComponent implements OnInit {
registerHost() {
let action: any;
if (this.remoteAppId) {
action = this._http.guacamole_add_remote_app(User.id, this.remoteAppId);
action = this._http.guacamoleAddRemoteApp(User.id, this.remoteAppId, this.sysUser.username, this.sysUser.password);
} else {
action = this._http.guacamole_add_asset(User.id, this.host.id, this.sysUser.id, this.sysUser.username, this.sysUser.password);
action = this._http.guacamoleAddAsset(User.id, this.host.id, this.sysUser.id, this.sysUser.username, this.sysUser.password);
}
action.subscribe(
data => {
const base = data.result;
this.target = document.location.origin + '/guacamole/#/client/' + base + '?token=' + DataStore.guacamole_token;
NavList.List[this.index].Rdp = this.el.nativeElement;
this.target = document.location.origin + '/guacamole/#/client/' + base + '?token=' + DataStore.guacamoleToken;
},
error => {
if (!this.registered) {
......@@ -52,11 +51,11 @@ export class ElementGuacamoleComponent implements OnInit {
const now = new Date();
const nowTime = now.getTime() / 1000;
this.registered = true;
this._http.get_guacamole_token(User.id, '').subscribe(
this._http.getGuacamoleToken(User.id, '').subscribe(
data => {
// /guacamole/client will redirect to http://guacamole/#/client
DataStore.guacamole_token = data['authToken'];
DataStore.guacamole_token_time = nowTime;
DataStore.guacamoleToken = data['authToken'];
DataStore.guacamoleTokenTime = nowTime;
this.registerHost();
},
error => {
......@@ -68,8 +67,8 @@ export class ElementGuacamoleComponent implements OnInit {
ngOnInit() {
// /guacamole/api/tokens will redirect to http://guacamole/api/tokens
this.view.type = 'rdp';
if (this.target) {
NavList.List[this.index].Rdp = this.el.nativeElement;
return null;
}
......@@ -86,7 +85,8 @@ export class ElementGuacamoleComponent implements OnInit {
}
Disconnect() {
NavList.List[this.index].connected = false;
// TOdo:
return;
}
active() {
......
import {Component, Input, OnInit} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {NavList} from '../../pages/control/control/control.component';
import {User, DataStore} from '../../globals';
import {HttpService, LogService} from '../../app.service';
import {environment} from '../../../environments/environment';
import {CookieService} from 'ngx-cookie-service';
@Component({
selector: 'elements-iframe',
......@@ -13,15 +8,9 @@ import {CookieService} from 'ngx-cookie-service';
styleUrls: ['./iframe.component.scss']
})
export class ElementIframeComponent implements OnInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
target: string;
constructor(private sanitizer: DomSanitizer,
private _http: HttpService,
private _cookie: CookieService,
private _logger: LogService) {
constructor(private sanitizer: DomSanitizer) {
}
ngOnInit() {
......@@ -30,8 +19,4 @@ export class ElementIframeComponent implements OnInit {
trust(url) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
Disconnect() {
NavList.List[this.index].connected = false;
}
}
<div class="sidebar" fxLayout="column" >
<div fxflex="1 1 30px" class="tree-filter">
<elements-tree-filter ></elements-tree-filter>
</div>
<div class="overflow ngx-scroll-overlay" fxflex="1 1 calc(100% - 60px)">
<elements-asset-tree ></elements-asset-tree>
</div>
<div class="footer-version" fxflex="1 1 30px">
<p> Version <strong>{{version}}</strong></p>
</div>
</div>
.sidebar {
height: 100%;
width: 100%;
overflow: auto;
}
label {
margin-bottom: 0;
}
.fa.fa-undefined:before {
content: "\f26c";
}
.overflow {
display: flex;
width: 100%;
height: 100%;
float: left;
position: inherit;
background: #2f2a2a;
color: #d6cbcb;
}
.footer-version {
background: #2f2a2a;
font-size: 9pt;
left: 0;
width: 100%;
padding: 1px 20px 0 20px;
border-top: 1px solid #e7eaec;
bottom: 0;
height: 30px;
}
.footer-version > p {
height: 8px;
font-size: 13px;
padding-top: 2px;
padding-bottom: 2px;
color: #d6cbcb;
}
.ngx-scroll-overlay {
overflow: auto; // for FF
}
/**
* 控制页面的左边栏主机树状页
*
* 以树状方式列出所有主机
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, Inject, OnInit, ViewChild, ElementRef} from '@angular/core';
import {AppService, HttpService, LogService} from '../../../app.service';
import {SearchComponent} from '../search/search.component';
import {DataStore} from '../../../globals';
import {version} from '../../../../environments/environment';
import {NavList, View} from '../control/control.component';
import {AppService, HttpService, LogService} from '@app/app.service';
// import {ElementTreeFilterComponent} from '../tree-filter/tree-filter.component';
import {DataStore} from '@app/globals';
import {version} from '@src/environments/environment';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {ElementServerMenuComponent} from '../../../elements/server-menu/server-menu.component';
import {DialogService} from '../../../elements/dialog/dialog.service';
export interface Node {
......@@ -37,12 +25,12 @@ export class Host {
}
@Component({
selector: 'pages-control-cleftbar',
templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.scss'],
providers: [SearchComponent, ElementServerMenuComponent]
selector: 'elements-left-bar',
templateUrl: './left-bar.component.html',
styleUrls: ['./left-bar.component.scss'],
// providers: [ElementTreeFilterComponent]
})
export class CleftbarComponent {
export class ElementLeftBarComponent {
DataStore = DataStore;
version = version;
q: string;
......@@ -55,7 +43,7 @@ export class CleftbarComponent {
}
static Hide() {
DataStore.leftbarshow = false;
DataStore.showLeftBar = false;
DataStore.Nav.map(function (value, i) {
value['children'].forEach((v, key) => {
if (DataStore.Nav[i]['children'][key]['id'] === 'HideLeftManager') {
......@@ -71,7 +59,7 @@ export class CleftbarComponent {
}
static Show() {
DataStore.leftbarshow = true;
DataStore.showLeftBar = true;
DataStore.Nav.map(function (value, i) {
value['children'].forEach((v, key) => {
if (DataStore.Nav[i]['children'][key]['id'] === 'ShowLeftManager') {
......@@ -88,16 +76,15 @@ export class CleftbarComponent {
constructor(private _appService: AppService,
private _http: HttpService,
private _search: SearchComponent,
private _logger: LogService,
private _menu: ElementServerMenuComponent,
public _dialog: MatDialog,
private _layer: DialogService) {
) {
this._logger.log('nav.ts:NavComponent');
}
Search(q) {
this._search.Search(q);
// Todo:
// this._search.Search(q);
}
onRightClick(event: MouseEvent): void {
......
<mat-sidenav-container>
<mat-sidenav #sidenav mode="side" opened="true" class="example-sidenav"
[fixedInViewport]="options.value.fixed" [fixedTopGap]="options.value.top"
[fixedBottomGap]="options.value.bottom">
<nav>
<div class="header">
<a href="http://www.jumpserver.org" target="_blank">
<img alt="image" height="55" src="/static/imgs/logo-text.png" style="margin-left: 10px">
</a>
</div>
<div class="body">
<ul class="nav metismenu nav-frist-level">
<li *ngFor="let bar of leftbar;let i = index" [ngClass]="{'active':i==active}">
<a (click)="gotoLink(bar.link,i,-1)">
<i class="{{bar.class}}"></i>
<span class="nav-label">{{bar.name|trans}}</span>
<span class="{{bar.label}}"></span>
</a>
<ul class="nav nav-second-level collapse">
<li *ngFor="let child of bar.child;let ii = index" [ngClass]="{'active':ii==active2}">
<a (click)="gotoLink(child.link,i,ii)">{{child.name|trans}}</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</mat-sidenav>
<div class="navbar-header">
<button class="navbar-minimalize minimalize-styl-2 btn btn-primary " (click)="sidenav.toggle()"><i
class="fa fa-bars"></i></button>
</div>
</mat-sidenav-container>
li a {
background-color: rgba(0, 0, 0, 0);
box-sizing: border-box;
color: rgb(167, 177, 194);
cursor: pointer;
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
font-weight: 600;
height: 46px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
padding-bottom: 14px;
padding-left: 25px;
padding-right: 20px;
padding-top: 14px;
position: relative;
text-align: left;
text-decoration-color: rgb(167, 177, 194);
text-decoration-line: none;
text-decoration-style: solid;
text-size-adjust: 100%;
width: 220px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
li.active > a {
width: 216px;
color: white;
}
li.active ul.nav.nav-second-level li {;
border-bottom-style: none;
border-bottom-width: 0px;
box-sizing: border-box;
color: rgb(103, 106, 108);
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
height: 32px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
position: relative;
text-align: left;
text-size-adjust: 100%;
visibility: visible;
width: 216px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
li.active ul.nav.nav-second-level {
box-sizing: border-box;
color: rgb(103, 106, 108);
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
margin-bottom: 0px;
margin-top: 0px;
padding-left: 0px;
text-align: left;
text-size-adjust: 100%;
visibility: visible;
width: 216px;
-webkit-margin-after: 0px;
-webkit-margin-before: 0px;
-webkit-margin-end: 0px;
-webkit-margin-start: 0px;
-webkit-padding-start: 0px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
mat-sidenav {
background-color: #2f4050;
}
nav {
width: 220px;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
}
div.header {
background-color: #202c37;
}
div.body {
background-color: #2f4050;
height: 100%;
}
li:hover {
color: white !important;
}
.nav-second-level li a {
padding: 7px 10px 7px 52px;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ElementLeftbarComponent } from './leftbar.component';
describe('ElementLeftbarComponent', () => {
let component: ElementLeftbarComponent;
let fixture: ComponentFixture<ElementLeftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ElementLeftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ElementLeftbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {Router} from '@angular/router';
@Component({
selector: 'elements-leftbar',
templateUrl: './leftbar.component.html',
styleUrls: ['./leftbar.component.scss']
})
export class ElementLeftbarComponent implements OnInit {
leftbar = [
{
'name': 'Dashboard',
'class': 'fa fa-dashboard',
'label': '',
'link': '/'
},
{
'name': 'Users',
'class': 'fa fa-group',
'label': 'fa arrow',
'child': [
{
'name': 'User',
}, {
'name': 'User group',
}, {
'name': 'Login logs',
}
]
},
{
'name': 'Assets',
'class': 'fa fa-inbox',
'label': 'fa arrow',
'child': [
{
'name': 'Asset',
}, {
'name': 'Asset group',
}, {
'name': 'Cluster',
}, {
'name': 'Admin user',
}, {
'name': 'System user',
}, {
'name': 'Labels',
}
]
},
{
'name': 'Perms',
'class': 'fa fa-edit',
'label': 'fa arrow',
'child': [{'name': 'Asset permission'}]
},
{
'name': 'Sessions',
'class': 'fa fa-rocket',
'label': 'fa arrow',
'child': [
{
'name': 'Session online'
},
{
'name': 'Session offline'
},
{
'name': 'Commands'
},
{
'name': 'Terminal'
},
]
},
{
'name': 'Job Center',
'class': 'fa fa-coffee',
'label': 'fa arrow',
'child': [
{
'name': 'Task',
'link': '/task'
},
]
},
{
'name': 'Settings',
'class': 'fa fa-gears',
'label': '',
'link': '/luna/setting'
},
];
options: FormGroup;
active: number;
active2: number;
constructor(fb: FormBuilder,
private _router: Router) {
this.options = fb.group({
'fixed': true,
'top': 0,
'bottom': 0,
});
}
ngOnInit() {
this.active = 6;
this.active2 = 0;
}
gotoLink(link: string, index: number, index2: number) {
if (link) {
if (link === '/luna/setting') {
this._router.navigate(['setting']);
} else {
window.location.href = link;
}
}
this.active = index;
this.active2 = index2;
}
}
.nav {
display: block;
margin-top: 2px;
height: 30px
height: 30px;
padding-top: 0;
background-color: #463e3e;
list-style: none;
}
.nav ul {
list-style-type: none;
line-height: 24px;
margin: 0;
box-sizing: border-box;
font-weight: 400;
text-align: left;
}
.nav li {
display: inline-block;
}
.nav a {
.nav .dropdown a {
color: #f0f0f1;
font-family: Roboto,sans-serif;
font-size: 13px;
font-weight: 300;
text-decoration: none;
padding: 6px 15px 6px 15px;
padding: 6px 15px;
height: 30px;
}
.nav a:hover {
......@@ -46,9 +56,9 @@
direction: ltr;
width: auto;
top: auto;
left: 0px;
margin-left: 0px;
margin-top: 0px;
left: 0;
margin-left: 0;
margin-top: 0;
min-width: 150px;
}
......
<script src="../../trans.pipe.spec.ts"></script>
<div class="nav">
<ul>
<li><a href="/"><img src="static/imgs/logo.png" height="26px"/></a>
<ul class="nav-main">
<li>
<a href="/"><img src="static/imgs/logo.png" height="26px"/></a>
</li>
<li *ngFor="let v of DataStore.Nav" [ngClass]="{'dropdown': v.children}">
<li *ngFor="let v of navs" [ngClass]="{'dropdown': v.children}" >
<a>{{v.name|trans}}</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|trans}}</a>
<a id="{{vv.id}}" *ngIf="vv.click" (click)="click(vv.click)">{{vv.name|trans}}</a>
<a id="{{vv.id}}" *ngIf="vv.click && !vv.hide" (click)="click(vv.click)">{{vv.name|trans}}</a>
</li>
</ul>
</li>
<li [ngClass]="{'dropdown': true}">
<a>{{"Tab List"|trans}}</a>
<ul *ngIf="NavList.List.length>1" [ngClass]="{'dropdown-content': true}">
<ng-container *ngFor="let t of NavList.List, let idx= index" >
<li *ngIf="t.nick!=null" [ngClass]="{'active':idx==NavList.Active,'disconnected':!t.connected, 'hidden': t.closed != false}">
<a id="{{ 'tab' + idx }}" (click)="toTab(idx)">{{t.nick}}</a>
</li>
</ng-container>
</ul>
</li>
</ul>
</div>
......@@ -6,10 +6,8 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, Inject, OnInit} from '@angular/core';
import {AppService, HttpService, LocalStorageService, LogService} from '../../app.service';
import {CleftbarComponent} from '../../pages/control/cleftbar/cleftbar.component';
import {ControlComponent, NavList, View} from '../../pages/control/control/control.component';
import {DataStore, i18n} from '../../globals';
import {HttpService, LocalStorageService, NavService, LogService} from '@app/app.service';
import {DataStore, i18n} from '@app/globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
declare let layer: any;
......@@ -21,50 +19,52 @@ declare let layer: any;
})
export class ElementNavComponent implements OnInit {
DataStore = DataStore;
NavList = NavList;
ChangeLanWarningDialog: any;
navs: Array<any>;
_asyncTree = false;
static Hide() {
jQuery('elements-nav').hide();
}
constructor(private _appService: AppService,
private _http: HttpService,
constructor(private _http: HttpService,
private _logger: LogService,
public _dialog: MatDialog,
public _navSvc: NavService,
private _localStorage: LocalStorageService) {
this._logger.log('nav.ts:NavComponent');
this.getNav();
}
ngOnInit() {
this.navs = this.getNav();
}
toTab(idx) {
ControlComponent.active(idx);
get treeLoadAsync() {
return this._asyncTree;
}
set treeLoadAsync(value) {
this._asyncTree = value;
}
click(event) {
this._logger.debug('nav.ts:NavComponent,click', event);
switch (event) {
case 'ReloadLeftbar': {
CleftbarComponent.Reload();
break;
}
case 'ConnectSFTP': {
window.open('/coco/elfinder/sftp/');
break;
}
case 'HideLeft': {
CleftbarComponent.Hide();
DataStore.showLeftBar = false;
this.refreshNav();
break;
}
case 'Settings': {
this.Settings();
case 'ShowLeft': {
DataStore.showLeftBar = true;
this.refreshNav();
break;
}
case 'ShowLeft': {
CleftbarComponent.Show();
case 'Settings': {
this.Settings();
break;
}
case 'Copy': {
......@@ -72,8 +72,10 @@ export class ElementNavComponent implements OnInit {
break;
}
case 'FullScreen': {
let ele:any = document.getElementsByClassName("window active ")[0];
const ele: any = document.getElementsByClassName('window active')[0];
if (!ele) {
return;
}
if (ele.requestFullscreen) {
ele.requestFullscreen();
} else if (ele.mozRequestFullScreen) {
......@@ -85,41 +87,29 @@ export class ElementNavComponent implements OnInit {
} else {
throw new Error('不支持全屏api');
}
window.dispatchEvent(new Event('resize'));
break;
}
case 'Reconnect': {
if (NavList.List[NavList.Active].termComp) {
NavList.List[NavList.Active].termComp.reconnect();
case'Disconnect': {
if (!confirm('断开当前连接?')) {
return
}
}
case 'Reconnect': {
break;
}
case 'Disconnect': {
if (!confirm('断开当前连接? (RDP暂不支持)')) {
break;
}
switch (NavList.List[NavList.Active].type) {
case 'ssh': {
ControlComponent.TerminalDisconnect(NavList.Active);
break;
}
case 'rdp': {
ControlComponent.RdpDisconnect(NavList.Active);
break;
}
default: {
// statements;
break;
}
}
this._navSvc.disconnectConnection();
break;
}
case'DisconnectAll': {
if (!confirm('断开所有连接? (RDP暂不支持)')) {
if (!confirm('断开所有连接?')) {
break;
}
ControlComponent.DisconnectAll();
this._navSvc.disconnectAllConnection();
break;
}
case 'Website': {
......@@ -162,10 +152,6 @@ export class ElementNavComponent implements OnInit {
});
break;
}
case 'EnterLicense': {
this.EnterLicense();
break;
}
case 'English': {
const dialog = this._dialog.open(
ChangLanWarningDialogComponent,
......@@ -206,6 +192,16 @@ export class ElementNavComponent implements OnInit {
});
break;
}
case 'LoadTreeAsync': {
this._navSvc.treeLoadAsync = !this._navSvc.treeLoadAsync;
this.refreshNav();
break;
}
case 'SkipManualPassword': {
this._navSvc.skipAllManualPassword = !this._navSvc.skipAllManualPassword;
this.refreshNav();
break;
}
default: {
break;
}
......@@ -213,45 +209,12 @@ export class ElementNavComponent implements OnInit {
}
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);
});
refreshNav() {
this.navs = this.getNav();
}
getNav() {
DataStore.Nav = [{
'id': 'File',
'name': 'Server',
'children': [
{
'id': 'Disconnect',
'click': 'Disconnect',
'name': 'Disconnect'
},
{
'id': 'DisconnectAll',
'click': 'DisconnectAll',
'name': 'Disconnect all'
},
{
'id': 'Reconnect',
'click': 'Reconnect',
'name': 'Reconnect'
},
]
}, {
return [{
'id': 'FileManager',
'name': 'File Manager',
'children': [
......@@ -269,7 +232,14 @@ export class ElementNavComponent implements OnInit {
{
'id': 'HideLeftManager',
'click': 'HideLeft',
'name': 'Hide left manager'
'name': 'Hide left manager',
'hide': !DataStore.showLeftBar
},
{
'id': 'ShowLeftManager',
'click': 'ShowLeft',
'name': 'Show left manager',
'hide': DataStore.showLeftBar
},
{
'id': 'RDPResolution',
......@@ -303,6 +273,30 @@ export class ElementNavComponent implements OnInit {
'id': 'FullScreen',
'click': 'FullScreen',
'name': 'Full Screen'
},
{
'id': 'LoadTreeAsync',
'click': 'LoadTreeAsync',
'name': 'Load Tree Async',
'hide': this._navSvc.treeLoadAsync
},
{
'id': 'LoadTreeSync',
'click': 'LoadTreeAsync',
'name': 'Load Tree Sync',
'hide': !this._navSvc.treeLoadAsync
},
{
'id': 'SkipManualPassword',
'click': 'SkipManualPassword',
'name': 'Skip manual password',
'hide': this._navSvc.skipAllManualPassword
},
{
'id': 'ShowManualPassword',
'click': 'SkipManualPassword',
'name': 'Show manual password',
'hide': !this._navSvc.skipAllManualPassword
}
]
}, {
......@@ -343,21 +337,6 @@ export class ElementNavComponent implements OnInit {
];
}
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.close(index);
});
}
English() {
this._localStorage.delete('lang');
i18n.clear();
......@@ -381,14 +360,6 @@ export class ElementNavComponent implements OnInit {
}
Settings() {
const id = NavList.List.length - 1;
NavList.List[id].nick = 'Setting';
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = 'settings';
NavList.List.push(new View());
NavList.Active = id;
}
}
......
<div class="footer fixed">
<div class="pull-right">
Version <strong>{{version}}</strong> GPLv2.
<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">
</div>
<div>
<strong>Copyright</strong> 北京堆栈科技有限公司 &copy; 2014-2018
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ElementOfooterComponent } from './ofooter.component';
describe('ElementOfooterComponent', () => {
let component: ElementOfooterComponent;
let fixture: ComponentFixture<ElementOfooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ElementOfooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ElementOfooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {version} from '../../../environments/environment';
import {DataStore} from '../../globals';
@Component({
selector: 'elements-ofooter',
templateUrl: './ofooter.component.html',
styleUrls: ['./ofooter.component.scss']
})
export class ElementOfooterComponent implements OnInit {
version = version;
constructor() {
DataStore.NavShow = false;
}
ngOnInit() {
}
}
......@@ -6,7 +6,7 @@
*/
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
declare let Mstsc: any;
......
import {Component, OnInit, Input, ElementRef, ViewChild} from '@angular/core';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
import {DomSanitizer} from '@angular/platform-browser';
@Component({
......
import {AfterViewInit, Component, Input, OnInit, OnDestroy} from '@angular/core';
import {Component, Input, OnInit, OnDestroy } from '@angular/core';
import {Terminal} from 'xterm';
import {NavList, View} from '../../pages/control/control/control.component';
import {UUIDService} from '../../app.service';
import {CookieService} from 'ngx-cookie-service';
import {Socket} from '../../utils/socket';
import {getWsSocket} from '../../globals';
import {TransPipe} from '../../pipes/trans.pipe';
import {View} from '@app/model';
import {LogService, UUIDService} from '@app/app.service';
import {Socket} from '@app/utils/socket';
import {getWsSocket, translate} from '@app/globals';
@Component({
......@@ -13,8 +11,9 @@ import {TransPipe} from '../../pipes/trans.pipe';
templateUrl: './ssh-term.component.html',
styleUrls: ['./ssh-term.component.scss']
})
export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy {
export class ElementSshTermComponent implements OnInit, OnDestroy {
@Input() host: any;
@Input() view: View;
@Input() sysUser: any;
@Input() index: number;
@Input() token: string;
......@@ -23,24 +22,18 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
secret: string;
ws: Socket;
roomID: string;
view: View;
transPipe: TransPipe;
constructor(private _uuid: UUIDService, private _cookie: CookieService) {
constructor(private _uuid: UUIDService, private _logger: LogService) {
}
ngOnInit() {
this.view = NavList.List[this.index];
this.secret = this._uuid.gen();
this.newTerm();
this.transPipe = new TransPipe();
getWsSocket().then(sock => {
this.ws = sock;
this.connectHost();
});
}
ngAfterViewInit() {
this.view.type = 'ssh';
}
newTerm() {
......@@ -54,7 +47,6 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
}
});
this.view.Term = this.term;
this.view.termComp = this;
}
changeWinSize(size: Array<number>) {
......@@ -64,12 +56,10 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
}
reconnect() {
if (NavList.List[this.index].connected === true) {
if (!confirm(this.transPipe.transform('Are you sure to reconnect it?(RDP not support)'))) {
if (this.view.connected === true) {
if (!confirm(translate('Are you sure to reconnect it?(RDP not support)'))) {
return;
}
this.close();
}
this.secret = this._uuid.gen();
this.emitHostAndTokenData();
......@@ -90,7 +80,7 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
'token': this.token, 'secret': this.secret,
'size': [this.term.cols, this.term.rows]
};
console.log('On token event trigger');
this._logger.debug('On token event trigger');
this.ws.emit('token', data);
}
}
......@@ -111,20 +101,20 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
// 服务器主动断开
this.ws.on('disconnect', () => {
console.log('On disconnect event trigger');
this.close();
this._logger.debug('On disconnect event trigger');
this.view.connected = false;
});
this.ws.on('logout', data => {
if (data.room === this.roomID) {
console.log('On logout event trigger: ', data.room, this.roomID);
this._logger.debug('On logout event trigger: ', data.room, this.roomID);
this.view.connected = false;
}
});
this.ws.on('room', data => {
if (data.secret === this.secret && data.room) {
console.log('On room', data);
this._logger.debug('On room', data);
this.roomID = data.room;
this.view.room = data.room;
this.view.connected = true;
......@@ -132,19 +122,15 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
});
}
// 客户端主动关闭
close() {
if (this.view && (this.view.room === this.roomID)) {
this.view.connected = false;
this.ws.emit('logout', this.roomID);
}
}
active() {
this.term.focus();
}
ngOnDestroy(): void {
this.close();
this._logger.debug('Close view');
if (this.view && (this.view.room === this.roomID)) {
this.view.connected = false;
this.ws.emit('logout', this.roomID);
}
}
}
import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {DatatableComponent} from '@swimlane/ngx-datatable';
import {MatPaginator} from '@angular/material';
import {LogService} from '../../app.service';
import {LogService} from '@app/app.service';
export let Config: {
search: boolean,
......
......@@ -2,15 +2,13 @@ import {AfterViewInit, Component, Input, Output, OnInit, ViewChild, EventEmitter
import {ElementRef} from '@angular/core';
import {Terminal} from 'xterm';
import {fit} from 'xterm/lib/addons/fit/fit';
import {LogService} from '@app/app.service';
import {Observable} from 'rxjs/Rx';
import {CookieService} from 'ngx-cookie-service';
import * as $ from 'jquery/dist/jquery.min.js';
import 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import {NavList} from '../../pages/control/control/control.component';
@Component({
selector: 'elements-term',
......@@ -24,7 +22,8 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
@Output() winSizeChangeTrigger = new EventEmitter<Array<number>>();
winSizeChange$: Observable<any>;
constructor(private _cookie: CookieService) {
constructor(private _logger: LogService){
}
ngOnInit() {
......@@ -34,9 +33,7 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
this.winSizeChange$
.subscribe(() => {
if (NavList.List[NavList.Active].type !== 'rdp') {
this.resizeTerm();
}
});
}
......@@ -66,10 +63,18 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
availableWidth = activeEle.width() - elementPaddingHor - (<any>this.term).viewport.scrollBarWidth;
}
const dimensions = (<any>this.term).renderer.dimensions;
const geometry = [
Math.floor(availableWidth / (<any>this.term).renderer.dimensions.actualCellWidth) - 1,
Math.floor(availableHeight / (<any>this.term).renderer.dimensions.actualCellHeight) - 1
Math.floor(availableWidth / dimensions.actualCellWidth) - 1,
Math.floor(availableHeight / dimensions.actualCellHeight) - 1
];
if (!isFinite(geometry[0])) {
geometry[0] = 80;
}
if (!isFinite(geometry[1])) {
geometry[1] = 24;
}
return geometry;
}
......
/*.left-search {*/
/*padding-left: 14px;*/
/*width: 100%;*/
/*border: none;*/
/*}*/
.search {
border: none;
border-left-width: 0;
border-bottom: #19aa8d 1px inset;
width: 100%;
background: #2f2a2a;
color: #d6cbcb;
height: 30px;
padding-left: 10px;
/* padding-top: 30px;
//position: fixed;
//height: 28px;
*/
}
<input id="search" class="search"
[formControl]="searchControl"
placeholder=" {{'Search'| trans }} ..."
maxlength="2048"
name="keyword"
autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false"
>
/**
* 控制页面的搜索框
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnChanges, Input, Pipe, PipeTransform} from '@angular/core';
import {Component, OnInit, Pipe, PipeTransform} from '@angular/core';
import {FormControl} from '@angular/forms';
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
import {AppService, HttpService, LogService} from '../../../app.service';
export let Q = '';
@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: LogService) {
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.search(q)
.subscribe(
data => {
this._logger.log(data);
},
err => {
this._logger.error(err);
},
() => {
}
);
this._logger.log(q);
}
}
import {LogService, TreeFilterService} from '@app/app.service';
@Pipe({name: 'SearchFilter'})
......@@ -68,3 +18,30 @@ export class SearchFilter implements PipeTransform {
return value;
}
}
@Component({
selector: 'elements-tree-filter',
templateUrl: './tree-filter.component.html',
styleUrls: ['./tree-filter.component.css'],
providers: [SearchFilter],
})
export class ElementTreeFilterComponent implements OnInit {
searchControl: FormControl;
private debounce = 400;
constructor(private _treeFilterService: TreeFilterService,
private _logger: LogService) {
}
ngOnInit(): void {
this.searchControl = new FormControl('');
this.searchControl.valueChanges
.pipe(debounceTime(this.debounce), distinctUntilChanged())
.subscribe(query => {
this._logger.debug('Tree filter: ', query);
this._treeFilterService.filter(query);
});
}
}
import {Component, Input, OnInit} from '@angular/core';
import {HttpService} from '../../app.service';
import {HttpService} from '@app/app.service';
export interface Assets {
name: string;
......
'use strict';
import {EventEmitter} from 'events/events';
import * as io from 'socket.io-client';
import * as neffos from 'neffos.js';
import {Terminal} from 'xterm';
// const abc = io.connect('/ssh');
import {Socket} from './utils/socket';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {ConnectEvt, User as _User } from './model';
import {DataStore as _DataStore, Browser as _Browser, Video as _Video, Monitor as _Monitor} from './model';
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws';
const port = document.location.port ? ':' + document.location.port : '';
const wsURL = scheme + '://' + document.location.hostname + port + '/socket.io/';
const hostname = document.location.hostname;
const wsURL = `${scheme}://${hostname}${port}/socket.io/`;
export let TermWS = null;
export const emitter = new(EventEmitter);
export const sep = '/';
export let Video: {
id: string,
src: string,
type: string,
height: number,
width: number,
json: object;
timelist: Array<number>;
totalTime: number;
} = {
id: '',
src: '',
type: '',
width: 0,
height: 0,
json: {},
timelist: [],
totalTime: 0,
};
export let Monitor: {
token: string,
room: string,
type: string,
} = {
token: '',
room: '',
type: 'term',
};
export class Group {
id: string;
name: string;
membercount: number;
comment: string;
}
export let User: {
id: string;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
wechat: string;
comment: string;
is_active: boolean;
is_superuser: boolean;
date_joined: string;
last_login: string;
date_expired: string;
groups: Array<Group>;
logined: boolean;
} = {
id: '',
name: 'nobody',
username: '',
password: '',
phone: '',
avatar: '',
role: '',
email: '',
wechat: '',
comment: '',
is_active: false,
is_superuser: false,
date_joined: '',
last_login: '',
date_expired: '',
groups: [],
logined: false,
};
export let DataStore: {
socket: any;
Nav: Array<{}>;
NavShow: boolean;
Path: {};
error: {};
msg: {};
loglevel: number;
leftbarshow: boolean;
windowsize: Array<number>;
autologin: boolean;
guacamole_token: string;
guacamole_token_time: number;
} = {
export let Video = new _Video();
export let Monitor = new _Monitor();
export let User = new _User();
export const DataStore: _DataStore = {
socket: TermWS,
Nav: [{}],
NavShow: true,
Path: {},
error: {},
msg: {},
loglevel: 0,
leftbarshow: true,
windowsize: [],
autologin: false,
guacamole_token: '',
guacamole_token_time: 0
};
export let CSRF = '';
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,
logLevel: 4,
showLeftBar: true,
windowSize: [],
autoLogin: false,
guacamoleToken: '',
guacamoleTokenTime: 0
};
export let Browser = new _Browser();
export const i18n = new Map();
export async function getWsSocket() {
......@@ -155,4 +49,13 @@ export async function getWsSocket() {
return TermWS;
}
export const connectEvt = new BehaviorSubject<ConnectEvt>(new ConnectEvt(null, null));
export function translate(value) {
if (i18n.has(value.toLowerCase())) {
return i18n.get(value.toLowerCase());
} else {
return value;
}
}
export class UserGroup {
id: string;
name: string;
comment: string;
}
export class User {
id: string;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
wechat: string;
comment: string;
is_active: boolean;
is_superuser: boolean;
date_joined: string;
last_login: string;
date_expired: string;
groups: Array <UserGroup>;
logined: boolean;
}
export class SystemUser {
id: string;
name: string;
login_mode: string;
username: string;
priority: number;
protocol: string;
password: string;
actions: Array<string>;
}
export class TreeNode {
id: string;
name: string;
comment: string;
title: string;
isParent: boolean;
pId: string;
open: boolean;
iconSkin: string;
meta: any;
}
export class Node {
id: string;
key: string;
value: string;
}
export class Asset {
id: string;
hostname: string;
ip: string;
comment: string;
domain: string;
os: string;
platform: string;
protocols: Array<string>;
}
export class GuacObjAddResp {
code: number;
result: string;
}
export class ConnectEvt {
node: TreeNode;
action: string;
constructor(node: TreeNode, action: string) {
this.node = node;
this.action = action;
}
}
export class NavEvt {
name: string;
value: any;
constructor(name: string, value: any) {
this.name = name;
this.value = value;
}
}
export class View {
id: string;
nick: string;
type: string;
editable: boolean;
active: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
host: any;
user: any;
remoteApp: string;
room: string;
Rdp: any;
Term: any;
}
export class ViewAction {
view: View;
name: string;
constructor(view: View, name: string) {
this.view = view;
this.name = name;
}
}
export class DataStore {
socket: any;
Nav: Array<object>;
NavShow = true;
Path: {};
error: {};
msg: {};
logLevel: number;
showLeftBar = true;
windowSize: Array<number>;
autoLogin: boolean;
guacamoleToken: string;
guacamoleTokenTime: number;
}
export class Browser {
userAgent: string;
appCodeName: string;
appName: string;
appVersion: string;
language: string;
platform: string;
product: string;
productSub: string;
vendor: string;
constructor() {
this.userAgent = navigator.userAgent;
this.appCodeName = navigator.appCodeName;
this.appName = navigator.appName;
this.appVersion = navigator.appVersion;
this.language = navigator.language;
this.platform = navigator.platform;
this.product = navigator.product;
this.productSub = navigator.productSub;
this.vendor = navigator.vendor;
}
}
export class Video {
id: string;
src: string;
type: string;
height: number;
width: number;
json: object;
timeList: Array<number>;
totalTime: number;
}
export class Monitor {
token: string;
room: string;
type: string;
}
<ng-progress></ng-progress>
<elements-nav *ngIf="DataStore.NavShow"></elements-nav>
<router-outlet></router-outlet>
<!--<elements-interactive></elements-interactive>-->
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});
/**
* 控制主页
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, HostListener} from '@angular/core';
import {Component} from '@angular/core';
import {DataStore} from '../globals';
import { environment } from '../../environments/environment';
import {AppService} from '../app.service';
@Component({
selector: 'app-root',
......@@ -18,16 +11,7 @@ import { environment } from '../../environments/environment';
export class AppComponent {
DataStore = DataStore;
constructor() {}
@HostListener('window:beforeunload', ['$event'])
unloadNotification($event: any) {
const notInIframe = window.self === window.top;
const notInReplay = location.pathname.indexOf('/luna/replay') === -1;
if (environment.production && notInIframe && notInReplay) {
return false;
}
return true;
constructor(appSrv: AppService) {
}
}
import {Component, OnInit} from '@angular/core';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
@Component({
selector: 'pages-blank',
......
import {Component, OnInit} from '@angular/core';
import {AppService, HttpService, LocalStorageService} from '../../app.service';
import {DataStore} from '../../globals';
import {AppService, HttpService, LocalStorageService} from '@app/app.service';
import {DataStore} from '@app/globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
......@@ -33,7 +33,7 @@ export class PagesConnectComponent implements OnInit {
jQuery('body').css('background-color', '#1f1b1b');
if (this.system === 'windows') {
if (!this.userid) {
this._http.get_user_id_from_token(this.token)
this._http.getUserIdFromToken(this.token)
.subscribe(
data => {
this._localStorage.set('user-' + this.token, data['user']);
......@@ -49,7 +49,7 @@ export class PagesConnectComponent implements OnInit {
getAuthToken() {
if (!this.authToken) {
this._http.get_guacamole_token(this.userid, this.token).subscribe(
this._http.getGuacamoleToken(this.userid, this.token).subscribe(
data => {
if (data['authToken']) {
this._localStorage.set('authToken-' + this.token, data['authToken']);
......@@ -65,7 +65,7 @@ export class PagesConnectComponent implements OnInit {
getBase() {
if (!this.base) {
this._http.guacamole_token_add_asset(this.token, this.authToken).subscribe(
this._http.guacamoleTokenAddAsset(this.token, this.authToken).subscribe(
data => {
if (data['result']) {
this._localStorage.set('base-' + this.token, data['result']);
......
<div class="sidebar" fxLayout="column" ngxSplit="column">
<div fxflex="0 0 30px" class="search">
<input #keyword id="keyword" class="left-search"
placeholder=" {{'Search'| trans }} ..."
maxlength="2048"
name="q"
autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" [(ngModel)]="q">
</div>
<div class="overflow ngx-scroll-overlay" fxflex="1 1 90%">
<elements-asset-tree [query]="q" ></elements-asset-tree>
</div>
<div class="footer-version" fxflex="0 0 26px">
<p> Version <strong>{{version}}</strong></p>
</div>
</div>
<!--<elements-server-menu></elements-server-menu>-->
.sidebar {
height: 100%;
width: 100%;
overflow: auto;
}
:root {
font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}
label {
margin-bottom: 0;
}
.filetree {
padding-left: 20px;
height: inherit;
}
.filetree input[type="checkbox"] {
display: none;
}
.filetree input[type=checkbox] + label:before {
font-family: FontAwesome;
display: inline-block;
}
.filetree input[type=checkbox] + label:before {
content: "\f114";
letter-spacing: 10px;
width: 30px;
}
.filetree input[type=checkbox]:checked + label:before {
content: "\f115";
}
.filetree ul {
height: 0;
overflow: hidden;
}
.filetree > li input:checked ~ ul, .filetree > li ul.insearch {
height: auto;
}
.filetree li {
list-style: none;
cursor: pointer;
color: #ffffff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.filetree label {
line-height: 33px;
display: inline-block;
}
.fa.fa-undefined:before {
content: "\f26c";
}
.left-search {
padding-left: 14px;
width: 100%;
border: none;
background: #2f2a2a;
color: #ffffff;
}
.search {
border-left-width: 0;
border-bottom: #19aa8d 2px inset;
//padding-top: 30px;
width: 100%;
//position: fixed;
//height: 28px;
}
.search > input {
height: 30px;
}
.overflow {
height: 100%;
width: 100%;
float: left;
position: inherit;
background: #2f2a2a;
color: #d6cbcb;
}
.footer-version {
background: #2f2a2a;
font-size: 9pt;
left: 0;
width: 100%;
padding: 1px 20px 0 20px;
border-top: 1px solid #e7eaec;
bottom: 0;
//height: 30px;
//position: fixed;
}
.footer-version > p {
height: 8px;
padding-top: 2px;
padding-bottom: 2px;
}
//@import "~@swimlane/ngx-ui/release/styles/components/scrollbars";
.ngx-scroll-overlay {
overflow: auto; // for FF
//-ms-overflow-style: -ms-autohiding-scrollbar;
//
//&::-webkit-scrollbar {
// display: none;
//}
//
//&:hover::-webkit-scrollbar {
// display: initial;
//}
}
//.sidebar::-webkit-scrollbar-track {
// -webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.3);
// background-color: #676a6c;
//}
//
//.sidebar::-webkit-scrollbar {
// width: 8px;
//}
//
//.sidebar::-webkit-scrollbar-thumb {
// background-color: #F5F5F5;
// border-radius: 6px;
// border: 2px solid transparent;
//}
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();
});
});
<div class="container-fluid row" fxLayout="row" ngxSplit="row">
<div fxFlex="1 1 20%" minBasis="100px" maxBasis="800px" fxFlexFill ngxSplitArea *ngIf="DataStore.leftbarshow">
<pages-control-cleftbar></pages-control-cleftbar>
</div>
<div fxFlex="0" ngxSplitHandle [style.display]="activeViewIsRdp() ? 'none' : 'block'" (mouseup)="dragSplitBtn($event)"></div>
<div [fxFlex]="DataStore.leftbarshow ? '1 1 80%' : '1 1 100%'" ngxSplitArea class="content">
<pages-control-control></pages-control-control>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesControlComponent } from './control.component';
describe('ControlPageComponent', () => {
let component: PagesControlComponent;
let fixture: ComponentFixture<PagesControlComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesControlComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesControlComponent);
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 {DataStore, User} from '../../globals';
import {NavList} from './control/control.component';
@Component({
selector: 'pages-control',
templateUrl: './control.component.html',
styleUrls: ['./control.component.css'],
})
export class PagesControlComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor() {
}
activeViewIsRdp() {
return NavList.List[NavList.Active].type === 'rdp';
}
dragSplitBtn(evt) {
window.dispatchEvent(new Event('resize'));
}
ngOnInit() {
}
}
<div fxLayout="column" ngxSplit="column" style="width: 100%;height: 100%;">
<div fxFlex="0 0 30px" class="search">
<pages-control-nav></pages-control-nav>
</div>
<div fxFlex="0 0 calc(100%-35px)" id="winContainer">
<div class="window" *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'active':i==NavList.Active}" style="height: 100%">
<elements-ssh-term [index]="i"
[host]="m.host"
[sysUser]="m.user"
*ngIf="m.type=='ssh'">
</elements-ssh-term>
<elements-guacamole [index]="i"
[host]="m.host"
[sysUser]="m.user"
[remoteAppId]="m.remoteApp"
*ngIf="m.type=='rdp'">
</elements-guacamole>
<app-sftp *ngIf="m.type=='sftp'" [host]="m.host">
</app-sftp>
<!--<elements-settings [index]="i"-->
<!--*ngIf="m.type=='settings'">-->
<!--</elements-settings>-->
</div>
</div>
</div>
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';
import {TermWS} from '../../../globals';
import * as jQuery from 'jquery/dist/jquery.min';
export class View {
nick: string;
type: string;
edit: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
host: any;
user: any;
remoteApp: string;
room: string;
Rdp: any;
Term: any;
termComp: any;
}
export let NavList: {
List: Array<View>;
Active: number;
} = {
List: [new View()],
Active: 0,
};
@Component({
selector: 'pages-control-control',
templateUrl: './control.component.html',
styleUrls: ['./control.component.css']
})
export class ControlComponent implements OnInit {
NavList = NavList;
static active(id) {
NavList.List.forEach((v, k) => {
v.hide = id.toString() !== k;
});
NavList.Active = id;
jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
static TerminalDisconnect(id) {
if (NavList.List[id].connected) {
NavList.List[id].connected = false;
NavList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
TermWS.emit('logout', NavList.List[id].room);
}
}
static RdpDisconnect(id) {
NavList.List[id].connected = false;
}
static DisconnectAll() {
for (let i = 0; i < NavList.List.length; i++) {
ControlComponent.TerminalDisconnect(i);
}
}
constructor() {
}
ngOnInit() {
}
// trackByFn(index: number, item: View) {
// return item.id;
// }
}
<div class="scroll-botton" style="padding: 0 5px">
<a class="left" (click)="scrollleft()"><i class="fa fa-caret-left"></i></a>
<a class="right" (click)="scrollright()"><i class="fa fa-caret-right"></i></a>
</div>
<div class="tabs">
<ul [ngStyle]="{'width':150*NavList.List.length+'px'}">
<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)" (dblclick)="m.edit=true;setActive(i)">
<span *ngIf="!m.edit" [attr.title]="m.nick">{{m.nick | truncatechars:17 }}</span>
<input *ngIf="m.edit" [(ngModel)]="m.nick" (blur)="m.edit=false" (keyup.enter)="m.edit=false" autofocus="true"/>
<a class="close" (click)="close(m,i)">&times;</a>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesControlNavComponent } from './nav.component';
describe('ControlPagesControlNavComponent', () => {
let component: PagesControlNavComponent;
let fixture: ComponentFixture<PagesControlNavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesControlNavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesControlNavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面的已连接主机选项卡
*
* 展示所有已连接的主机
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {ControlComponent, NavList} from '../control.component';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
selector: 'pages-control-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css'],
})
export class PagesControlNavComponent implements OnInit {
setActive = PagesControlNavComponent.setActive;
NavList = NavList;
static checkActive(index) {
const len = NavList.List.length;
if (len === 1) {
// 唯一一个
NavList.Active = 0;
} else if (len - 1 === index) {
// 删了最后一个
NavList.Active = len - 2;
} else {
NavList.Active = index;
}
PagesControlNavComponent.setActive(NavList.Active);
}
static setActive(index) {
NavList.List.forEach((value, key) => {
NavList.List[key].hide = true;
});
NavList.List[index].hide = false;
NavList.Active = index;
if (!NavList.List[index].edit) {
if (NavList.List[index].type === 'ssh') {
NavList.List[index].Term.focus();
} else if (NavList.List[index].type === 'rdp') {
// NavList.List[index].Rdp.focus();
}
} else {
}
}
constructor() {
}
ngOnInit() {
}
close(host, index) {
if (host.type === 'rdp') {
ControlComponent.RdpDisconnect(index);
} else if (host.type === 'ssh') {
ControlComponent.TerminalDisconnect(index);
}
NavList.List.splice(index, 1);
PagesControlNavComponent.checkActive(index);
}
scrollleft() {
jQuery('.tabs').scrollLeft(jQuery('.tabs').scrollLeft() - 100);
}
scrollright() {
jQuery('.tabs').scrollLeft(jQuery('.tabs').scrollLeft() + 100);
}
}
.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)">
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -6,10 +6,10 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService, HttpService, LogService} from '../../app.service';
import {AppService, HttpService, LogService} from '@app/app.service';
import {NgForm} from '@angular/forms';
import {Router} from '@angular/router';
import {DataStore, User} from '../../globals';
import {DataStore, User} from '@app/globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
......@@ -44,7 +44,7 @@ export class PagesLoginComponent implements OnInit {
DataStore.error['login'] = '';
this._logger.log(User);
if (User.username.length > 0 && User.password.length > 6 && User.password.length < 100) {
this._http.check_login(JSON.stringify(User))
this._http.checkLogin(JSON.stringify(User))
.subscribe(
data => {
User.logined = data['logined'];
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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