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

Merge pull request #107 from jumpserver/dev_beta

Dev beta
parents 74688df1 69320967
...@@ -6531,9 +6531,9 @@ ...@@ -6531,9 +6531,9 @@
"dev": true "dev": true
}, },
"guacamole-common-js": { "guacamole-common-js": {
"version": "0.9.14-b", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/guacamole-common-js/-/guacamole-common-js-0.9.14-b.tgz", "resolved": "https://registry.npmjs.org/guacamole-common-js/-/guacamole-common-js-1.1.0.tgz",
"integrity": "sha1-zwzjETnPNIYTqQt/lfz8z+0ZZZc=" "integrity": "sha512-X0BOOGL5DWirKk0Q+2tpr1r3yvoRlF4A76T6CfKMNWSWz3goD2/aSCQcY3I07XSop0SCjOZRNCmPPETmV/6ayg=="
}, },
"handle-thing": { "handle-thing": {
"version": "2.0.0", "version": "2.0.0",
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
"codemirror": "^5.42.0", "codemirror": "^5.42.0",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"guacamole-common-js": "0.9.14-b", "guacamole-common-js": "1.1.0",
"jquery": "^3.4.1", "jquery": "^3.4.1",
"metismenu": "^2.7.9", "metismenu": "^2.7.9",
"neffos.js": "^0.1.19", "neffos.js": "^0.1.19",
......
...@@ -28,6 +28,10 @@ ...@@ -28,6 +28,10 @@
}, },
"secure": false "secure": false
}, },
"/media/": {
"target": "http://127.0.0.1:8080",
"secure": false
},
"/guacamole/": { "/guacamole/": {
"target": "http://127.0.0.1:8083", "target": "http://127.0.0.1:8083",
"secure": false, "secure": false,
......
...@@ -91,10 +91,6 @@ export class HttpService { ...@@ -91,10 +91,6 @@ export class HttpService {
return this.http.get<Array<SystemUser>>(url); return this.http.get<Array<SystemUser>>(url);
} }
refreshMyGrantedNodes() {
return this.http.get<Array<TreeNode>>('/api/perms/v1/user/nodes-assets/tree/?cache_policy=2');
}
getGuacamoleToken(user_id: string, authToken: string) { getGuacamoleToken(user_id: string, authToken: string) {
const body = new HttpParams() const body = new HttpParams()
.set('username', user_id) .set('username', user_id)
......
...@@ -93,7 +93,7 @@ export class ElementAssetTreeComponent implements OnInit, OnDestroy { ...@@ -93,7 +93,7 @@ export class ElementAssetTreeComponent implements OnInit, OnDestroy {
if (this._navSvc.treeLoadAsync) { if (this._navSvc.treeLoadAsync) {
setting['async'] = { setting['async'] = {
enable: true, enable: true,
url: '/api/perms/v1/users/nodes/children-with-assets/tree/', url: '/api/perms/v1/users/nodes/children-with-assets/tree/?cache_policy=1',
autoParam: ['id=key', 'name=n', 'level=lv'], autoParam: ['id=key', 'name=n', 'level=lv'],
type: 'get' type: 'get'
}; };
......
import {Component, OnInit, Output, Inject, OnDestroy, EventEmitter} from '@angular/core'; import {Component, OnInit, Output, Inject, OnDestroy, EventEmitter} from '@angular/core';
import {connectEvt} from '@app/globals'; import {connectEvt} from '@app/globals';
import {AppService, HttpService, LogService} from '@app/app.service'; import {AppService, HttpService, LogService, NavService} from '@app/app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material'; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms'; import {FormControl, Validators} from '@angular/forms';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
...@@ -19,6 +19,7 @@ export class ElementConnectComponent implements OnInit, OnDestroy { ...@@ -19,6 +19,7 @@ export class ElementConnectComponent implements OnInit, OnDestroy {
constructor(private _appSvc: AppService, constructor(private _appSvc: AppService,
public _dialog: MatDialog, public _dialog: MatDialog,
public _logger: LogService, public _logger: LogService,
private _navSrv: NavService,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private _http: HttpService, private _http: HttpService,
) { ) {
...@@ -144,7 +145,7 @@ export class ElementConnectComponent implements OnInit, OnDestroy { ...@@ -144,7 +145,7 @@ export class ElementConnectComponent implements OnInit, OnDestroy {
} }
manualSetUserAuthLoginIfNeed(node: any, user: SystemUser, callback) { manualSetUserAuthLoginIfNeed(node: any, user: SystemUser, callback) {
if (user.login_mode !== 'manual' || user.protocol !== 'rdp') { if (user.login_mode !== 'manual' || user.protocol !== 'rdp' || this._navSrv.skipAllManualPassword) {
return callback(node, user); return callback(node, user);
} }
user = Object.assign({}, user); user = Object.assign({}, user);
...@@ -235,7 +236,6 @@ export class AssetTreeDialogComponent implements OnInit { ...@@ -235,7 +236,6 @@ export class AssetTreeDialogComponent implements OnInit {
templateUrl: 'manual-password-dialog.html', templateUrl: 'manual-password-dialog.html',
}) })
export class ManualPasswordDialogComponent implements OnInit { export class ManualPasswordDialogComponent implements OnInit {
PasswordControl = new FormControl('', [Validators.required]);
constructor(@Inject(MAT_DIALOG_DATA) public data: any, constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) { public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) {
} }
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field style="width: 100%"> <mat-form-field style="width: 100%">
<input matInput [type]="'password'" [(ngModel)]="data.password" <input matInput [type]="'password'" [(ngModel)]="data.password" required
[formControl]="PasswordControl" placeholder="{{'Password'|trans}}" placeholder="{{'Password'|trans}}"
(keyup.enter)="onEnter()" [attr.cdkFocusInitial]="data.username? true : null"> (keyup.enter)="onEnter()" [attr.cdkFocusInitial]="data.username? true : null">
</mat-form-field> </mat-form-field>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
[ngClass]="{'active': view.active,'disconnected': !view.connected, 'hidden': view.closed != false}" [ngClass]="{'active': view.active,'disconnected': !view.connected, 'hidden': view.closed != false}"
[id]="view.id" (click)="setActive()" (dblclick)="view.editable=true;setActive()" [id]="view.id" (click)="setActive()" (dblclick)="view.editable=true;setActive()"
> >
<span *ngIf="view.nick">{{view.nick | truncatechars:25 }}</span> <span *ngIf="view.nick && !view.editable">{{view.nick | truncatechars:25 }}</span>
<input *ngIf="view.editable" [(ngModel)]="view.nick" (blur)="view.editable=false" (keyup.enter)="view.editable=false" autofocus="true"/> <input *ngIf="view.editable" [(ngModel)]="view.nick" (blur)="view.editable=false" (keyup.enter)="view.editable=false" autofocus="autofocus"/>
<a class="close" (click)="close()">&times;</a> <a class="close" (click)="close()">&times;</a>
</li> </li>
...@@ -415,7 +415,7 @@ export class RDPSolutionDialogComponent implements OnInit { ...@@ -415,7 +415,7 @@ export class RDPSolutionDialogComponent implements OnInit {
@Component({ @Component({
selector: 'elements-font-size-dialog', selector: 'elements-font-size-dialog',
templateUrl: 'fontDialog.html', templateUrl: 'fontDialog.html',
styles: ['.mat-form-field { width: 100%; }'] styles: ['.mat-form-field { width: 100%; }'],
}) })
export class FontDialogComponent implements OnInit { export class FontDialogComponent implements OnInit {
fontSize: string; fontSize: string;
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
outline: none; outline: none;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
z-index: 9999; z-index: 99;
height: 5px; height: 5px;
width: 0; width: 0;
display: block; display: block;
......
<div id="player"> <div id="player">
<div class="controls"> <div class="controls">
<button id="play-pause" class="btn" (click)="toggle()"> <button id="play-pause" class="btn" (click)="toggle()">
<i class="fa" [ngClass]="{'fa-play':!isPlaying,'fa-pause': isPlaying}"></i> <i class="fa" [ngClass]="{'fa-play':!isPlaying,'fa-pause': isPlaying}"></i>
</button> </button>
<button type="button" class="btn" (click)="restart()"> <button type="button" class="btn" (click)="restart()">
<i class="fa fa-repeat" aria-hidden="true"></i> <i class="fa fa-repeat" aria-hidden="true"></i>
</button> </button>
<input id="position-slider" type="range" [(ngModel)]="percent" [attr.max]="max" (mouseup)="runFrom()"> <input id="position-slider" type="range" [(ngModel)]="percent" [attr.max]="max" (mouseup)="runFrom()">
<span id="position">{{ position }}</span> <span id="position">{{ position }}</span>
<span>/</span> <span>/</span>
<span id="duration">{{ duration }}</span> <span id="duration">{{ duration }}</span>
</div> </div>
<div id="display" (click)="toggle()"> <div id="display" (click)="toggle()">
<div class="notification-container"> <div class="notification-container">
<div class="seek-notification"> <div class="seek-notification">
<p> <p>
Seek in progress... Seek in progress...
<button id="cancel-seek" class="btn" (click)="cancelSeek($event)">Cancel</button> <button id="cancel-seek" class="btn" (click)="cancelSeek($event)">Cancel</button>
</p> </p>
</div>
</div> </div>
</div>
<div id="screen"></div>
</div> </div>
</div> </div>
#player { #player {
width: 800px; width: 100%;
height: 100%;
padding: 5px;
} }
#display { #display {
position: relative; position: relative;
width: calc(100vw - 10px);
height: calc(100vh - 40px);
}
#screen * {
} }
#player .notification-container { #player .notification-container {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
top: 0; top: 0;
right: 0; right: 0;
left: 0; left: 0;
bottom: 0; bottom: 0;
} }
#player .seek-notification { #player .seek-notification {
color: white;
background: rgba(0, 0, 0, 0.75);
color: white; display: none; /* Initially hidden */
background: rgba(0, 0, 0, 0.75); width: 100%;
height: 100%;
display: none; /* Initially hidden */
width: 100%;
height: 100%;
} }
#player.seeking .seek-notification { #player.seeking .seek-notification {
display: table; display: table;
} }
#player .seek-notification p { #player .seek-notification p {
display: table-cell; display: table-cell;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
font-family: sans-serif; font-family: sans-serif;
} }
#player .controls { #player .controls {
width: 100%; width: 100%;
height: 30px;
/* IE10 */
display: -ms-flexbox;
-ms-flex-align: center;
-ms-flex-direction: row;
/* IE10 */ /* Ancient Mozilla */
display: -ms-flexbox; display: -moz-box;
-ms-flex-align: center; -moz-box-align: center;
-ms-flex-direction: row; -moz-box-orient: horizontal;
/* Ancient Mozilla */ /* Ancient WebKit */
display: -moz-box; display: -webkit-box;
-moz-box-align: center; -webkit-box-align: center;
-moz-box-orient: horizontal; -webkit-box-orient: horizontal;
/* Ancient WebKit */ /* Old WebKit */
display: -webkit-box; display: -webkit-flex;
-webkit-box-align: center; -webkit-align-items: center;
-webkit-box-orient: horizontal; -webkit-flex-direction: row;
/* Old WebKit */ /* W3C */
display: -webkit-flex; display: flex;
-webkit-align-items: center; align-items: center;
-webkit-flex-direction: row; flex-direction: row;
/* W3C */ padding-right: 10px;
display: flex;
align-items: center;
flex-direction: row;
} }
#player .controls > * { #player .controls > * {
margin: 0.25em; margin: 0.25em;
} }
#player .controls #position-slider { #player .controls #position-slider {
-ms-flex: 1 1 auto; -ms-flex: 1 1 auto;
-moz-box-flex: 1; -moz-box-flex: 1;
-webkit-box-flex: 1; -webkit-box-flex: 1;
-webkit-flex: 1 1 auto; -webkit-flex: 1 1 auto;
flex: 1 1 auto; flex: 1 1 auto;
} }
#player .controls #play-pause { #player .controls #play-pause {
margin-left: 0; margin-left: 0;
//min-width: 5em; //min-width: 5em;
} }
#player .controls #position, #player .controls #position,
#player .controls #duration { #player .controls #duration {
font-family: monospace; font-family: monospace;
} }
#player .controls #duration { #player .controls #duration {
margin-right: 0; margin-right: 0;
} }
...@@ -55,6 +55,8 @@ export class ReplayGuacamoleComponent implements OnInit { ...@@ -55,6 +55,8 @@ export class ReplayGuacamoleComponent implements OnInit {
recording: any; recording: any;
playerRef: any; playerRef: any;
displayRef: any; displayRef: any;
screenRef: any;
recordingDisplay: any;
max = 100; max = 100;
percent = 0; percent = 0;
duration = '00:00'; duration = '00:00';
...@@ -70,46 +72,51 @@ export class ReplayGuacamoleComponent implements OnInit { ...@@ -70,46 +72,51 @@ export class ReplayGuacamoleComponent implements OnInit {
} }
this.playerRef = document.getElementById('player'); this.playerRef = document.getElementById('player');
this.displayRef = document.getElementById('display'); this.displayRef = document.getElementById('display');
this.screenRef = document.getElementById('screen');
const tunnel = new Guacamole.StaticHTTPTunnel(this.replay.src); const tunnel = new Guacamole.StaticHTTPTunnel(this.replay.src);
this.recording = new Guacamole.SessionRecording(tunnel); this.recording = new Guacamole.SessionRecording(tunnel);
const recordingDisplay = this.recording.getDisplay(); this.recordingDisplay = this.recording.getDisplay();
const recordingElement = this.recordingDisplay.getElement();
this.displayRef.appendChild(recordingDisplay.getElement()); recordingElement.style.margin = '0 auto';
this.screenRef.appendChild(recordingElement);
this.initRecording(); this.initRecording();
const that = this;
recordingDisplay.onresize = function displayResized(width, height) {
// Do not scale if displayRef has no width
if (!width) {
return;
}
// Scale displayRef to fit width of container
recordingDisplay.scale(that.displayRef.offsetWidth / width);
};
// this.toggle(); // this.toggle();
} }
initRecording() { initRecording() {
const that = this;
this.recording.connect(''); this.recording.connect('');
this.recording.onplay = function() { this.recording.onplay = () => {
that.isPlaying = true; this.isPlaying = true;
}; };
this.recording.onseek = function (millis) { this.recording.onseek = (millis) => {
that.position = formatTime(millis); this.position = formatTime(millis);
that.percent = millis; this.percent = millis;
}; };
this.recording.onprogress = function (millis) { this.recording.onprogress = (millis) => {
that.duration = formatTime(millis); this.duration = formatTime(millis);
that.max = millis; this.max = millis;
that.toggle(); this.toggle();
}; };
// If paused, the play/pause button should read "Play" // If paused, the play/pause button should read "Play"
this.recording.onpause = function() { this.recording.onpause = () => {
that.isPlaying = false; this.isPlaying = false;
};
this.recordingDisplay.onresize = (width, height) => {
// Do not scale if displayRef has no width
if (!height) {
return;
}
// Scale displayRef to fit width of container
const widthScale = this.displayRef.offsetWidth / width;
const heightScale = this.displayRef.offsetHeight / height;
const minScale = widthScale < heightScale ? widthScale : heightScale;
this.recordingDisplay.scale(minScale);
}; };
} }
......
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {HttpService, LogService} from '@app/app.service'; import {HttpService, LogService} from '@app/app.service';
import {DataStore} from '@app/globals';
import {Replay} from './replay.model'; import {Replay} from './replay.model';
@Component({ @Component({
...@@ -15,25 +14,22 @@ export class PagesReplayComponent implements OnInit { ...@@ -15,25 +14,22 @@ export class PagesReplayComponent implements OnInit {
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private _http: HttpService, private _http: HttpService,
private _logger: LogService) { private _logger: LogService) {
DataStore.NavShow = false;
} }
ngOnInit() { ngOnInit() {
let token = ''; let sid = '';
this.route.params this.route.params.subscribe(params => {
.subscribe(params => { sid = params['sid'];
token = params['token']; });
}); this._http.getReplay(sid).subscribe(
this._http.getReplay(token) data => {
.subscribe( this.replay.type = data['type'];
data => { this.replay.src = data['src'];
this.replay.type = data['type']; this.replay.id = data['id'];
this.replay.src = data['src']; },
this.replay.id = data['id']; err => {
}, alert('没找到录像文件');
err => { }
alert('没找到录像文件'); );
}
);
} }
} }
...@@ -10,7 +10,7 @@ import {ElementSftpComponent} from '../elements/sftp/sftp.component'; ...@@ -10,7 +10,7 @@ import {ElementSftpComponent} from '../elements/sftp/sftp.component';
const appRoutes: Routes = [ const appRoutes: Routes = [
{path: 'replay/:token', component: PagesReplayComponent}, {path: 'replay/:sid', component: PagesReplayComponent},
{path: 'monitor/:token', component: PagesMonitorComponent}, {path: 'monitor/:token', component: PagesMonitorComponent},
{path: 'connect', component: PagesConnectComponent}, {path: 'connect', component: PagesConnectComponent},
{path: 'sftp', component: ElementSftpComponent}, {path: 'sftp', component: ElementSftpComponent},
......
...@@ -6,6 +6,7 @@ $fa-font-path: '~font-awesome/fonts'; ...@@ -6,6 +6,7 @@ $fa-font-path: '~font-awesome/fonts';
// Todo: 去掉依赖 // Todo: 去掉依赖
@import '~bootstrap/scss/bootstrap'; @import '~bootstrap/scss/bootstrap';
//@import "~@angular/material/prebuilt-themes/indigo-pink.css";
//$FontPathOpenSans: '~npm-font-open-sans/fonts'; //$FontPathOpenSans: '~npm-font-open-sans/fonts';
//@import '~npm-font-open-sans/open-sans'; //@import '~npm-font-open-sans/open-sans';
//$roboto-font-path: '~roboto-fontface/fonts'; //$roboto-font-path: '~roboto-fontface/fonts';
...@@ -99,3 +100,7 @@ button.icon-split-handle.ngx-split-button { ...@@ -99,3 +100,7 @@ button.icon-split-handle.ngx-split-button {
//border: 1px solid #3a3333; //border: 1px solid #3a3333;
opacity: 0.0; opacity: 0.0;
} }
button.mat-raised-button {
margin-left: 5px;
}
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