zone-ified sftp promises

上级 a2ed674b
......@@ -5,6 +5,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { AsyncSubject, Subject, Observable } from 'rxjs'
import { UnlockVaultModalComponent } from '../components/unlockVaultModal.component'
import { NotificationsService } from '../services/notifications.service'
import { wrapPromise } from 'utils'
const PBKDF_ITERATIONS = 100000
const PBKDF_DIGEST = 'sha512'
......@@ -120,7 +121,7 @@ export class VaultService {
passphrase = await this.getPassphrase()
}
try {
return await this.wrapPromise(decryptVault(storage, passphrase))
return await wrapPromise(this.zone, decryptVault(storage, passphrase))
} catch (e) {
_rememberedPassphrase = null
if (e.toString().includes('BAD_DECRYPT')) {
......@@ -144,7 +145,7 @@ export class VaultService {
if (_rememberedPassphrase) {
_rememberedPassphrase = passphrase
}
return this.wrapPromise(encryptVault(vault, passphrase))
return wrapPromise(this.zone, encryptVault(vault, passphrase))
}
async save (vault: Vault, passphrase?: string): Promise<void> {
......@@ -210,14 +211,4 @@ export class VaultService {
isEnabled (): boolean {
return !!this.store
}
private wrapPromise <T> (promise: Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
promise.then(result => {
this.zone.run(() => resolve(result))
}).catch(error => {
this.zone.run(() => reject(error))
})
})
}
}
import * as os from 'os'
import { NgZone } from '@angular/core'
export const WIN_BUILD_CONPTY_SUPPORTED = 17692
export const WIN_BUILD_CONPTY_STABLE = 18309
......@@ -20,3 +21,13 @@ export function getCSSFontFamily (config: any): string {
fonts = fonts.map(x => `"${x}"`)
return fonts.join(', ')
}
export function wrapPromise <T> (zone: NgZone, promise: Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
promise.then(result => {
zone.run(() => resolve(result))
}).catch(error => {
zone.run(() => reject(error))
})
})
}
......@@ -5,7 +5,7 @@ import * as os from 'os'
import promiseIpc from 'electron-promise-ipc'
import { execFile } from 'mz/child_process'
import { Injectable, NgZone } from '@angular/core'
import { PlatformService, ClipboardContent, HostAppService, Platform, ElectronService, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions } from 'terminus-core'
import { PlatformService, ClipboardContent, HostAppService, Platform, ElectronService, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise } from 'terminus-core'
const fontManager = require('fontmanager-redux') // eslint-disable-line
/* eslint-disable block-scoped-var */
......@@ -181,7 +181,7 @@ export class ElectronPlatformService extends PlatformService {
return Promise.all(result.filePaths.map(async p => {
const transfer = new ElectronFileUpload(p)
await this.wrapPromise(transfer.open())
await wrapPromise(this.zone, transfer.open())
this.fileTransferStarted.next(transfer)
return transfer
}))
......@@ -198,20 +198,10 @@ export class ElectronPlatformService extends PlatformService {
return null
}
const transfer = new ElectronFileDownload(result.filePath, size)
await this.wrapPromise(transfer.open())
await wrapPromise(this.zone, transfer.open())
this.fileTransferStarted.next(transfer)
return transfer
}
private wrapPromise <T> (promise: Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
promise.then(result => {
this.zone.run(() => resolve(result))
}).catch(error => {
this.zone.run(() => reject(error))
})
})
}
}
class ElectronFileUpload extends FileUpload {
......
......@@ -5,12 +5,13 @@ import * as sshpk from 'sshpk'
import colors from 'ansi-colors'
import stripAnsi from 'strip-ansi'
import socksv5 from 'socksv5'
import { Injector } from '@angular/core'
import { Injector, NgZone } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { HostAppService, Logger, NotificationsService, Platform, PlatformService } from 'terminus-core'
import { HostAppService, Logger, NotificationsService, Platform, PlatformService, wrapPromise } from 'terminus-core'
import { BaseSession } from 'terminus-terminal'
import { Server, Socket, createServer, createConnection } from 'net'
import { Client, ClientChannel, SFTPWrapper } from 'ssh2'
import type { FileEntry, Stats } from 'ssh2-streams'
import { Subject, Observable } from 'rxjs'
import { ProxyCommandStream } from './services/ssh.service'
import { PasswordStorageService } from './services/passwordStorage.service'
......@@ -137,6 +138,77 @@ interface AuthMethod {
path?: string
}
export class SFTPFileHandle {
position = 0
constructor (
private sftp: SFTPWrapper,
private handle: Buffer,
private zone: NgZone,
) { }
read (): Promise<Buffer> {
const buffer = Buffer.alloc(256 * 1024)
return wrapPromise(this.zone, new Promise((resolve, reject) => {
while (true) {
const wait = this.sftp.read(this.handle, buffer, 0, buffer.length, this.position, (err, read) => {
if (err) {
reject(err)
return
}
this.position += read
resolve(buffer.slice(0, read))
})
if (!wait) {
break
}
}
}))
}
write (chunk: Buffer): Promise<void> {
return wrapPromise(this.zone, new Promise<void>((resolve, reject) => {
while (true) {
const wait = this.sftp.write(this.handle, chunk, 0, chunk.length, this.position, err => {
if (err) {
return reject(err)
}
this.position += chunk.length
resolve()
})
if (!wait) {
break
}
}
}))
}
close (): Promise<void> {
return wrapPromise(this.zone, promisify(this.sftp.close.bind(this.sftp))(this.handle))
}
}
export class SFTPSession {
constructor (private sftp: SFTPWrapper, private zone: NgZone) { }
readdir (p: string): Promise<FileEntry[]> {
return wrapPromise(this.zone, promisify<FileEntry[]>(f => this.sftp.readdir(p, f))())
}
readlink (p: string): Promise<string> {
return wrapPromise(this.zone, promisify<string>(f => this.sftp.readlink(p, f))())
}
stat (p: string): Promise<Stats> {
return wrapPromise(this.zone, promisify<Stats>(f => this.sftp.stat(p, f))())
}
async open (p: string, mode: string): Promise<SFTPFileHandle> {
const handle = await wrapPromise(this.zone, promisify<Buffer>(f => this.sftp.open(p, mode, f))())
return new SFTPFileHandle(this.sftp, handle, this.zone)
}
}
export class SSHSession extends BaseSession {
scripts?: LoginScript[]
shell?: ClientChannel
......@@ -161,6 +233,7 @@ export class SSHSession extends BaseSession {
private hostApp: HostAppService
private platform: PlatformService
private notifications: NotificationsService
private zone: NgZone
constructor (
injector: Injector,
......@@ -172,6 +245,7 @@ export class SSHSession extends BaseSession {
this.hostApp = injector.get(HostAppService)
this.platform = injector.get(PlatformService)
this.notifications = injector.get(NotificationsService)
this.zone = injector.get(NgZone)
this.scripts = connection.scripts ?? []
this.destroyed$.subscribe(() => {
......@@ -223,11 +297,11 @@ export class SSHSession extends BaseSession {
this.remainingAuthMethods.push({ type: 'hostbased' })
}
async openSFTP (): Promise<SFTPWrapper> {
async openSFTP (): Promise<SFTPSession> {
if (!this.sftp) {
this.sftp = await promisify<SFTPWrapper>(f => this.ssh.sftp(f))()
this.sftp = await wrapPromise(this.zone, promisify<SFTPWrapper>(f => this.ssh.sftp(f))())
}
return this.sftp
return new SFTPSession(this.sftp, this.zone)
}
async start (): Promise<void> {
......
import { Component, Input, Output, EventEmitter } from '@angular/core'
import { SFTPWrapper } from 'ssh2'
import type { FileEntry, Stats } from 'ssh2-streams'
import { promisify } from 'util'
import { SSHSession } from '../api'
import type { FileEntry } from 'ssh2-streams'
import { SSHSession, SFTPSession } from '../api'
import * as path from 'path'
import * as C from 'constants'
import { FileUpload, PlatformService } from 'terminus-core'
......@@ -21,7 +19,7 @@ interface PathSegment {
export class SFTPPanelComponent {
@Input() session: SSHSession
@Output() closed = new EventEmitter<void>()
sftp: SFTPWrapper
sftp: SFTPSession
fileList: FileEntry[]|null = null
path = '/'
pathSegments: PathSegment[] = []
......@@ -49,8 +47,7 @@ export class SFTPPanelComponent {
}
this.fileList = null
this.fileList = await promisify<FileEntry[]>(f => this.sftp.readdir(this.path, f))()
console.log(this.fileList)
this.fileList = await this.sftp.readdir(this.path)
const dirKey = a => (a.attrs.mode & C.S_IFDIR) === C.S_IFDIR ? 1 : 0
this.fileList.sort((a, b) =>
......@@ -77,8 +74,8 @@ export class SFTPPanelComponent {
if ((item.attrs.mode & C.S_IFDIR) === C.S_IFDIR) {
this.navigate(path.join(this.path, item.filename))
} else if ((item.attrs.mode & C.S_IFLNK) === C.S_IFLNK) {
const target = await promisify<string>(f => this.sftp.readlink(itemPath, f))()
const stat = await promisify<Stats>(f => this.sftp.stat(target, f))()
const target = await this.sftp.readlink(itemPath)
const stat = await this.sftp.stat(target)
if (stat.isDirectory()) {
this.navigate(itemPath)
} else {
......@@ -104,30 +101,15 @@ export class SFTPPanelComponent {
async uploadOne (transfer: FileUpload): Promise<void> {
const itemPath = path.join(this.path, transfer.getName())
try {
const handle = await promisify<Buffer>(f => this.sftp.open(itemPath, 'w', f))()
let position = 0
const handle = await this.sftp.open(itemPath, 'w')
while (true) {
const chunk = await transfer.read()
if (!chunk.length) {
break
}
const p = position
await new Promise<void>((resolve, reject) => {
while (true) {
const wait = this.sftp.write(handle, chunk, 0, chunk.length, p, err => {
if (err) {
return reject(err)
}
resolve()
})
if (!wait) {
break
}
}
})
position += chunk.length
await handle.write(chunk)
}
this.sftp.close(handle, () => null)
handle.close()
transfer.close()
} catch (e) {
transfer.cancel()
......@@ -141,33 +123,16 @@ export class SFTPPanelComponent {
return
}
try {
const handle = await promisify<Buffer>(f => this.sftp.open(itemPath, 'r', f))()
const buffer = Buffer.alloc(256 * 1024)
let position = 0
const handle = await this.sftp.open(itemPath, 'r')
while (true) {
const p = position
const chunk: Buffer = await new Promise((resolve, reject) => {
while (true) {
const wait = this.sftp.read(handle, buffer, 0, buffer.length, p, (err, read) => {
if (err) {
reject(err)
return
}
resolve(buffer.slice(0, read))
})
if (!wait) {
break
}
}
})
const chunk = await handle.read()
if (!chunk.length) {
break
}
await transfer.write(chunk)
position += chunk.length
}
transfer.close()
this.sftp.close(handle, () => null)
handle.close()
} catch (e) {
transfer.cancel()
throw e
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册