Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/src/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export class Application {
}

get defaultTemplate() {
return this.launchMethod.defaultTemplate // need resolved
return this.resolvedDefaultTemplate(this.launchType)
}

get template() {
Expand Down
22 changes: 13 additions & 9 deletions frontend/src/components/ConnectAttribute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { CopyIconButton } from '../buttons/CopyIconButton'
import { ComboButton } from '../buttons/ComboButton'
import { LaunchButton } from '../buttons/LaunchButton'
import { Box } from '@mui/material'
import { copyReady } from '../helpers/connectionHelper'

export const ConnectAttribute = ({ device, service, connection }: IDataOptions) => {
const app = useApplication(service, connection)
const canCopy = copyReady(connection)
const buttons =
connection &&
connection.online &&
Expand All @@ -23,15 +25,17 @@ export const ConnectAttribute = ({ device, service, connection }: IDataOptions)
{buttons && (
<>
&nbsp;
<CopyIconButton
app={app}
color="primary"
icon="clone"
type="regular"
size="sm"
buttonBaseSize="small"
fixedWidth
/>
{canCopy && (
<CopyIconButton
app={app}
color="primary"
icon="clone"
type="regular"
size="sm"
buttonBaseSize="small"
fixedWidth
/>
)}
<LaunchButton
app={app}
connection={connection}
Expand Down
66 changes: 36 additions & 30 deletions frontend/src/components/ConnectionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { makeStyles } from '@mui/styles'
import { replaceHost } from '@common/nameHelper'
import { Application } from '@common/applications'
import { Typography, Tooltip, Collapse, Paper, Box, alpha } from '@mui/material'
import { getEndpoint, isSecureReverseProxy } from '../helpers/connectionHelper'
import { copyReady, getEndpoint, isSecureReverseProxy } from '../helpers/connectionHelper'
import { LaunchQuickSelect } from './LaunchQuickSelect'
import { CopyIconButton } from '../buttons/CopyIconButton'
import { LaunchButton } from '../buttons/LaunchButton'
Expand Down Expand Up @@ -58,6 +58,7 @@ export const ConnectionDetails: React.FC<Props> = ({ showTitle, show, app, conne
let port = connection?.port

const disabled = !(connection?.enabled || session) || !connection?.online
const canCopy = copyReady(connection)
const endpoint = getEndpoint(name, port)
const endpointName = (connection?.public || connection?.connectLink ? 'Public' : 'Local') + ' Endpoint'
const secureReverseProxy = isSecureReverseProxy(app.string)
Expand All @@ -76,6 +77,7 @@ export const ConnectionDetails: React.FC<Props> = ({ showTitle, show, app, conne
variant="h3"
className={css.h3}
onClick={() => {
if (!canCopy) return
buttonRef.current?.click()
setCopied('Copied')
}}
Expand Down Expand Up @@ -193,43 +195,47 @@ export const ConnectionDetails: React.FC<Props> = ({ showTitle, show, app, conne
<Typography variant="h5" color="alwaysWhite.main" sx={{ my: 0.5 }}>
Copy {hover === 'launch' ? '' : hover === 'copy' ? app.contextTitle : hover}
</Typography>
<CopyIconButton
ref={buttonRef}
color="alwaysWhite"
icon="clone"
value={endpoint}
onMouseEnter={() => setHover('endpoint')}
onMouseLeave={() => setHover(undefined)}
onCopy={() => setCopied(undefined)}
/>
{connection?.host && (
{canCopy && (
<>
{connection.port && (
<CopyIconButton
ref={buttonRef}
color="alwaysWhite"
icon="clone"
value={endpoint}
onMouseEnter={() => setHover('endpoint')}
onMouseLeave={() => setHover(undefined)}
onCopy={() => setCopied(undefined)}
/>
{connection?.host && (
<>
{connection.port && (
<>
<CopyIconButton
color="alwaysWhite"
icon="host"
value={connection.host}
onMouseEnter={() => setHover('host')}
onMouseLeave={() => setHover(undefined)}
/>
<CopyIconButton
color="alwaysWhite"
icon="port"
value={connection.port}
onMouseEnter={() => setHover('port')}
onMouseLeave={() => setHover(undefined)}
/>
</>
)}
<CopyIconButton
color="alwaysWhite"
icon="host"
value={connection.host}
onMouseEnter={() => setHover('host')}
onMouseLeave={() => setHover(undefined)}
/>
<CopyIconButton
color="alwaysWhite"
icon="port"
value={connection.port}
onMouseEnter={() => setHover('port')}
icon={app.copyIcon}
app={app}
value={app.sshConfigString}
onMouseEnter={() => setHover('copy')}
onMouseLeave={() => setHover(undefined)}
/>
</>
)}
<CopyIconButton
color="alwaysWhite"
icon={app.copyIcon}
app={app}
value={app.sshConfigString}
onMouseEnter={() => setHover('copy')}
onMouseLeave={() => setHover(undefined)}
/>
</>
)}
</span>
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/EventList/EventHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,23 @@ const hasDateChanged = (lhs?: Date, rhs?: Date) => lhs?.getTime() !== rhs?.getTi
export const EventHeader: React.FC<{ device?: IDevice }> = ({ device }) => {
const dispatch = useDispatch<Dispatch>()
const { fetch, set } = dispatch.logs
const { setLogsFilter } = dispatch.ui

const logLimit = useSelector((state: State) => selectLimit(state, undefined, 'log-limit'))
const activeAccount = useSelector(selectActiveAccountId)
const { events, minDate, selectedDate, eventTypes } = useSelector((state: State) => state.logs)
const logsFilters = useSelector((state: State) => state.ui.logsFilters)

const allowedDays = Math.max(limitDays(logLimit?.value) || 0, 0)
const minDateValue = useMemo(() => retentionStartDate(allowedDays, device), [allowedDays, device?.createdAt])
const logsFilterContext: LogsFilterContext = device ? 'device' : 'root'
const savedEventTypes = activeAccount ? logsFilters[activeAccount]?.[logsFilterContext]?.eventTypes : undefined

// Clear logs and fetch whenever device or account changes
useEffect(() => {
set({
after: undefined,
eventTypes: undefined,
eventTypes: savedEventTypes,
events: { ...events, items: [] },
maxDate: undefined,
selectedDate: undefined,
Expand Down Expand Up @@ -77,6 +81,7 @@ export const EventHeader: React.FC<{ device?: IDevice }> = ({ device }) => {
events: { ...events, items: [] },
planUpgrade: false,
})
setLogsFilter({ accountId: activeAccount, context: logsFilterContext, eventTypes: nextEventTypes })
fetch({ allowedDays, deviceId: device?.id })
}

Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/EventList/EventIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export function EventIcon({ item, loggedInUser }: Props): JSX.Element {
icon = 'arrow-right-to-bracket'
color = 'grayDarker'
break
case 'AUTH_PASSWORD_CHANGE':
case 'AUTH_PASSWORD_RESET':
icon = 'key-skeleton'
color = 'grayDarker'
Expand Down
7 changes: 0 additions & 7 deletions frontend/src/components/EventList/EventMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,6 @@ export function EventMessage({
</>
)
break
case 'AUTH_PASSWORD_CHANGE':
message = (
<>
<b>{actorName}</b> changed {actorAdjective} password
</>
)
break
case 'AUTH_PASSWORD_RESET':
message = (
<>
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/EventList/eventTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ export const eventFilterOptions: EventFilterOption[] = [
},
{
key: 'password-activity',
label: 'Password Activity',
types: ['AUTH_PASSWORD_CHANGE', 'AUTH_PASSWORD_RESET'],
iconTypes: ['AUTH_PASSWORD_CHANGE'],
label: 'Password Reset',
types: ['AUTH_PASSWORD_RESET'],
},
{ key: 'phone-change', label: 'Phone Change', types: ['AUTH_PHONE_CHANGE'] },
{
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/helpers/connectionHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ export function getEndpoint(name?: string, port?: number) {
return name + (port ? ':' + port : '')
}

export function copyReady(connection?: IConnection) {
return !!(connection?.connectLink || connection?.ready)
}

export function sanitizeUrl(name: string) {
return name?.toLowerCase().replace(REGEX_CONNECTION_NAME, '-').replace(REGEX_CONNECTION_TRIM, '')
}
Expand Down
23 changes: 23 additions & 0 deletions frontend/src/models/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const SAVED_ACROSS_LOGOUT = [
'serviceTimeSeries',
'showDesktopNotice',
'mobileWelcome',
'logsFilters',
]

export type UIState = {
Expand Down Expand Up @@ -109,6 +110,7 @@ export type UIState = {
scriptForm?: IFileForm
scriptRunForms: ILookup<IFileForm>
viewAsUser: { id: string; email: string } | null
logsFilters: LogsFiltersByAccount
}

export const defaultState: UIState = {
Expand Down Expand Up @@ -207,6 +209,7 @@ export const defaultState: UIState = {
scriptForm: undefined,
scriptRunForms: {},
viewAsUser: null,
logsFilters: {},
}

export default createModel<RootModel>()({
Expand Down Expand Up @@ -299,6 +302,26 @@ export default createModel<RootModel>()({
defaultSelection[id][key] = value
dispatch.ui.set({ defaultSelection })
},
async setLogsFilter(
{
accountId,
context,
eventTypes,
}: {
accountId?: string
context: LogsFilterContext
eventTypes?: IEventType[]
},
state
) {
const id = accountId || selectActiveAccountId(state)
if (!id) return

const logsFilters = structuredClone(state.ui.logsFilters)
logsFilters[id] = logsFilters[id] || {}
logsFilters[id][context] = { eventTypes }
dispatch.ui.setPersistent({ logsFilters })
},
async setPersistent(params: ILookup<any>, state) {
dispatch.ui.set(params)
Object.keys(params).forEach(key => {
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,16 @@ declare global {
children?: React.ReactNode
}

type LogsFilterContext = 'root' | 'device'

type LogsFilterPreference = {
eventTypes?: IEventType[]
}

type LogsFiltersByAccount = {
[accountId: string]: Partial<Record<LogsFilterContext, LogsFilterPreference>>
}

type ILayout = {
insets: SafeAreaInsets['insets'] & {
topPx: string
Expand Down
1 change: 0 additions & 1 deletion types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,6 @@ declare global {
type IEventType =
| 'AUTH_LOGIN'
| 'AUTH_LOGIN_ATTEMPT'
| 'AUTH_PASSWORD_CHANGE'
| 'AUTH_PASSWORD_RESET'
| 'AUTH_PHONE_CHANGE'
| 'AUTH_MFA_ENABLED'
Expand Down
Loading