Files
Cloud-Tour-to-Libo/admin-web/src/api.ts
T

609 lines
20 KiB
TypeScript

import axios from "axios";
const api = axios.create({ baseURL: "/v1/admin" });
export type KgProjectContext = {
tenantId: string;
projectId: string;
graphName: string;
};
export const PROJECT_CONTEXT_KEY = "znkg.currentProjectContext";
export const DEFAULT_PROJECT_CONTEXT: KgProjectContext = {
tenantId: "CityGraph-new2",
projectId: "CityGraph-new2",
graphName: "guiyang_new2",
};
export const EMPTY_PROJECT_CONTEXT: KgProjectContext = {
tenantId: "",
projectId: "",
graphName: "",
};
const PROJECT_DISPLAY_NAMES: Record<string, string> = {
"CityGraph-new2": "城市图谱",
guiyang_new2: "贵阳城市图谱",
city_graph: "城市图谱",
travel_agency: "旅行社",
travel_graph: "旅行社线路制定",
travel_fixed_route_item: "旅行社固定线路资源图谱",
travel_agency_2_0_test: "旅行社2.0测试",
baixinghui_travel_agency: "百姓惠旅行社",
};
const GRAPH_DISPLAY_NAMES: Record<string, string> = {
guiyang_new2: "贵阳城市图谱",
travel_agency: "旅行社资料库",
travel_graph: "旅行社线路制定",
travel_fixed_route_item: "旅行社固定线路资源图谱",
travel_agency_2_0_test: "旅行社2.0测试图谱",
baixinghui_travel_agency: "百姓惠旅行社图谱",
};
const STATUS_DISPLAY_NAMES: Record<string, string> = {
active: "启用中",
published: "已发布",
draft: "草稿",
archived: "已归档",
};
export function displayProjectName(value?: string | null): string {
const key = String(value || "").trim();
return PROJECT_DISPLAY_NAMES[key] || key || "-";
}
export function displayGraphName(value?: string | null): string {
const key = String(value || "").trim();
return GRAPH_DISPLAY_NAMES[key] || PROJECT_DISPLAY_NAMES[key] || key || "-";
}
export function displayTenantName(value?: string | null): string {
return displayProjectName(value);
}
export function displayStatusName(value?: string | null): string {
const key = String(value || "").trim();
return STATUS_DISPLAY_NAMES[key] || key || "-";
}
export function getProjectContext(): KgProjectContext {
try {
const stored = localStorage.getItem(PROJECT_CONTEXT_KEY);
if (stored === null) return DEFAULT_PROJECT_CONTEXT;
const parsed = JSON.parse(stored);
if (parsed?.projectId === "") {
return EMPTY_PROJECT_CONTEXT;
}
return {
tenantId: parsed.tenantId || DEFAULT_PROJECT_CONTEXT.tenantId,
projectId: parsed.projectId || DEFAULT_PROJECT_CONTEXT.projectId,
graphName: parsed.graphName || DEFAULT_PROJECT_CONTEXT.graphName,
};
} catch {
return DEFAULT_PROJECT_CONTEXT;
}
}
export function setProjectContext(context: KgProjectContext) {
localStorage.setItem(PROJECT_CONTEXT_KEY, JSON.stringify(context));
window.dispatchEvent(new CustomEvent("znkg-project-context", { detail: context }));
}
export function clearProjectContext() {
setProjectContext(EMPTY_PROJECT_CONTEXT);
}
api.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) config.headers.Authorization = `Bearer ${token}`;
const context = getProjectContext();
if (context.tenantId) config.headers["X-KG-Tenant-Id"] = context.tenantId;
else delete config.headers["X-KG-Tenant-Id"];
if (context.projectId) config.headers["X-KG-Project-Id"] = context.projectId;
else delete config.headers["X-KG-Project-Id"];
if (context.graphName) config.headers["X-KG-Graph-Name"] = context.graphName;
else delete config.headers["X-KG-Graph-Name"];
return config;
});
api.interceptors.response.use(
(r) => r,
(err) => {
if (err.response?.status === 401) {
localStorage.removeItem("token");
window.location.href = "/admin/login";
}
return Promise.reject(err);
}
);
export default api;
// ── Auth ──
export const login = (username: string, password: string) =>
api.post("/auth/login", { username, password });
export const whoami = () => api.get("/auth/me");
// ── Projects / Graph releases ──
export type ProjectSummary = {
project_id: string;
tenant_id?: string;
display_name?: string;
name?: string;
description?: string | null;
status?: string;
metadata_jsonb?: {
spatial_map?: Partial<SpatialMapConfig>;
[key: string]: unknown;
};
created_at?: string;
updated_at?: string;
};
export type SpatialMapScope = "libo" | "guizhou";
export type SpatialMapRegionLevel = "province" | "city" | "district";
export type SpatialMapConfig = {
enabled: boolean;
scope: SpatialMapScope;
region_name: string;
region_adcode: string;
region_level: SpatialMapRegionLevel;
};
export type GraphReleaseSummary = {
graph_release_id: string | number;
project_id: string;
tenant_id?: string;
graph_name: string;
alias?: string;
status?: string;
schema_id?: string | number | null;
created_at?: string;
updated_at?: string;
};
export type GraphProjectProvisionRequest = {
project_id: string;
display_name: string;
tenant_id?: string;
graph_name?: string;
spatial_map?: SpatialMapConfig;
schema: Record<string, unknown>;
graph_data: {
nodes: Array<Record<string, unknown>>;
relations: Array<Record<string, unknown>>;
};
};
export type GraphProjectProvisionResponse = {
project?: ProjectSummary;
graph_release?: GraphReleaseSummary;
schema?: Record<string, unknown>;
graph?: { graph_name: string; nodes: number; relations: number };
counts?: Record<string, number>;
};
export type ProjectDeletionGraphImpact = {
graph_name: string;
exists_in_falkordb: boolean | null;
shared: boolean;
other_project_ids: string[];
will_delete: boolean;
};
export type ProjectDeletionImpact = {
project_id: string;
display_name: string;
counts: Record<string, number>;
graphs: ProjectDeletionGraphImpact[];
protected_resources: string[];
falkordb_error?: string;
};
export type GraphProjectDeleteResponse = {
status: "deleted" | "deleted_with_cleanup_pending";
project_id: string;
deleted_counts: Record<string, number>;
deleted_graphs: string[];
already_absent_graphs: string[];
shared_graphs_preserved: string[];
cleanup_pending: Array<{
graph_name: string;
error: string;
task_id?: string | number;
}>;
protected_resources: string[];
};
export const listProjects = () => api.get<ProjectSummary[]>("/projects");
export const createProject = (data: unknown) => api.post("/projects", data);
export const listGraphReleases = (projectId: string) =>
api.get<GraphReleaseSummary[]>(`/projects/${encodeURIComponent(projectId)}/graph-releases`);
export const createGraphRelease = (projectId: string, data: unknown) =>
api.post(`/projects/${encodeURIComponent(projectId)}/graph-releases`, data);
export const provisionGraphProject = (data: GraphProjectProvisionRequest) =>
api.post<GraphProjectProvisionResponse>("/projects/provision", data);
export const updateGraphProject = (projectId: string, data: { display_name: string }) =>
api.patch<ProjectSummary>(`/projects/${encodeURIComponent(projectId)}`, data);
export const getGraphProjectDeletionImpact = (projectId: string) =>
api.get<ProjectDeletionImpact>(
`/projects/${encodeURIComponent(projectId)}/deletion-impact`,
);
export const deleteGraphProject = (projectId: string, confirmProjectId: string) =>
api.delete<GraphProjectDeleteResponse>(`/projects/${encodeURIComponent(projectId)}`, {
params: { confirm_project_id: confirmProjectId },
});
// ── Project relational data platform ──
export const listProjectDatabases = () =>
api.get("/data-platform/databases");
export const createRelationalDatabase = (data: unknown) =>
api.post("/data-platform/databases", data);
export const updateRelationalDatabase = (projectId: string, data: unknown) =>
api.patch(`/data-platform/databases/${encodeURIComponent(projectId)}`, data);
export const deleteRelationalDatabase = (projectId: string, data: unknown) =>
api.delete(`/data-platform/databases/${encodeURIComponent(projectId)}`, { data });
export const listProjectDataTables = (projectId: string) =>
api.get(`/data-platform/databases/${encodeURIComponent(projectId)}/tables`);
export const executeProjectDatabaseSql = (projectId: string, sql: string) =>
api.post(
`/data-platform/databases/${encodeURIComponent(projectId)}/console/execute`,
{ sql },
);
export const getProjectDataTableInspection = (projectId: string, tableCode: string) =>
api.get(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/inspection`,
);
export const createProjectDataTableColumn = (
projectId: string,
tableCode: string,
data: unknown,
) =>
api.post(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/columns`,
data,
);
export const updateProjectDataTableColumn = (
projectId: string,
tableCode: string,
columnCode: string,
data: unknown,
) =>
api.patch(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/columns/${encodeURIComponent(columnCode)}`,
data,
);
export const deleteProjectDataTableColumn = (
projectId: string,
tableCode: string,
columnCode: string,
data: unknown,
) =>
api.delete(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/columns/${encodeURIComponent(columnCode)}`,
{ data },
);
export const createProjectDataTable = (projectId: string, data: unknown) =>
api.post(`/data-platform/databases/${encodeURIComponent(projectId)}/tables`, data);
export const createProjectDataTableFromSql = (projectId: string, data: unknown) =>
api.post(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/from-sql`,
data,
);
export const updateProjectDataTable = (
projectId: string,
tableCode: string,
data: unknown,
) =>
api.patch(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}`,
data,
);
export const deleteProjectDataTable = (
projectId: string,
tableCode: string,
data: unknown,
) =>
api.delete(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}`,
{ data },
);
export const listProjectTableRecords = (
projectId: string,
tableCode: string,
params?: Record<string, unknown>,
) =>
api.get(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records`,
{ params },
);
export const createProjectTableRecord = (
projectId: string,
tableCode: string,
data: unknown,
) =>
api.post(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records`,
data,
);
export const updateProjectTableRecord = (
projectId: string,
tableCode: string,
recordId: string,
data: unknown,
) =>
api.patch(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records/${encodeURIComponent(recordId)}`,
data,
);
export const deleteProjectTableRecord = (
projectId: string,
tableCode: string,
recordId: string,
) =>
api.delete(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records/${encodeURIComponent(recordId)}`,
);
export const previewProjectTableCsvImport = (
projectId: string,
tableCode: string,
file: File,
) => {
const form = new FormData();
form.append("file", file);
return api.post(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records/import/preview`,
form,
);
};
export const importProjectTableCsv = (
projectId: string,
tableCode: string,
file: File,
) => {
const form = new FormData();
form.append("file", file);
return api.post(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records/import`,
form,
);
};
export const exportProjectTableCsv = (
projectId: string,
tableCode: string,
params?: Record<string, unknown>,
) =>
api.get(
`/data-platform/databases/${encodeURIComponent(projectId)}/tables/${encodeURIComponent(tableCode)}/records/export`,
{ params, responseType: "blob" },
);
// ── Interface Center (controlled external Data APIs) ──
export type InterfaceSummary = {
active_clients: number;
active_credentials: number;
active_policies: number;
calls_24h: number;
errors_24h: number;
active_dbeaver_grants?: number;
data_engine: string;
public_base_path: string;
direct_database_access: string;
direct_access_enabled?: boolean;
direct_access_host?: string;
direct_access_port?: number;
direct_access_transport?: string;
ssh_tunnel_required?: boolean;
ssh_host?: string;
ssh_port?: number;
ssh_username?: string;
ssh_auth_method?: string;
managed_access_enabled?: boolean;
ssh_key_auto_install?: boolean;
account_provisioning_ready?: boolean;
ssh_host_key_fingerprint?: string;
database_host?: string;
database_port?: number;
database_account_policy?: string;
mysql_host_bind?: string;
mysql_publicly_bound?: boolean;
database_audit_enabled?: boolean;
application_audit_enabled?: boolean;
sql_console_write_enabled?: boolean;
backup_enabled?: boolean;
backup_encryption_required?: boolean;
backup_retention_days?: number;
};
export type DbeaverAccessGrant = {
id: string;
display_name: string;
mysql_username: string;
mysql_password?: string;
password_shown_once?: boolean;
database_id: string;
database_name: string;
permission_level: "read" | "write";
ssh_key_type: string;
ssh_key_fingerprint: string;
ssh_host?: string;
ssh_port?: number;
ssh_username?: string;
database_host?: string;
database_port?: number;
created_by?: string;
status: "active" | "revoked";
revoked_at?: string | null;
created_at?: string;
};
export type InterfaceClient = {
id: string;
name: string;
description: string;
owner: string;
status: "active" | "disabled";
credential_count?: number;
policy_count?: number;
created_at?: string;
updated_at?: string;
};
export type InterfaceCredential = {
id: string;
client_id: string;
client_name?: string;
name: string;
key_prefix: string;
api_key?: string;
expires_at?: string | null;
last_used_at?: string | null;
revoked_at?: string | null;
created_at?: string;
};
export type InterfacePolicy = {
id: string;
client_id: string;
client_name?: string;
database_id: string;
database_name?: string;
table_code: string;
actions: string[];
readable_fields: string[];
writable_fields: string[];
row_filter: Record<string, unknown>;
status: "active" | "disabled";
created_at?: string;
updated_at?: string;
};
export type InterfaceCatalogField = {
code: string;
label: string;
data_type: string;
};
export type InterfaceCatalogTable = {
code: string;
label: string;
group: string;
fields: InterfaceCatalogField[];
};
export type InterfaceCatalogDatabase = {
project_id: string;
display_name: string;
database_name: string;
tables: InterfaceCatalogTable[];
};
export type InterfaceCallLog = {
id: number;
request_id: string;
client_name?: string | null;
method: string;
path: string;
database_id?: string | null;
table_code?: string | null;
action_name?: string | null;
status_code: number;
duration_ms: number;
source_ip?: string | null;
error_message?: string | null;
created_at: string;
};
export const getInterfaceCenterSummary = () =>
api.get<InterfaceSummary>("/interface-center/summary");
export const getInterfaceCenterCatalog = () =>
api.get<InterfaceCatalogDatabase[]>("/interface-center/catalog");
export const listDbeaverAccessGrants = () =>
api.get<DbeaverAccessGrant[]>("/interface-center/dbeaver-access");
export const issueDbeaverAccess = (data: unknown) =>
api.post<DbeaverAccessGrant>("/interface-center/dbeaver-access", data);
export const revokeDbeaverAccess = (grantId: string) =>
api.post(`/interface-center/dbeaver-access/${encodeURIComponent(grantId)}/revoke`, {});
export const listInterfaceClients = () =>
api.get<InterfaceClient[]>("/interface-center/clients");
export const createInterfaceClient = (data: unknown) =>
api.post<InterfaceClient>("/interface-center/clients", data);
export const updateInterfaceClient = (clientId: string, data: unknown) =>
api.patch<InterfaceClient>(`/interface-center/clients/${encodeURIComponent(clientId)}`, data);
export const deleteInterfaceClient = (clientId: string) =>
api.delete(`/interface-center/clients/${encodeURIComponent(clientId)}`);
export const listInterfaceCredentials = (clientId?: string) =>
api.get<InterfaceCredential[]>("/interface-center/credentials", {
params: clientId ? { client_id: clientId } : undefined,
});
export const createInterfaceCredential = (clientId: string, data: unknown) =>
api.post<InterfaceCredential>(
`/interface-center/clients/${encodeURIComponent(clientId)}/credentials`,
data,
);
export const revokeInterfaceCredential = (credentialId: string) =>
api.post(`/interface-center/credentials/${encodeURIComponent(credentialId)}/revoke`, {});
export const listInterfacePolicies = () =>
api.get<InterfacePolicy[]>("/interface-center/policies");
export const createInterfacePolicy = (data: unknown) =>
api.post<InterfacePolicy>("/interface-center/policies", data);
export const updateInterfacePolicy = (policyId: string, data: unknown) =>
api.patch<InterfacePolicy>(`/interface-center/policies/${encodeURIComponent(policyId)}`, data);
export const deleteInterfacePolicy = (policyId: string) =>
api.delete(`/interface-center/policies/${encodeURIComponent(policyId)}`);
export const listInterfaceCallLogs = (limit = 200) =>
api.get<InterfaceCallLog[]>("/interface-center/logs", { params: { limit } });
export const listOntologySchemas = () => api.get("/ontology-schemas");
export const getCurrentOntologySchema = () => api.get("/ontology-schemas/current");
export const getOntologySchema = (schemaId: string | number) =>
api.get(`/ontology-schemas/${encodeURIComponent(String(schemaId))}`);
// ── Plaza (STEP 02) ──
export const plazaOverview = () => api.get("/plaza/overview");
export const plazaAmapConfig = () => api.get("/plaza/amap-config");
export const plazaWeatherHourly = () => api.get("/plaza/weather/hourly");
export const plazaMapPois = () => api.get("/plaza/map-pois");
export const plazaBusRoutes = () => api.get("/plaza/bus-routes");
export const plazaMapZones = () => api.get("/plaza/map-zones");
export const plazaMapPoiDetail = (placeId: string) =>
api.get(`/plaza/map-pois/${encodeURIComponent(placeId)}`);
export const plazaUserQuery = (data: unknown) => api.post("/plaza/user-query", data);
export const travelAssistantQuery = (data: unknown) => api.post("/travel/assistant-query", data);
// ── OCR (旅行社名单录入) ──
export const ocrPassport = (file: File) => {
const form = new FormData();
form.append("file", file);
return api.post("/ocr/passport", form, {
headers: { "Content-Type": "multipart/form-data" },
});
};
export const ocrPassportByUrl = (url: string) => {
const form = new FormData();
form.append("url", url);
return api.post("/ocr/passport", form);
};
export const graphOverview = () => api.get("/graph/overview");
export const graphQuery = (cypher: string, limit = 200) =>
api.post("/graph/query", { cypher, limit });
// ── User management ──
export const listUsers = () => api.get("/users");
export const createUser = (data: unknown) => api.post("/users", data);
export const updateUser = (id: number, data: unknown) =>
api.patch(`/users/${id}`, data);
// ── Super Agent (autonomous KG curator) ──
export const superAgentRun = () => api.post("/super-agent/run", {});
export const superAgentLatest = () => api.get("/super-agent/runs/latest");
export const superAgentGet = (id: number) => api.get(`/super-agent/runs/${id}`);
export const superAgentStop = (id: number) =>
api.post(`/super-agent/runs/${id}/stop`);
export const superAgentCoverage = () => api.get("/super-agent/coverage");
export const superAgentTasks = (runId?: number) =>
api.get("/super-agent/tasks", { params: runId ? { run_id: runId } : {} });