mirror of
https://github.com/Eugeny/tabby
synced 2024-11-15 09:27:24 +00:00
cleaned up #409 and renamed to Groups
This commit is contained in:
parent
f357dab8f0
commit
d6f163b048
7 changed files with 100 additions and 157 deletions
|
@ -83,6 +83,10 @@ $list-group-border-color: rgba(255,255,255,.1);
|
||||||
$list-group-hover-bg: rgba(255,255,255,.1);
|
$list-group-hover-bg: rgba(255,255,255,.1);
|
||||||
$list-group-link-active-bg: rgba(255,255,255,.2);
|
$list-group-link-active-bg: rgba(255,255,255,.2);
|
||||||
|
|
||||||
|
$list-group-action-color: $body-color;
|
||||||
|
$list-group-action-bg: rgba(255,255,255,.05);
|
||||||
|
$list-group-action-active-bg: $list-group-link-active-bg;
|
||||||
|
|
||||||
$pre-bg: $dropdown-bg;
|
$pre-bg: $dropdown-bg;
|
||||||
$pre-color: $dropdown-link-color;
|
$pre-color: $dropdown-link-color;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ export interface SSHConnection {
|
||||||
user: string
|
user: string
|
||||||
password?: string
|
password?: string
|
||||||
privateKey?: string
|
privateKey?: string
|
||||||
path?: string
|
group?: string
|
||||||
scripts?: LoginScript[]
|
scripts?: LoginScript[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ export class SSHSession extends BaseSession {
|
||||||
|
|
||||||
constructor (private shell: any, conn: SSHConnection) {
|
constructor (private shell: any, conn: SSHConnection) {
|
||||||
super()
|
super()
|
||||||
this.scripts = conn.scripts.slice(0);
|
this.scripts = conn.scripts ? [...conn.scripts] : []
|
||||||
}
|
}
|
||||||
|
|
||||||
start () {
|
start () {
|
||||||
|
@ -101,3 +101,8 @@ export class SSHSession extends BaseSession {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ISSHConnectionGroup {
|
||||||
|
name: string
|
||||||
|
connections: SSHConnection[]
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,14 @@
|
||||||
[(ngModel)]='connection.name',
|
[(ngModel)]='connection.name',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label Group
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
placeholder='Ungrouped',
|
||||||
|
[(ngModel)]='connection.group',
|
||||||
|
)
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Host
|
label Host
|
||||||
input.form-control(
|
input.form-control(
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
[(ngModel)]='quickTarget',
|
[(ngModel)]='quickTarget',
|
||||||
autofocus,
|
autofocus,
|
||||||
placeholder='Quick connect: [user@]host[:port]',
|
placeholder='Quick connect: [user@]host[:port]',
|
||||||
(ngModelChange)='filter()',
|
(ngModelChange)='refresh()',
|
||||||
(keyup.enter)='quickConnect()'
|
(keyup.enter)='quickConnect()'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,15 +14,15 @@
|
||||||
span {{lastConnection.name}}
|
span {{lastConnection.name}}
|
||||||
|
|
||||||
.list-group.mt-3
|
.list-group.mt-3
|
||||||
a.list-group-item.list-group-item-action(*ngFor='let folder of childFolders', (click)='cd(folder)')
|
ng-container(*ngFor='let group of childGroups')
|
||||||
i.fa.fa-fw.fa-folder
|
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||||
span {{folder}}
|
(click)='groupCollapsed[group.name] = !groupCollapsed[group.name]'
|
||||||
a.list-group-item.list-group-item-action(*ngFor='let connection of childConnections', (click)='connect(connection)')
|
)
|
||||||
i.fa.fa-fw.fa-globe
|
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||||
span {{connection.name}}
|
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||||
a.list-group-item.list-group-item-action((click)='manageConnections()')
|
.ml-2 {{group.name || "Ungrouped"}}
|
||||||
i.fa.fa-fw.fa-wrench
|
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||||
span Manage connections
|
.list-group-item.list-group-item-action.pl-5.d-flex.align-items-center(
|
||||||
|
*ngFor='let connection of group.connections',
|
||||||
//.modal-footer
|
(click)='connect(connection)'
|
||||||
button.btn.btn-outline-primary((click)='close()') Cancel
|
) {{connection.name}}
|
||||||
|
|
|
@ -4,19 +4,18 @@ import { ToastrService } from 'ngx-toastr'
|
||||||
import { ConfigService, AppService } from 'terminus-core'
|
import { ConfigService, AppService } from 'terminus-core'
|
||||||
import { SettingsTabComponent } from 'terminus-settings'
|
import { SettingsTabComponent } from 'terminus-settings'
|
||||||
import { SSHService } from '../services/ssh.service'
|
import { SSHService } from '../services/ssh.service'
|
||||||
import { SSHConnection } from '../api'
|
import { SSHConnection, ISSHConnectionGroup } from '../api'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./sshModal.component.pug'),
|
template: require('./sshModal.component.pug'),
|
||||||
//styles: [require('./sshModal.component.scss')],
|
|
||||||
})
|
})
|
||||||
export class SSHModalComponent {
|
export class SSHModalComponent {
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
|
childFolders: ISSHConnectionGroup[]
|
||||||
quickTarget: string
|
quickTarget: string
|
||||||
lastConnection: SSHConnection
|
lastConnection: SSHConnection
|
||||||
currentPath: string
|
childGroups: ISSHConnectionGroup[]
|
||||||
childFolders: string[]
|
groupCollapsed: {[id: string]: boolean} = {}
|
||||||
childConnections: SSHConnection[]
|
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public modalInstance: NgbActiveModal,
|
public modalInstance: NgbActiveModal,
|
||||||
|
@ -31,18 +30,7 @@ export class SSHModalComponent {
|
||||||
if (window.localStorage.lastConnection) {
|
if (window.localStorage.lastConnection) {
|
||||||
this.lastConnection = JSON.parse(window.localStorage.lastConnection)
|
this.lastConnection = JSON.parse(window.localStorage.lastConnection)
|
||||||
}
|
}
|
||||||
this.currentPath = "/"
|
this.refresh()
|
||||||
this.findChildren()
|
|
||||||
}
|
|
||||||
|
|
||||||
filter () {
|
|
||||||
if (!this.quickTarget) {
|
|
||||||
this.findChildren()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.childFolders = [];
|
|
||||||
this.childConnections = this.connections.filter(connection => connection.name.toLowerCase().indexOf(this.quickTarget) >= 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quickConnect () {
|
quickConnect () {
|
||||||
|
@ -81,37 +69,25 @@ export class SSHModalComponent {
|
||||||
this.modalInstance.close()
|
this.modalInstance.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
findChildren () {
|
refresh () {
|
||||||
this.childFolders = []
|
this.childGroups = []
|
||||||
this.childConnections = []
|
|
||||||
|
|
||||||
if (this.currentPath != "/")
|
let connections = this.connections
|
||||||
this.childFolders.push("..")
|
if (this.quickTarget) {
|
||||||
|
connections = connections.filter(connection => (connection.name + connection.group).toLowerCase().indexOf(this.quickTarget) >= 0)
|
||||||
|
}
|
||||||
|
|
||||||
for (let connection of this.connections) {
|
for (let connection of connections) {
|
||||||
if (!connection.path)
|
connection.group = connection.group || null
|
||||||
connection.path = "/"
|
let group = this.childGroups.find(x => x.name === connection.group)
|
||||||
if (connection.path.startsWith(this.currentPath)) {
|
if (!group) {
|
||||||
let folder = connection.path.substr(this.currentPath.length, connection.path.indexOf("/", this.currentPath.length) - this.currentPath.length)
|
group = {
|
||||||
if (folder.length == 0) {
|
name: connection.group,
|
||||||
this.childConnections.push(connection)
|
connections: [],
|
||||||
}
|
|
||||||
else if (this.childFolders.indexOf(folder) < 0) {
|
|
||||||
this.childFolders.push(folder)
|
|
||||||
}
|
}
|
||||||
|
this.childGroups.push(group)
|
||||||
}
|
}
|
||||||
|
group.connections.push(connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cd (path: string) {
|
|
||||||
if (path == "..") {
|
|
||||||
path = this.currentPath.substr(0, this.currentPath.lastIndexOf("/", this.currentPath.length - 2) + 1)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
path = this.currentPath + path + '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentPath = path
|
|
||||||
this.findChildren()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,25 @@
|
||||||
h3 Connections ({{currentPath}})
|
h3 Connections
|
||||||
|
|
||||||
.list-group.mt-3.mb-3
|
.list-group.mt-3.mb-3
|
||||||
.list-group-item(*ngFor='let folder of childFolders')
|
ng-container(*ngFor='let group of childGroups')
|
||||||
.d-flex.w-100
|
.list-group-item.list-group-item-action.d-flex.align-items-center((click)='groupCollapsed[group.name] = !groupCollapsed[group.name]')
|
||||||
a.mr-auto.list-group-item-action((click)='cd(folder)')
|
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||||
div.fa.fa-fw.fa-folder
|
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||||
span.ml-2 {{folder}}
|
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
|
||||||
button.btn.btn-outline-info.ml-2(*ngIf='folder != ".."', (click)='editFolder(folder)')
|
button.btn.btn-outline-info.ml-2((click)='editGroup(group)')
|
||||||
i.fa.fa-pencil
|
i.fa.fa-pencil
|
||||||
button.btn.btn-outline-info.disabled.ml-2(*ngIf='folder == ".."')
|
button.btn.btn-outline-danger.ml-1((click)='deleteGroup(group)')
|
||||||
i.fa.fa-pencil
|
|
||||||
button.btn.btn-outline-danger.ml-1(*ngIf='folder != ".."', (click)='deleteFolder(folder)')
|
|
||||||
i.fa.fa-trash-o
|
|
||||||
button.btn.btn-outline-danger.disabled.ml-1(*ngIf='folder == ".."')
|
|
||||||
i.fa.fa-trash-o
|
|
||||||
.list-group-item(*ngFor='let connection of childConnections')
|
|
||||||
.d-flex.w-100
|
|
||||||
.mr-auto
|
|
||||||
div.fa.fa-fw.fa-globe
|
|
||||||
span.ml-2 {{connection.name}}
|
|
||||||
.text-muted {{connection.host}}
|
|
||||||
button.btn.btn-outline-info.ml-2((click)='editConnection(connection)')
|
|
||||||
i.fa.fa-pencil
|
|
||||||
button.btn.btn-outline-danger.ml-1((click)='deleteConnection(connection)')
|
|
||||||
i.fa.fa-trash-o
|
i.fa.fa-trash-o
|
||||||
|
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||||
|
.list-group-item.pl-5.d-flex.align-items-center(*ngFor='let connection of group.connections')
|
||||||
|
.mr-auto
|
||||||
|
div {{connection.name}}
|
||||||
|
.text-muted {{connection.host}}
|
||||||
|
button.btn.btn-outline-info.ml-2((click)='editConnection(connection)')
|
||||||
|
i.fa.fa-pencil
|
||||||
|
button.btn.btn-outline-danger.ml-1((click)='deleteConnection(connection)')
|
||||||
|
i.fa.fa-trash-o
|
||||||
|
|
||||||
button.btn.btn-outline-primary((click)='createFolder()')
|
button.btn.btn-outline-primary((click)='createConnection()')
|
||||||
div.fa.fa-fw.fa-folder
|
|
||||||
span.ml-2 Add Folder
|
|
||||||
button.btn.btn-outline-primary.ml-2((click)='createConnection()')
|
|
||||||
div.fa.fa-fw.fa-globe
|
div.fa.fa-fw.fa-globe
|
||||||
span.ml-2 Add connection
|
span.ml-2 Add connection
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ConfigService } from 'terminus-core'
|
import { ConfigService } from 'terminus-core'
|
||||||
import { SSHConnection } from '../api'
|
import { SSHConnection, ISSHConnectionGroup } from '../api'
|
||||||
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
||||||
import { PromptModalComponent } from './promptModal.component'
|
import { PromptModalComponent } from './promptModal.component'
|
||||||
|
|
||||||
|
@ -10,17 +10,15 @@ import { PromptModalComponent } from './promptModal.component'
|
||||||
})
|
})
|
||||||
export class SSHSettingsTabComponent {
|
export class SSHSettingsTabComponent {
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
currentPath: string
|
childGroups: ISSHConnectionGroup[]
|
||||||
childFolders: string[]
|
groupCollapsed: {[id: string]: boolean} = {}
|
||||||
childConnections: SSHConnection[]
|
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
) {
|
) {
|
||||||
this.connections = this.config.store.ssh.connections
|
this.connections = this.config.store.ssh.connections
|
||||||
this.currentPath = "/"
|
this.refresh()
|
||||||
this.findChildren()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createConnection () {
|
createConnection () {
|
||||||
|
@ -29,7 +27,6 @@ export class SSHSettingsTabComponent {
|
||||||
host: '',
|
host: '',
|
||||||
port: 22,
|
port: 22,
|
||||||
user: 'root',
|
user: 'root',
|
||||||
path: this.currentPath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let modal = this.ngbModal.open(EditConnectionModalComponent)
|
let modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||||
|
@ -38,7 +35,7 @@ export class SSHSettingsTabComponent {
|
||||||
this.connections.push(result)
|
this.connections.push(result)
|
||||||
this.config.store.ssh.connections = this.connections
|
this.config.store.ssh.connections = this.connections
|
||||||
this.config.save()
|
this.config.save()
|
||||||
this.childConnections.push(result)
|
this.refresh()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +45,7 @@ export class SSHSettingsTabComponent {
|
||||||
modal.result.then(result => {
|
modal.result.then(result => {
|
||||||
Object.assign(connection, result)
|
Object.assign(connection, result)
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,89 +54,49 @@ export class SSHSettingsTabComponent {
|
||||||
this.connections = this.connections.filter(x => x !== connection)
|
this.connections = this.connections.filter(x => x !== connection)
|
||||||
this.config.store.ssh.connections = this.connections
|
this.config.store.ssh.connections = this.connections
|
||||||
this.config.save()
|
this.config.save()
|
||||||
this.childConnections = this.connections.filter(x => x !== connection)
|
this.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createFolder () {
|
editGroup (group: ISSHConnectionGroup) {
|
||||||
let modal = this.ngbModal.open(PromptModalComponent)
|
let modal = this.ngbModal.open(PromptModalComponent)
|
||||||
modal.componentInstance.prompt = 'folder name'
|
modal.componentInstance.prompt = 'New group name'
|
||||||
modal.componentInstance.password = false
|
modal.componentInstance.value = group
|
||||||
modal.result.then(result => {
|
modal.result.then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
if (!this.childFolders.includes(result)) {
|
for (let connection of this.connections.filter(x => x.group === group.name)) {
|
||||||
this.childFolders.push(result)
|
connection.group = result
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
editFolder (folder: string) {
|
|
||||||
let modal = this.ngbModal.open(PromptModalComponent)
|
|
||||||
modal.componentInstance.prompt = 'folder name'
|
|
||||||
modal.componentInstance.password = false
|
|
||||||
modal.componentInstance.value = folder
|
|
||||||
modal.result.then(result => {
|
|
||||||
if (result) {
|
|
||||||
let oldPath = this.currentPath + folder + "/"
|
|
||||||
let newPath = this.currentPath + result + "/"
|
|
||||||
for (let connection of this.connections) {
|
|
||||||
connection.path = connection.path.replace(oldPath, newPath)
|
|
||||||
}
|
|
||||||
let i = this.childFolders.indexOf(folder)
|
|
||||||
if (this.childFolders.includes(result)) {
|
|
||||||
this.childFolders.splice(i, 1)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.childFolders.splice(i, 1, result)
|
|
||||||
}
|
}
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteFolder (folder: string) {
|
deleteGroup (group: ISSHConnectionGroup) {
|
||||||
if (confirm(`Delete "${folder}"?`)) {
|
if (confirm(`Delete "${group}"?`)) {
|
||||||
let oldPath = this.currentPath + folder + "/"
|
for (let connection of this.connections.filter(x => x.group === group.name)) {
|
||||||
for (let connection of this.connections) {
|
connection.group = null
|
||||||
connection.path = connection.path.replace(oldPath, this.currentPath)
|
|
||||||
}
|
}
|
||||||
this.config.save()
|
this.config.save()
|
||||||
this.findChildren()
|
this.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findChildren () {
|
refresh () {
|
||||||
this.childFolders = []
|
this.childGroups = []
|
||||||
this.childConnections = []
|
|
||||||
|
|
||||||
if (this.currentPath != "/")
|
|
||||||
this.childFolders.push("..")
|
|
||||||
|
|
||||||
for (let connection of this.connections) {
|
for (let connection of this.connections) {
|
||||||
if (!connection.path)
|
connection.group = connection.group || null
|
||||||
connection.path = "/"
|
let group = this.childGroups.find(x => x.name === connection.group)
|
||||||
if (connection.path.startsWith(this.currentPath)) {
|
if (!group) {
|
||||||
let folder = connection.path.substr(this.currentPath.length, connection.path.indexOf("/", this.currentPath.length) - this.currentPath.length)
|
group = {
|
||||||
if (folder.length == 0) {
|
name: connection.group,
|
||||||
this.childConnections.push(connection)
|
connections: [],
|
||||||
}
|
|
||||||
else if (this.childFolders.indexOf(folder) < 0) {
|
|
||||||
this.childFolders.push(folder)
|
|
||||||
}
|
}
|
||||||
|
this.childGroups.push(group)
|
||||||
}
|
}
|
||||||
|
group.connections.push(connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cd (path: string) {
|
|
||||||
if (path == "..") {
|
|
||||||
path = this.currentPath.substr(0, this.currentPath.lastIndexOf("/", this.currentPath.length - 2) + 1)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
path = this.currentPath + path + '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentPath = path
|
|
||||||
this.findChildren()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue