Unverified Commit e12b10c4 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #46 from jumpserver/dev

支持sftp
parents 4b6f5c40 d3ceac9b
...@@ -49,5 +49,9 @@ ...@@ -49,5 +49,9 @@
"confirm": "确认", "confirm": "确认",
"document": "文档", "document": "文档",
"support": "商业支持", "support": "商业支持",
"speed": "速度" "speed": "速度",
"File Manager": "文件管理",
"File": "文件管理",
"New Connection": "连接",
"Connect": "连接"
} }
cn.json
\ No newline at end of file
{
"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": "文件管理",
"new connection": "连接",
"connect": "连接"
}
cn.json
\ No newline at end of file
{
"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": "连接"
}
...@@ -18,7 +18,7 @@ import socket ...@@ -18,7 +18,7 @@ import socket
import logging import logging
import select import select
from coco.models import WSProxy, Client, Request, Connection from coco.models import WSProxy, Client, Request, Connection
from coco.httpd import ProxyNamespace from coco.httpd.ws import ProxyNamespace
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
......
This diff is collapsed.
{ {
"name": "luna", "name": "luna",
"version": "1.3.2", "version": "1.4.3",
"license": "GPLv3", "license": "GPLv3",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
"@angular/router": "5.2.0", "@angular/router": "5.2.0",
"@swimlane/ngx-datatable": "^11.3.2", "@swimlane/ngx-datatable": "^11.3.2",
"@swimlane/ngx-ui": "^20.2.1", "@swimlane/ngx-ui": "^20.2.1",
"@types/jquery": "^3.3.6",
"@types/socket.io-client": "^1.4.32", "@types/socket.io-client": "^1.4.32",
"ajv": "^6.5.0", "ajv": "^6.5.0",
"animate.css": "^3.6.1", "animate.css": "^3.6.1",
......
...@@ -8,10 +8,19 @@ ...@@ -8,10 +8,19 @@
"secure": false "secure": false
}, },
"/socket.io/": { "/socket.io/": {
"target": "http://127.0.0.1:5001", "target": "http://127.0.0.1:5000",
"secure": false, "secure": false,
"ws": true "ws": true
}, },
"/coco/": {
"target": "http://127.0.0.1:5000",
"secure:": false,
"ws": true
},
"/static": {
"target": "http://127.0.0.1:5000",
"secure:": false
},
"/rdp/socket.io/": { "/rdp/socket.io/": {
"target": "http://localhost:9250", "target": "http://localhost:9250",
"pathRewrite": { "pathRewrite": {
......
...@@ -31,6 +31,7 @@ import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dial ...@@ -31,6 +31,7 @@ import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dial
import {PluginModules} from './plugins/plugins'; import {PluginModules} from './plugins/plugins';
import {TestPageComponent} from './test-page/test-page.component'; import {TestPageComponent} from './test-page/test-page.component';
import {AssetTreeDialogComponent} from './elements/asset-tree/asset-tree.component'; import {AssetTreeDialogComponent} from './elements/asset-tree/asset-tree.component';
import {SftpComponent} from './elements/sftp/sftp.component';
@NgModule({ @NgModule({
...@@ -48,6 +49,7 @@ import {AssetTreeDialogComponent} from './elements/asset-tree/asset-tree.compone ...@@ -48,6 +49,7 @@ import {AssetTreeDialogComponent} from './elements/asset-tree/asset-tree.compone
...Pipes, ...Pipes,
...ElementComponents, ...ElementComponents,
...PagesComponents, ...PagesComponents,
SftpComponent,
], ],
entryComponents: [ entryComponents: [
AssetTreeDialogComponent, AssetTreeDialogComponent,
......
<ul id="ztree" class="ztree"></ul> <ul id="ztree" class="ztree"></ul>
<div #rMenu *ngIf="isShowRMenu" class="basicContext" [style.top]="pos.top" [style.left]="pos.left">
<table>
<tbody>
<tr class="basicContext__item ">
<td class="basicContext__data" data-num="0" (click)="connectTerminal()"> <span class="basicContext__icon fa fa-terminal new-connection"></span>{{ "New Connection"|trans }}</td>
</tr>
<tr class="basicContext__item basicContext__item--separator"></tr>
<tr class="basicContext__item ">
<td class="basicContext__data" data-num="2" (click)="connectFileManager()"><span class="basicContext__icon fa fa-file refresh"></span>{{ "File Manager"|trans }}</td>
</tr>
</tbody>
</table>
</div>
...@@ -5,3 +5,101 @@ ...@@ -5,3 +5,101 @@
} }
.basicContext {
background: #000;
border: none;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
margin: 0;
box-shadow: 0 3px 6px 0px rgba(0,0,0,0.16);
position: absolute;
opacity: 1;
padding: 6px 0;
z-index: 1000;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.basicContext, .basicContext * {
box-sizing: border-box;
}
.basicContextContainer {
position: fixed;
width: 100%;
height: 100%;
z-index: 1000;
-webkit-tap-highlight-color: transparent;
}
.basicContext tr {
margin: 0;
}
.fa {
width: 24px;
height: 24px;
line-height: 24px;
background-size: 18px;
background-position: 3px 3px;
margin-left: -30px;
position: absolute;
margin-right: 10px;
text-align: center;
display: inline-block;
}
.basicContext__item {
cursor: pointer;
padding: 0 6px;
}
.basicContext td {
display: block;
padding: 0 35px;
text-decoration: none;
width: auto;
opacity: 1;
white-space: nowrap;
line-height: 24px;
text-shadow: none;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
cursor: pointer;
}
.basicContext__item.basicContext__item--separator {
background: #181414;
border: 0;
border-top: none;
height: 1px;
min-height: 1px;
max-height: 1px;
padding: 0;
border-left: none;
text-shadow: 0 0 0 transparent;
box-shadow: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
margin: 2px 0;
}
tr {
display: table-row;
vertical-align: inherit;
border-color: inherit;
}
tr:hover {
background-color: #463e3e;
}
.basicContext table {
border-spacing: 0 !important;
}
.basicContext__data {
padding: 0 6px;
}
import {Component, Input, OnInit, Inject, SimpleChanges, OnChanges, EventEmitter} from '@angular/core'; import {Component, Input, OnInit, Inject, SimpleChanges, OnChanges, ElementRef, ViewChild} from '@angular/core';
import {NavList, View} from '../../pages/control/control/control.component'; import {NavList, View} from '../../pages/control/control/control.component';
import {AppService, LogService} from '../../app.service'; import {AppService, LogService} from '../../app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material'; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
...@@ -16,6 +16,7 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges { ...@@ -16,6 +16,7 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
@Input() Data: any; @Input() Data: any;
@Input() query: string; @Input() query: string;
@Input() searchEvt$: BehaviorSubject<string>; @Input() searchEvt$: BehaviorSubject<string>;
@ViewChild('rMenu') rMenu: ElementRef;
nodes = []; nodes = [];
setting = { setting = {
view: { view: {
...@@ -31,11 +32,16 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges { ...@@ -31,11 +32,16 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
} }
}, },
callback: { callback: {
onClick: this.onCzTreeOnClick.bind(this) onClick: this.onCzTreeOnClick.bind(this),
onRightClick: this.onRightClick.bind(this)
}, },
}; };
pos = {left: '100px', top: '200px'};
hiddenNodes: any; hiddenNodes: any;
expandNodes: any; expandNodes: any;
zTree: any;
isShowRMenu = false;
rightClickSelectNode: any;
onCzTreeOnClick(event, treeId, treeNode, clickFlag) { onCzTreeOnClick(event, treeId, treeNode, clickFlag) {
if (treeNode.isParent) { if (treeNode.isParent) {
...@@ -56,6 +62,7 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges { ...@@ -56,6 +62,7 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
if (this.Data) { if (this.Data) {
this.draw(); this.draw();
} }
document.addEventListener('click', this.hideRMenu.bind(this), false);
this.searchEvt$.asObservable() this.searchEvt$.asObservable()
.debounceTime(300) .debounceTime(300)
.distinctUntilChanged() .distinctUntilChanged()
...@@ -122,9 +129,33 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges { ...@@ -122,9 +129,33 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
} }
}); });
$.fn.zTree.init($('#ztree'), this.setting, this.nodes); $.fn.zTree.init($('#ztree'), this.setting, this.nodes);
const zTree = $.fn.zTree.getZTreeObj('ztree'); this.zTree = $.fn.zTree.getZTreeObj('ztree');
const root = zTree.getNodes()[0]; const root = this.zTree.getNodes()[0];
zTree.expandNode(root, true); this.zTree.expandNode(root, true);
}
showRMenu(left, top) {
this.pos.left = left + 'px';
this.pos.top = top + 'px';
this.isShowRMenu = true;
}
hideRMenu() {
this.isShowRMenu = false;
}
onRightClick(event, treeId, treeNode) {
if (!treeNode || treeNode.isParent || treeNode.platform.toLowerCase() === 'windows') {
return null;
}
if (!treeNode && event.target.tagName.toLowerCase() !== 'button' && $(event.target).parents('a').length === 0) {
this.zTree.cancelSelectedNode();
this.showRMenu(event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
this.zTree.selectNode(treeNode);
this.showRMenu(event.clientX, event.clientY);
this.rightClickSelectNode = treeNode;
}
} }
Connect(host) { Connect(host) {
...@@ -160,6 +191,27 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges { ...@@ -160,6 +191,27 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
} }
} }
connectFileManager() {
const host = this.rightClickSelectNode;
const id = NavList.List.length - 1;
if (host) {
NavList.List[id].nick = '[FILE]' + host.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].type = 'sftp';
NavList.List.push(new View());
NavList.Active = id;
}
this._logger.debug(NavList);
}
connectTerminal() {
const host = this.rightClickSelectNode;
this.Connect(host);
}
login(host, user) { login(host, user) {
const id = NavList.List.length - 1; const id = NavList.List.length - 1;
this._logger.debug(NavList); this._logger.debug(NavList);
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<li><a href="/"><img src="static/imgs/logo.png" height="26px"/></a> <li><a href="/"><img src="static/imgs/logo.png" height="26px"/></a>
</li> </li>
<li *ngFor="let v of DataStore.Nav" [ngClass]="{'dropdown': v.children}"> <li *ngFor="let v of DataStore.Nav" [ngClass]="{'dropdown': v.children}">
<a>{{v.name | trans}}</a> <a>{{v.name|trans}}</a>
<ul [ngClass]="{'dropdown-content': v.children}"> <ul [ngClass]="{'dropdown-content': v.children}">
<li *ngFor="let vv of v.children" [ngClass]="{'disabled': vv.disable}"> <li *ngFor="let vv of v.children" [ngClass]="{'disabled': vv.disable}">
<a *ngIf="vv.href" [routerLink]="[vv.href]">{{vv.name|trans}}</a> <a *ngIf="vv.href" [routerLink]="[vv.href]">{{vv.name|trans}}</a>
......
...@@ -47,7 +47,10 @@ export class ElementNavComponent implements OnInit { ...@@ -47,7 +47,10 @@ export class ElementNavComponent implements OnInit {
CleftbarComponent.Reload(); CleftbarComponent.Reload();
break; break;
} }
case 'ConnectSFTP': {
window.open('/coco/elfinder/sftp/');
break;
}
case 'HideLeft': { case 'HideLeft': {
CleftbarComponent.Hide(); CleftbarComponent.Hide();
break; break;
...@@ -182,18 +185,6 @@ export class ElementNavComponent implements OnInit { ...@@ -182,18 +185,6 @@ export class ElementNavComponent implements OnInit {
'id': 'File', 'id': 'File',
'name': 'Server', 'name': 'Server',
'children': [ 'children': [
// {
// 'id': 'NewConnection',
// 'href': '',
// 'name': 'New connection',
// 'disable': true
// },
// {
// 'id': 'Connect',
// 'click': 'Connect',
// 'name': 'Connect',
// 'disable': true
// },
{ {
'id': 'Disconnect', 'id': 'Disconnect',
'click': 'Disconnect', 'click': 'Disconnect',
...@@ -235,6 +226,17 @@ export class ElementNavComponent implements OnInit { ...@@ -235,6 +226,17 @@ export class ElementNavComponent implements OnInit {
// } // }
] ]
}, { }, {
'id': 'FileManager',
'name': 'File Manager',
'children': [
{
'id': 'Connect',
'click': 'ConnectSFTP',
'name': 'Connect'
},
]
},
{
'id': 'View', 'id': 'View',
'name': 'View', 'name': 'View',
'children': [ 'children': [
...@@ -266,11 +268,6 @@ export class ElementNavComponent implements OnInit { ...@@ -266,11 +268,6 @@ export class ElementNavComponent implements OnInit {
'id': 'Help', 'id': 'Help',
'name': 'Help', 'name': 'Help',
'children': [ 'children': [
// {
// 'id': 'EnterLicense',
// 'click': 'EnterLicense',
// 'name': 'Enter License'
// },
{ {
'id': 'Website', 'id': 'Website',
'click': 'Website', 'click': 'Website',
...@@ -316,9 +313,7 @@ export class ElementNavComponent implements OnInit { ...@@ -316,9 +313,7 @@ export class ElementNavComponent implements OnInit {
moveType: 1 moveType: 1
}, function (value, index) { }, function (value, index) {
DataStore.socket.emit('key', value); DataStore.socket.emit('key', value);
// layer.msg(value); //得到value
layer.close(index); layer.close(index);
}); });
} }
......
<!--<iframe src="/elfinder/sftp/host123/" width="100%" height="100%" ></iframe>-->
<iframe #sftp [src]="target" width="100%" height="100%" ></iframe>
iframe {
border: none;
background-color: white;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SftpComponent } from './sftp.component';
describe('SftpComponent', () => {
let component: SftpComponent;
let fixture: ComponentFixture<SftpComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SftpComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SftpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit, Input, ElementRef, ViewChild} from '@angular/core';
import {DataStore} from '../../globals';
import {DomSanitizer} from '@angular/platform-browser';
@Component({
selector: 'app-sftp',
templateUrl: './sftp.component.html',
styleUrls: ['./sftp.component.scss']
})
export class SftpComponent implements OnInit {
@Input() host: any;
target: any;
@ViewChild('sftp') el: ElementRef;
constructor(private sanitizer: DomSanitizer) {
if (!this.host) {
DataStore.NavShow = false;
}
}
ngOnInit() {
let _target = '/coco/elfinder/sftp/';
if (this.host) {
// _target += 'f5857eee-c114-4564-af8f-96329c400a8a' + '/';
_target += this.host.id + '/';
}
this.trust(_target);
}
trust(url) {
this.target = this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
[userid]="m.user.id" [userid]="m.user.id"
*ngIf="m.type=='rdp'"> *ngIf="m.type=='rdp'">
</elements-guacamole> </elements-guacamole>
<app-sftp *ngIf="m.type=='sftp'" [host]="m.host">
</app-sftp>
<!--<elements-settings [index]="i"--> <!--<elements-settings [index]="i"-->
<!--*ngIf="m.type=='settings'">--> <!--*ngIf="m.type=='settings'">-->
<!--</elements-settings>--> <!--</elements-settings>-->
......
...@@ -52,7 +52,6 @@ export class ControlComponent implements OnInit { ...@@ -52,7 +52,6 @@ export class ControlComponent implements OnInit {
NavList.List.forEach((v, k) => { NavList.List.forEach((v, k) => {
v.hide = id.toString() !== k; v.hide = id.toString() !== k;
}); });
NavList.Active = id; NavList.Active = id;
} }
......
...@@ -15,6 +15,7 @@ import {PagesReplayComponent} from '../pages/replay/replay.component'; ...@@ -15,6 +15,7 @@ import {PagesReplayComponent} from '../pages/replay/replay.component';
import {PagesControlComponent} from '../pages/control/control.component'; import {PagesControlComponent} from '../pages/control/control.component';
import {PagesNotFoundComponent} from '../pages/not-found/not-found.component'; import {PagesNotFoundComponent} from '../pages/not-found/not-found.component';
import {PagesMonitorComponent} from '../pages/monitor/monitor.component'; import {PagesMonitorComponent} from '../pages/monitor/monitor.component';
import {SftpComponent} from '../elements/sftp/sftp.component';
const appRoutes: Routes = [ const appRoutes: Routes = [
...@@ -22,6 +23,7 @@ const appRoutes: Routes = [ ...@@ -22,6 +23,7 @@ const appRoutes: Routes = [
{path: 'monitor/:token', component: PagesMonitorComponent}, {path: 'monitor/:token', component: PagesMonitorComponent},
{path: 'test', component: TestPageComponent}, {path: 'test', component: TestPageComponent},
{path: 'connect', component: PagesConnectComponent}, {path: 'connect', component: PagesConnectComponent},
{path: 'sftp', component: SftpComponent},
{path: 'undefined', component: PagesBlankComponent}, {path: 'undefined', component: PagesBlankComponent},
{path: '', component: PagesControlComponent}, {path: '', component: PagesControlComponent},
{path: '**', component: PagesNotFoundComponent} {path: '**', component: PagesNotFoundComponent}
......
...@@ -2,5 +2,5 @@ export const environment = { ...@@ -2,5 +2,5 @@ export const environment = {
production: true production: true
}; };
// export const version = '1.3.0-{{BUILD_NUMBER}} GPLv2.'; // export const version = '1.3.0-{{BUILD_NUMBER}} GPLv2.';
// export const version = '1.3.3-101 GPLv2.'; export const version = '1.4.3-101 GPLv2.';
export const version = '1.4.1-{{BUILD_NUMBER}} GPLv2.'; // export const version = '1.4.1-{{BUILD_NUMBER}} GPLv2.';
...@@ -103,3 +103,17 @@ body ::-webkit-scrollbar-thumb { ...@@ -103,3 +103,17 @@ body ::-webkit-scrollbar-thumb {
/*background-color: #3a3333;*/ /*background-color: #3a3333;*/
/*color: white*/ /*color: white*/
/*}*/ /*}*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
background: #F9F9F9;
border: none !important;
line-height: 15px;
font-weight: bold;
color: #676a6c;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
}
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {
color: white;
font-weight: bolder;
}
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