mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-19 18:07:58 +00:00
automatically clean up defaults from the config file
This commit is contained in:
@@ -20,7 +20,7 @@ export { ProfileProvider, Profile, ProfileSettingsComponent } from './profilePro
|
|||||||
export { PromptModalComponent } from '../components/promptModal.component'
|
export { PromptModalComponent } from '../components/promptModal.component'
|
||||||
|
|
||||||
export { AppService } from '../services/app.service'
|
export { AppService } from '../services/app.service'
|
||||||
export { ConfigService } from '../services/config.service'
|
export { ConfigService, configMerge, ConfigProxy } from '../services/config.service'
|
||||||
export { DockingService, Screen } from '../services/docking.service'
|
export { DockingService, Screen } from '../services/docking.service'
|
||||||
export { Logger, ConsoleLogger, LogService } from '../services/log.service'
|
export { Logger, ConsoleLogger, LogService } from '../services/log.service'
|
||||||
export { HomeBaseService } from '../services/homeBase.service'
|
export { HomeBaseService } from '../services/homeBase.service'
|
||||||
|
@@ -29,6 +29,7 @@ export abstract class ProfileProvider {
|
|||||||
name: string
|
name: string
|
||||||
supportsQuickConnect = false
|
supportsQuickConnect = false
|
||||||
settingsComponent: new (...args: any[]) => ProfileSettingsComponent
|
settingsComponent: new (...args: any[]) => ProfileSettingsComponent
|
||||||
|
configDefaults = {}
|
||||||
|
|
||||||
abstract getBuiltinProfiles (): Promise<Profile[]>
|
abstract getBuiltinProfiles (): Promise<Profile[]>
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import deepEqual from 'deep-equal'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import * as yaml from 'js-yaml'
|
import * as yaml from 'js-yaml'
|
||||||
import { Observable, Subject, AsyncSubject } from 'rxjs'
|
import { Observable, Subject, AsyncSubject } from 'rxjs'
|
||||||
@@ -8,7 +9,8 @@ import { HostAppService } from '../api/hostApp'
|
|||||||
import { Vault, VaultService } from './vault.service'
|
import { Vault, VaultService } from './vault.service'
|
||||||
const deepmerge = require('deepmerge')
|
const deepmerge = require('deepmerge')
|
||||||
|
|
||||||
const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
export const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
|
|
||||||
const LATEST_VERSION = 1
|
const LATEST_VERSION = 1
|
||||||
|
|
||||||
@@ -46,24 +48,24 @@ export class ConfigProxy {
|
|||||||
{
|
{
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
get: () => this.getValue(key),
|
get: () => this.__getValue(key),
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
this.setValue(key, value)
|
this.__setValue(key, value)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getValue = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
|
this.__getValue = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
|
||||||
if (real[key] !== undefined) {
|
if (real[key] !== undefined) {
|
||||||
return real[key]
|
return real[key]
|
||||||
} else {
|
} else {
|
||||||
return this.getDefault(key)
|
return this.__getDefault(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getDefault = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
|
this.__getDefault = (key: string) => { // eslint-disable-line @typescript-eslint/unbound-method
|
||||||
if (isNonStructuralObjectMember(defaults[key])) {
|
if (isNonStructuralObjectMember(defaults[key])) {
|
||||||
real[key] = { ...defaults[key] }
|
real[key] = { ...defaults[key] }
|
||||||
delete real[key].__nonStructural
|
delete real[key].__nonStructural
|
||||||
@@ -73,22 +75,36 @@ export class ConfigProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setValue = (key: string, value: any) => { // eslint-disable-line @typescript-eslint/unbound-method
|
this.__setValue = (key: string, value: any) => { // eslint-disable-line @typescript-eslint/unbound-method
|
||||||
if (value === this.getDefault(key)) {
|
if (deepEqual(value, this.__getDefault(key))) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete real[key]
|
delete real[key]
|
||||||
} else {
|
} else {
|
||||||
real[key] = value
|
real[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.__cleanup = () => { // eslint-disable-line @typescript-eslint/unbound-method
|
||||||
|
// Trigger removal of default values
|
||||||
|
for (const key in defaults) {
|
||||||
|
if (isStructuralMember(defaults[key])) {
|
||||||
|
this[key].__cleanup()
|
||||||
|
} else {
|
||||||
|
const v = this.__getValue(key)
|
||||||
|
this.__setValue(key, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||||
getValue (_key: string): any { }
|
__getValue (_key: string): any { }
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||||
setValue (_key: string, _value: any) { }
|
__setValue (_key: string, _value: any) { }
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||||
getDefault (_key: string): any { }
|
__getDefault (_key: string): any { }
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function
|
||||||
|
__cleanup () { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
@@ -177,6 +193,7 @@ export class ConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async save (): Promise<void> {
|
async save (): Promise<void> {
|
||||||
|
this.store.__cleanup()
|
||||||
// Scrub undefined values
|
// Scrub undefined values
|
||||||
let cleanStore = JSON.parse(JSON.stringify(this._store))
|
let cleanStore = JSON.parse(JSON.stringify(this._store))
|
||||||
cleanStore = await this.maybeEncryptConfig(cleanStore)
|
cleanStore = await this.maybeEncryptConfig(cleanStore)
|
||||||
|
@@ -4,12 +4,26 @@ import { BaseTabComponent } from '../components/baseTab.component'
|
|||||||
import { Profile, ProfileProvider } from '../api/profileProvider'
|
import { Profile, ProfileProvider } from '../api/profileProvider'
|
||||||
import { SelectorOption } from '../api/selector'
|
import { SelectorOption } from '../api/selector'
|
||||||
import { AppService } from './app.service'
|
import { AppService } from './app.service'
|
||||||
import { ConfigService } from './config.service'
|
import { configMerge, ConfigProxy, ConfigService } from './config.service'
|
||||||
import { NotificationsService } from './notifications.service'
|
import { NotificationsService } from './notifications.service'
|
||||||
import { SelectorService } from './selector.service'
|
import { SelectorService } from './selector.service'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ProfilesService {
|
export class ProfilesService {
|
||||||
|
private profileDefaults = {
|
||||||
|
id: '',
|
||||||
|
type: '',
|
||||||
|
name: '',
|
||||||
|
group: '',
|
||||||
|
options: {},
|
||||||
|
icon: '',
|
||||||
|
color: '',
|
||||||
|
disableDynamicTitle: false,
|
||||||
|
weight: 0,
|
||||||
|
isBuiltin: false,
|
||||||
|
isTemplate: false,
|
||||||
|
}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private app: AppService,
|
private app: AppService,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
@@ -19,6 +33,7 @@ export class ProfilesService {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
async openNewTabForProfile (profile: Profile): Promise<BaseTabComponent|null> {
|
async openNewTabForProfile (profile: Profile): Promise<BaseTabComponent|null> {
|
||||||
|
profile = this.getConfigProxyForProfile(profile)
|
||||||
const params = await this.newTabParametersForProfile(profile)
|
const params = await this.newTabParametersForProfile(profile)
|
||||||
if (params) {
|
if (params) {
|
||||||
const tab = this.app.openNewTab(params)
|
const tab = this.app.openNewTab(params)
|
||||||
@@ -33,6 +48,7 @@ export class ProfilesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async newTabParametersForProfile (profile: Profile): Promise<NewTabParameters<BaseTabComponent>|null> {
|
async newTabParametersForProfile (profile: Profile): Promise<NewTabParameters<BaseTabComponent>|null> {
|
||||||
|
profile = this.getConfigProxyForProfile(profile)
|
||||||
return this.providerForProfile(profile)?.getNewTabParameters(profile) ?? null
|
return this.providerForProfile(profile)?.getNewTabParameters(profile) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,4 +166,10 @@ export class ProfilesService {
|
|||||||
this.notifications.error(`Could not parse "${query}"`)
|
this.notifications.error(`Could not parse "${query}"`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getConfigProxyForProfile (profile: Profile): Profile {
|
||||||
|
const provider = this.providerForProfile(profile)
|
||||||
|
const defaults = configMerge(this.profileDefaults, provider?.configDefaults ?? {})
|
||||||
|
return new ConfigProxy(profile, defaults) as unknown as Profile
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ export interface SerialProfileOptions extends StreamProcessingOptions, LoginScri
|
|||||||
xon?: boolean
|
xon?: boolean
|
||||||
xoff?: boolean
|
xoff?: boolean
|
||||||
xany?: boolean
|
xany?: boolean
|
||||||
color?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BAUD_RATES = [
|
export const BAUD_RATES = [
|
||||||
|
@@ -13,6 +13,24 @@ export class SerialProfilesService extends ProfileProvider {
|
|||||||
id = 'serial'
|
id = 'serial'
|
||||||
name = 'Serial'
|
name = 'Serial'
|
||||||
settingsComponent = SerialProfileSettingsComponent
|
settingsComponent = SerialProfileSettingsComponent
|
||||||
|
configDefaults = {
|
||||||
|
options: {
|
||||||
|
port: null,
|
||||||
|
baudrate: null,
|
||||||
|
databits: 8,
|
||||||
|
stopbits: 1,
|
||||||
|
parity: 'none',
|
||||||
|
rtscts: false,
|
||||||
|
xon: false,
|
||||||
|
xoff: false,
|
||||||
|
xany: false,
|
||||||
|
inputMode: 'local-echo',
|
||||||
|
outputMode: null,
|
||||||
|
inputNewlines: null,
|
||||||
|
outputNewlines: 'crlf',
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private selector: SelectorService,
|
private selector: SelectorService,
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs'
|
import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs'
|
||||||
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'
|
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ConfigService, Profile, ProfileProvider, ProfileSettingsComponent } from 'tabby-core'
|
import { ConfigProxy, ConfigService, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService } from 'tabby-core'
|
||||||
|
|
||||||
const iconsData = require('../../../tabby-core/src/icons.json')
|
const iconsData = require('../../../tabby-core/src/icons.json')
|
||||||
const iconsClassList = Object.keys(iconsData).map(
|
const iconsClassList = Object.keys(iconsData).map(
|
||||||
@@ -16,17 +16,19 @@ const iconsClassList = Object.keys(iconsData).map(
|
|||||||
template: require('./editProfileModal.component.pug'),
|
template: require('./editProfileModal.component.pug'),
|
||||||
})
|
})
|
||||||
export class EditProfileModalComponent {
|
export class EditProfileModalComponent {
|
||||||
@Input() profile: Profile
|
@Input() profile: Profile|ConfigProxy
|
||||||
@Input() profileProvider: ProfileProvider
|
@Input() profileProvider: ProfileProvider
|
||||||
@Input() settingsComponent: new () => ProfileSettingsComponent
|
@Input() settingsComponent: new () => ProfileSettingsComponent
|
||||||
groupNames: string[]
|
groupNames: string[]
|
||||||
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
|
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
|
||||||
|
|
||||||
|
private _profile: Profile
|
||||||
private settingsComponentInstance: ProfileSettingsComponent
|
private settingsComponentInstance: ProfileSettingsComponent
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
private componentFactoryResolver: ComponentFactoryResolver,
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
|
private profilesService: ProfilesService,
|
||||||
config: ConfigService,
|
config: ConfigService,
|
||||||
private modalInstance: NgbActiveModal,
|
private modalInstance: NgbActiveModal,
|
||||||
) {
|
) {
|
||||||
@@ -37,6 +39,11 @@ export class EditProfileModalComponent {
|
|||||||
)].sort() as string[]
|
)].sort() as string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this._profile = this.profile
|
||||||
|
this.profile = this.profilesService.getConfigProxyForProfile(this.profile)
|
||||||
|
}
|
||||||
|
|
||||||
ngAfterViewInit () {
|
ngAfterViewInit () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.profileProvider.settingsComponent)
|
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.profileProvider.settingsComponent)
|
||||||
@@ -63,7 +70,8 @@ export class EditProfileModalComponent {
|
|||||||
save () {
|
save () {
|
||||||
this.profile.group ||= undefined
|
this.profile.group ||= undefined
|
||||||
this.settingsComponentInstance.save?.()
|
this.settingsComponentInstance.save?.()
|
||||||
this.modalInstance.close(this.profile)
|
this.profile.__cleanup()
|
||||||
|
this.modalInstance.close(this._profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel () {
|
cancel () {
|
||||||
|
@@ -82,7 +82,14 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
modal.componentInstance.profile = Object.assign({}, profile)
|
modal.componentInstance.profile = Object.assign({}, profile)
|
||||||
modal.componentInstance.profileProvider = this.profilesService.providerForProfile(profile)
|
modal.componentInstance.profileProvider = this.profilesService.providerForProfile(profile)
|
||||||
const result = await modal.result
|
const result = await modal.result
|
||||||
|
|
||||||
|
// Fully replace the config
|
||||||
|
for (const k in profile) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
delete profile[k]
|
||||||
|
}
|
||||||
Object.assign(profile, result)
|
Object.assign(profile, result)
|
||||||
|
|
||||||
await this.config.save()
|
await this.config.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,8 +4,8 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
|||||||
|
|
||||||
import { ConfigService, FileProvidersService, Platform, HostAppService, PromptModalComponent } from 'tabby-core'
|
import { ConfigService, FileProvidersService, Platform, HostAppService, PromptModalComponent } from 'tabby-core'
|
||||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||||
import { ForwardedPortConfig, SSHAlgorithmType, ALGORITHM_BLACKLIST, SSHProfile } from '../api'
|
import { ForwardedPortConfig, SSHAlgorithmType, SSHProfile } from '../api'
|
||||||
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
|
import { SSHProfilesService } from '../profiles'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
@@ -18,7 +18,6 @@ export class SSHProfileSettingsComponent {
|
|||||||
useProxyCommand: boolean
|
useProxyCommand: boolean
|
||||||
|
|
||||||
supportedAlgorithms: Record<string, string> = {}
|
supportedAlgorithms: Record<string, string> = {}
|
||||||
defaultAlgorithms: Record<string, string[]> = {}
|
|
||||||
algorithms: Record<string, Record<string, boolean>> = {}
|
algorithms: Record<string, Record<string, boolean>> = {}
|
||||||
jumpHosts: SSHProfile[]
|
jumpHosts: SSHProfile[]
|
||||||
|
|
||||||
@@ -28,36 +27,16 @@ export class SSHProfileSettingsComponent {
|
|||||||
private passwordStorage: PasswordStorageService,
|
private passwordStorage: PasswordStorageService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
private fileProviders: FileProvidersService,
|
private fileProviders: FileProvidersService,
|
||||||
|
sshProfilesService: SSHProfilesService,
|
||||||
) {
|
) {
|
||||||
for (const k of Object.values(SSHAlgorithmType)) {
|
this.supportedAlgorithms = sshProfilesService.supportedAlgorithms
|
||||||
const supportedAlg = {
|
|
||||||
[SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
|
|
||||||
[SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
|
|
||||||
[SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
|
|
||||||
[SSHAlgorithmType.HMAC]: 'SUPPORTED_MAC',
|
|
||||||
}[k]
|
|
||||||
const defaultAlg = {
|
|
||||||
[SSHAlgorithmType.KEX]: 'DEFAULT_KEX',
|
|
||||||
[SSHAlgorithmType.HOSTKEY]: 'DEFAULT_SERVER_HOST_KEY',
|
|
||||||
[SSHAlgorithmType.CIPHER]: 'DEFAULT_CIPHER',
|
|
||||||
[SSHAlgorithmType.HMAC]: 'DEFAULT_MAC',
|
|
||||||
}[k]
|
|
||||||
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x)).sort()
|
|
||||||
this.defaultAlgorithms[k] = ALGORITHMS[defaultAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit () {
|
async ngOnInit () {
|
||||||
this.jumpHosts = this.config.store.profiles.filter(x => x.type === 'ssh' && x !== this.profile)
|
this.jumpHosts = this.config.store.profiles.filter(x => x.type === 'ssh' && x !== this.profile)
|
||||||
this.profile.options.algorithms = this.profile.options.algorithms ?? {}
|
|
||||||
for (const k of Object.values(SSHAlgorithmType)) {
|
for (const k of Object.values(SSHAlgorithmType)) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
||||||
if (!this.profile.options.algorithms[k]) {
|
|
||||||
this.profile.options.algorithms[k] = this.defaultAlgorithms[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.algorithms[k] = {}
|
this.algorithms[k] = {}
|
||||||
for (const alg of this.profile.options.algorithms[k]) {
|
for (const alg of this.profile.options.algorithms?.[k] ?? []) {
|
||||||
this.algorithms[k][alg] = true
|
this.algorithms[k][alg] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,6 +87,7 @@ export class SSHProfileSettingsComponent {
|
|||||||
this.profile.options.algorithms![k] = Object.entries(this.algorithms[k])
|
this.profile.options.algorithms![k] = Object.entries(this.algorithms[k])
|
||||||
.filter(([_, v]) => !!v)
|
.filter(([_, v]) => !!v)
|
||||||
.map(([key, _]) => key)
|
.map(([key, _]) => key)
|
||||||
|
this.profile.options.algorithms![k].sort()
|
||||||
}
|
}
|
||||||
if (!this.useProxyCommand) {
|
if (!this.useProxyCommand) {
|
||||||
this.profile.options.proxyCommand = undefined
|
this.profile.options.proxyCommand = undefined
|
||||||
|
@@ -3,7 +3,9 @@ import { ProfileProvider, Profile, NewTabParameters } from 'tabby-core'
|
|||||||
import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component'
|
import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component'
|
||||||
import { SSHTabComponent } from './components/sshTab.component'
|
import { SSHTabComponent } from './components/sshTab.component'
|
||||||
import { PasswordStorageService } from './services/passwordStorage.service'
|
import { PasswordStorageService } from './services/passwordStorage.service'
|
||||||
import { SSHProfile } from './api'
|
import { ALGORITHM_BLACKLIST, SSHAlgorithmType, SSHProfile } from './api'
|
||||||
|
|
||||||
|
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SSHProfilesService extends ProfileProvider {
|
export class SSHProfilesService extends ProfileProvider {
|
||||||
@@ -11,11 +13,57 @@ export class SSHProfilesService extends ProfileProvider {
|
|||||||
name = 'SSH'
|
name = 'SSH'
|
||||||
supportsQuickConnect = true
|
supportsQuickConnect = true
|
||||||
settingsComponent = SSHProfileSettingsComponent
|
settingsComponent = SSHProfileSettingsComponent
|
||||||
|
configDefaults = {
|
||||||
|
options: {
|
||||||
|
host: null,
|
||||||
|
port: 22,
|
||||||
|
user: 'root',
|
||||||
|
auth: null,
|
||||||
|
password: null,
|
||||||
|
privateKeys: [],
|
||||||
|
keepaliveInterval: null,
|
||||||
|
keepaliveCountMax: null,
|
||||||
|
readyTimeout: null,
|
||||||
|
x11: false,
|
||||||
|
skipBanner: false,
|
||||||
|
jumpHost: null,
|
||||||
|
agentForward: false,
|
||||||
|
warnOnClose: null,
|
||||||
|
algorithms: {
|
||||||
|
hmac: [],
|
||||||
|
kex: [],
|
||||||
|
cipher: [],
|
||||||
|
serverHostKey: [],
|
||||||
|
},
|
||||||
|
proxyCommand: null,
|
||||||
|
forwardedPorts: [],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
supportedAlgorithms: Record<string, string> = {}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private passwordStorage: PasswordStorageService
|
private passwordStorage: PasswordStorageService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
for (const k of Object.values(SSHAlgorithmType)) {
|
||||||
|
const supportedAlg = {
|
||||||
|
[SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
|
||||||
|
[SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
|
||||||
|
[SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
|
||||||
|
[SSHAlgorithmType.HMAC]: 'SUPPORTED_MAC',
|
||||||
|
}[k]
|
||||||
|
const defaultAlg = {
|
||||||
|
[SSHAlgorithmType.KEX]: 'DEFAULT_KEX',
|
||||||
|
[SSHAlgorithmType.HOSTKEY]: 'DEFAULT_SERVER_HOST_KEY',
|
||||||
|
[SSHAlgorithmType.CIPHER]: 'DEFAULT_CIPHER',
|
||||||
|
[SSHAlgorithmType.HMAC]: 'DEFAULT_MAC',
|
||||||
|
}[k]
|
||||||
|
this.supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x)).sort()
|
||||||
|
this.configDefaults.options.algorithms[k] = ALGORITHMS[defaultAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
|
||||||
|
this.configDefaults.options.algorithms[k].sort()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBuiltinProfiles (): Promise<Profile[]> {
|
async getBuiltinProfiles (): Promise<Profile[]> {
|
||||||
|
@@ -10,6 +10,17 @@ export class TelnetProfilesService extends ProfileProvider {
|
|||||||
name = 'Telnet'
|
name = 'Telnet'
|
||||||
supportsQuickConnect = false
|
supportsQuickConnect = false
|
||||||
settingsComponent = TelnetProfileSettingsComponent
|
settingsComponent = TelnetProfileSettingsComponent
|
||||||
|
configDefaults = {
|
||||||
|
options: {
|
||||||
|
host: null,
|
||||||
|
port: 23,
|
||||||
|
inputMode: 'local-echo',
|
||||||
|
outputMode: null,
|
||||||
|
inputNewlines: null,
|
||||||
|
outputNewlines: 'crlf',
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
async getBuiltinProfiles (): Promise<TelnetProfile[]> {
|
async getBuiltinProfiles (): Promise<TelnetProfile[]> {
|
||||||
return [
|
return [
|
||||||
|
Reference in New Issue
Block a user