fix(dameng): support configurable system schemas
This commit is contained in:
15 files changed
+146
-66
No files matched your search
@@ -80,8 +80,8 @@ import {
|
||||
Square,
|
||||
Trash2,
|
||||
} from "@lucide/vue";
|
||||
import { buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatabases, initialVisibleDatabaseSelection, visibleDatabaseSelectionIsStale } from "@/lib/connection/connectionVisibleDatabases";
|
||||
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, isSystemDatabaseName, normalizeVisibleDatabaseSelection, buildDraftVisibleSchemasConnectionId, normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
|
||||
import { buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatabases, initialVisibleDatabaseSelection, visibleObjectFiltersNeedReset } from "@/lib/connection/connectionVisibleDatabases";
|
||||
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, filterSchemaNamesForVisiblePicker, normalizeVisibleDatabaseSelection, buildDraftVisibleSchemasConnectionId, normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
|
||||
import { isSchemaAware, isSingleDatabase } from "@/lib/database/databaseFeatureSupport";
|
||||
import VisibleSchemasDialog from "@/components/sidebar/VisibleSchemasDialog.vue";
|
||||
import CloudflareD1ConnectionFields from "@/components/connection/CloudflareD1ConnectionFields.vue";
|
||||
@@ -1745,6 +1745,7 @@ watch(
|
||||
is_production: config.is_production || false,
|
||||
production_databases: config.production_databases || [],
|
||||
visible_databases: config.visible_databases,
|
||||
visible_schemas: config.visible_schemas,
|
||||
};
|
||||
productionProtectionEnabled.value = !!config.is_production || (config.production_databases?.length ?? 0) > 0;
|
||||
connectionUrlInput.value = config.db_type === "h2" && config.connection_string ? config.connection_string : "";
|
||||
@@ -2240,12 +2241,12 @@ const visibleDatabaseSummary = computed(() => {
|
||||
if (!Array.isArray(configured)) return t("visibleDatabases.showAll");
|
||||
return t("visibleDatabases.selectedCount", { selected: configured.length, total: visibleDatabaseNames.value.length });
|
||||
});
|
||||
const listedVisibleDatabaseNames = computed(() => {
|
||||
if (visibleFilterUsesSchemas.value) return visibleDatabaseNames.value;
|
||||
const defaultListedVisibleDatabaseNames = computed(() => {
|
||||
const connection = connectionConfigSnapshotForVisibleDatabases();
|
||||
if (visibleDatabaseShowSystem.value) return visibleDatabaseNames.value;
|
||||
if (visibleFilterUsesSchemas.value) return filterSchemaNamesForVisiblePicker(visibleDatabaseNames.value, connection);
|
||||
return filterDatabaseNamesForVisiblePicker(visibleDatabaseNames.value, connection);
|
||||
});
|
||||
const listedVisibleDatabaseNames = computed(() => (visibleDatabaseShowSystem.value ? visibleDatabaseNames.value : defaultListedVisibleDatabaseNames.value));
|
||||
const filteredVisibleDatabaseNames = computed(() => {
|
||||
const query = visibleDatabaseSearchText.value.trim().toLowerCase();
|
||||
if (!query) return listedVisibleDatabaseNames.value;
|
||||
@@ -2254,11 +2255,8 @@ const filteredVisibleDatabaseNames = computed(() => {
|
||||
const visibleDatabaseSelectedCount = computed(() => visibleDatabaseSelection.value.size);
|
||||
const visibleDatabaseTotalCount = computed(() => listedVisibleDatabaseNames.value.length);
|
||||
const visibleDatabaseCanSave = computed(() => canSaveVisibleDatabaseSelection([...visibleDatabaseSelection.value]));
|
||||
const visibleDatabaseHasSystemDatabases = computed(() => {
|
||||
if (visibleFilterUsesSchemas.value) return false;
|
||||
const connection = connectionConfigSnapshotForVisibleDatabases();
|
||||
return visibleDatabaseNames.value.some((database) => isSystemDatabaseName(connection.db_type, database));
|
||||
});
|
||||
const visibleDatabaseHasSystemObjects = computed(() => defaultListedVisibleDatabaseNames.value.length < visibleDatabaseNames.value.length);
|
||||
const visibleSystemObjectsLabelKey = computed(() => (visibleFilterUsesSchemas.value ? "visibleSchemas.showSystemSchemas" : "visibleDatabases.showSystemDatabases"));
|
||||
const filteredProductionDatabaseNames = computed(() => {
|
||||
const query = productionDatabaseSearchText.value.trim().toLowerCase();
|
||||
if (!query) return productionDatabaseNames.value;
|
||||
@@ -3227,10 +3225,12 @@ async function openVisibleDatabasesPicker() {
|
||||
await api.connectDb(draftConfig);
|
||||
const names = await loadVisibleDatabaseNames(draftId, draftConfig);
|
||||
visibleDatabaseNames.value = names;
|
||||
visibleDatabaseShowSystem.value = false;
|
||||
const configuredSchemas = visibleSchemaObjectSelection.value;
|
||||
const initialSelection = visibleFilterUsesSchemas.value ? (Array.isArray(configuredSchemas) ? normalizeVisibleSchemaSelection(configuredSchemas, names) : names) : initialVisibleDatabaseSelection(names, form.value.visible_databases, draftConfig);
|
||||
const initialSelection = visibleFilterUsesSchemas.value ? (Array.isArray(configuredSchemas) ? normalizeVisibleSchemaSelection(configuredSchemas, names) : filterSchemaNamesForVisiblePicker(names, draftConfig)) : initialVisibleDatabaseSelection(names, form.value.visible_databases, draftConfig);
|
||||
visibleDatabaseSelection.value = new Set(initialSelection);
|
||||
visibleDatabaseShowSystem.value = !visibleFilterUsesSchemas.value && initialSelection.some((database) => isSystemDatabaseName(draftConfig.db_type, database));
|
||||
const defaultVisible = new Set(defaultListedVisibleDatabaseNames.value);
|
||||
visibleDatabaseShowSystem.value = initialSelection.some((name) => !defaultVisible.has(name));
|
||||
showVisibleDatabasesDialog.value = true;
|
||||
} catch (e: any) {
|
||||
visibleDatabaseNames.value = [];
|
||||
@@ -3623,18 +3623,19 @@ watch([() => form.value.db_type, () => form.value.username], () => {
|
||||
watch(
|
||||
() => connectionConfigSnapshotForVisibleDatabases(),
|
||||
(current, previous) => {
|
||||
if (!previous || !form.value.visible_databases?.length) return;
|
||||
if (!visibleDatabaseSelectionIsStale(previous, current)) return;
|
||||
if (!previous || !visibleObjectFiltersNeedReset(previous, current)) return;
|
||||
form.value.visible_databases = undefined;
|
||||
visibleDatabaseNames.value = [];
|
||||
visibleDatabaseSelection.value = new Set();
|
||||
form.value.visible_schemas = undefined;
|
||||
resetVisibleDatabaseDraftState();
|
||||
resetVisibleSchemasState();
|
||||
},
|
||||
);
|
||||
|
||||
watch(visibleDatabaseShowSystem, (show) => {
|
||||
if (show) return;
|
||||
const connection = connectionConfigSnapshotForVisibleDatabases();
|
||||
visibleDatabaseSelection.value = new Set([...visibleDatabaseSelection.value].filter((database) => !isSystemDatabaseName(connection.db_type, database)));
|
||||
const visible = new Set(visibleFilterUsesSchemas.value ? filterSchemaNamesForVisiblePicker(visibleDatabaseNames.value, connection) : filterDatabaseNamesForVisiblePicker(visibleDatabaseNames.value, connection));
|
||||
visibleDatabaseSelection.value = new Set([...visibleDatabaseSelection.value].filter((name) => visible.has(name)));
|
||||
});
|
||||
|
||||
watch(canUseTransportLayers, (value) => {
|
||||
@@ -6250,9 +6251,9 @@ function openExternalUrl(url: string) {
|
||||
{{ t(visibleObjectEmptySelectionKey) }}
|
||||
</p>
|
||||
|
||||
<label v-if="visibleDatabaseHasSystemDatabases" class="flex h-8 items-center gap-2 rounded-md px-1 text-xs text-muted-foreground">
|
||||
<label v-if="visibleDatabaseHasSystemObjects" class="flex h-8 items-center gap-2 rounded-md px-1 text-xs text-muted-foreground">
|
||||
<input v-model="visibleDatabaseShowSystem" type="checkbox" class="h-3.5 w-3.5 accent-primary" :disabled="isLoadingVisibleDatabases || !!visibleDatabaseError" />
|
||||
<span>{{ t("visibleDatabases.showSystemDatabases") }}</span>
|
||||
<span>{{ t(visibleSystemObjectsLabelKey) }}</span>
|
||||
</label>
|
||||
|
||||
<div class="h-72 overflow-y-auto rounded-md border bg-background/50 p-1">
|
||||
@@ -6364,6 +6365,8 @@ function openExternalUrl(url: string) {
|
||||
:connection-id="''"
|
||||
:connection-name="form.name || selectedProfile().label"
|
||||
:database="visibleSchemasDatabaseKey"
|
||||
:database-type="form.db_type"
|
||||
:username="form.username"
|
||||
:draft-schema-names="visibleSchemaNames"
|
||||
:draft-initial-selection="visibleSchemaInitialSelection"
|
||||
:draft-loading="isLoadingVisibleSchemas"
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, isSystemDatabaseName, normalizeVisibleDatabaseSelection } from "@/lib/database/visibleDatabases";
|
||||
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, filterSchemaNamesForVisiblePicker, normalizeVisibleDatabaseSelection } from "@/lib/database/visibleDatabases";
|
||||
import * as api from "@/lib/backend/api";
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -40,11 +40,12 @@ const descriptionKey = computed(() => (isSchemaFilterMode.value ? "visibleSchema
|
||||
const searchPlaceholderKey = computed(() => (isSchemaFilterMode.value ? "visibleSchemas.searchPlaceholder" : "visibleDatabases.searchPlaceholder"));
|
||||
const emptySelectionKey = computed(() => (isSchemaFilterMode.value ? "visibleSchemas.emptySelection" : "visibleDatabases.emptySelection"));
|
||||
const loadFailedKey = computed(() => (isSchemaFilterMode.value ? "visibleSchemas.loadFailed" : "visibleDatabases.loadFailed"));
|
||||
const listedObjectNames = computed(() => {
|
||||
if (isSchemaFilterMode.value) return objectNames.value;
|
||||
if (showSystemDatabases.value) return objectNames.value;
|
||||
const showSystemObjectsKey = computed(() => (isSchemaFilterMode.value ? "visibleSchemas.showSystemSchemas" : "visibleDatabases.showSystemDatabases"));
|
||||
const defaultObjectNames = computed(() => {
|
||||
if (isSchemaFilterMode.value) return filterSchemaNamesForVisiblePicker(objectNames.value, connection.value);
|
||||
return filterDatabaseNamesForVisiblePicker(objectNames.value, connection.value);
|
||||
});
|
||||
const listedObjectNames = computed(() => (showSystemDatabases.value ? objectNames.value : defaultObjectNames.value));
|
||||
const filteredObjectNames = computed(() => {
|
||||
const query = searchText.value.trim().toLowerCase();
|
||||
if (!query) return listedObjectNames.value;
|
||||
@@ -53,7 +54,7 @@ const filteredObjectNames = computed(() => {
|
||||
const selectedCount = computed(() => selectedNames.value.size);
|
||||
const totalCount = computed(() => listedObjectNames.value.length);
|
||||
const canSaveSelection = computed(() => canSaveVisibleDatabaseSelection([...selectedNames.value]));
|
||||
const hasSystemDatabases = computed(() => !isSchemaFilterMode.value && objectNames.value.some((database) => isSystemDatabaseName(connection.value?.db_type, database)));
|
||||
const hasSystemObjects = computed(() => defaultObjectNames.value.length < objectNames.value.length);
|
||||
const showAllDisabled = computed(() => {
|
||||
if (isSchemaFilterMode.value) {
|
||||
return !connection.value?.visible_schemas?.[databaseKey.value];
|
||||
@@ -73,7 +74,8 @@ watch(
|
||||
|
||||
watch(showSystemDatabases, (show) => {
|
||||
if (show) return;
|
||||
selectedNames.value = new Set([...selectedNames.value].filter((database) => !isSystemDatabaseName(connection.value?.db_type, database)));
|
||||
const visible = new Set(listedObjectNames.value);
|
||||
selectedNames.value = new Set([...selectedNames.value].filter((name) => visible.has(name)));
|
||||
});
|
||||
|
||||
async function loadDatabases() {
|
||||
@@ -83,10 +85,12 @@ async function loadDatabases() {
|
||||
try {
|
||||
const names = await loadObjectNames();
|
||||
objectNames.value = names;
|
||||
showSystemDatabases.value = false;
|
||||
const configured = isSchemaFilterMode.value ? connection.value?.visible_schemas?.[databaseKey.value] : connection.value?.visible_databases;
|
||||
const initialSelection = Array.isArray(configured) ? normalizeVisibleDatabaseSelection(configured, names) : listedObjectNames.value;
|
||||
selectedNames.value = new Set(initialSelection);
|
||||
showSystemDatabases.value = !isSchemaFilterMode.value && initialSelection.some((database) => isSystemDatabaseName(connection.value?.db_type, database));
|
||||
const defaultVisible = new Set(defaultObjectNames.value);
|
||||
showSystemDatabases.value = initialSelection.some((name) => !defaultVisible.has(name));
|
||||
} catch (e: any) {
|
||||
objectNames.value = [];
|
||||
selectedNames.value = new Set();
|
||||
@@ -190,9 +194,9 @@ async function saveSelection() {
|
||||
{{ t(emptySelectionKey) }}
|
||||
</p>
|
||||
|
||||
<label v-if="hasSystemDatabases" class="flex h-8 items-center gap-2 rounded-md px-1 text-xs text-muted-foreground">
|
||||
<label v-if="hasSystemObjects" class="flex h-8 items-center gap-2 rounded-md px-1 text-xs text-muted-foreground">
|
||||
<input v-model="showSystemDatabases" type="checkbox" class="h-3.5 w-3.5 accent-primary" :disabled="isLoading || !!errorMessage" />
|
||||
<span>{{ t("visibleDatabases.showSystemDatabases") }}</span>
|
||||
<span>{{ t(showSystemObjectsKey) }}</span>
|
||||
</label>
|
||||
|
||||
<div class="h-72 overflow-y-auto rounded-md border bg-background/50 p-1">
|
||||
|
||||
@@ -7,7 +7,8 @@ import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
|
||||
import { filterSchemaNamesForVisiblePicker, normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { addVisibleSchemas, initialVisibleSchemaSelection, resolveVisibleSchemaSaveAction, selectVisibleSchemas } from "./visibleSchemasDialogState";
|
||||
|
||||
@@ -16,6 +17,8 @@ const props = defineProps<{
|
||||
connectionId: string;
|
||||
connectionName: string;
|
||||
database: string;
|
||||
databaseType?: DatabaseType;
|
||||
username?: string;
|
||||
/** Draft mode: parent provides schemas/loading/error externally. Component emits events instead of calling store. */
|
||||
draftMode?: boolean;
|
||||
draftSchemaNames?: string[];
|
||||
@@ -39,22 +42,26 @@ const searchText = ref("");
|
||||
const isLoading = ref(false);
|
||||
const isSaving = ref(false);
|
||||
const errorMessage = ref("");
|
||||
const showSystemSchemas = ref(false);
|
||||
|
||||
const isDraftMode = computed(() => props.draftMode);
|
||||
const isLoadingState = computed(() => (isDraftMode.value ? props.draftLoading : isLoading.value));
|
||||
const errorMessageState = computed(() => (isDraftMode.value ? props.draftError || "" : errorMessage.value));
|
||||
const connection = computed(() => (isDraftMode.value ? { db_type: props.databaseType, username: props.username || "" } : connectionStore.getConfig(props.connectionId)));
|
||||
const allSchemaNames = computed(() => (isDraftMode.value ? props.draftSchemaNames || [] : schemaNames.value));
|
||||
const listedSchemaNames = computed(() => (showSystemSchemas.value ? allSchemaNames.value : filterSchemaNamesForVisiblePicker(allSchemaNames.value, connection.value)));
|
||||
const hasSystemSchemas = computed(() => filterSchemaNamesForVisiblePicker(allSchemaNames.value, connection.value).length < allSchemaNames.value.length);
|
||||
|
||||
const filteredSchemaNames = computed(() => {
|
||||
const query = searchText.value.trim().toLowerCase();
|
||||
const names = isDraftMode.value ? props.draftSchemaNames || [] : schemaNames.value;
|
||||
const names = listedSchemaNames.value;
|
||||
if (!query) return names;
|
||||
return names.filter((name) => name.toLowerCase().includes(query));
|
||||
});
|
||||
const filteredSchemaItems = computed(() => filteredSchemaNames.value.map((name) => ({ name })));
|
||||
const selectedCount = computed(() => selectedNames.value.size);
|
||||
const totalCount = computed(() => {
|
||||
const names = isDraftMode.value ? props.draftSchemaNames || [] : schemaNames.value;
|
||||
return names.length;
|
||||
return listedSchemaNames.value.length;
|
||||
});
|
||||
const canSaveSelection = computed(() => selectedNames.value.size > 0);
|
||||
|
||||
@@ -75,8 +82,7 @@ watch(
|
||||
function initDraftMode() {
|
||||
searchText.value = "";
|
||||
const names = props.draftSchemaNames || [];
|
||||
// Draft callers historically use an empty array for both absent and all-selected state.
|
||||
selectedNames.value = initialVisibleSchemaSelection(names, props.draftInitialSelection, true);
|
||||
initializeSelection(names, props.draftInitialSelection, true);
|
||||
}
|
||||
|
||||
async function loadSchemas() {
|
||||
@@ -90,7 +96,7 @@ async function loadSchemas() {
|
||||
const names = await api.listSchemas(props.connectionId, database);
|
||||
schemaNames.value = names;
|
||||
const configured = config?.visible_schemas?.[database || ""];
|
||||
selectedNames.value = initialVisibleSchemaSelection(names, configured);
|
||||
initializeSelection(names, configured);
|
||||
} catch (e: any) {
|
||||
schemaNames.value = [];
|
||||
selectedNames.value = new Set();
|
||||
@@ -100,6 +106,21 @@ async function loadSchemas() {
|
||||
}
|
||||
}
|
||||
|
||||
function initializeSelection(names: string[], configured: string[] | undefined, emptySelectionMeansAll = false) {
|
||||
showSystemSchemas.value = false;
|
||||
const hasExplicitSelection = configured !== undefined && !(emptySelectionMeansAll && configured.length === 0);
|
||||
const initial = hasExplicitSelection ? initialVisibleSchemaSelection(names, configured) : selectVisibleSchemas(filterSchemaNamesForVisiblePicker(names, connection.value));
|
||||
const defaultVisible = new Set(filterSchemaNamesForVisiblePicker(names, connection.value));
|
||||
selectedNames.value = initial;
|
||||
showSystemSchemas.value = hasExplicitSelection && [...initial].some((name) => !defaultVisible.has(name));
|
||||
}
|
||||
|
||||
watch(showSystemSchemas, (show) => {
|
||||
if (show) return;
|
||||
const visible = new Set(listedSchemaNames.value);
|
||||
selectedNames.value = new Set([...selectedNames.value].filter((name) => visible.has(name)));
|
||||
});
|
||||
|
||||
function toggleSchema(schema: string) {
|
||||
const next = new Set(selectedNames.value);
|
||||
if (next.has(schema)) next.delete(schema);
|
||||
@@ -110,8 +131,7 @@ function toggleSchema(schema: string) {
|
||||
const isSearching = computed(() => searchText.value.trim().length > 0);
|
||||
|
||||
function selectAll() {
|
||||
const names = isDraftMode.value ? props.draftSchemaNames || [] : schemaNames.value;
|
||||
selectedNames.value = selectVisibleSchemas(names);
|
||||
selectedNames.value = selectVisibleSchemas(listedSchemaNames.value);
|
||||
}
|
||||
|
||||
function selectFiltered() {
|
||||
@@ -148,7 +168,7 @@ async function showAllSchemas() {
|
||||
|
||||
async function saveSelection() {
|
||||
if (!canSaveSelection.value || isSaving.value) return;
|
||||
const names = isDraftMode.value ? props.draftSchemaNames || [] : schemaNames.value;
|
||||
const names = listedSchemaNames.value;
|
||||
const normalized = normalizeVisibleSchemaSelection([...selectedNames.value], names);
|
||||
if (isDraftMode.value) {
|
||||
emit("draft:save", normalized);
|
||||
@@ -211,6 +231,11 @@ async function saveSelection() {
|
||||
{{ t("visibleSchemas.emptySelection") }}
|
||||
</p>
|
||||
|
||||
<label v-if="hasSystemSchemas" class="flex h-8 items-center gap-2 rounded-md px-1 text-xs text-muted-foreground">
|
||||
<input v-model="showSystemSchemas" type="checkbox" class="h-3.5 w-3.5 accent-primary" :disabled="isLoadingState || !!errorMessageState" />
|
||||
<span>{{ t("visibleSchemas.showSystemSchemas") }}</span>
|
||||
</label>
|
||||
|
||||
<div v-if="isLoadingState || errorMessageState || !filteredSchemaItems.length" class="h-72 overflow-y-auto rounded-md border bg-background/50 p-1">
|
||||
<div v-if="isLoadingState" class="flex h-full items-center justify-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 class="h-4 w-4 animate-spin" />
|
||||
|
||||
Reference in new issue
Block a user