@electerm/electerm-react 1.80.18 → 1.90.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/common/constants.js +3 -1
- package/client/common/default-setting.js +2 -1
- package/client/common/fetch-from-server.js +1 -1
- package/client/common/sftp.js +13 -9
- package/client/common/transfer.js +4 -4
- package/client/common/ws.js +1 -2
- package/client/components/batch-op/batch-op.jsx +3 -4
- package/client/components/bookmark-form/ftp-form-ui.jsx +168 -0
- package/client/components/bookmark-form/ftp-form.jsx +16 -0
- package/client/components/bookmark-form/index.jsx +8 -3
- package/client/components/bookmark-form/render-ssh-tunnel.jsx +2 -0
- package/client/components/bookmark-form/x11.jsx +6 -15
- package/client/components/file-transfer/transfer.jsx +26 -24
- package/client/components/footer/tab-select.jsx +1 -1
- package/client/components/rdp/rdp-session.jsx +2 -3
- package/client/components/session/session.jsx +21 -22
- package/client/components/setting-panel/setting-terminal.jsx +5 -0
- package/client/components/sftp/address-bar.jsx +6 -1
- package/client/components/sftp/file-info-modal.jsx +0 -2
- package/client/components/sftp/file-item.jsx +60 -24
- package/client/components/sftp/owner-list.js +4 -4
- package/client/components/sftp/sftp-entry.jsx +62 -21
- package/client/components/sftp/transfer-common.js +1 -2
- package/client/components/sidebar/transfer-list-control.jsx +5 -5
- package/client/components/terminal/terminal-apis.js +4 -8
- package/client/components/terminal/terminal.jsx +6 -10
- package/client/components/terminal-info/base.jsx +4 -8
- package/client/components/terminal-info/run-cmd.jsx +2 -3
- package/client/components/terminal-info/terminal-info.jsx +2 -3
- package/client/components/theme/theme-list-item.jsx +22 -42
- package/client/components/theme/theme-list.jsx +23 -1
- package/client/components/tree-list/bookmark-toolbar.jsx +58 -46
- package/client/components/vnc/vnc-session.jsx +2 -3
- package/client/entry/worker.js +1 -2
- package/client/store/item.js +1 -1
- package/client/store/tab.js +7 -5
- package/client/store/ui-theme.js +6 -6
- package/client/store/watch.js +3 -2
- package/package.json +1 -1
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
Splitter
|
|
22
22
|
} from 'antd'
|
|
23
23
|
import { pick } from 'lodash-es'
|
|
24
|
-
import generate from '../../common/uid'
|
|
25
24
|
import copy from 'json-deep-copy'
|
|
26
25
|
import classnames from 'classnames'
|
|
27
26
|
import {
|
|
@@ -30,7 +29,8 @@ import {
|
|
|
30
29
|
terminalRdpType,
|
|
31
30
|
terminalVncType,
|
|
32
31
|
terminalWebType,
|
|
33
|
-
terminalTelnetType
|
|
32
|
+
terminalTelnetType,
|
|
33
|
+
terminalFtpType
|
|
34
34
|
} from '../../common/constants'
|
|
35
35
|
import { SplitViewIcon } from '../icons/split-view'
|
|
36
36
|
import { refs } from '../common/ref'
|
|
@@ -50,7 +50,6 @@ export default class SessionWrapper extends Component {
|
|
|
50
50
|
key: Math.random(),
|
|
51
51
|
splitSize: [50, 50],
|
|
52
52
|
sessionOptions: null,
|
|
53
|
-
sessionId: generate(),
|
|
54
53
|
delKeyPressed: false,
|
|
55
54
|
broadcastInput: false
|
|
56
55
|
}
|
|
@@ -60,11 +59,6 @@ export default class SessionWrapper extends Component {
|
|
|
60
59
|
minWithForSplit = 640
|
|
61
60
|
minHeightForSplit = 400
|
|
62
61
|
|
|
63
|
-
componentDidMount () {
|
|
64
|
-
this.updateTab()
|
|
65
|
-
// this.initEvent()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
62
|
componentWillUnmount () {
|
|
69
63
|
clearTimeout(this.backspaceKeyPressedTimer)
|
|
70
64
|
}
|
|
@@ -244,14 +238,6 @@ export default class SessionWrapper extends Component {
|
|
|
244
238
|
this.editTab(update)
|
|
245
239
|
}
|
|
246
240
|
|
|
247
|
-
updateTab = () => {
|
|
248
|
-
this.editTab(
|
|
249
|
-
{
|
|
250
|
-
sessionId: this.state.sessionId
|
|
251
|
-
}
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
241
|
computePosition = (index) => {
|
|
256
242
|
return {
|
|
257
243
|
left: 0,
|
|
@@ -266,7 +252,6 @@ export default class SessionWrapper extends Component {
|
|
|
266
252
|
renderTerminals = () => {
|
|
267
253
|
const {
|
|
268
254
|
sessionOptions,
|
|
269
|
-
sessionId,
|
|
270
255
|
sftpPathFollowSsh,
|
|
271
256
|
broadcastInput
|
|
272
257
|
} = this.state
|
|
@@ -292,7 +277,6 @@ export default class SessionWrapper extends Component {
|
|
|
292
277
|
if (type === terminalRdpType || type === terminalVncType) {
|
|
293
278
|
const rdpProps = {
|
|
294
279
|
tab: this.props.tab,
|
|
295
|
-
sessionId,
|
|
296
280
|
...pick(this.props, [
|
|
297
281
|
'resolutions',
|
|
298
282
|
'height',
|
|
@@ -319,6 +303,7 @@ export default class SessionWrapper extends Component {
|
|
|
319
303
|
/>
|
|
320
304
|
)
|
|
321
305
|
}
|
|
306
|
+
|
|
322
307
|
return (
|
|
323
308
|
<RdpSession
|
|
324
309
|
{...rdpProps}
|
|
@@ -326,6 +311,22 @@ export default class SessionWrapper extends Component {
|
|
|
326
311
|
)
|
|
327
312
|
}
|
|
328
313
|
|
|
314
|
+
if (type === terminalFtpType) {
|
|
315
|
+
const ftpProps = {
|
|
316
|
+
...this.props,
|
|
317
|
+
...pick(this, [
|
|
318
|
+
'onChangePane',
|
|
319
|
+
'setCwd'
|
|
320
|
+
]),
|
|
321
|
+
isFtp: true
|
|
322
|
+
}
|
|
323
|
+
return (
|
|
324
|
+
<Sftp
|
|
325
|
+
{...ftpProps}
|
|
326
|
+
/>
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
329
330
|
const cls = pane === paneMap.terminal ||
|
|
330
331
|
(sshSftpSplitView && this.canSplitView())
|
|
331
332
|
? 'terms-box'
|
|
@@ -363,7 +364,6 @@ export default class SessionWrapper extends Component {
|
|
|
363
364
|
>
|
|
364
365
|
<Term
|
|
365
366
|
logName={logName}
|
|
366
|
-
sessionId={sessionId}
|
|
367
367
|
sessionOptions={sessionOptions}
|
|
368
368
|
{...pops}
|
|
369
369
|
/>
|
|
@@ -376,7 +376,8 @@ export default class SessionWrapper extends Component {
|
|
|
376
376
|
return type === terminalRdpType ||
|
|
377
377
|
type === terminalVncType ||
|
|
378
378
|
type === terminalWebType ||
|
|
379
|
-
type === terminalTelnetType
|
|
379
|
+
type === terminalTelnetType ||
|
|
380
|
+
type === terminalFtpType
|
|
380
381
|
}
|
|
381
382
|
|
|
382
383
|
calcSftpWidthHeight = () => {
|
|
@@ -424,7 +425,6 @@ export default class SessionWrapper extends Component {
|
|
|
424
425
|
renderSftp = () => {
|
|
425
426
|
const {
|
|
426
427
|
sessionOptions,
|
|
427
|
-
sessionId,
|
|
428
428
|
enableSftp,
|
|
429
429
|
sftpPathFollowSsh,
|
|
430
430
|
cwd
|
|
@@ -452,7 +452,6 @@ export default class SessionWrapper extends Component {
|
|
|
452
452
|
enableSftp,
|
|
453
453
|
sessionOptions,
|
|
454
454
|
height,
|
|
455
|
-
sessionId,
|
|
456
455
|
pane,
|
|
457
456
|
...this.calcSftpWidthHeight()
|
|
458
457
|
}
|
|
@@ -494,6 +494,7 @@ export default class SettingTerminal extends Component {
|
|
|
494
494
|
<Link to={regexHelpLink}>wiki</Link>
|
|
495
495
|
</div>
|
|
496
496
|
)
|
|
497
|
+
const startDirectoryLocalTxt = `${e('startDirectory')}:${e('local')}`
|
|
497
498
|
return (
|
|
498
499
|
<div className='form-wrap pd1y pd2x'>
|
|
499
500
|
<div className='pd1y font16 bold'>
|
|
@@ -559,6 +560,10 @@ export default class SettingTerminal extends Component {
|
|
|
559
560
|
</div>
|
|
560
561
|
<div className='pd1b'>{e('terminalBackgroundImage')}</div>
|
|
561
562
|
<TerminalBackgroundConfig {...bgProps} />
|
|
563
|
+
<div className='pd1b'>{startDirectoryLocalTxt}</div>
|
|
564
|
+
{
|
|
565
|
+
this.renderText('startDirectoryLocal', startDirectoryLocalTxt)
|
|
566
|
+
}
|
|
562
567
|
<div className='pd1b'>{e('terminalWordSeparator')}</div>
|
|
563
568
|
{
|
|
564
569
|
this.renderText('terminalWordSeparator', e('terminalWordSeparator'))
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
EyeFilled,
|
|
5
5
|
ReloadOutlined,
|
|
6
6
|
ArrowRightOutlined,
|
|
7
|
-
LoadingOutlined
|
|
7
|
+
LoadingOutlined,
|
|
8
|
+
HomeOutlined
|
|
8
9
|
} from '@ant-design/icons'
|
|
9
10
|
import {
|
|
10
11
|
Input,
|
|
@@ -55,6 +56,10 @@ function renderAddonBefore (props, realPath) {
|
|
|
55
56
|
className='mg1r'
|
|
56
57
|
/>
|
|
57
58
|
</Tooltip>
|
|
59
|
+
<HomeOutlined
|
|
60
|
+
onClick={() => props.gotoHome(type)}
|
|
61
|
+
className='mg1r'
|
|
62
|
+
/>
|
|
58
63
|
<KeywordFilter {...keywordProps} />
|
|
59
64
|
<AddrBookmark
|
|
60
65
|
store={window.store}
|
|
@@ -28,7 +28,6 @@ export default class FileMode extends React.PureComponent {
|
|
|
28
28
|
loading: false,
|
|
29
29
|
tab: null,
|
|
30
30
|
pid: '',
|
|
31
|
-
sessionId: '',
|
|
32
31
|
size: 0,
|
|
33
32
|
file: {},
|
|
34
33
|
editPermission: false
|
|
@@ -128,7 +127,6 @@ export default class FileMode extends React.PureComponent {
|
|
|
128
127
|
const cmd = `du -sh '${folder}'`
|
|
129
128
|
const r = await runCmd(
|
|
130
129
|
this.state.pid,
|
|
131
|
-
this.state.sessionId,
|
|
132
130
|
cmd
|
|
133
131
|
).catch(window.store.onError)
|
|
134
132
|
return r ? r.split(/\s+/)[0] : 0
|
|
@@ -6,7 +6,8 @@ import React from 'react'
|
|
|
6
6
|
import ExtIcon from './file-icon'
|
|
7
7
|
import {
|
|
8
8
|
FolderOutlined,
|
|
9
|
-
FileOutlined
|
|
9
|
+
FileOutlined,
|
|
10
|
+
ArrowRightOutlined
|
|
10
11
|
} from '@ant-design/icons'
|
|
11
12
|
import classnames from 'classnames'
|
|
12
13
|
import copy from 'json-deep-copy'
|
|
@@ -397,7 +398,6 @@ export default class FileSection extends React.Component {
|
|
|
397
398
|
tab: this.props.tab,
|
|
398
399
|
visible: true,
|
|
399
400
|
pid: this.props.pid,
|
|
400
|
-
sessionId: this.props.sessionId,
|
|
401
401
|
uidTree: this.props[`${type}UidTree`],
|
|
402
402
|
gidTree: this.props[`${type}GidTree`]
|
|
403
403
|
})
|
|
@@ -883,27 +883,55 @@ export default class FileSection extends React.Component {
|
|
|
883
883
|
return !isWin
|
|
884
884
|
}
|
|
885
885
|
|
|
886
|
+
handleContextMenuCapture = (e) => {
|
|
887
|
+
this.contextMenuPosition = {
|
|
888
|
+
clientY: e.clientY
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
itemToMenuFormat = (r) => {
|
|
893
|
+
const { func, text, disabled, icon, subText, requireConfirm } = r
|
|
894
|
+
const IconCom = iconsMap[icon]
|
|
895
|
+
return {
|
|
896
|
+
key: func,
|
|
897
|
+
label: text,
|
|
898
|
+
disabled,
|
|
899
|
+
icon: <IconCom />,
|
|
900
|
+
extra: subText,
|
|
901
|
+
danger: requireConfirm
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
886
905
|
renderContextMenu = () => {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
906
|
+
const items = this.renderContextItems()
|
|
907
|
+
|
|
908
|
+
// Check if we need to split the menu
|
|
909
|
+
if (this.contextMenuPosition) {
|
|
910
|
+
const windowHeight = window.innerHeight
|
|
911
|
+
const { clientY } = this.contextMenuPosition
|
|
912
|
+
const estimatedMenuHeight = items.length * 32 // Approximate height per menu item
|
|
913
|
+
const availableHeight = windowHeight - clientY
|
|
914
|
+
|
|
915
|
+
// If menu would extend beyond window, split into two parts
|
|
916
|
+
if (estimatedMenuHeight > availableHeight && items.length > 6) {
|
|
917
|
+
const firstHalf = items.slice(0, Math.ceil(items.length / 2))
|
|
918
|
+
const secondHalf = items.slice(Math.ceil(items.length / 2))
|
|
919
|
+
|
|
920
|
+
// Create "More..." submenu with second half of items
|
|
921
|
+
const moreSubmenu = {
|
|
922
|
+
key: 'more-submenu',
|
|
923
|
+
label: '…',
|
|
924
|
+
icon: <ArrowRightOutlined />,
|
|
925
|
+
children: secondHalf.map(this.itemToMenuFormat)
|
|
905
926
|
}
|
|
906
|
-
|
|
927
|
+
|
|
928
|
+
// Return first half + "More..." submenu
|
|
929
|
+
return [...firstHalf.map(this.itemToMenuFormat), moreSubmenu]
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Otherwise return normal menu
|
|
934
|
+
return items.map(this.itemToMenuFormat)
|
|
907
935
|
}
|
|
908
936
|
|
|
909
937
|
renderContextItems () {
|
|
@@ -957,7 +985,8 @@ export default class FileSection extends React.Component {
|
|
|
957
985
|
(
|
|
958
986
|
(hasHost && enableSsh !== false && isRemote) ||
|
|
959
987
|
(isLocal && !hasHost)
|
|
960
|
-
)
|
|
988
|
+
) &&
|
|
989
|
+
!this.props.isFtp
|
|
961
990
|
) {
|
|
962
991
|
res.push({
|
|
963
992
|
func: 'gotoFolderInTerminal',
|
|
@@ -1055,7 +1084,10 @@ export default class FileSection extends React.Component {
|
|
|
1055
1084
|
icon: 'ReloadOutlined',
|
|
1056
1085
|
text: e('refresh')
|
|
1057
1086
|
})
|
|
1058
|
-
if (
|
|
1087
|
+
if (
|
|
1088
|
+
this.showModeEdit(type, isRealFile) &&
|
|
1089
|
+
!this.props.isFtp
|
|
1090
|
+
) {
|
|
1059
1091
|
res.push({
|
|
1060
1092
|
func: 'editPermission',
|
|
1061
1093
|
icon: 'LockOutlined',
|
|
@@ -1073,7 +1105,10 @@ export default class FileSection extends React.Component {
|
|
|
1073
1105
|
}
|
|
1074
1106
|
|
|
1075
1107
|
onContextMenu = ({ key }) => {
|
|
1076
|
-
|
|
1108
|
+
// If it's not the submenu itself
|
|
1109
|
+
if (key !== 'more-submenu') {
|
|
1110
|
+
this[key]()
|
|
1111
|
+
}
|
|
1077
1112
|
}
|
|
1078
1113
|
|
|
1079
1114
|
renderEditing (file) {
|
|
@@ -1207,6 +1242,7 @@ export default class FileSection extends React.Component {
|
|
|
1207
1242
|
<div
|
|
1208
1243
|
ref={this.domRef}
|
|
1209
1244
|
{...props}
|
|
1245
|
+
onContextMenu={this.handleContextMenuCapture}
|
|
1210
1246
|
>
|
|
1211
1247
|
<div className='file-bg' />
|
|
1212
1248
|
<div className='file-props-div'>
|
|
@@ -27,8 +27,8 @@ function parseNames (str) {
|
|
|
27
27
|
const linuxListUser = 'cat /etc/passwd'
|
|
28
28
|
const linuxListGroup = 'cat /etc/group'
|
|
29
29
|
|
|
30
|
-
export async function remoteListUsers (pid
|
|
31
|
-
const users = await runCmd(pid,
|
|
30
|
+
export async function remoteListUsers (pid) {
|
|
31
|
+
const users = await runCmd(pid, linuxListUser)
|
|
32
32
|
.catch(log.error)
|
|
33
33
|
if (users) {
|
|
34
34
|
return parseNames(users)
|
|
@@ -36,8 +36,8 @@ export async function remoteListUsers (pid, sessionId) {
|
|
|
36
36
|
return {}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export async function remoteListGroups (pid
|
|
40
|
-
const groups = await runCmd(pid,
|
|
39
|
+
export async function remoteListGroups (pid) {
|
|
40
|
+
const groups = await runCmd(pid, linuxListGroup)
|
|
41
41
|
.catch(log.error)
|
|
42
42
|
if (groups) {
|
|
43
43
|
return parseNames(groups)
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
typeMap, maxSftpHistory, paneMap,
|
|
16
16
|
fileTypeMap,
|
|
17
17
|
terminalSerialType,
|
|
18
|
+
terminalFtpType,
|
|
18
19
|
unexpectedPacketErrorDesc,
|
|
19
20
|
sftpRetryInterval
|
|
20
21
|
} from '../../common/constants'
|
|
@@ -48,10 +49,12 @@ export default class Sftp extends Component {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
componentDidMount () {
|
|
51
|
-
this.id = 'sftp-' + this.props.
|
|
52
|
-
this.tid = 'sftp-' + this.props.tab.id
|
|
52
|
+
this.id = 'sftp-' + this.props.tab.id
|
|
53
53
|
refs.add(this.id, this)
|
|
54
|
-
|
|
54
|
+
if (this.props.isFtp) {
|
|
55
|
+
this.type = 'ftp'
|
|
56
|
+
this.initData()
|
|
57
|
+
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
componentDidUpdate (prevProps, prevState) {
|
|
@@ -89,11 +92,12 @@ export default class Sftp extends Component {
|
|
|
89
92
|
|
|
90
93
|
componentWillUnmount () {
|
|
91
94
|
refs.remove(this.id)
|
|
92
|
-
refs.remove(this.tid)
|
|
93
95
|
this.sftp && this.sftp.destroy()
|
|
94
96
|
this.sftp = null
|
|
95
97
|
clearTimeout(this.timer4)
|
|
96
98
|
this.timer4 = null
|
|
99
|
+
clearTimeout(this.timer5)
|
|
100
|
+
this.timer5 = null
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
directions = [
|
|
@@ -112,15 +116,15 @@ export default class Sftp extends Component {
|
|
|
112
116
|
[`sortProp.${k}`]: window.store.sftpSortSetting[k].prop,
|
|
113
117
|
[`sortDirection.${k}`]: window.store.sftpSortSetting[k].direction,
|
|
114
118
|
[k]: [],
|
|
115
|
-
[`${k}FileTree`]:
|
|
119
|
+
[`${k}FileTree`]: new Map(),
|
|
116
120
|
[`${k}Loading`]: false,
|
|
117
121
|
[`${k}InputFocus`]: false,
|
|
118
122
|
[`${k}ShowHiddenFile`]: def,
|
|
119
123
|
[`${k}Path`]: '',
|
|
120
124
|
[`${k}PathTemp`]: '',
|
|
121
125
|
[`${k}PathHistory`]: [],
|
|
122
|
-
[`${k}GidTree`]:
|
|
123
|
-
[`${k}UidTree`]:
|
|
126
|
+
[`${k}GidTree`]: new Map(),
|
|
127
|
+
[`${k}UidTree`]: new Map(),
|
|
124
128
|
[`${k}Keyword`]: ''
|
|
125
129
|
})
|
|
126
130
|
return prev
|
|
@@ -170,9 +174,11 @@ export default class Sftp extends Component {
|
|
|
170
174
|
}, isEqual)
|
|
171
175
|
|
|
172
176
|
isActive () {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
const { currentBatchTabId, pane, sshSftpSplitView } = this.props
|
|
178
|
+
const { tab } = this.props
|
|
179
|
+
const isFtp = tab.type === terminalFtpType
|
|
180
|
+
|
|
181
|
+
return (currentBatchTabId === tab.id && (pane === paneMap.fileManager || sshSftpSplitView)) || isFtp
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
updateKeyword = (keyword, type) => {
|
|
@@ -191,6 +197,26 @@ export default class Sftp extends Component {
|
|
|
191
197
|
}
|
|
192
198
|
}
|
|
193
199
|
|
|
200
|
+
gotoHome = async (type) => {
|
|
201
|
+
const n = `${type}Path`
|
|
202
|
+
const nt = n + 'Temp'
|
|
203
|
+
let path
|
|
204
|
+
|
|
205
|
+
if (type === typeMap.remote) {
|
|
206
|
+
path = this.props.tab.startDirectoryRemote
|
|
207
|
+
if (!path && this.sftp) {
|
|
208
|
+
path = await this.getPwd(this.props.tab.username)
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
path = this.getLocalHome()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.setState({
|
|
215
|
+
[n]: path,
|
|
216
|
+
[nt]: path
|
|
217
|
+
}, () => this[`${type}List`]())
|
|
218
|
+
}
|
|
219
|
+
|
|
194
220
|
updateCwd = (cwd) => {
|
|
195
221
|
if (!this.state.inited) {
|
|
196
222
|
return
|
|
@@ -401,7 +427,8 @@ export default class Sftp extends Component {
|
|
|
401
427
|
this[type + 'Dom'].onPaste()
|
|
402
428
|
}
|
|
403
429
|
|
|
404
|
-
initData = () => {
|
|
430
|
+
initData = (terminalId) => {
|
|
431
|
+
this.terminalId = terminalId
|
|
405
432
|
if (this.shouldRenderRemote()) {
|
|
406
433
|
this.initRemoteAll()
|
|
407
434
|
}
|
|
@@ -502,12 +529,10 @@ export default class Sftp extends Component {
|
|
|
502
529
|
|
|
503
530
|
remoteListOwner = async () => {
|
|
504
531
|
const remoteUidTree = await owner.remoteListUsers(
|
|
505
|
-
this.props.pid
|
|
506
|
-
this.props.sessionId
|
|
532
|
+
this.props.pid
|
|
507
533
|
)
|
|
508
534
|
const remoteGidTree = await owner.remoteListGroups(
|
|
509
|
-
this.props.pid
|
|
510
|
-
this.props.sessionId
|
|
535
|
+
this.props.pid
|
|
511
536
|
)
|
|
512
537
|
this.setState({
|
|
513
538
|
remoteGidTree,
|
|
@@ -549,7 +574,7 @@ export default class Sftp extends Component {
|
|
|
549
574
|
remotePathReal,
|
|
550
575
|
oldPath
|
|
551
576
|
) => {
|
|
552
|
-
const { tab, sessionOptions
|
|
577
|
+
const { tab, sessionOptions } = this.props
|
|
553
578
|
const { username, startDirectory } = tab
|
|
554
579
|
let remotePath
|
|
555
580
|
const noPathInit = remotePathReal || this.state.remotePath
|
|
@@ -567,7 +592,7 @@ export default class Sftp extends Component {
|
|
|
567
592
|
let sftp = this.sftp
|
|
568
593
|
try {
|
|
569
594
|
if (!this.sftp) {
|
|
570
|
-
sftp = await Client(
|
|
595
|
+
sftp = await Client(this.terminalId, this.type)
|
|
571
596
|
if (!sftp) {
|
|
572
597
|
return
|
|
573
598
|
}
|
|
@@ -580,7 +605,7 @@ export default class Sftp extends Component {
|
|
|
580
605
|
const opts = deepCopy({
|
|
581
606
|
...tab,
|
|
582
607
|
readyTimeout: config.sshReadyTimeout,
|
|
583
|
-
|
|
608
|
+
terminalId: this.terminalId,
|
|
584
609
|
keepaliveInterval: config.keepaliveInterval,
|
|
585
610
|
proxy: getProxy(tab, config),
|
|
586
611
|
...sessionOptions
|
|
@@ -649,11 +674,21 @@ export default class Sftp extends Component {
|
|
|
649
674
|
]).slice(0, maxSftpHistory)
|
|
650
675
|
}
|
|
651
676
|
this.setState(update, () => {
|
|
652
|
-
this.
|
|
677
|
+
if (this.type !== 'ftp') {
|
|
678
|
+
this.updateRemoteList(remote, remotePath, sftp)
|
|
679
|
+
}
|
|
653
680
|
this.props.editTab(tab.id, {
|
|
654
681
|
sftpCreated: true
|
|
655
682
|
})
|
|
656
683
|
})
|
|
684
|
+
this.timer5 = setTimeout(() => {
|
|
685
|
+
if (this.type !== 'ftp') {
|
|
686
|
+
this.updateRemoteList(remote, remotePath, sftp)
|
|
687
|
+
}
|
|
688
|
+
this.props.editTab(tab.id, {
|
|
689
|
+
sftpCreated: true
|
|
690
|
+
})
|
|
691
|
+
}, 1000)
|
|
657
692
|
} catch (e) {
|
|
658
693
|
const update = {
|
|
659
694
|
remoteLoading: false,
|
|
@@ -716,6 +751,12 @@ export default class Sftp extends Component {
|
|
|
716
751
|
this.setState(update)
|
|
717
752
|
}
|
|
718
753
|
|
|
754
|
+
getLocalHome = () => {
|
|
755
|
+
return this.props.tab.startDirectoryLocal ||
|
|
756
|
+
this.props.config.startDirectoryLocal ||
|
|
757
|
+
window.pre.homeOrTmp
|
|
758
|
+
}
|
|
759
|
+
|
|
719
760
|
localList = async (returnList = false, localPathReal, oldPath) => {
|
|
720
761
|
if (!fs) return
|
|
721
762
|
if (!returnList) {
|
|
@@ -730,8 +771,7 @@ export default class Sftp extends Component {
|
|
|
730
771
|
const noPathInit = localPathReal || this.state.localPath
|
|
731
772
|
const localPath = noPathInit ||
|
|
732
773
|
this.getCwdLocal() ||
|
|
733
|
-
this.
|
|
734
|
-
window.pre.homeOrTmp
|
|
774
|
+
this.getLocalHome()
|
|
735
775
|
const locals = await fs.readdirAsync(localPath)
|
|
736
776
|
const local = []
|
|
737
777
|
for (const name of locals) {
|
|
@@ -1015,6 +1055,7 @@ export default class Sftp extends Component {
|
|
|
1015
1055
|
[
|
|
1016
1056
|
'onChange',
|
|
1017
1057
|
'onGoto',
|
|
1058
|
+
'gotoHome',
|
|
1018
1059
|
'onInputFocus',
|
|
1019
1060
|
'onInputBlur',
|
|
1020
1061
|
'toggleShowHiddenFile',
|
|
@@ -37,15 +37,15 @@ export default class TransferModalUI extends Component {
|
|
|
37
37
|
const {
|
|
38
38
|
id,
|
|
39
39
|
title,
|
|
40
|
-
|
|
40
|
+
tabId
|
|
41
41
|
} = k
|
|
42
|
-
if (!p[
|
|
43
|
-
p[
|
|
42
|
+
if (!p[tabId]) {
|
|
43
|
+
p[tabId] = {
|
|
44
44
|
title,
|
|
45
45
|
transfers: []
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
p[
|
|
48
|
+
p[tabId].transfers.push(id)
|
|
49
49
|
return p
|
|
50
50
|
}, {})
|
|
51
51
|
return Object.keys(tree)
|
|
@@ -72,7 +72,7 @@ export default class TransferModalUI extends Component {
|
|
|
72
72
|
const fileTransfers = this.props.fileTransfers
|
|
73
73
|
return filter === 'all'
|
|
74
74
|
? fileTransfers
|
|
75
|
-
: fileTransfers.filter(d => d.
|
|
75
|
+
: fileTransfers.filter(d => d.tabId === filter)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
computePercent = () => {
|
|
@@ -11,37 +11,33 @@ export function createTerm (body) {
|
|
|
11
11
|
})
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function runCmd (pid,
|
|
14
|
+
export function runCmd (pid, cmd) {
|
|
15
15
|
return fetch({
|
|
16
16
|
pid,
|
|
17
|
-
sessionId,
|
|
18
17
|
cmd,
|
|
19
18
|
action: 'run-cmd'
|
|
20
19
|
})
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
export function resizeTerm (pid,
|
|
22
|
+
export function resizeTerm (pid, cols, rows) {
|
|
24
23
|
return fetch({
|
|
25
24
|
pid,
|
|
26
|
-
sessionId,
|
|
27
25
|
cols,
|
|
28
26
|
rows,
|
|
29
27
|
action: 'resize-terminal'
|
|
30
28
|
})
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
export function toggleTerminalLog (pid
|
|
31
|
+
export function toggleTerminalLog (pid) {
|
|
34
32
|
return fetch({
|
|
35
33
|
pid,
|
|
36
|
-
sessionId,
|
|
37
34
|
action: 'toggle-terminal-log'
|
|
38
35
|
})
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
export function toggleTerminalLogTimestamp (pid
|
|
38
|
+
export function toggleTerminalLogTimestamp (pid) {
|
|
42
39
|
return fetch({
|
|
43
40
|
pid,
|
|
44
|
-
sessionId,
|
|
45
41
|
action: 'toggle-terminal-log-timestamp'
|
|
46
42
|
})
|
|
47
43
|
}
|
|
@@ -336,7 +336,6 @@ clear\r`
|
|
|
336
336
|
try {
|
|
337
337
|
const fileData = JSON.parse(fromFile)
|
|
338
338
|
const filePath = resolve(fileData.path, fileData.name)
|
|
339
|
-
console.log('filePath', filePath)
|
|
340
339
|
if (this.isUnsafeFilename(filePath)) {
|
|
341
340
|
message.error(notSafeMsg)
|
|
342
341
|
return
|
|
@@ -1078,11 +1077,10 @@ clear\r`
|
|
|
1078
1077
|
host,
|
|
1079
1078
|
port,
|
|
1080
1079
|
tokenElecterm,
|
|
1081
|
-
id
|
|
1082
|
-
this.props.sessionId
|
|
1080
|
+
id
|
|
1083
1081
|
)
|
|
1084
1082
|
}
|
|
1085
|
-
return `ws://${host}:${port}/terminals/${id}?
|
|
1083
|
+
return `ws://${host}:${port}/terminals/${id}?token=${tokenElecterm}`
|
|
1086
1084
|
}
|
|
1087
1085
|
|
|
1088
1086
|
remoteInit = async (term = this.term) => {
|
|
@@ -1094,7 +1092,7 @@ clear\r`
|
|
|
1094
1092
|
const {
|
|
1095
1093
|
keywords = []
|
|
1096
1094
|
} = config
|
|
1097
|
-
const {
|
|
1095
|
+
const { logName } = this.props
|
|
1098
1096
|
const tab = window.store.applyProfileToTabs(deepCopy(this.props.tab || {}))
|
|
1099
1097
|
const {
|
|
1100
1098
|
srcId, from = 'bookmarks',
|
|
@@ -1128,7 +1126,6 @@ clear\r`
|
|
|
1128
1126
|
'debug'
|
|
1129
1127
|
]),
|
|
1130
1128
|
keepaliveInterval: tab.keepaliveInterval === undefined ? config.keepaliveInterval : tab.keepaliveInterval,
|
|
1131
|
-
sessionId,
|
|
1132
1129
|
tabId: id,
|
|
1133
1130
|
uid: id,
|
|
1134
1131
|
srcTabId: tab.id,
|
|
@@ -1159,7 +1156,7 @@ clear\r`
|
|
|
1159
1156
|
return
|
|
1160
1157
|
}
|
|
1161
1158
|
this.setStatus(statusMap.success)
|
|
1162
|
-
refs.get('sftp-' + id)?.initData()
|
|
1159
|
+
refs.get('sftp-' + id)?.initData(id)
|
|
1163
1160
|
term.pid = id
|
|
1164
1161
|
this.pid = id
|
|
1165
1162
|
const wsUrl = this.buildWsUrl()
|
|
@@ -1300,7 +1297,7 @@ clear\r`
|
|
|
1300
1297
|
|
|
1301
1298
|
onResizeTerminal = size => {
|
|
1302
1299
|
const { cols, rows } = size
|
|
1303
|
-
resizeTerm(this.pid,
|
|
1300
|
+
resizeTerm(this.pid, cols, rows)
|
|
1304
1301
|
}
|
|
1305
1302
|
|
|
1306
1303
|
handleCancel = () => {
|
|
@@ -1309,12 +1306,11 @@ clear\r`
|
|
|
1309
1306
|
}
|
|
1310
1307
|
|
|
1311
1308
|
handleShowInfo = () => {
|
|
1312
|
-
const {
|
|
1309
|
+
const { logName, tab } = this.props
|
|
1313
1310
|
const infoProps = {
|
|
1314
1311
|
logName,
|
|
1315
1312
|
id: tab.id,
|
|
1316
1313
|
pid: tab.id,
|
|
1317
|
-
sessionId,
|
|
1318
1314
|
isRemote: this.isRemote(),
|
|
1319
1315
|
isActive: this.isActiveTerminal()
|
|
1320
1316
|
}
|