diff --git a/common/src/applications.ts b/common/src/applications.ts
index e23edc974..215a89d2a 100644
--- a/common/src/applications.ts
+++ b/common/src/applications.ts
@@ -152,7 +152,7 @@ export class Application {
}
get defaultTemplate() {
- return this.launchMethod.defaultTemplate // need resolved
+ return this.resolvedDefaultTemplate(this.launchType)
}
get template() {
diff --git a/frontend/src/components/ConnectAttribute.tsx b/frontend/src/components/ConnectAttribute.tsx
index d87240d29..daacb952e 100644
--- a/frontend/src/components/ConnectAttribute.tsx
+++ b/frontend/src/components/ConnectAttribute.tsx
@@ -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 &&
@@ -23,15 +25,17 @@ export const ConnectAttribute = ({ device, service, connection }: IDataOptions)
{buttons && (
<>
-
+ {canCopy && (
+
+ )}
= ({ 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)
@@ -76,6 +77,7 @@ export const ConnectionDetails: React.FC = ({ showTitle, show, app, conne
variant="h3"
className={css.h3}
onClick={() => {
+ if (!canCopy) return
buttonRef.current?.click()
setCopied('Copied')
}}
@@ -193,43 +195,47 @@ export const ConnectionDetails: React.FC = ({ showTitle, show, app, conne
Copy {hover === 'launch' ? '' : hover === 'copy' ? app.contextTitle : hover}
- setHover('endpoint')}
- onMouseLeave={() => setHover(undefined)}
- onCopy={() => setCopied(undefined)}
- />
- {connection?.host && (
+ {canCopy && (
<>
- {connection.port && (
+ setHover('endpoint')}
+ onMouseLeave={() => setHover(undefined)}
+ onCopy={() => setCopied(undefined)}
+ />
+ {connection?.host && (
<>
+ {connection.port && (
+ <>
+ setHover('host')}
+ onMouseLeave={() => setHover(undefined)}
+ />
+ setHover('port')}
+ onMouseLeave={() => setHover(undefined)}
+ />
+ >
+ )}
setHover('host')}
- onMouseLeave={() => setHover(undefined)}
- />
- setHover('port')}
+ icon={app.copyIcon}
+ app={app}
+ value={app.sshConfigString}
+ onMouseEnter={() => setHover('copy')}
onMouseLeave={() => setHover(undefined)}
/>
>
)}
- setHover('copy')}
- onMouseLeave={() => setHover(undefined)}
- />
>
)}
diff --git a/frontend/src/components/EventList/EventHeader.tsx b/frontend/src/components/EventList/EventHeader.tsx
index 806262b42..6d4e08fce 100644
--- a/frontend/src/components/EventList/EventHeader.tsx
+++ b/frontend/src/components/EventList/EventHeader.tsx
@@ -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()
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,
@@ -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 })
}
diff --git a/frontend/src/components/EventList/EventIcon.tsx b/frontend/src/components/EventList/EventIcon.tsx
index c1c8230ef..59946f01e 100644
--- a/frontend/src/components/EventList/EventIcon.tsx
+++ b/frontend/src/components/EventList/EventIcon.tsx
@@ -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'
diff --git a/frontend/src/components/EventList/EventMessage.tsx b/frontend/src/components/EventList/EventMessage.tsx
index 78009e092..7ebc1badb 100644
--- a/frontend/src/components/EventList/EventMessage.tsx
+++ b/frontend/src/components/EventList/EventMessage.tsx
@@ -48,13 +48,6 @@ export function EventMessage({
>
)
break
- case 'AUTH_PASSWORD_CHANGE':
- message = (
- <>
- {actorName} changed {actorAdjective} password
- >
- )
- break
case 'AUTH_PASSWORD_RESET':
message = (
<>
diff --git a/frontend/src/components/EventList/eventTypes.tsx b/frontend/src/components/EventList/eventTypes.tsx
index e558ede25..974cc6aad 100644
--- a/frontend/src/components/EventList/eventTypes.tsx
+++ b/frontend/src/components/EventList/eventTypes.tsx
@@ -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'] },
{
diff --git a/frontend/src/helpers/connectionHelper.ts b/frontend/src/helpers/connectionHelper.ts
index 81f913d19..9c4ab601f 100644
--- a/frontend/src/helpers/connectionHelper.ts
+++ b/frontend/src/helpers/connectionHelper.ts
@@ -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, '')
}
diff --git a/frontend/src/models/ui.ts b/frontend/src/models/ui.ts
index a2b98ce80..0bd010d7d 100644
--- a/frontend/src/models/ui.ts
+++ b/frontend/src/models/ui.ts
@@ -33,6 +33,7 @@ const SAVED_ACROSS_LOGOUT = [
'serviceTimeSeries',
'showDesktopNotice',
'mobileWelcome',
+ 'logsFilters',
]
export type UIState = {
@@ -109,6 +110,7 @@ export type UIState = {
scriptForm?: IFileForm
scriptRunForms: ILookup
viewAsUser: { id: string; email: string } | null
+ logsFilters: LogsFiltersByAccount
}
export const defaultState: UIState = {
@@ -207,6 +209,7 @@ export const defaultState: UIState = {
scriptForm: undefined,
scriptRunForms: {},
viewAsUser: null,
+ logsFilters: {},
}
export default createModel()({
@@ -299,6 +302,26 @@ export default createModel()({
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, state) {
dispatch.ui.set(params)
Object.keys(params).forEach(key => {
diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts
index de9615db2..cb2f040cf 100644
--- a/frontend/src/types.d.ts
+++ b/frontend/src/types.d.ts
@@ -244,6 +244,16 @@ declare global {
children?: React.ReactNode
}
+ type LogsFilterContext = 'root' | 'device'
+
+ type LogsFilterPreference = {
+ eventTypes?: IEventType[]
+ }
+
+ type LogsFiltersByAccount = {
+ [accountId: string]: Partial>
+ }
+
type ILayout = {
insets: SafeAreaInsets['insets'] & {
topPx: string
diff --git a/types.d.ts b/types.d.ts
index f93374a78..453bb2e48 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -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'