diff --git a/src/locales/en.ts b/src/locales/en.ts index e583163..c723011 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -1,3 +1,5 @@ +import { error } from "console" + const en = { editCameraPage: { notFrigateCamera: 'Not frigate camera', @@ -14,6 +16,20 @@ const en = { saveAndRestart: 'Save & Restart', editorNotExist: 'Editor does not exists', }, + settingsPage: { + oidpClientId: 'OIDP Client ID', + oidpClientIdPH: 'frigate-cli', + clientSecret: 'OIDP Client secret', + clientSecretPH: 'super secret from OIDP server client', + clientUsername: 'OIDP Client username', + clientUsernamePH: 'frigate-admin@yourmail.com', + clientPassword: 'OIDP Client password', + clientPasswordPH: 'User password on OIDP server', + realmUrl: 'OIDP realm URL path', + realmUrlPH: 'https://your.oidp.server.com/realms/frigate-realm', + adminRole: 'Select admin role', + birdseyeRole: 'Select birds eye role user', + }, systemPage: { cameraStats: 'Cameras stats', storageStats: 'Storages stats', @@ -110,6 +126,9 @@ const en = { goToMainPage: "Return to main page", retry: "Retry", youCanRetryOrGoToMain: "You can retry or return to the main page", + successfully: "Sucessfully", + successfullySaved: "Sucessfully saved", + error: "Error", errors: { emptyResponse: 'Empty response', somthingGoesWrong: "Something went wrong", diff --git a/src/locales/ru.ts b/src/locales/ru.ts index aade65b..0e59497 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -14,6 +14,19 @@ const ru = { saveAndRestart: 'Сохранить & Перезагрузить', editorNotExist: 'Редактор не найден', }, + settingsPage: { + oidpClientId: 'OIDP ID клиента', + oidpClientIdPH: 'frigate-cli', + clientSecret: 'OIDP секрет клиента', + clientSecretPH: 'супер секретный пароль от клиента OIDP сервера', + clientUsername: 'OIDP имя пользователя', + clientUsernamePH: 'frigate-admin@yourmail.com', + clientPassword: 'OIDP пароль', + clientPasswordPH: 'Пароль пользователя на OIDP сервере', + realmUrl: 'OIDP realm URL путь', + realmUrlPH: 'https://your.oidp.server.com/realms/frigate-realm', + adminRole: 'Выбери роль администратора', + birdseyeRole: 'Выбери роль birdseye пользователя',}, systemPage: { cameraStats: 'Статистика Камер', storageStats: 'Статистика Хранения', @@ -110,6 +123,9 @@ const ru = { goToMainPage: "Вернуться на главную", retry: "Повторить", youCanRetryOrGoToMain: "Вы можете повторить или вернуться на главную", + successfully: "Успешно", + successfullySaved: "Успешно сохранено", + error: "Ошибка", errors: { emptyResponse: 'Пустой ответ', somthingGoesWrong: "Что-то пошло не так", diff --git a/src/pages/AccessSettingsPage.tsx b/src/pages/AccessSettingsPage.tsx index 1e6f8d4..f13566f 100644 --- a/src/pages/AccessSettingsPage.tsx +++ b/src/pages/AccessSettingsPage.tsx @@ -1,4 +1,4 @@ -import { Flex, Group, Select, Text } from '@mantine/core'; +import { Flex, Group, Text } from '@mantine/core'; import { useMediaQuery } from '@mantine/hooks'; import { useQuery } from '@tanstack/react-query'; import { observer } from 'mobx-react-lite'; @@ -8,13 +8,12 @@ import { Context } from '..'; import { useAdminRole } from '../hooks/useAdminRole'; import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api'; import CamerasTransferList from '../shared/components/CamerasTransferList'; -import { OneSelectItem } from '../shared/components/filters/OneSelectFilter'; +import RoleSelectFilter from '../shared/components/filters/RoleSelectFilter'; import CenterLoader from '../shared/components/loaders/CenterLoader'; import { dimensions } from '../shared/dimensions/dimensions'; import { isProduction } from '../shared/env.const'; import Forbidden from './403'; import RetryErrorPage from './RetryErrorPage'; -import RoleSelectFilter from '../shared/components/filters/RoleSelectFilter'; const AccessSettings = () => { const { t } = useTranslation() @@ -42,7 +41,6 @@ const AccessSettings = () => { if (isPending || adminLoading) return if (isError || !data) return if (!isAdmin) return - const rolesSelect: OneSelectItem[] = data.map(role => ({ value: role.id, label: role.name })) const handleSelectRole = (value: string) => { setRoleId(value) diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index a2318ad..c6b8780 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -1,35 +1,25 @@ -import { Button, Flex, Space } from '@mantine/core'; +import { Flex, Space } from '@mantine/core'; import { useMediaQuery } from '@mantine/hooks'; -import { - useMutation, - useQuery, - useQueryClient, -} from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import { observer } from 'mobx-react-lite'; -import React, { useContext, useEffect, useRef, useState } from 'react'; +import { useContext, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Context } from '..'; import { useAdminRole } from '../hooks/useAdminRole'; -import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api'; -import { GetConfig, PutConfig } from '../services/frigate.proxy/frigate.schema'; -import { FloatingLabelInput } from '../shared/components/inputs/FloatingLabelInput'; +import { frigateApi } from '../services/frigate.proxy/frigate.api'; +import { GetRole } from '../services/frigate.proxy/frigate.schema'; import CenterLoader from '../shared/components/loaders/CenterLoader'; import { dimensions } from '../shared/dimensions/dimensions'; -import { isProduction } from '../shared/env.const'; +import OIDPSettingsForm from '../widgets/OIDPSettingsForm'; +import RolesSettingsForm from '../widgets/RolesSettingsForm'; import Forbidden from './403'; -import RetryErrorPage from './RetryErrorPage'; -import { notifications } from '@mantine/notifications'; -import { v4 } from 'uuid'; -import { IconAlertCircle, IconCircleCheck } from '@tabler/icons-react'; const SettingsPage = () => { const { t } = useTranslation() const executed = useRef(false) - const queryClient = useQueryClient() - const { isPending: configPending, error: configError, data, refetch } = useQuery({ - queryKey: [frigateQueryKeys.getConfig], - queryFn: frigateApi.getConfig, - }) + + const [showRoles, setShowRoles] = useState(false) + const [allRoles, setAllRoles] = useState() const { sideBarsStore } = useContext(Context) useEffect(() => { @@ -43,127 +33,40 @@ const SettingsPage = () => { const { isAdmin, isLoading: adminLoading } = useAdminRole() - - const ecryptedTemplate = 'encrypted value is exist' - const mapEncryptedToView = (data: GetConfig[] | undefined): GetConfig[] | undefined => { - return data?.map(item => { - const { value, encrypted, ...rest } = item - if (encrypted && value) return { value: ecryptedTemplate, encrypted, ...rest } - return item - }) - } - - const [configs, setConfigs] = useState(data) const isMobile = useMediaQuery(dimensions.mobileSize) const mutation = useMutation({ - mutationFn: (config: PutConfig[]) => frigateApi.putConfig(config).catch(error => { - if (error.response && error.response.data) { - return Promise.reject(error.response.data) - } - return Promise.reject(error) - }), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [frigateQueryKeys.getConfig] }) - notifications.show({ - id: v4(), - withCloseButton: true, - autoClose: 5000, - title: `Sucessfully`, - message: `Sucessfully saved`, - color: 'green', - icon: - }) - }, - onError: (e) => { - notifications.show({ - id: e.message, - withCloseButton: true, - autoClose: false, - title: "Error", - message: e.message, - color: 'red', - icon: , - }) + mutationFn: frigateApi.getRoles, + onSuccess: (data) => { + setAllRoles(data) } }) - const handleDiscard = () => { - if (!isProduction) console.log('Discard changes') - refetch() - setConfigs(data ? mapEncryptedToView(data) : []) - } - useEffect(() => { - if (!isProduction) console.log('data changed') - setConfigs(mapEncryptedToView(data)) - }, [data]) - - useEffect(() => { - if (!isProduction) console.log('configs changed') - }, [configs]) - - const handleSubmit = (event: React.FormEvent) => { - event.preventDefault() - const formData = new FormData(event.currentTarget); - const formDataObj: any = Array.from(formData.entries()).reduce((acc, [key, value]) => ({ - ...acc, - [key]: value, - }), {}); - - const configsToUpdate = Object.keys(formDataObj).map(key => { - const value = formDataObj[key] - const currData = data?.find(val => val.key === key) - const notChangedEncrypted = value === ecryptedTemplate - if (currData && currData.encrypted && notChangedEncrypted) { - return { - key, - value: currData.value - } - } - return { - key, - value: value, - } - }); - if (!isProduction) console.log('configsToUpdate', configsToUpdate) - mutation.mutate(configsToUpdate); + const handleOIDPConfigValid = (valid: boolean) => { + setShowRoles(valid) + mutation.mutate() } if (!isAdmin) return - if (configPending || adminLoading) return - if (configError) return + if (adminLoading) return return ( {!isMobile ? < Space w='20%' /> - : <> + : null } -
- {!configs ? <> - : - configs.map((config) => ( - - ))} - - - - - - + + { + showRoles && allRoles && allRoles.length > 0 ? + + : null + }
{!isMobile ? - : <> + : null }
); diff --git a/src/services/frigate.proxy/frigate.api.ts b/src/services/frigate.proxy/frigate.api.ts index 6b8ec98..7a3f03f 100644 --- a/src/services/frigate.proxy/frigate.api.ts +++ b/src/services/frigate.proxy/frigate.api.ts @@ -3,7 +3,9 @@ import { proxyURL } from "../../shared/env.const" import { GetConfig, DeleteFrigateHost, GetFrigateHost, PutConfig, PutFrigateHost, GetCameraWHostWConfig, GetRole, - GetRoleWCameras, GetExportedFile, recordingSchema + GetRoleWCameras, GetExportedFile, recordingSchema, + oidpConfig, + OIDPConfig } from "./frigate.schema"; import { FrigateConfig } from "../../types/frigateConfig"; import { RecordSummary } from "../../types/record"; @@ -31,8 +33,12 @@ instanceApi.interceptors.request.use( ); export const frigateApi = { - getConfig: () => instanceApi.get('apiv1/config').then(res => res.data), - putConfig: (config: PutConfig[]) => instanceApi.put('apiv1/config', config).then(res => res.data), + getAllConfig: () => instanceApi.get('apiv1/config').then(res => res.data), + getConfig: (key: string) => instanceApi.get(`apiv1/config/${key}`).then(res => res.data), + getOIDPConfig: () => instanceApi.get('apiv1/config/oidp').then(res => res.data), + putConfigs: (config: PutConfig[]) => instanceApi.put('apiv1/config', config).then(res => res.data), + putOIDPConfig: (config: OIDPConfig) => instanceApi.put('apiv1/config/oidp', config).then(res => res.data), + putOIDPConfigTest: (config: OIDPConfig) => instanceApi.put('apiv1/config/oidp/test', config).then(res => res.data), getHosts: () => instanceApi.get('apiv1/frigate-hosts').then(res => { return res.data }), @@ -212,7 +218,9 @@ export const mapHostToHostname = (host?: GetFrigateHost): string | undefined => } export const frigateQueryKeys = { - getConfig: 'config', + getAllConfig: 'getAllConfig', + getConfig: 'getConfig', + getOIDPConfig: 'OIDPconfig', getFrigateHosts: 'frigate-hosts', getFrigateHostsConfigs: 'frigate-hosts-configs', getFrigateHost: 'frigate-host', diff --git a/src/services/frigate.proxy/frigate.schema.ts b/src/services/frigate.proxy/frigate.schema.ts index d19fce7..ce5860a 100644 --- a/src/services/frigate.proxy/frigate.schema.ts +++ b/src/services/frigate.proxy/frigate.schema.ts @@ -6,6 +6,14 @@ export const putConfigSchema = z.object({ value: z.string(), }) +export const oidpConfig = z.object({ + clientId: z.string(), + clientSecret: z.string(), + clientUsername: z.string(), + clientPassword: z.string(), + clientURL: z.string(), +}) + export const getConfigSchema = z.object({ key: z.string(), value: z.string(), @@ -115,6 +123,7 @@ export const recordingSchema = z.object({ export type GetConfig = z.infer export type PutConfig = z.infer +export type OIDPConfig = z.infer export type GetFrigateHost = z.infer // export type GetFrigateHostWithCameras = z.infer export type GetFrigateHostWConfig = GetFrigateHost & { config: FrigateConfig } diff --git a/src/shared/components/filters/RoleSelectFilter.tsx b/src/shared/components/filters/RoleSelectFilter.tsx index 7ccf4c1..2c94eac 100644 --- a/src/shared/components/filters/RoleSelectFilter.tsx +++ b/src/shared/components/filters/RoleSelectFilter.tsx @@ -33,9 +33,9 @@ const RoleSelectFilter: React.FC = ({ return (