- added endpoints for dns and other

This commit is contained in:
2025-11-04 17:04:19 +00:00
parent 647fd201a3
commit 861f2dd31d
9 changed files with 261 additions and 84 deletions

View File

@@ -4,7 +4,7 @@ import BearerTypeFields from "./BearerTypeFields";
const BearerTypeCard = () => { const BearerTypeCard = () => {
return ( return (
<Card className="p-4"> <Card className="p-4 h-60">
<CardHeader title="Bearer Type" /> <CardHeader title="Bearer Type" />
<BearerTypeFields /> <BearerTypeFields />
</Card> </Card>

View File

@@ -37,7 +37,7 @@ const ChannelCard = ({ touched, isSubmitting, isBof2ConstantsLoading, isDispatch
}, [backOfficeQuery.isSuccess, mapped, setFieldValue]); }, [backOfficeQuery.isSuccess, mapped, setFieldValue]);
return ( return (
<Card className="p-4"> <Card className="p-4 overflow-y-auto ">
<CardHeader title={`Channel (${values?.format})`} /> <CardHeader title={`Channel (${values?.format})`} />
{!isBof2ConstantsLoading && !isDispatcherLoading && !isBackOfficeQueryLoading ? ( {!isBof2ConstantsLoading && !isDispatcherLoading && !isBackOfficeQueryLoading ? (
<ChannelFields <ChannelFields

View File

@@ -118,59 +118,88 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
{format?.toLowerCase() === "bof2" && ( {format?.toLowerCase() === "bof2" && (
<> <>
<div className="border-b border-gray-500 my-3"> <div className="space-y-3">
<h2 className="font-bold">{values.format} Constants</h2> <div className="border-b border-gray-500 my-3">
<h2 className="font-bold">{values.format} Constants</h2>
</div>
<FormGroup>
<label htmlFor="FFID">Feed ID / Force ID</label>
<Field
name={"FFID"}
type="text"
id="FFID"
placeholder="ABC123"
className={`p-1.5 border ${
errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 "
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
<FormGroup>
<label htmlFor="SCID">Source ID / Camera ID</label>
<Field
name={"SCID"}
type="text"
id="SCID"
placeholder="DEF345"
className={`p-1.5 border ${
errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 "
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
<FormGroup>
<label htmlFor="timestampSource">Timestamp Source</label>
<Field
name={"timestampSource"}
as="select"
id="timestampSource"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value={"UTC"}>UTC</option>
<option value={"local"}>Local</option>
</Field>
</FormGroup>
<FormGroup>
<label htmlFor="GPSFormat">GPS Format</label>
<Field
name={"GPSFormat"}
as="select"
id="GPSFormat"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value={"Minutes"}>Minutes</option>
<option value={"Decimal Degrees"}>Decimal degrees</option>
</Field>
</FormGroup>
</div>
<div className="space-y-3">
<div className="border-b border-gray-500 my-3">
<h2 className="font-bold">{values.format} Lane ID Config</h2>
</div>
<FormGroup>
<label htmlFor="LID1">Lane ID 1</label>
<Field
name={"LID1"}
type="text"
id="LID1"
placeholder="10"
className={`p-1.5 border ${
errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 "
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
<FormGroup>
<label htmlFor="LID2">Lane ID 2</label>
<Field
name={"LID2"}
type="text"
id="LID2"
placeholder="20"
className={`p-1.5 border ${
errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 "
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
</div> </div>
<FormGroup>
<label htmlFor="FFID">Feed ID / Force ID</label>
<Field
name={"FFID"}
type="text"
id="FFID"
placeholder="ABC123"
className={`p-1.5 border ${
errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 "
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
<FormGroup>
<label htmlFor="SCID">Source ID / Camera ID</label>
<Field
name={"SCID"}
type="text"
id="SCID"
placeholder="DEF345"
className={`p-1.5 border ${
errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 "
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
<FormGroup>
<label htmlFor="timestampSource">Timestamp Source</label>
<Field
name={"timestampSource"}
as="select"
id="timestampSource"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value={"UTC"}>UTC</option>
<option value={"local"}>Local</option>
</Field>
</FormGroup>
<FormGroup>
<label htmlFor="GPSFormat">GPS Format</label>
<Field
name={"GPSFormat"}
as="select"
id="GPSFormat"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value={"Minutes"}>Minutes</option>
<option value={"Decimal Degrees"}>Decimal degrees</option>
</Field>
</FormGroup>
</> </>
)} )}
</div> </div>

View File

@@ -7,6 +7,7 @@ import type {
InitialValuesForm, InitialValuesForm,
InitialValuesFormErrors, InitialValuesFormErrors,
OptionalBOF2Constants, OptionalBOF2Constants,
OptionalBOF2LaneIDs,
} from "../../../types/types"; } from "../../../types/types";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig"; import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig";
@@ -14,7 +15,8 @@ import { useFormVaidate } from "../../../hooks/useFormValidate";
const SettingForms = () => { const SettingForms = () => {
const qc = useQueryClient(); const qc = useQueryClient();
const { dispatcherQuery, dispatcherMutation, backOfficeDispatcherMutation } = useCameraOutput(); const { dispatcherQuery, dispatcherMutation, backOfficeDispatcherMutation, bof2LandMutation, laneIdQuery } =
useCameraOutput();
const { backOfficeMutation } = useUpdateBackOfficeConfig(); const { backOfficeMutation } = useUpdateBackOfficeConfig();
const { bof2ConstantsQuery } = useGetDispatcherConfig(); const { bof2ConstantsQuery } = useGetDispatcherConfig();
const { validateMutation } = useFormVaidate(); const { validateMutation } = useFormVaidate();
@@ -22,6 +24,9 @@ const SettingForms = () => {
const format = dispatcherQuery?.data?.propFormat?.value; const format = dispatcherQuery?.data?.propFormat?.value;
const enabled = dispatcherQuery?.data?.propEnabled?.value; const enabled = dispatcherQuery?.data?.propEnabled?.value;
const LID1 = laneIdQuery?.data?.propLaneID1?.value;
const LID2 = laneIdQuery?.data?.propLaneID2?.value;
const FFID = bof2ConstantsQuery?.data?.propFeedIdentifier?.value; const FFID = bof2ConstantsQuery?.data?.propFeedIdentifier?.value;
const SCID = bof2ConstantsQuery?.data?.propSourceIdentifier?.value; const SCID = bof2ConstantsQuery?.data?.propSourceIdentifier?.value;
const GPSFormat = bof2ConstantsQuery?.data?.propGpsFormat?.value; const GPSFormat = bof2ConstantsQuery?.data?.propGpsFormat?.value;
@@ -30,7 +35,7 @@ const SettingForms = () => {
const isDispatcherLoading = dispatcherQuery?.isFetching; const isDispatcherLoading = dispatcherQuery?.isFetching;
const isBof2ConstantsLoading = bof2ConstantsQuery?.isFetching; const isBof2ConstantsLoading = bof2ConstantsQuery?.isFetching;
const initialValues: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants = { const initialValues: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants & OptionalBOF2LaneIDs = {
format: format ?? "JSON", format: format ?? "JSON",
enabled: enabled === "true", enabled: enabled === "true",
backOfficeURL: "", backOfficeURL: "",
@@ -44,6 +49,10 @@ const SettingForms = () => {
SCID: SCID ?? "", SCID: SCID ?? "",
timestampSource: timestampSource ?? "", timestampSource: timestampSource ?? "",
GPSFormat: GPSFormat ?? "", GPSFormat: GPSFormat ?? "",
//BOF2 - optional Lane IDs
LID1: LID1 ?? "",
LID2: LID2 ?? "",
}; };
const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => { const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => {
@@ -65,7 +74,9 @@ const SettingForms = () => {
return errors; return errors;
}; };
const handleSubmit = async (values: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants) => { const handleSubmit = async (
values: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants & OptionalBOF2LaneIDs
) => {
const validResponse = await validateMutation.mutateAsync(values); const validResponse = await validateMutation.mutateAsync(values);
const dispatcherData = { const dispatcherData = {
@@ -88,6 +99,13 @@ const SettingForms = () => {
timestampSource: values.timestampSource, timestampSource: values.timestampSource,
GPSFormat: values.GPSFormat, GPSFormat: values.GPSFormat,
}; };
const bof2LaneData: OptionalBOF2LaneIDs = {
LID1: values.LID1,
LID2: values.LID2,
LID3: values.LID3,
};
await bof2LandMutation.mutateAsync(bof2LaneData);
await backOfficeDispatcherMutation.mutateAsync(bof2ConstantsData); await backOfficeDispatcherMutation.mutateAsync(bof2ConstantsData);
} }
} else { } else {

View File

@@ -2,6 +2,8 @@ import { toast } from "sonner";
import type { SystemValues } from "../../../types/types"; import type { SystemValues } from "../../../types/types";
import { CAM_BASE } from "../../../utils/config"; import { CAM_BASE } from "../../../utils/config";
const camBase = import.meta.env.MODE !== "development" ? CAM_BASE : "";
export async function handleSystemSave(values: SystemValues) { export async function handleSystemSave(values: SystemValues) {
const payload = { const payload = {
// Build JSON // Build JSON
@@ -18,7 +20,7 @@ export async function handleSystemSave(values: SystemValues) {
}; };
try { try {
const response = await fetch(`${CAM_BASE}/api/update-config`, { const response = await fetch(`${camBase}/api/update-config`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -29,11 +31,7 @@ export async function handleSystemSave(values: SystemValues) {
if (!response.ok) { if (!response.ok) {
const text = await response.text().catch(() => ""); const text = await response.text().catch(() => "");
throw new Error( throw new Error(`HTTP ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`);
`HTTP ${response.status} ${response.statusText}${
text ? ` - ${text}` : ""
}`
);
} }
} catch (err) { } catch (err) {
if (err instanceof Error) { if (err instanceof Error) {
@@ -47,7 +45,7 @@ export async function handleSystemSave(values: SystemValues) {
} }
export async function handleSystemRecall() { export async function handleSystemRecall() {
const url = `${CAM_BASE}/api/fetch-config?id=GLOBAL--Device`; const url = `${camBase}/api/fetch-config?id=GLOBAL--Device`;
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 7000); const timeoutId = setTimeout(() => controller.abort(), 7000);
@@ -61,11 +59,7 @@ export async function handleSystemRecall() {
if (!response.ok) { if (!response.ok) {
const text = await response.text().catch(() => ""); const text = await response.text().catch(() => "");
throw new Error( throw new Error(`HTTP ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`);
`HTTP ${response.status} ${response.statusText}${
text ? ` - ${text}` : ""
}`
);
} }
const data = await response.json(); const data = await response.json();
@@ -76,9 +70,7 @@ export async function handleSystemRecall() {
const sntpIntervalRaw = data?.propSNTPIntervalMinutes?.value; const sntpIntervalRaw = data?.propSNTPIntervalMinutes?.value;
let sntpInterval = let sntpInterval =
typeof sntpIntervalRaw === "number" typeof sntpIntervalRaw === "number" ? sntpIntervalRaw : Number.parseInt(String(sntpIntervalRaw).trim(), 10);
? sntpIntervalRaw
: Number.parseInt(String(sntpIntervalRaw).trim(), 10);
if (!Number.isFinite(sntpInterval)) { if (!Number.isFinite(sntpInterval)) {
sntpInterval = 60; sntpInterval = 60;

View File

@@ -4,21 +4,30 @@ import { useReboots } from "../../../hooks/useReboots";
import { timezones } from "./timezones"; import { timezones } from "./timezones";
import SystemFileUpload from "./SystemFileUpload"; import SystemFileUpload from "./SystemFileUpload";
import type { SystemValues, SystemValuesErrors } from "../../../types/types"; import type { SystemValues, SystemValuesErrors } from "../../../types/types";
import { useSystemConfig } from "../../../hooks/useSystemConfig"; import { useDNSSettings, useSystemConfig } from "../../../hooks/useSystemConfig";
const SystemConfigFields = () => { const SystemConfigFields = () => {
const { saveSystemSettings, systemSettingsData, saveSystemSettingsLoading } = useSystemConfig(); const { saveSystemSettings, systemSettingsData, saveSystemSettingsLoading } = useSystemConfig();
const { softRebootMutation, hardRebootMutation } = useReboots(); const { softRebootMutation, hardRebootMutation } = useReboots();
const { dnsQuery, dnsMutation } = useDNSSettings();
console.log(dnsQuery?.data);
const dnsPrimary = dnsQuery?.data?.propNameServerPrimary?.value;
const dnsSecondary = dnsQuery?.data?.propNameServerSecondary?.value;
const initialvalues: SystemValues = { const initialvalues: SystemValues = {
deviceName: systemSettingsData?.deviceName ?? "", deviceName: systemSettingsData?.deviceName ?? "",
timeZone: systemSettingsData?.timeZone ?? "", timeZone: systemSettingsData?.timeZone ?? "",
sntpServer: systemSettingsData?.sntpServer ?? "", sntpServer: systemSettingsData?.sntpServer ?? "",
sntpInterval: systemSettingsData?.sntpInterval ?? 60, sntpInterval: systemSettingsData?.sntpInterval ?? 60,
serverPrimary: dnsPrimary ?? "",
serverSecondary: dnsSecondary ?? "",
softwareUpdate: null, softwareUpdate: null,
}; };
const handleSubmit = (values: SystemValues) => saveSystemSettings(values); const handleSubmit = async (values: SystemValues) => {
saveSystemSettings(values);
await dnsMutation.mutateAsync(values);
};
const validateValues = (values: SystemValues) => { const validateValues = (values: SystemValues) => {
const errors: SystemValuesErrors = {}; const errors: SystemValuesErrors = {};
@@ -102,6 +111,34 @@ const SystemConfigFields = () => {
autoComplete="off" autoComplete="off"
/> />
</FormGroup> </FormGroup>
<FormGroup>
<label htmlFor="serverPrimary" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Server Primary
</label>
<Field
id="serverPrimary"
name="serverPrimary"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter DNS primary address"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<label htmlFor="serverSecondary" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Server Secondary
</label>
<Field
id="serverSecondary"
name="serverSecondary"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter DNS secondary address"
autoComplete="off"
/>
</FormGroup>
<FormGroup> <FormGroup>
<label htmlFor="sntpInterval" className="font-medium whitespace-nowrap md:w-1/2 text-left"> <label htmlFor="sntpInterval" className="font-medium whitespace-nowrap md:w-1/2 text-left">
SNTP Interval minutes SNTP Interval minutes

View File

@@ -2,7 +2,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
import { CAM_BASE } from "../utils/config"; import { CAM_BASE } from "../utils/config";
import { useEffect } from "react"; import { useEffect } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import type { BearerTypeFieldType, OptionalBOF2Constants } from "../types/types"; import type { BearerTypeFieldType, OptionalBOF2Constants, OptionalBOF2LaneIDs } from "../types/types";
const getDispatcherConfig = async () => { const getDispatcherConfig = async () => {
const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher`); const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher`);
@@ -63,11 +63,40 @@ const updateBackOfficeDispatcher = async (data: OptionalBOF2Constants) => {
}; };
const getBof2DispatcherData = async () => { const getBof2DispatcherData = async () => {
const response = await fetch(`http://100.118.196.113:8080/api/fetch-config?id=Dispatcher-bof2-constants`); const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher-bof2-constants`);
if (!response.ok) throw new Error("Cannot get BOF2 dispatcher config"); if (!response.ok) throw new Error("Cannot get BOF2 dispatcher config");
return response.json(); return response.json();
}; };
const updateBOF2LaneId = async (data: OptionalBOF2LaneIDs) => {
const bof2LaneIds = {
id: "SightingAmmendA-lane-ids",
fields: [
{
property: "propLaneID1",
value: data?.LID1,
},
{
property: "propLaneID2",
value: data?.LID2,
},
],
};
const response = await fetch(`${CAM_BASE}/api/update-config?id=SightingAmmendA-lane-ids`, {
method: "post",
body: JSON.stringify(bof2LaneIds),
});
if (!response.ok) throw new Error("cannot send to lane IDs");
return response.json();
};
const getBOF2LaneId = async () => {
const response = await fetch(`${CAM_BASE}/api/fetch-config?id=SightingAmmendA-lane-ids`);
if (!response.ok) throw new Error("Canot get Lane Ids");
return response.json();
};
export const useCameraOutput = () => { export const useCameraOutput = () => {
const dispatcherQuery = useQuery({ const dispatcherQuery = useQuery({
queryKey: ["dispatcher"], queryKey: ["dispatcher"],
@@ -95,6 +124,16 @@ export const useCameraOutput = () => {
}, },
}); });
const bof2LandMutation = useMutation({
mutationKey: ["updateBOF2LaneId"],
mutationFn: updateBOF2LaneId,
});
const laneIdQuery = useQuery({
queryKey: ["getBOF2LaneId"],
queryFn: getBOF2LaneId,
});
useEffect(() => { useEffect(() => {
if (dispatcherQuery.isError) toast.error(dispatcherQuery.error.message); if (dispatcherQuery.isError) toast.error(dispatcherQuery.error.message);
}, [dispatcherQuery?.error?.message, dispatcherQuery.isError]); }, [dispatcherQuery?.error?.message, dispatcherQuery.isError]);
@@ -103,6 +142,8 @@ export const useCameraOutput = () => {
dispatcherQuery, dispatcherQuery,
dispatcherMutation, dispatcherMutation,
backOfficeDispatcherMutation, backOfficeDispatcherMutation,
bof2LandMutation,
laneIdQuery,
}; };
}; };

View File

@@ -1,11 +1,41 @@
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { sendBlobFileUpload } from "../components/SettingForms/System/Upload"; import { sendBlobFileUpload } from "../components/SettingForms/System/Upload";
import { toast } from "sonner"; import { toast } from "sonner";
import { import { handleSystemSave, handleSystemRecall } from "../components/SettingForms/System/SettingSaveRecall";
handleSystemSave,
handleSystemRecall,
} from "../components/SettingForms/System/SettingSaveRecall";
import { useEffect } from "react"; import { useEffect } from "react";
import { CAM_BASE } from "../utils/config";
import type { DNSSettingsType } from "../types/types";
const camBase = import.meta.env.MODE !== "development" ? CAM_BASE : "";
const getDNSSettings = async () => {
const response = await fetch(`${camBase}/api/fetch-config?id=GLOBAL--NetworkConfig`);
if (!response.ok) throw new Error("Cannot get DNS Settings");
return response.json();
};
const updateDNSSettings = async (data: DNSSettingsType) => {
const dnsSettingsPayload = {
id: "GLOBAL--NetworkConfig",
fields: [
{
property: "propNameServerPrimary",
value: data?.serverPrimary,
},
{
property: "propNameServerSecondary",
value: data?.serverSecondary,
},
],
};
const response = await fetch(`${camBase}/api/update-config?id=GLOBAL--NetworkConfig`, {
method: "post",
body: JSON.stringify(dnsSettingsPayload),
});
if (!response.ok) throw new Error("cannot send to dns endpoint");
return response.json();
};
export const useSystemConfig = () => { export const useSystemConfig = () => {
const uploadSettingsMutation = useMutation({ const uploadSettingsMutation = useMutation({
@@ -51,3 +81,20 @@ export const useSystemConfig = () => {
saveSystemSettingsLoading: saveSystemSettings.isPending, saveSystemSettingsLoading: saveSystemSettings.isPending,
}; };
}; };
export const useDNSSettings = () => {
const dnsQuery = useQuery({
queryKey: ["getDNSSettings"],
queryFn: getDNSSettings,
});
const dnsMutation = useMutation({
mutationKey: ["updateDNSSettings"],
mutationFn: updateDNSSettings,
});
return {
dnsQuery,
dnsMutation,
};
};

View File

@@ -69,10 +69,16 @@ export type InitialValuesFormErrors = {
}; };
export type OptionalBOF2Constants = { export type OptionalBOF2Constants = {
FFID?: ""; FFID?: string;
SCID?: ""; SCID?: string;
timestampSource?: ""; timestampSource?: string;
GPSFormat?: ""; GPSFormat?: string;
};
export type OptionalBOF2LaneIDs = {
LID1?: string;
LID2?: string;
LID3?: string;
}; };
export type NPEDFieldType = { export type NPEDFieldType = {
@@ -157,6 +163,13 @@ export type SystemValues = {
sntpInterval: number; sntpInterval: number;
timeZone: string; timeZone: string;
softwareUpdate?: File | null; softwareUpdate?: File | null;
serverPrimary?: string;
serverSecondary?: string;
};
export type DNSSettingsType = {
serverPrimary?: string;
serverSecondary?: string;
}; };
export type SystemValuesErrors = { export type SystemValuesErrors = {