blob: 5428625c5380c5864a97e1f3504b97469d0abb4e [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import { useQuery } from '@tanstack/react-query';
import { legacyPostJsonMutation } from './operations';
export type ConfigurationScope = 'fe' | 'be';
export interface ConfigurationRow {
key: string;
name: string;
node: string;
nodeType: string;
valueType: string;
masterOnly: boolean | null;
currentValue: string;
mutable: boolean;
}
interface ConfigurationPayload {
column_names?: unknown;
columnNames?: unknown;
rows?: unknown;
}
const aliases = {
name: ['配置项', 'Name'],
node: ['节点', 'Node'],
nodeType: ['节点类型', 'Node Type'],
valueType: ['配置值类型', 'Value Type'],
masterOnly: ['MasterOnly', 'Master Only'],
currentValue: ['配置值', 'Current Value', 'Value'],
mutable: ['可修改', 'Mutable'],
} as const;
function columnIndex(columns: string[], names: readonly string[], fallback: number): number {
const index = names.map((name) => columns.indexOf(name)).find((candidate) => candidate !== -1);
return index ?? fallback;
}
function cell(row: unknown[], index: number): string {
if (index < 0 || index >= row.length) return '';
const value = row[index];
if (value === null || value === undefined) return '';
if (typeof value === 'string') return value;
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
return String(value);
}
return JSON.stringify(value);
}
function booleanValue(value: string): boolean {
return /^(true|yes|1)$/i.test(value.trim());
}
export function adaptConfiguration(payload: ConfigurationPayload, scope: ConfigurationScope): ConfigurationRow[] {
const rawColumns = payload.column_names ?? payload.columnNames;
const columns = Array.isArray(rawColumns) ? rawColumns.map(String) : [];
const fallback = scope === 'fe'
? { name: 0, node: 1, nodeType: 2, valueType: 3, masterOnly: 4, currentValue: 5, mutable: 6 }
: { name: 0, node: 1, nodeType: 2, valueType: 3, masterOnly: -1, currentValue: 4, mutable: 5 };
const indexes = {
name: columnIndex(columns, aliases.name, fallback.name),
node: columnIndex(columns, aliases.node, fallback.node),
nodeType: columnIndex(columns, aliases.nodeType, fallback.nodeType),
valueType: columnIndex(columns, aliases.valueType, fallback.valueType),
masterOnly: columnIndex(columns, aliases.masterOnly, fallback.masterOnly),
currentValue: columnIndex(columns, aliases.currentValue, fallback.currentValue),
mutable: columnIndex(columns, aliases.mutable, fallback.mutable),
};
const rows = Array.isArray(payload.rows) ? payload.rows : [];
return rows.filter(Array.isArray).map((rawRow, rowIndex) => {
const row = rawRow as unknown[];
const name = cell(row, indexes.name);
const node = cell(row, indexes.node);
return {
key: `${scope}:${node}:${name}:${rowIndex}`,
name,
node,
nodeType: cell(row, indexes.nodeType) || scope.toUpperCase(),
valueType: cell(row, indexes.valueType),
masterOnly: indexes.masterOnly < 0 ? null : booleanValue(cell(row, indexes.masterOnly)),
currentValue: cell(row, indexes.currentValue),
mutable: booleanValue(cell(row, indexes.mutable)),
};
});
}
/**
* Reads configuration. The UI never writes it: changing a setting goes through
* ADMIN SET FRONTEND CONFIG or the backend configuration API, both of which
* authenticate the operator directly.
*
* This is a POST that carries the CSRF header because the FE endpoint is a POST.
*/
export async function fetchConfiguration(scope: ConfigurationScope): Promise<ConfigurationRow[]> {
const payload = await legacyPostJsonMutation<ConfigurationPayload>(
`/rest/v2/manager/node/configuration_info?type=${scope}`,
{},
);
return adaptConfiguration(payload, scope);
}
export function useConfiguration(scope: ConfigurationScope) {
return useQuery({
queryKey: ['configuration', scope],
queryFn: () => fetchConfiguration(scope),
refetchInterval: false,
});
}