Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95ff68ebec | |||
| e2a1bcef98 | |||
| ab7451d7c2 |
@@ -294,6 +294,7 @@ func (s *Store) ListSSHAccessProfiles() ([]models.SSHAccessProfile, error) {
|
||||
var item models.SSHAccessProfile
|
||||
var grantIDsJSON string
|
||||
var serverTagsJSON string
|
||||
var groupTagsJSON string
|
||||
|
||||
rows, err = s.Query(`SELECT
|
||||
p.public_id,
|
||||
@@ -308,6 +309,7 @@ func (s *Store) ListSSHAccessProfiles() ([]models.SSHAccessProfile, error) {
|
||||
COALESCE(g.created_by_subject_name, ''),
|
||||
COALESCE(g.created_at, 0),
|
||||
COALESCE(g.updated_at, 0),
|
||||
COALESCE(g.tags_json, '[]'),
|
||||
p.name,
|
||||
p.description,
|
||||
p.remote_username,
|
||||
@@ -374,6 +376,7 @@ func (s *Store) ListSSHAccessProfiles() ([]models.SSHAccessProfile, error) {
|
||||
&item.ServerGroup.CreatedBySubjectName,
|
||||
&item.ServerGroup.CreatedAt,
|
||||
&item.ServerGroup.UpdatedAt,
|
||||
&groupTagsJSON,
|
||||
&item.Name,
|
||||
&item.Description,
|
||||
&item.RemoteUsername,
|
||||
@@ -425,6 +428,8 @@ func (s *Store) ListSSHAccessProfiles() ([]models.SSHAccessProfile, error) {
|
||||
|
||||
item.Server.Tags, err = decodeStringList(serverTagsJSON)
|
||||
if err != nil { return nil, err }
|
||||
item.ServerGroup.Tags, err = decodeStringList(groupTagsJSON)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
item.Targets, err = s.listSSHAccessProfileTargets(item.ID)
|
||||
if err != nil { return nil, err }
|
||||
@@ -442,6 +447,7 @@ func (s *Store) GetSSHAccessProfile(id string) (models.SSHAccessProfile, error)
|
||||
var err error
|
||||
var grantIDsJSON string
|
||||
var serverTagsJSON string
|
||||
var groupTagsJSON string
|
||||
|
||||
row = s.QueryRow(`SELECT
|
||||
p.public_id,
|
||||
@@ -456,6 +462,7 @@ func (s *Store) GetSSHAccessProfile(id string) (models.SSHAccessProfile, error)
|
||||
COALESCE(g.created_by_subject_name, ''),
|
||||
COALESCE(g.created_at, 0),
|
||||
COALESCE(g.updated_at, 0),
|
||||
COALESCE(g.tags_json, '[]'),
|
||||
p.name,
|
||||
p.description,
|
||||
p.remote_username,
|
||||
@@ -518,6 +525,7 @@ func (s *Store) GetSSHAccessProfile(id string) (models.SSHAccessProfile, error)
|
||||
&item.ServerGroup.CreatedBySubjectName,
|
||||
&item.ServerGroup.CreatedAt,
|
||||
&item.ServerGroup.UpdatedAt,
|
||||
&groupTagsJSON,
|
||||
&item.Name,
|
||||
&item.Description,
|
||||
&item.RemoteUsername,
|
||||
@@ -573,6 +581,10 @@ func (s *Store) GetSSHAccessProfile(id string) (models.SSHAccessProfile, error)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
item.ServerGroup.Tags, err = decodeStringList(groupTagsJSON)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
item.Targets, err = s.listSSHAccessProfileTargets(item.ID)
|
||||
if err != nil {
|
||||
return item, err
|
||||
@@ -920,6 +932,7 @@ func (s *Store) ListSSHAccessProfilesForUser(userID string) ([]models.SSHAccessP
|
||||
var item models.SSHAccessProfile
|
||||
var grantIDsJSON string
|
||||
var serverTagsJSON string
|
||||
var groupTagsJSON string
|
||||
var trimmedUserID string
|
||||
|
||||
trimmedUserID = strings.TrimSpace(userID)
|
||||
@@ -936,6 +949,7 @@ func (s *Store) ListSSHAccessProfilesForUser(userID string) ([]models.SSHAccessP
|
||||
COALESCE(g.created_by_subject_name, ''),
|
||||
COALESCE(g.created_at, 0),
|
||||
COALESCE(g.updated_at, 0),
|
||||
COALESCE(g.tags_json, '[]'),
|
||||
p.name,
|
||||
p.description,
|
||||
p.remote_username,
|
||||
@@ -1025,6 +1039,7 @@ func (s *Store) ListSSHAccessProfilesForUser(userID string) ([]models.SSHAccessP
|
||||
&item.ServerGroup.CreatedBySubjectName,
|
||||
&item.ServerGroup.CreatedAt,
|
||||
&item.ServerGroup.UpdatedAt,
|
||||
&groupTagsJSON,
|
||||
&item.Name,
|
||||
&item.Description,
|
||||
&item.RemoteUsername,
|
||||
@@ -1076,6 +1091,8 @@ func (s *Store) ListSSHAccessProfilesForUser(userID string) ([]models.SSHAccessP
|
||||
|
||||
item.Server.Tags, err = decodeStringList(serverTagsJSON)
|
||||
if err != nil { return nil, err }
|
||||
item.ServerGroup.Tags, err = decodeStringList(groupTagsJSON)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
item.Targets, err = s.listSSHAccessProfileTargets(item.ID)
|
||||
if err != nil { return nil, err }
|
||||
@@ -1094,6 +1111,7 @@ func (s *Store) GetSSHAccessProfileForUser(userID string, id string) (models.SSH
|
||||
var item models.SSHAccessProfile
|
||||
var grantIDsJSON string
|
||||
var serverTagsJSON string
|
||||
var groupTagsJSON string
|
||||
var trimmedUserID string
|
||||
var trimmedID string
|
||||
var err error
|
||||
@@ -1116,6 +1134,7 @@ func (s *Store) GetSSHAccessProfileForUser(userID string, id string) (models.SSH
|
||||
COALESCE(g.created_by_subject_name, ''),
|
||||
COALESCE(g.created_at, 0),
|
||||
COALESCE(g.updated_at, 0),
|
||||
COALESCE(g.tags_json, '[]'),
|
||||
p.name,
|
||||
p.description,
|
||||
p.remote_username,
|
||||
@@ -1200,6 +1219,7 @@ func (s *Store) GetSSHAccessProfileForUser(userID string, id string) (models.SSH
|
||||
&item.ServerGroup.CreatedBySubjectName,
|
||||
&item.ServerGroup.CreatedAt,
|
||||
&item.ServerGroup.UpdatedAt,
|
||||
&groupTagsJSON,
|
||||
&item.Name,
|
||||
&item.Description,
|
||||
&item.RemoteUsername,
|
||||
|
||||
@@ -85,6 +85,10 @@ erDiagram
|
||||
ssh_access_profiles ||--o{ ssh_sessions : starts
|
||||
ssh_servers ||--o{ ssh_sessions : target
|
||||
users ||--o{ ssh_sessions : opens
|
||||
ssh_sessions ||..o{ ssh_file_transfers : source
|
||||
ssh_sessions ||..o{ ssh_file_transfers : target
|
||||
ssh_servers ||..o{ ssh_file_transfers : target_snapshot
|
||||
users ||..o{ ssh_file_transfers : actor
|
||||
```
|
||||
|
||||
### TLS And RPM Mirror State
|
||||
@@ -576,6 +580,7 @@ Important fields:
|
||||
|
||||
- `name`, `description`
|
||||
- `enabled`
|
||||
- `tags_json`: UI/API tags for filtering and display.
|
||||
- `created_by_kind`, `created_by_subject_id`, `created_by_subject_name`
|
||||
- `created_at`, `updated_at`
|
||||
|
||||
@@ -668,6 +673,29 @@ Important fields:
|
||||
|
||||
Transcripts are stored on disk under the configured data directory, not in this table.
|
||||
|
||||
### `ssh_file_transfers`
|
||||
|
||||
Stores SSH file transfer audit history.
|
||||
|
||||
Important fields:
|
||||
|
||||
- `session_id`: internal source session reference when available.
|
||||
- `user_id`, `username`: user who initiated the transfer.
|
||||
- `profile_id`, `server_id`, `server_name`: source profile/server context and server-name snapshot.
|
||||
- `remote_username`: remote account used for the source session.
|
||||
- `operation`: `upload`, `download`, or `copy_to_session`.
|
||||
- `source_session_id`, `target_session_id`: source and destination sessions for inter-session copies.
|
||||
- `target_server_id`, `target_server_name`: destination server context and server-name snapshot for inter-session copies.
|
||||
- `target_dir`: destination directory for uploads/copies.
|
||||
- `paths_json`: transferred source path list. Duplicate input paths are de-duplicated before execution.
|
||||
- `bytes_transferred`: total successfully transferred bytes recorded by the backend.
|
||||
- `status`: `running`, `success`, `partial`, `failed`, or `cancelled`.
|
||||
- `error`: transfer error summary.
|
||||
- `started_at`, `finished_at`, `duration_ms`: lifecycle timing.
|
||||
- `remote_addr`, `user_agent`: requester metadata.
|
||||
|
||||
This table is audit/history data. References are nullable and intentionally tolerate deleted sessions, users, profiles, or servers by keeping string snapshots such as `username`, `server_name`, and `target_server_name`.
|
||||
|
||||
## TLS
|
||||
|
||||
### `tls_listeners`
|
||||
|
||||
+354
-2
@@ -4809,6 +4809,59 @@ paths:
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/admin/ssh/file-transfers:
|
||||
get:
|
||||
tags:
|
||||
- Admin SSH
|
||||
summary: List SSH file transfer audit history
|
||||
operationId: ListSSHFileTransfersAdmin
|
||||
x-codit-handler: ListSSHFileTransfersAdmin
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
- name: offset
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
- name: q
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: status
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: session_id
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: user_id
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SSHFileTransferListResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/admin/ssh/sessions/{id}/transcript:
|
||||
get:
|
||||
tags:
|
||||
@@ -5685,6 +5738,70 @@ paths:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/ssh/access-profiles/{id}/servers/{serverId}:
|
||||
get:
|
||||
tags:
|
||||
- SSH Self Service
|
||||
summary: Get one SSH server available through an access profile
|
||||
operationId: GetSSHAccessProfileServerForSelf
|
||||
x-codit-handler: GetSSHAccessProfileServerForSelf
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: serverId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SSHServer'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/ssh/access-profiles/{id}/server-groups/{serverGroupId}/servers:
|
||||
get:
|
||||
tags:
|
||||
- SSH Self Service
|
||||
summary: List member servers for a server group available through an access profile
|
||||
operationId: ListSSHAccessProfileServerGroupMembersForSelf
|
||||
x-codit-handler: ListSSHAccessProfileServerGroupMembersForSelf
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: serverGroupId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SSHServer'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/ssh/access-profiles/{id}/connect:
|
||||
post:
|
||||
tags:
|
||||
@@ -5783,6 +5900,52 @@ paths:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/ssh/file-transfers:
|
||||
get:
|
||||
tags:
|
||||
- SSH Self Service
|
||||
summary: List SSH file transfer audit history for the current user
|
||||
operationId: ListSSHFileTransfersForSelf
|
||||
x-codit-handler: ListSSHFileTransfersForSelf
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
- name: offset
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
- name: q
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: status
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: session_id
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SSHFileTransferListResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
/api/ssh/stream:
|
||||
get:
|
||||
tags:
|
||||
@@ -10114,6 +10277,18 @@ components:
|
||||
- user
|
||||
owner_user_id:
|
||||
type: string
|
||||
created_by_kind:
|
||||
type: string
|
||||
created_by_subject_id:
|
||||
type: string
|
||||
created_by_subject_name:
|
||||
type: string
|
||||
created_at:
|
||||
type: integer
|
||||
format: int64
|
||||
updated_at:
|
||||
type: integer
|
||||
format: int64
|
||||
editable:
|
||||
type: boolean
|
||||
additionalProperties: true
|
||||
@@ -10155,6 +10330,18 @@ components:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
server_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
servers:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SSHServer'
|
||||
created_by_kind:
|
||||
type: string
|
||||
created_by_subject_id:
|
||||
@@ -10177,6 +10364,10 @@ components:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
SSHServerGroupMemberRequest:
|
||||
type: object
|
||||
@@ -10262,6 +10453,8 @@ components:
|
||||
type: string
|
||||
server_id:
|
||||
type: string
|
||||
server:
|
||||
$ref: '#/components/schemas/SSHServer'
|
||||
server_target_type:
|
||||
type: string
|
||||
enum:
|
||||
@@ -10269,8 +10462,8 @@ components:
|
||||
- group
|
||||
server_group_id:
|
||||
type: string
|
||||
server_group_name:
|
||||
type: string
|
||||
server_group:
|
||||
$ref: '#/components/schemas/SSHServerGroup'
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
@@ -10294,12 +10487,24 @@ components:
|
||||
enum:
|
||||
- admin_shared
|
||||
- user
|
||||
owner_user_id:
|
||||
type: string
|
||||
allow_user_edit:
|
||||
type: boolean
|
||||
ssh_credential_id:
|
||||
type: string
|
||||
ssh_credential_name:
|
||||
type: string
|
||||
auth_public_key:
|
||||
type: string
|
||||
auth_public_key_fingerprint:
|
||||
type: string
|
||||
ssh_user_ca_id:
|
||||
type: string
|
||||
ssh_principal_grant_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
default_cert_valid_seconds:
|
||||
type: integer
|
||||
format: int64
|
||||
@@ -10314,9 +10519,45 @@ components:
|
||||
format: int64
|
||||
enabled:
|
||||
type: boolean
|
||||
created_by_kind:
|
||||
type: string
|
||||
created_by_subject_id:
|
||||
type: string
|
||||
created_by_subject_name:
|
||||
type: string
|
||||
created_at:
|
||||
type: integer
|
||||
format: int64
|
||||
updated_at:
|
||||
type: integer
|
||||
format: int64
|
||||
targets:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SSHAccessProfileTarget'
|
||||
editable:
|
||||
type: boolean
|
||||
additionalProperties: true
|
||||
SSHAccessProfileTarget:
|
||||
type: object
|
||||
properties:
|
||||
profile_id:
|
||||
type: string
|
||||
target_type:
|
||||
type: string
|
||||
enum:
|
||||
- user
|
||||
- group
|
||||
target_id:
|
||||
type: string
|
||||
target_name:
|
||||
type: string
|
||||
target_active:
|
||||
type: boolean
|
||||
created_at:
|
||||
type: integer
|
||||
format: int64
|
||||
additionalProperties: true
|
||||
SSHSessionFileDownloadRequest:
|
||||
type: object
|
||||
properties:
|
||||
@@ -10415,16 +10656,53 @@ components:
|
||||
type: string
|
||||
server_name:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
remote_username:
|
||||
type: string
|
||||
auth_method:
|
||||
type: string
|
||||
enum:
|
||||
- managed_ssh_cert
|
||||
- prompted_password
|
||||
- stored_password
|
||||
- stored_private_key
|
||||
second_factor_mode:
|
||||
type: string
|
||||
enum:
|
||||
- none
|
||||
- prompted_totp
|
||||
host:
|
||||
type: string
|
||||
port:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
host_key_fingerprint:
|
||||
type: string
|
||||
requested_term:
|
||||
type: string
|
||||
requested_cols:
|
||||
type: integer
|
||||
requested_rows:
|
||||
type: integer
|
||||
started_at:
|
||||
type: integer
|
||||
format: int64
|
||||
connected_at:
|
||||
type: integer
|
||||
format: int64
|
||||
ended_at:
|
||||
type: integer
|
||||
format: int64
|
||||
remote_addr:
|
||||
type: string
|
||||
user_agent:
|
||||
type: string
|
||||
error:
|
||||
type: string
|
||||
transcript_available:
|
||||
type: boolean
|
||||
additionalProperties: true
|
||||
@@ -10442,3 +10720,77 @@ components:
|
||||
has_more:
|
||||
type: boolean
|
||||
additionalProperties: true
|
||||
SSHFileTransfer:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
session_id:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
profile_id:
|
||||
type: string
|
||||
server_id:
|
||||
type: string
|
||||
server_name:
|
||||
type: string
|
||||
remote_username:
|
||||
type: string
|
||||
operation:
|
||||
type: string
|
||||
enum:
|
||||
- upload
|
||||
- download
|
||||
- copy_to_session
|
||||
source_session_id:
|
||||
type: string
|
||||
target_session_id:
|
||||
type: string
|
||||
target_server_id:
|
||||
type: string
|
||||
target_server_name:
|
||||
type: string
|
||||
target_dir:
|
||||
type: string
|
||||
paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
bytes_transferred:
|
||||
type: integer
|
||||
format: int64
|
||||
status:
|
||||
type: string
|
||||
error:
|
||||
type: string
|
||||
started_at:
|
||||
type: integer
|
||||
format: int64
|
||||
finished_at:
|
||||
type: integer
|
||||
format: int64
|
||||
duration_ms:
|
||||
type: integer
|
||||
format: int64
|
||||
remote_addr:
|
||||
type: string
|
||||
user_agent:
|
||||
type: string
|
||||
additionalProperties: true
|
||||
SSHFileTransferListResponse:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SSHFileTransfer'
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
has_more:
|
||||
type: boolean
|
||||
additionalProperties: true
|
||||
|
||||
@@ -759,7 +759,7 @@ export default function SSHServersPage() {
|
||||
{item.name} ({item.id})
|
||||
{item.enabled ? null : (<> <Chip size="small" color="error" label="Disabled" /></>)}
|
||||
{(item.tags || []).map((tag: string) => (
|
||||
<Chip key={tag} label={tag} size="small" variant="outlined" sx={{ ml: 0.5 }} />
|
||||
<Chip key={tag} label={tag} size="small" sx={{ ml: 0.5 }} />
|
||||
))}
|
||||
</Typography>
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ function SessionTerminalPanel(props: SessionPanelProps) {
|
||||
const [uploadDialogOpen, setUploadDialogOpen] = useState(false)
|
||||
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false)
|
||||
const [copyDialogOpen, setCopyDialogOpen] = useState(false)
|
||||
const [terminalFocused, setTerminalFocused] = useState(false)
|
||||
const terminalHostRef = useRef<HTMLDivElement | null>(null)
|
||||
const terminalRef = useRef<Terminal | null>(null)
|
||||
const fitAddonRef = useRef<FitAddon | null>(null)
|
||||
@@ -341,8 +342,14 @@ function SessionTerminalPanel(props: SessionPanelProps) {
|
||||
term.loadAddon(unicodeAddon)
|
||||
term.unicode.activeVersion = '11'
|
||||
term.open(terminalHostRef.current)
|
||||
|
||||
// attach the event listeners before calling term.focus()
|
||||
// otherwise, we lost the first focus.
|
||||
term.textarea?.addEventListener('focus', () => { setTerminalFocused(true) })
|
||||
term.textarea?.addEventListener('blur', () => { setTerminalFocused(false) })
|
||||
fitAddon.fit()
|
||||
term.focus()
|
||||
|
||||
term.writeln('Connecting...')
|
||||
terminalRef.current = term
|
||||
fitAddonRef.current = fitAddon
|
||||
@@ -420,6 +427,7 @@ function SessionTerminalPanel(props: SessionPanelProps) {
|
||||
fitAddonRef.current = null
|
||||
unicodeAddonRef.current = null
|
||||
terminalRef.current = null
|
||||
setTerminalFocused(false)
|
||||
unregisterStream(sessionID)
|
||||
term?.dispose()
|
||||
}
|
||||
@@ -628,7 +636,10 @@ function SessionTerminalPanel(props: SessionPanelProps) {
|
||||
flexDirection: 'column',
|
||||
gap: 0.75,
|
||||
overflow: 'hidden',
|
||||
minWidth: 0
|
||||
minWidth: 0,
|
||||
outline: (terminalFocused && openSessionIDs.length > 1) ? (theme) => `3px solid ${theme.palette.primary.main}` : '3px solid transparent',
|
||||
outlineOffset: '-3px',
|
||||
transition: 'outline-color 0.15s ease'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, flexWrap: 'wrap', px: 0.25 }}>
|
||||
|
||||
Reference in New Issue
Block a user