Unverified Commit 1cc8c3e6 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #50 from jumpserver/dev

Dev
parents 2ba57398 3c619311
......@@ -50,8 +50,11 @@
"document": "文档",
"support": "商业支持",
"speed": "速度",
"File Manager": "文件管理",
"File": "文件管理",
"New Connection": "连接",
"Connect": "连接"
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率"
}
......@@ -51,6 +51,10 @@
"support": "商业支持",
"speed": "速度",
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接"
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率"
}
......@@ -50,8 +50,11 @@
"document": "文档",
"support": "商业支持",
"speed": "速度",
"File Manager": "文件管理",
"File": "文件管理",
"New Connection": "连接",
"Connect": "连接"
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率"
}
This diff is collapsed.
{
"/api/perms/v1/user/nodes/children/": {
"target": "http://127.0.0.1:8080",
"secure": false
},
"/api": {
"target": "http://127.0.0.1:5001",
"secure": false
......
......@@ -26,7 +26,7 @@ import {MAT_LABEL_GLOBAL_OPTIONS} from '@angular/material';
import {Pipes} from './pipes/pipes';
import {PagesComponents} from './pages/pages.component';
import {ElementComponents} from './elements/elements.component';
import {ChangLanWarningDialogComponent} from './elements/nav/nav.component';
import {ChangLanWarningDialogComponent, RDPSolutionDialogComponent} 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';
......@@ -55,6 +55,7 @@ import {SftpComponent} from './elements/sftp/sftp.component';
AssetTreeDialogComponent,
ElementDialogAlertComponent,
ChangLanWarningDialogComponent,
RDPSolutionDialogComponent,
],
bootstrap: [AppComponent],
providers: [
......
......@@ -14,7 +14,7 @@ import {DataStore, User, Browser, i18n} from './globals';
import {environment} from '../environments/environment';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {NGXLogger} from 'ngx-logger';
import {HostGroup} from './pages/control/cleftbar/cleftbar.component';
import {HostGroup, Node} from './pages/control/cleftbar/cleftbar.component';
import * as UUID from 'uuid-js/lib/uuid.js';
declare function unescape(s: string): string;
......@@ -66,8 +66,8 @@ export class HttpService {
return this.http.get('/api/users/v1/profile/');
}
get_my_asset_groups_assets() {
return this.http.get<Array<HostGroup>>('/api/perms/v1/user/nodes-assets/');
get_my_granted_nodes() {
return this.http.get<Array<Node>>('/api/perms/v1/user/nodes-assets/tree/');
}
get_guacamole_token(user_id: string, authToken: string) {
......@@ -89,11 +89,18 @@ export class HttpService {
}
guacamole_add_asset(user_id: string, asset_id: string, system_user_id: string) {
const params = new HttpParams()
let params = new HttpParams()
.set('user_id', user_id)
.set('asset_id', asset_id)
.set('system_user_id', system_user_id)
.set('token', DataStore.guacamole_token);
const solution = localStorage.getItem('rdpSolution') || 'Auto';
if (solution !== 'Auto') {
const width = solution.split('x')[0];
const height = solution.split('x')[1];
params = params.set('width', width).set('height', height);
}
return this.http.get(
'/guacamole/api/session/ext/jumpserver/asset/add',
{
......@@ -104,9 +111,15 @@ export class HttpService {
}
guacamole_token_add_asset(assetToken: string, token: string) {
const params = new HttpParams()
let params = new HttpParams()
.set('asset_token', assetToken)
.set('token', token);
const solution = localStorage.getItem('rdpSolution') || 'Auto';
if (solution !== 'Auto') {
const width = solution.split('x')[0];
const height = solution.split('x')[1];
params = params.set('width', width).set('height', height);
}
return this.http.get(
'/guacamole/api/ext/jumpserver/asset/token/add',
{
......
......@@ -33,25 +33,25 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
},
callback: {
onClick: this.onCzTreeOnClick.bind(this),
onRightClick: this.onRightClick.bind(this),
onAsyncSuccess: this.onzTreeAsyncSuccess.bind(this)
onRightClick: this.onRightClick.bind(this)
},
async: {
enable: true,
url: '/api/perms/v1/user/nodes/children/',
autoParam: ['node_id=id', 'name=n', 'level=lv'],
type: 'get',
dataFilter: this.nodeFilter
}
};
pos = {left: '100px', top: '200px'};
hiddenNodes: any;
expandNodes: any;
zTree: any;
searching = false;
isShowRMenu = false;
rightClickSelectNode: any;
onCzTreeOnClick(event, treeId, treeNode, clickFlag) {
if (treeNode.isParent) {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
zTreeObj.expandNode(treeNode);
} else {
this.Connect(treeNode);
}
}
constructor(private _appService: AppService,
public _dialog: MatDialog,
public _logger: LogService) {
......@@ -67,7 +67,7 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
.debounceTime(300)
.distinctUntilChanged()
.subscribe((n) => {
this.searchNode();
this.filter();
});
}
......@@ -80,53 +80,9 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
}
nodeFilter(treeId, parentNode, childNodes) {
$.each(childNodes, function (index, value) {
value['node_id'] = value['id'];
value['id'] = value['tree_id'];
if (value['tree_id'] !== value['tree_parent']) {
value['pId'] = value['tree_parent'];
} else {
value['isParent'] = true;
}
value['name'] = value['value'];
if (!value['is_node']) {
const platform = value['asset']['platform'].toLowerCase().indexOf('win') === 0 ? 'windows' : 'linux';
value['title'] = value['asset']['ip'] || value['name'];
value['iconSkin'] = platform;
}
value['isParent'] = value['is_node'];
});
return childNodes;
}
onzTreeAsyncSuccess(event, treeId, treeNode, msg) {
// 代表第一次加载
if (!treeNode) {
this.zTree = $.fn.zTree.getZTreeObj(treeId);
if (this.searching) {
this.zTree.expandAll(true);
} else {
const root = this.zTree.getNodes()[0];
this.zTree.expandNode(root, true);
}
}
}
onCzTreeOnClick(event, treeId, treeNode, clickFlag) {
if (treeNode.isParent) {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
zTreeObj.expandNode(treeNode);
} else {
this.Connect(treeNode.asset);
}
}
draw() {
$.fn.zTree.init($('#ztree'), this.setting, this.nodes);
// this.zTree = $.fn.zTree.getZTreeObj('ztree');
// const root = this.zTree.getNodes()[0];
// this.zTree.expandNode(root, true);
$.fn.zTree.init($('#ztree'), this.setting, this.Data);
this.zTree = $.fn.zTree.getZTreeObj('ztree');
}
showRMenu(left, top) {
......@@ -140,9 +96,13 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
onRightClick(event, treeId, treeNode) {
if (!treeNode || treeNode.isParent || treeNode.asset.platform.toLowerCase() === 'windows') {
if (!treeNode || treeNode.isParent ) {
return null;
}
const host = treeNode.meta.asset;
if (host.protocol.toLowerCase() === 'rdp') {
alert('Windows 请使用Ctrl+Shift+Alt呼出侧边栏上传下载');
}
if (!treeNode && event.target.tagName.toLowerCase() !== 'button' && $(event.target).parents('a').length === 0) {
this.zTree.cancelSelectedNode();
this.showRMenu(event.clientX, event.clientY);
......@@ -153,22 +113,24 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
}
Connect(host) {
Connect(node) {
const system_users = node.meta.system_users;
const host = node.meta.asset;
let user: any;
if (host.system_users_granted.length > 1) {
user = this.checkPriority(host.system_users_granted);
if (system_users.length > 1) {
user = this.checkPriority(system_users);
if (user) {
this.login(host, user);
} else {
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: host.system_users_granted}
data: {users: system_users}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of host.system_users_granted) {
for (const i of system_users) {
if (i.id.toString() === result.toString()) {
user = i;
break;
......@@ -178,8 +140,8 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
});
}
} else if (host.system_users_granted.length === 1) {
user = host.system_users_granted[0];
} else if (system_users.length === 1) {
user = system_users[0];
this.login(host, user);
} else {
alert('该主机没有授权登录用户');
......@@ -187,7 +149,7 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
connectFileManager() {
const host = this.rightClickSelectNode.asset;
const host = this.rightClickSelectNode.meta.asset;
const id = NavList.List.length - 1;
if (host) {
NavList.List[id].nick = '[FILE]' + host.hostname;
......@@ -209,6 +171,8 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
login(host, user) {
const id = NavList.List.length - 1;
this._logger.debug(NavList);
this._logger.debug(host);
if (user) {
NavList.List[id].nick = host.hostname;
NavList.List[id].connected = true;
......@@ -241,25 +205,78 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
return user;
}
searchNode() {
recurseParent(node) {
const parentNode = node.getParentNode();
if (parentNode && parentNode.pId) {
return [parentNode, ...this.recurseParent(parentNode)];
} else if (parentNode) {
return [parentNode];
} else {
return [];
}
}
recurseChildren(node) {
if (!node.isParent) {
return [];
}
const children = node.children;
if (!children) {
return [];
}
let all_children = [];
children.forEach((n) => {
all_children = [...children, ...this.recurseChildren(n)];
});
return all_children;
}
filter() {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
if (!zTreeObj) {
return null;
}
const _keywords = this.query;
const nodes = zTreeObj.transformToArray(zTreeObj.getNodes());
if (!_keywords) {
// 第一次刷新
if (!this.searching) {
if (this.hiddenNodes) {
zTreeObj.showNodes(this.hiddenNodes);
this.hiddenNodes = null;
}
if (this.expandNodes) {
this.expandNodes.forEach((node) => {
if (node.id !== nodes[0].id) {
zTreeObj.expandNode(node, false);
}
});
this.expandNodes = null;
}
return null;
}
// 以后搜索后回来
this.searching = false;
zTreeObj.setting.async.url = '/api/perms/v1/user/nodes/children/';
let shouldShow = [];
const matchedNodes = zTreeObj.getNodesByFilter(function(node) {
if (node.meta.type === 'asset') {
const host = node.meta.asset;
return host.hostname.indexOf(_keywords) !== -1 || host.ip.indexOf(_keywords) !== -1;
} else {
this.searching = true;
zTreeObj.setting.async.url = `/api/perms/v1/user/nodes/children/?search=${_keywords}`;
return node.name.indexOf(_keywords) !== -1;
}
zTreeObj.reAsyncChildNodes(null, 'refresh');
});
matchedNodes.forEach((node) => {
const parents = this.recurseParent(node);
const children = this.recurseChildren(node);
shouldShow = [...shouldShow, ...parents, ...children, node];
});
this.hiddenNodes = nodes;
this.expandNodes = shouldShow;
zTreeObj.hideNodes(nodes);
zTreeObj.showNodes(shouldShow);
shouldShow.forEach((node) => {
if (node.isParent) {
zTreeObj.expandNode(node, true);
}
});
// zTreeObj.expandAll(true);
}
}
......
......@@ -14,6 +14,7 @@ import {ElementDialogAlertComponent} from './dialog/dialog.service';
import {ElementGuacamoleComponent} from './guacamole/guacamole.component';
import {ElementSshTermComponent} from './ssh-term/ssh-term.component';
import {AssetTreeDialogComponent, ElementAssetTreeComponent} from './asset-tree/asset-tree.component';
import {RDPSolutionDialogComponent} from './nav/nav.component';
export const ElementComponents = [
ElementLeftbarComponent,
......@@ -31,5 +32,6 @@ export const ElementComponents = [
ElementGuacamoleComponent,
ElementAssetTreeComponent,
ElementSshTermComponent,
AssetTreeDialogComponent
AssetTreeDialogComponent,
RDPSolutionDialogComponent
];
......@@ -106,6 +106,26 @@ export class ElementNavComponent implements OnInit {
window.open('https://market.aliyun.com/products/53690006/cmgj026011.html?spm=5176.730005.0.0.cY2io1');
break;
}
case 'SetResolution': {
const dialog = this._dialog.open(
RDPSolutionDialogComponent,
{
height: '200px',
width: '300px',
data: {
title: 'Warning',
note: 'The page will be reload, can you acceptable?',
cancel: 'Cancel',
confirm: 'Confirm',
},
});
dialog.afterClosed().subscribe(result => {
if (result) {
console.log(result);
}
});
break;
}
case 'EnterLicense': {
this.EnterLicense();
break;
......@@ -175,12 +195,6 @@ export class ElementNavComponent implements OnInit {
}
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',
......@@ -195,35 +209,6 @@ export class ElementNavComponent implements OnInit {
'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': 'FileManager',
......@@ -245,6 +230,11 @@ export class ElementNavComponent implements OnInit {
'click': 'HideLeft',
'name': 'Hide left manager'
},
{
'id': 'RDPResolution',
'click': 'SetResolution',
'name': 'RDP Resolution'
},
{
'id': 'SplitVertical',
'href': '',
......@@ -369,3 +359,34 @@ export class ChangLanWarningDialogComponent implements OnInit {
this.dialogRef.close();
}
}
@Component({
selector: 'elements-rdp-solution-dialog',
templateUrl: 'rdpSolutionDialog.html',
})
export class RDPSolutionDialogComponent implements OnInit {
solutions = ['Auto', '1024x768', '1366x768', '1400x900'];
solution: string;
cacheKey = 'rdpSolution';
constructor(public dialogRef: MatDialogRef<RDPSolutionDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
}
ngOnInit() {
this.solution = localStorage.getItem(this.cacheKey) || 'Auto';
}
setSolution(value: string) {
localStorage.setItem(this.cacheKey, value);
}
onSubmit() {
this.setSolution(this.solution);
this.dialogRef.close();
}
onNoClick(): void {
this.dialogRef.close();
}
}
<h1 mat-dialog-title>{{"Set RDP solution"|trans}}</h1>
<mat-form-field>
<mat-select [(value)]="solution"
placeholder="{{'Select a solution'|trans}}" >
<mat-option *ngFor="let s of solutions" value="{{s}}">{{s}}</mat-option>
</mat-select>
</mat-form-field>
<div style="float: right">
<button mat-raised-button (click)="onNoClick()">{{"Cancel"|trans}}</button>
<button mat-raised-button color="primary" (click)="onSubmit()">{{"Confirm"|trans}}</button>
</div>
......@@ -5,8 +5,9 @@
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component} from '@angular/core';
import {Component, HostListener} from '@angular/core';
import {DataStore} from '../globals';
import { environment } from '../../environments/environment';
@Component({
selector: 'app-root',
......@@ -16,4 +17,14 @@ import {DataStore} from '../globals';
export class AppComponent {
DataStore = DataStore;
constructor() {}
@HostListener('window:beforeunload', ['$event'])
unloadNotification($event: any) {
if (environment.production) {
$event.returnValue = true;
}
}
}
......@@ -25,6 +25,18 @@ export interface HostGroup {
children: Array<Host>;
}
export interface Node {
id: string;
name: string;
comment: string;
title: string;
isParent: boolean;
pId: string;
open: boolean;
iconSkin: string;
meta: object;
}
export class Host {
name: string;
id: string;
......@@ -93,7 +105,7 @@ export class CleftbarComponent implements OnInit {
}
ngOnInit() {
this._http.get_my_asset_groups_assets()
this._http.get_my_granted_nodes()
.subscribe(response => {
this.zNodes = response;
// this.HostGroups = response;
......
......@@ -2,5 +2,5 @@ export const environment = {
production: true
};
// export const version = '1.3.0-{{BUILD_NUMBER}} GPLv2.';
export const version = '1.4.4-101 GPLv2.';
export const version = '1.4.5-101 GPLv2.';
// export const version = '1.4.1-{{BUILD_NUMBER}} GPLv2.';
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