mirror of
https://github.com/Eugeny/tabby.git
synced 2025-10-04 22:14:55 +00:00
better ssh connection list management - fixes #1351
This commit is contained in:
@@ -416,11 +416,6 @@ export class SSHSession extends BaseSession {
|
||||
}
|
||||
}
|
||||
|
||||
export interface SSHConnectionGroup {
|
||||
name: string
|
||||
connections: SSHConnection[]
|
||||
}
|
||||
|
||||
export const ALGORITHM_BLACKLIST = [
|
||||
// cause native crashes in node crypto, use EC instead
|
||||
'diffie-hellman-group-exchange-sha256',
|
||||
|
@@ -17,6 +17,7 @@
|
||||
type='text',
|
||||
placeholder='Ungrouped',
|
||||
[(ngModel)]='connection.group',
|
||||
[ngbTypeahead]='groupTypeahead',
|
||||
)
|
||||
|
||||
.d-flex.w-100(*ngIf='!useProxyCommand')
|
||||
|
@@ -1,6 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { Observable } from 'rxjs'
|
||||
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
|
||||
|
||||
import { ElectronService, HostAppService, ConfigService } from 'terminus-core'
|
||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||
import { SSHConnection, LoginScript, SSHAlgorithmType, ALGORITHM_BLACKLIST } from '../api'
|
||||
@@ -20,6 +23,8 @@ export class EditConnectionModalComponent {
|
||||
defaultAlgorithms: Record<string, string[]> = {}
|
||||
algorithms: Record<string, Record<string, boolean>> = {}
|
||||
|
||||
private groupNames: string[]
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private modalInstance: NgbActiveModal,
|
||||
@@ -44,8 +49,18 @@ export class EditConnectionModalComponent {
|
||||
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
||||
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
||||
}
|
||||
|
||||
this.groupNames = [...new Set(config.store.ssh.connections.map(x => x.group))] as string[]
|
||||
this.groupNames = this.groupNames.filter(x => x).sort()
|
||||
}
|
||||
|
||||
groupTypeahead = (text$: Observable<string>) =>
|
||||
text$.pipe(
|
||||
debounceTime(200),
|
||||
distinctUntilChanged(),
|
||||
map(q => this.groupNames.filter(x => !q || x.toLowerCase().includes(q.toLowerCase())))
|
||||
)
|
||||
|
||||
async ngOnInit () {
|
||||
this.hasSavedPassword = !!await this.passwordStorage.loadPassword(this.connection)
|
||||
this.connection.algorithms = this.connection.algorithms ?? {}
|
||||
|
@@ -1,33 +1,55 @@
|
||||
h3 Connections
|
||||
.d-flex.align-items-center.mb-3
|
||||
h3.m-0 SSH Connections
|
||||
|
||||
.list-group.list-group-flush.mt-3.mb-3
|
||||
button.btn.btn-primary.ml-auto((click)='createConnection()')
|
||||
i.fas.fa-fw.fa-plus
|
||||
span.ml-2 Add connection
|
||||
|
||||
.input-group.mb-3
|
||||
.input-group-prepend
|
||||
.input-group-text
|
||||
i.fas.fa-fw.fa-search
|
||||
input.form-control(type='search', placeholder='Filter', [(ngModel)]='filter')
|
||||
|
||||
.list-group.list-group-light.mt-3.mb-3
|
||||
ng-container(*ngFor='let group of childGroups')
|
||||
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='groupCollapsed[group.name] = !groupCollapsed[group.name]'
|
||||
)
|
||||
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
|
||||
button.btn.btn-outline-info.ml-2((click)='editGroup(group)')
|
||||
i.fas.fa-edit
|
||||
button.btn.btn-outline-danger.ml-1((click)='deleteGroup(group)')
|
||||
i.fas.fa-trash
|
||||
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||
.list-group-item.list-group-item-action.pl-5.d-flex.align-items-center(
|
||||
*ngFor='let connection of group.connections',
|
||||
(click)='editConnection(connection)'
|
||||
ng-container(*ngIf='isGroupVisible(group)')
|
||||
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='groupCollapsed[group.name] = !groupCollapsed[group.name]'
|
||||
)
|
||||
.mr-auto
|
||||
div {{connection.name}}
|
||||
.text-muted {{connection.host}}
|
||||
button.btn.btn-outline-info.ml-1((click)='$event.stopPropagation(); copyConnection(connection)')
|
||||
i.fas.fa-copy
|
||||
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteConnection(connection)')
|
||||
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
|
||||
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
|
||||
[class.invisible]='!group.name',
|
||||
(click)='$event.stopPropagation(); editGroup(group)'
|
||||
)
|
||||
i.fas.fa-edit
|
||||
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
|
||||
[class.invisible]='!group.name',
|
||||
(click)='$event.stopPropagation(); deleteGroup(group)'
|
||||
)
|
||||
i.fas.fa-trash
|
||||
|
||||
button.btn.btn-primary((click)='createConnection()')
|
||||
i.fas.fa-fw.fa-plus
|
||||
span.ml-2 Add connection
|
||||
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||
ng-container(*ngFor='let connection of group.connections')
|
||||
.list-group-item.list-group-item-action.pl-5.d-flex.align-items-center(
|
||||
*ngIf='isConnectionVisible(connection)',
|
||||
(click)='editConnection(connection)'
|
||||
)
|
||||
.mr-3 {{connection.name}}
|
||||
.mr-auto.text-muted {{connection.host}}
|
||||
|
||||
.hover-reveal(ngbDropdown, placement='bottom-right')
|
||||
button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()')
|
||||
i.fas.fa-fw.fa-ellipsis-v
|
||||
div(ngbDropdownMenu)
|
||||
button.dropdown-item((click)='$event.stopPropagation(); copyConnection(connection)')
|
||||
i.fas.fa-copy
|
||||
span Duplicate
|
||||
button.dropdown-item((click)='$event.stopPropagation(); deleteConnection(connection)')
|
||||
i.fas.fa-trash
|
||||
span Delete
|
||||
|
||||
h3.mt-5 Options
|
||||
|
||||
|
@@ -0,0 +1,3 @@
|
||||
.list-group-item {
|
||||
padding: 0.3rem 1rem;
|
||||
}
|
@@ -4,18 +4,25 @@ import { Component } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ConfigService, ElectronService, HostAppService } from 'terminus-core'
|
||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||
import { SSHConnection, SSHConnectionGroup } from '../api'
|
||||
import { SSHConnection } from '../api'
|
||||
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
||||
import { PromptModalComponent } from './promptModal.component'
|
||||
|
||||
interface SSHConnectionGroup {
|
||||
name: string|null
|
||||
connections: SSHConnection[]
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./sshSettingsTab.component.pug'),
|
||||
styles: [require('./sshSettingsTab.component.scss')],
|
||||
})
|
||||
export class SSHSettingsTabComponent {
|
||||
connections: SSHConnection[]
|
||||
childGroups: SSHConnectionGroup[]
|
||||
groupCollapsed: Record<string, boolean> = {}
|
||||
filter = ''
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
@@ -133,7 +140,7 @@ export class SSHSettingsTabComponent {
|
||||
let group = this.childGroups.find(x => x.name === connection.group)
|
||||
if (!group) {
|
||||
group = {
|
||||
name: connection.group!,
|
||||
name: connection.group,
|
||||
connections: [],
|
||||
}
|
||||
this.childGroups.push(group)
|
||||
@@ -141,4 +148,12 @@ export class SSHSettingsTabComponent {
|
||||
group.connections.push(connection)
|
||||
}
|
||||
}
|
||||
|
||||
isGroupVisible (group: SSHConnectionGroup): boolean {
|
||||
return !this.filter || group.connections.some(x => this.isConnectionVisible(x))
|
||||
}
|
||||
|
||||
isConnectionVisible (connection: SSHConnection): boolean {
|
||||
return !this.filter || `${connection.name}$${connection.host}`.toLowerCase().includes(this.filter.toLowerCase())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user