refactor(admin): remove geoserver config UI
This commit is contained in:
@@ -91,18 +91,6 @@ type ProjectDatabaseConfig = {
|
|||||||
has_dsn: boolean;
|
has_dsn: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ProjectGeoServerConfig = {
|
|
||||||
id?: string | null;
|
|
||||||
project_id: string;
|
|
||||||
gs_base_url?: string | null;
|
|
||||||
gs_admin_user?: string | null;
|
|
||||||
gs_datastore_name: string;
|
|
||||||
default_extent?: Record<string, unknown> | null;
|
|
||||||
srid: number;
|
|
||||||
configured?: boolean;
|
|
||||||
has_password: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DatabaseHealth = {
|
type DatabaseHealth = {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
detail: string;
|
detail: string;
|
||||||
@@ -166,15 +154,6 @@ const createDefaultDatabaseForms = () => ({
|
|||||||
iot_data: { ...defaultDatabaseForms.iot_data },
|
iot_data: { ...defaultDatabaseForms.iot_data },
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultGeoserverForm = {
|
|
||||||
gs_base_url: "",
|
|
||||||
gs_admin_user: "",
|
|
||||||
gs_admin_password: "",
|
|
||||||
gs_datastore_name: "ds_postgis",
|
|
||||||
default_extent: "",
|
|
||||||
srid: 4326,
|
|
||||||
};
|
|
||||||
|
|
||||||
const getBusinessRoleLabel = (role: string) =>
|
const getBusinessRoleLabel = (role: string) =>
|
||||||
businessRoleOptions.find((option) => option.value === role)?.label ?? role;
|
businessRoleOptions.find((option) => option.value === role)?.label ?? role;
|
||||||
|
|
||||||
@@ -363,8 +342,6 @@ export const SystemAdminPanel = () => {
|
|||||||
const [members, setMembers] = useState<ProjectMember[]>([]);
|
const [members, setMembers] = useState<ProjectMember[]>([]);
|
||||||
const [projects, setProjects] = useState<AdminProject[]>([]);
|
const [projects, setProjects] = useState<AdminProject[]>([]);
|
||||||
const [databases, setDatabases] = useState<ProjectDatabaseConfig[]>([]);
|
const [databases, setDatabases] = useState<ProjectDatabaseConfig[]>([]);
|
||||||
const [geoserverConfig, setGeoserverConfig] =
|
|
||||||
useState<ProjectGeoServerConfig | null>(null);
|
|
||||||
const [currentAdmin, setCurrentAdmin] = useState<MetadataUser | null>(null);
|
const [currentAdmin, setCurrentAdmin] = useState<MetadataUser | null>(null);
|
||||||
const [adminChecked, setAdminChecked] = useState(false);
|
const [adminChecked, setAdminChecked] = useState(false);
|
||||||
const [isAuthorized, setIsAuthorized] = useState(false);
|
const [isAuthorized, setIsAuthorized] = useState(false);
|
||||||
@@ -388,7 +365,6 @@ export const SystemAdminPanel = () => {
|
|||||||
biz_data: null,
|
biz_data: null,
|
||||||
iot_data: null,
|
iot_data: null,
|
||||||
});
|
});
|
||||||
const [geoserverForm, setGeoserverForm] = useState(defaultGeoserverForm);
|
|
||||||
|
|
||||||
const activeUsers = useMemo(
|
const activeUsers = useMemo(
|
||||||
() => users.filter((user) => user.is_active).length,
|
() => users.filter((user) => user.is_active).length,
|
||||||
@@ -532,36 +508,6 @@ export const SystemAdminPanel = () => {
|
|||||||
});
|
});
|
||||||
}, [projectId]);
|
}, [projectId]);
|
||||||
|
|
||||||
const loadGeoserverConfig = useCallback(async () => {
|
|
||||||
if (!projectId.trim()) return;
|
|
||||||
const response = await apiFetch(
|
|
||||||
`${config.BACKEND_URL}/api/v1/admin/projects/${projectId.trim()}/geoserver`,
|
|
||||||
);
|
|
||||||
if (response.status === 404) {
|
|
||||||
const errorText = await readErrorText(response);
|
|
||||||
if (isFastApiRouteNotFound(response, errorText)) {
|
|
||||||
setMetadataConfigAvailable(false);
|
|
||||||
}
|
|
||||||
setGeoserverConfig(null);
|
|
||||||
setGeoserverForm(defaultGeoserverForm);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(await readErrorText(response));
|
|
||||||
}
|
|
||||||
setMetadataConfigAvailable(true);
|
|
||||||
const payload = (await response.json()) as ProjectGeoServerConfig;
|
|
||||||
setGeoserverConfig(payload);
|
|
||||||
setGeoserverForm({
|
|
||||||
gs_base_url: payload.gs_base_url ?? "",
|
|
||||||
gs_admin_user: payload.gs_admin_user ?? "",
|
|
||||||
gs_admin_password: "",
|
|
||||||
gs_datastore_name: payload.gs_datastore_name || "ds_postgis",
|
|
||||||
default_extent: formatJsonField(payload.default_extent),
|
|
||||||
srid: payload.srid || 4326,
|
|
||||||
});
|
|
||||||
}, [projectId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|
||||||
@@ -657,18 +603,16 @@ export const SystemAdminPanel = () => {
|
|||||||
if (!isAuthorized || !hasProjectId || !metadataConfigAvailable) return;
|
if (!isAuthorized || !hasProjectId || !metadataConfigAvailable) return;
|
||||||
setDatabaseHealth({ biz_data: null, iot_data: null });
|
setDatabaseHealth({ biz_data: null, iot_data: null });
|
||||||
loadDatabases().catch((err) => setError(String(err)));
|
loadDatabases().catch((err) => setError(String(err)));
|
||||||
loadGeoserverConfig().catch((err) => setError(String(err)));
|
|
||||||
}, [
|
}, [
|
||||||
hasProjectId,
|
hasProjectId,
|
||||||
isAuthorized,
|
isAuthorized,
|
||||||
loadDatabases,
|
loadDatabases,
|
||||||
loadGeoserverConfig,
|
|
||||||
metadataConfigAvailable,
|
metadataConfigAvailable,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!metadataConfigAvailable && tab !== 4) {
|
if (!metadataConfigAvailable && tab !== 3) {
|
||||||
setTab(4);
|
setTab(3);
|
||||||
}
|
}
|
||||||
}, [metadataConfigAvailable, tab]);
|
}, [metadataConfigAvailable, tab]);
|
||||||
|
|
||||||
@@ -769,8 +713,6 @@ export const SystemAdminPanel = () => {
|
|||||||
setMembers([]);
|
setMembers([]);
|
||||||
setDatabases([]);
|
setDatabases([]);
|
||||||
setDatabaseForms(createDefaultDatabaseForms());
|
setDatabaseForms(createDefaultDatabaseForms());
|
||||||
setGeoserverConfig(null);
|
|
||||||
setGeoserverForm(defaultGeoserverForm);
|
|
||||||
setDatabaseHealth({ biz_data: null, iot_data: null });
|
setDatabaseHealth({ biz_data: null, iot_data: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -957,51 +899,6 @@ export const SystemAdminPanel = () => {
|
|||||||
await loadDatabases();
|
await loadDatabases();
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveGeoserverConfig = async (event: FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (!projectId.trim()) return;
|
|
||||||
setError(null);
|
|
||||||
setMessage(null);
|
|
||||||
try {
|
|
||||||
const payload: Record<string, unknown> = {
|
|
||||||
gs_base_url: geoserverForm.gs_base_url.trim() || null,
|
|
||||||
gs_admin_user: geoserverForm.gs_admin_user.trim() || null,
|
|
||||||
gs_datastore_name: geoserverForm.gs_datastore_name.trim() || "ds_postgis",
|
|
||||||
default_extent: parseOptionalJsonObject(
|
|
||||||
geoserverForm.default_extent,
|
|
||||||
"默认范围",
|
|
||||||
),
|
|
||||||
srid: Number(geoserverForm.srid),
|
|
||||||
};
|
|
||||||
if (geoserverForm.gs_admin_password.trim()) {
|
|
||||||
payload.gs_admin_password = geoserverForm.gs_admin_password.trim();
|
|
||||||
}
|
|
||||||
const response = await apiFetch(
|
|
||||||
`${config.BACKEND_URL}/api/v1/admin/projects/${projectId.trim()}/geoserver`,
|
|
||||||
{
|
|
||||||
method: "PUT",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await readErrorText(response);
|
|
||||||
if (isFastApiRouteNotFound(response, errorText)) {
|
|
||||||
setMetadataConfigAvailable(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error(errorText);
|
|
||||||
}
|
|
||||||
const saved = (await response.json()) as ProjectGeoServerConfig;
|
|
||||||
setGeoserverConfig(saved);
|
|
||||||
setGeoserverForm((current) => ({ ...current, gs_admin_password: "" }));
|
|
||||||
setMessage("GeoServer 配置已保存");
|
|
||||||
await loadGeoserverConfig();
|
|
||||||
} catch (err) {
|
|
||||||
setError(String(err));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -1089,7 +986,7 @@ export const SystemAdminPanel = () => {
|
|||||||
)}
|
)}
|
||||||
{isAuthorized && !metadataConfigAvailable && (
|
{isAuthorized && !metadataConfigAvailable && (
|
||||||
<Alert severity="warning" sx={{ borderRadius: 2 }}>
|
<Alert severity="warning" sx={{ borderRadius: 2 }}>
|
||||||
后端尚未启用项目配置接口,请重启或部署包含 /api/v1/admin/projects 的后端后再使用项目配置、数据库配置和 GeoServer 配置。
|
后端尚未启用项目配置接口,请重启或部署包含 /api/v1/admin/projects 的后端后再使用项目配置和数据库配置。
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1226,17 +1123,11 @@ export const SystemAdminPanel = () => {
|
|||||||
label="数据库配置"
|
label="数据库配置"
|
||||||
disabled={!metadataConfigAvailable}
|
disabled={!metadataConfigAvailable}
|
||||||
/>
|
/>
|
||||||
<Tab
|
|
||||||
icon={<SecurityIcon />}
|
|
||||||
iconPosition="start"
|
|
||||||
label="GeoServer"
|
|
||||||
disabled={!metadataConfigAvailable}
|
|
||||||
/>
|
|
||||||
<Tab icon={<PeopleIcon />} iconPosition="start" label="系统用户" />
|
<Tab icon={<PeopleIcon />} iconPosition="start" label="系统用户" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<Box sx={{ p: { xs: 2, md: 2.5 } }}>
|
<Box sx={{ p: { xs: 2, md: 2.5 } }}>
|
||||||
{tab === 4 && (
|
{tab === 3 && (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title="系统用户"
|
title="系统用户"
|
||||||
@@ -1912,151 +1803,6 @@ export const SystemAdminPanel = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tab === 3 && (
|
|
||||||
<Stack spacing={2}>
|
|
||||||
<SectionHeader
|
|
||||||
title="项目 GeoServer 配置"
|
|
||||||
description="维护当前管理项目的 GeoServer 服务、管理员账号、datastore、默认范围和 SRID。"
|
|
||||||
action={
|
|
||||||
<Button
|
|
||||||
startIcon={<RefreshIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={loadGeoserverConfig}
|
|
||||||
disabled={!hasProjectId}
|
|
||||||
>
|
|
||||||
刷新配置
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{!hasProjectId && (
|
|
||||||
<Alert severity="warning" sx={{ borderRadius: 2 }}>
|
|
||||||
当前未选择管理项目。
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{hasProjectId && (
|
|
||||||
<Paper
|
|
||||||
component="form"
|
|
||||||
variant="outlined"
|
|
||||||
sx={{ p: 2, borderRadius: 2 }}
|
|
||||||
onSubmit={saveGeoserverConfig}
|
|
||||||
>
|
|
||||||
<Stack spacing={2}>
|
|
||||||
<Stack
|
|
||||||
direction={{ xs: "column", md: "row" }}
|
|
||||||
spacing={1}
|
|
||||||
alignItems={{ xs: "stretch", md: "center" }}
|
|
||||||
justifyContent="space-between"
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Typography variant="subtitle2" fontWeight={800}>
|
|
||||||
{selectedProject?.name ?? projectId}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
{selectedProject?.gs_workspace ?? "未设置工作区"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Chip
|
|
||||||
color={geoserverConfig?.has_password ? "success" : "default"}
|
|
||||||
label={
|
|
||||||
geoserverConfig?.has_password ? "密码已配置" : "未配置密码"
|
|
||||||
}
|
|
||||||
sx={{ alignSelf: { xs: "flex-start", md: "center" } }}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Stack direction={{ xs: "column", md: "row" }} spacing={1.5}>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
label="Base URL"
|
|
||||||
value={geoserverForm.gs_base_url}
|
|
||||||
onChange={(event) =>
|
|
||||||
setGeoserverForm({
|
|
||||||
...geoserverForm,
|
|
||||||
gs_base_url: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
label="管理员账号"
|
|
||||||
value={geoserverForm.gs_admin_user}
|
|
||||||
onChange={(event) =>
|
|
||||||
setGeoserverForm({
|
|
||||||
...geoserverForm,
|
|
||||||
gs_admin_user: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
type="password"
|
|
||||||
label="管理员密码"
|
|
||||||
value={geoserverForm.gs_admin_password}
|
|
||||||
onChange={(event) =>
|
|
||||||
setGeoserverForm({
|
|
||||||
...geoserverForm,
|
|
||||||
gs_admin_password: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Stack direction={{ xs: "column", md: "row" }} spacing={1.5}>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
label="Datastore"
|
|
||||||
value={geoserverForm.gs_datastore_name}
|
|
||||||
onChange={(event) =>
|
|
||||||
setGeoserverForm({
|
|
||||||
...geoserverForm,
|
|
||||||
gs_datastore_name: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fullWidth
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
type="number"
|
|
||||||
label="SRID"
|
|
||||||
value={geoserverForm.srid}
|
|
||||||
onChange={(event) =>
|
|
||||||
setGeoserverForm({
|
|
||||||
...geoserverForm,
|
|
||||||
srid: Number(event.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sx={{ width: { xs: "100%", md: 180 } }}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
label="默认范围 JSON"
|
|
||||||
value={geoserverForm.default_extent}
|
|
||||||
onChange={(event) =>
|
|
||||||
setGeoserverForm({
|
|
||||||
...geoserverForm,
|
|
||||||
default_extent: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fullWidth
|
|
||||||
multiline
|
|
||||||
minRows={4}
|
|
||||||
placeholder='{"bbox":[120.1,30.1,120.2,30.2]}'
|
|
||||||
/>
|
|
||||||
<Stack direction="row" justifyContent="flex-end">
|
|
||||||
<Button type="submit" variant="contained" startIcon={<SaveIcon />}>
|
|
||||||
保存 GeoServer
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|||||||
Reference in New Issue
Block a user