mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-01-23 02:35:14 +00:00
Fix key error with react.
This commit is contained in:
parent
e80d30689c
commit
9757f6a48d
2 changed files with 302 additions and 304 deletions
|
|
@ -203,7 +203,7 @@ def environment(request):
|
|||
country_code = None
|
||||
country_name = None
|
||||
|
||||
# 1) Get the public IP
|
||||
# 1) Get the public IP from ipify.org API
|
||||
try:
|
||||
r = requests.get("https://api64.ipify.org?format=json", timeout=5)
|
||||
r.raise_for_status()
|
||||
|
|
@ -211,17 +211,17 @@ def environment(request):
|
|||
except requests.RequestException as e:
|
||||
public_ip = f"Error: {e}"
|
||||
|
||||
# 2) Get the local IP
|
||||
# 2) Get the local IP by connecting to a public DNS server
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
# connect to a “public” address so the OS can determine our local interface
|
||||
# connect to a "public" address so the OS can determine our local interface
|
||||
s.connect(("8.8.8.8", 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except Exception as e:
|
||||
local_ip = f"Error: {e}"
|
||||
|
||||
# 3) If we got a valid public_ip, fetch geo info from ipapi.co or ip-api.com
|
||||
# 3) Get geolocation data from ipapi.co or ip-api.com
|
||||
if public_ip and "Error" not in public_ip:
|
||||
try:
|
||||
# Attempt to get geo information from ipapi.co first
|
||||
|
|
@ -250,6 +250,7 @@ def environment(request):
|
|||
country_code = None
|
||||
country_name = None
|
||||
|
||||
# 4) Get environment mode from system environment variable
|
||||
return Response(
|
||||
{
|
||||
"authenticated": True,
|
||||
|
|
|
|||
|
|
@ -143,15 +143,14 @@ const SettingsPage = () => {
|
|||
}, {})
|
||||
);
|
||||
|
||||
const proxySettings = JSON.parse(
|
||||
settings['proxy-settings'].value || '{}'
|
||||
);
|
||||
proxySettingsForm.setValues(
|
||||
Object.keys(PROXY_SETTINGS_OPTIONS).reduce((acc, key) => {
|
||||
acc[key] = proxySettings[key] || '';
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
if (settings['proxy-settings']?.value) {
|
||||
try {
|
||||
const proxySettings = JSON.parse(settings['proxy-settings'].value);
|
||||
proxySettingsForm.setValues(proxySettings);
|
||||
} catch (error) {
|
||||
console.error('Error parsing proxy settings:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
|
|
@ -246,143 +245,217 @@ const SettingsPage = () => {
|
|||
defaultValue="ui-settings"
|
||||
onChange={setAccordianValue}
|
||||
>
|
||||
{[
|
||||
<Accordion.Item value="ui-settings">
|
||||
<Accordion.Control>UI Settings</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Select
|
||||
label="Table Size"
|
||||
value={tableSize}
|
||||
onChange={(val) => onUISettingsChange('table-size', val)}
|
||||
data={[
|
||||
{
|
||||
value: 'default',
|
||||
label: 'Default',
|
||||
},
|
||||
{
|
||||
value: 'compact',
|
||||
label: 'Compact',
|
||||
},
|
||||
{
|
||||
value: 'large',
|
||||
label: 'Large',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>,
|
||||
].concat(
|
||||
authUser.user_level == USER_LEVELS.ADMIN
|
||||
? [
|
||||
<Accordion.Item value="stream-settings">
|
||||
<Accordion.Control>Stream Settings</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||
<Select
|
||||
searchable
|
||||
{...form.getInputProps('default-user-agent')}
|
||||
key={form.key('default-user-agent')}
|
||||
id={
|
||||
settings['default-user-agent']?.id ||
|
||||
'default-user-agent'
|
||||
}
|
||||
name={
|
||||
settings['default-user-agent']?.key ||
|
||||
'default-user-agent'
|
||||
}
|
||||
label={
|
||||
settings['default-user-agent']?.name ||
|
||||
'Default User Agent'
|
||||
}
|
||||
data={userAgents.map((option) => ({
|
||||
value: `${option.id}`,
|
||||
label: option.name,
|
||||
}))}
|
||||
/>
|
||||
<Accordion.Item value="ui-settings">
|
||||
<Accordion.Control>UI Settings</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Select
|
||||
label="Table Size"
|
||||
value={tableSize}
|
||||
onChange={(val) => onUISettingsChange('table-size', val)}
|
||||
data={[
|
||||
{
|
||||
value: 'default',
|
||||
label: 'Default',
|
||||
},
|
||||
{
|
||||
value: 'compact',
|
||||
label: 'Compact',
|
||||
},
|
||||
{
|
||||
value: 'large',
|
||||
label: 'Large',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Select
|
||||
searchable
|
||||
{...form.getInputProps('default-stream-profile')}
|
||||
key={form.key('default-stream-profile')}
|
||||
id={
|
||||
settings['default-stream-profile']?.id ||
|
||||
'default-stream-profile'
|
||||
}
|
||||
name={
|
||||
settings['default-stream-profile']?.key ||
|
||||
'default-stream-profile'
|
||||
}
|
||||
label={
|
||||
settings['default-stream-profile']?.name ||
|
||||
'Default Stream Profile'
|
||||
}
|
||||
data={streamProfiles.map((option) => ({
|
||||
value: `${option.id}`,
|
||||
label: option.name,
|
||||
}))}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
{...form.getInputProps('preferred-region')}
|
||||
key={form.key('preferred-region')}
|
||||
id={
|
||||
settings['preferred-region']?.id ||
|
||||
'preferred-region'
|
||||
}
|
||||
name={
|
||||
settings['preferred-region']?.key ||
|
||||
'preferred-region'
|
||||
}
|
||||
label={
|
||||
settings['preferred-region']?.name ||
|
||||
'Preferred Region'
|
||||
}
|
||||
data={regionChoices.map((r) => ({
|
||||
label: r.label,
|
||||
value: `${r.value}`,
|
||||
}))}
|
||||
/>
|
||||
{authUser.user_level == USER_LEVELS.ADMIN && (
|
||||
<>
|
||||
<Accordion.Item value="stream-settings">
|
||||
<Accordion.Control>Stream Settings</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||
<Select
|
||||
searchable
|
||||
{...form.getInputProps('default-user-agent')}
|
||||
key={form.key('default-user-agent')}
|
||||
id={
|
||||
settings['default-user-agent']?.id ||
|
||||
'default-user-agent'
|
||||
}
|
||||
name={
|
||||
settings['default-user-agent']?.key ||
|
||||
'default-user-agent'
|
||||
}
|
||||
label={
|
||||
settings['default-user-agent']?.name ||
|
||||
'Default User Agent'
|
||||
}
|
||||
data={userAgents.map((option) => ({
|
||||
value: `${option.id}`,
|
||||
label: option.name,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<Group
|
||||
justify="space-between"
|
||||
style={{ paddingTop: 5 }}
|
||||
<Select
|
||||
searchable
|
||||
{...form.getInputProps('default-stream-profile')}
|
||||
key={form.key('default-stream-profile')}
|
||||
id={
|
||||
settings['default-stream-profile']?.id ||
|
||||
'default-stream-profile'
|
||||
}
|
||||
name={
|
||||
settings['default-stream-profile']?.key ||
|
||||
'default-stream-profile'
|
||||
}
|
||||
label={
|
||||
settings['default-stream-profile']?.name ||
|
||||
'Default Stream Profile'
|
||||
}
|
||||
data={streamProfiles.map((option) => ({
|
||||
value: `${option.id}`,
|
||||
label: option.name,
|
||||
}))}
|
||||
/>
|
||||
<Select
|
||||
searchable
|
||||
{...form.getInputProps('preferred-region')}
|
||||
key={form.key('preferred-region')}
|
||||
id={
|
||||
settings['preferred-region']?.id ||
|
||||
'preferred-region'
|
||||
}
|
||||
name={
|
||||
settings['preferred-region']?.key ||
|
||||
'preferred-region'
|
||||
}
|
||||
label={
|
||||
settings['preferred-region']?.name ||
|
||||
'Preferred Region'
|
||||
}
|
||||
data={regionChoices.map((r) => ({
|
||||
label: r.label,
|
||||
value: `${r.value}`,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<Group
|
||||
justify="space-between"
|
||||
style={{ paddingTop: 5 }}
|
||||
>
|
||||
<Text size="sm" fw={500}>
|
||||
Auto-Import Mapped Files
|
||||
</Text>
|
||||
<Switch
|
||||
{...form.getInputProps('auto-import-mapped-files', {
|
||||
type: 'checkbox',
|
||||
})}
|
||||
key={form.key('auto-import-mapped-files')}
|
||||
id={
|
||||
settings['auto-import-mapped-files']?.id ||
|
||||
'auto-import-mapped-files'
|
||||
}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<MultiSelect
|
||||
id="m3u-hash-key"
|
||||
name="m3u-hash-key"
|
||||
label="M3U Hash Key"
|
||||
data={[
|
||||
{
|
||||
value: 'name',
|
||||
label: 'Name',
|
||||
},
|
||||
{
|
||||
value: 'url',
|
||||
label: 'URL',
|
||||
},
|
||||
{
|
||||
value: 'tvg_id',
|
||||
label: 'TVG-ID',
|
||||
},
|
||||
]}
|
||||
{...form.getInputProps('m3u-hash-key')}
|
||||
key={form.key('m3u-hash-key')}
|
||||
/>
|
||||
|
||||
<Flex
|
||||
mih={50}
|
||||
gap="xs"
|
||||
justify="flex-end"
|
||||
align="flex-end"
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={form.submitting}
|
||||
variant="default"
|
||||
>
|
||||
<Text size="sm" fw={500}>
|
||||
Auto-Import Mapped Files
|
||||
</Text>
|
||||
<Switch
|
||||
{...form.getInputProps('auto-import-mapped-files', {
|
||||
type: 'checkbox',
|
||||
})}
|
||||
key={form.key('auto-import-mapped-files')}
|
||||
id={
|
||||
settings['auto-import-mapped-files']?.id ||
|
||||
'auto-import-mapped-files'
|
||||
}
|
||||
/>
|
||||
</Group>
|
||||
Save
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<MultiSelect
|
||||
id="m3u-hash-key"
|
||||
name="m3u-hash-key"
|
||||
label="M3U Hash Key"
|
||||
data={[
|
||||
{
|
||||
value: 'name',
|
||||
label: 'Name',
|
||||
},
|
||||
{
|
||||
value: 'url',
|
||||
label: 'URL',
|
||||
},
|
||||
{
|
||||
value: 'tvg_id',
|
||||
label: 'TVG-ID',
|
||||
},
|
||||
]}
|
||||
{...form.getInputProps('m3u-hash-key')}
|
||||
key={form.key('m3u-hash-key')}
|
||||
/>
|
||||
<Accordion.Item value="user-agents">
|
||||
<Accordion.Control>User-Agents</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<UserAgentsTable />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="stream-profiles">
|
||||
<Accordion.Control>Stream Profiles</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<StreamProfilesTable />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="network-access">
|
||||
<Accordion.Control>
|
||||
<Box>Network Access</Box>
|
||||
{accordianValue == 'network-access' && (
|
||||
<Box>
|
||||
<Text size="sm">Comma-Delimited CIDR ranges</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<form
|
||||
onSubmit={networkAccessForm.onSubmit(
|
||||
onNetworkAccessSubmit
|
||||
)}
|
||||
>
|
||||
<Stack gap="sm">
|
||||
{networkAccessSaved && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color="green"
|
||||
title="Saved Successfully"
|
||||
></Alert>
|
||||
)}
|
||||
{networkAccessError && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color="red"
|
||||
title={networkAccessError}
|
||||
></Alert>
|
||||
)}
|
||||
{Object.entries(NETWORK_ACCESS_OPTIONS).map(
|
||||
([key, config]) => {
|
||||
return (
|
||||
<TextInput
|
||||
label={config.label}
|
||||
{...networkAccessForm.getInputProps(key)}
|
||||
key={networkAccessForm.key(key)}
|
||||
description={config.description}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
|
||||
<Flex
|
||||
mih={50}
|
||||
|
|
@ -392,181 +465,105 @@ const SettingsPage = () => {
|
|||
>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={form.submitting}
|
||||
disabled={networkAccessForm.submitting}
|
||||
variant="default"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>,
|
||||
</Stack>
|
||||
</form>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="user-agents">
|
||||
<Accordion.Control>User-Agents</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<UserAgentsTable />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>,
|
||||
|
||||
<Accordion.Item value="stream-profiles">
|
||||
<Accordion.Control>Stream Profiles</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<StreamProfilesTable />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>,
|
||||
|
||||
<Accordion.Item value="network-access">
|
||||
<Accordion.Control>
|
||||
<Box>Network Access</Box>
|
||||
{accordianValue == 'network-access' && (
|
||||
<Box>
|
||||
<Text size="sm">Comma-Delimited CIDR ranges</Text>
|
||||
</Box>
|
||||
<Accordion.Item value="proxy-settings">
|
||||
<Accordion.Control>
|
||||
<Box>Proxy Settings</Box>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<form
|
||||
onSubmit={proxySettingsForm.onSubmit(
|
||||
onProxySettingsSubmit
|
||||
)}
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<form
|
||||
onSubmit={networkAccessForm.onSubmit(
|
||||
onNetworkAccessSubmit
|
||||
>
|
||||
<Stack gap="sm">
|
||||
{proxySettingsSaved && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color="green"
|
||||
title="Saved Successfully"
|
||||
></Alert>
|
||||
)}
|
||||
>
|
||||
<Stack gap="sm">
|
||||
{networkAccessSaved && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color="green"
|
||||
title="Saved Successfully"
|
||||
></Alert>
|
||||
)}
|
||||
{networkAccessError && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color="red"
|
||||
title={networkAccessError}
|
||||
></Alert>
|
||||
)}
|
||||
{Object.entries(NETWORK_ACCESS_OPTIONS).map(
|
||||
([key, config]) => {
|
||||
{Object.entries(PROXY_SETTINGS_OPTIONS).map(
|
||||
([key, config]) => {
|
||||
// Determine if this field should be a NumberInput
|
||||
const isNumericField = [
|
||||
'buffering_timeout',
|
||||
'redis_chunk_ttl',
|
||||
'channel_shutdown_delay',
|
||||
'channel_init_grace_period'
|
||||
].includes(key);
|
||||
|
||||
const isFloatField = key === 'buffering_speed';
|
||||
|
||||
if (isNumericField) {
|
||||
return (
|
||||
<NumberInput
|
||||
key={key}
|
||||
label={config.label}
|
||||
{...proxySettingsForm.getInputProps(key)}
|
||||
description={config.description || null}
|
||||
min={0}
|
||||
max={key === 'buffering_timeout' ? 300 :
|
||||
key === 'redis_chunk_ttl' ? 3600 :
|
||||
key === 'channel_shutdown_delay' ? 300 : 60}
|
||||
/>
|
||||
);
|
||||
} else if (isFloatField) {
|
||||
return (
|
||||
<NumberInput
|
||||
key={key}
|
||||
label={config.label}
|
||||
{...proxySettingsForm.getInputProps(key)}
|
||||
description={config.description || null}
|
||||
min={0.0}
|
||||
max={10.0}
|
||||
step={0.01}
|
||||
precision={1}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<TextInput
|
||||
key={key}
|
||||
label={config.label}
|
||||
{...networkAccessForm.getInputProps(key)}
|
||||
key={networkAccessForm.key(key)}
|
||||
description={config.description}
|
||||
{...proxySettingsForm.getInputProps(key)}
|
||||
description={config.description || null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
|
||||
<Flex
|
||||
mih={50}
|
||||
gap="xs"
|
||||
justify="flex-end"
|
||||
align="flex-end"
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={networkAccessForm.submitting}
|
||||
variant="default"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</form>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>,
|
||||
|
||||
<Accordion.Item value="proxy-settings">
|
||||
<Accordion.Control>
|
||||
<Box>Proxy Settings</Box>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<form
|
||||
onSubmit={proxySettingsForm.onSubmit(
|
||||
onProxySettingsSubmit
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Stack gap="sm">
|
||||
{proxySettingsSaved && (
|
||||
<Alert
|
||||
variant="light"
|
||||
color="green"
|
||||
title="Saved Successfully"
|
||||
></Alert>
|
||||
)}
|
||||
{Object.entries(PROXY_SETTINGS_OPTIONS).map(
|
||||
([key, config]) => {
|
||||
// Determine if this field should be a NumberInput
|
||||
const isNumericField = [
|
||||
'buffering_timeout',
|
||||
'redis_chunk_ttl',
|
||||
'channel_shutdown_delay',
|
||||
'channel_init_grace_period'
|
||||
].includes(key);
|
||||
|
||||
const isFloatField = key === 'buffering_speed';
|
||||
|
||||
if (isNumericField) {
|
||||
return (
|
||||
<NumberInput
|
||||
key={key}
|
||||
label={config.label}
|
||||
{...proxySettingsForm.getInputProps(key)}
|
||||
description={config.description || null}
|
||||
min={0}
|
||||
max={key === 'buffering_timeout' ? 300 :
|
||||
key === 'redis_chunk_ttl' ? 3600 :
|
||||
key === 'channel_shutdown_delay' ? 300 : 60}
|
||||
/>
|
||||
);
|
||||
} else if (isFloatField) {
|
||||
return (
|
||||
<NumberInput
|
||||
key={key}
|
||||
label={config.label}
|
||||
{...proxySettingsForm.getInputProps(key)}
|
||||
description={config.description || null}
|
||||
min={0.1}
|
||||
max={10.0}
|
||||
step={0.1}
|
||||
precision={1}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<TextInput
|
||||
key={key}
|
||||
label={config.label}
|
||||
{...proxySettingsForm.getInputProps(key)}
|
||||
description={config.description || null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
)}
|
||||
|
||||
<Flex
|
||||
mih={50}
|
||||
gap="xs"
|
||||
justify="flex-end"
|
||||
align="flex-end"
|
||||
<Flex
|
||||
mih={50}
|
||||
gap="xs"
|
||||
justify="flex-end"
|
||||
align="flex-end"
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={networkAccessForm.submitting}
|
||||
variant="default"
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={networkAccessForm.submitting}
|
||||
variant="default"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</form>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>,
|
||||
]
|
||||
: []
|
||||
Save
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</form>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</>
|
||||
)}
|
||||
</Accordion>
|
||||
</Box>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue