mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 08:13:34 +00:00
fix: replace crypto.randomUUID with polyfill for HTTP contexts
crypto.randomUUID() is only available in secure contexts (HTTPS/localhost). Add uuid() utility with fallback for HTTP deployments.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, ref, watch } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -400,7 +401,7 @@ async function testConnection() {
|
||||
isTesting.value = true;
|
||||
testResult.value = null;
|
||||
try {
|
||||
const config: ConnectionConfig = { ...form.value, id: editingId.value || crypto.randomUUID() };
|
||||
const config: ConnectionConfig = { ...form.value, id: editingId.value || uuid() };
|
||||
const msg = await api.testConnection(config);
|
||||
if (runId !== testRunId) return;
|
||||
testResult.value = { ok: true, message: msg };
|
||||
@@ -465,7 +466,7 @@ async function save() {
|
||||
await store.updateConnection(updated);
|
||||
store.stopEditing();
|
||||
} else {
|
||||
const config: ConnectionConfig = { ...form.value, id: crypto.randomUUID() };
|
||||
const config: ConnectionConfig = { ...form.value, id: uuid() };
|
||||
await store.addConnection(config);
|
||||
open.value = false;
|
||||
await nextTick();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
ArrowUp,
|
||||
@@ -276,7 +277,7 @@ async function send() {
|
||||
isGenerating.value = true;
|
||||
messages.value.push({ role: "assistant", content: "" });
|
||||
const assistantIdx = messages.value.length - 1;
|
||||
const sessionId = crypto.randomUUID();
|
||||
const sessionId = uuid();
|
||||
currentSessionId.value = sessionId;
|
||||
try {
|
||||
const context = await buildAiContext(props.tab, props.connection);
|
||||
@@ -344,7 +345,7 @@ function clearMessages() {
|
||||
|
||||
async function persistConversation() {
|
||||
if (!messages.value.length || !props.connection) return;
|
||||
if (!conversationId.value) conversationId.value = crypto.randomUUID();
|
||||
if (!conversationId.value) conversationId.value = uuid();
|
||||
const first = messages.value.find((m) => m.role === "user");
|
||||
await saveAiConversation({
|
||||
id: conversationId.value,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { isTauriRuntime } from "@/lib/tauriRuntime";
|
||||
import { Dialog, DialogHeader, DialogTitle, DialogFooter, DialogScrollContent } from "@/components/ui/dialog";
|
||||
@@ -165,7 +166,7 @@ async function startImport() {
|
||||
running.value = true;
|
||||
cancelling.value = false;
|
||||
errorMessage.value = "";
|
||||
importId.value = crypto.randomUUID();
|
||||
importId.value = uuid();
|
||||
progress.value = {
|
||||
importId: importId.value,
|
||||
status: "running",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Plus, Trash2 } from "lucide-vue-next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -67,7 +68,7 @@ function fieldValueTone(value: string): string {
|
||||
|
||||
function createBlankNode(keyName: string, readonlyKey: boolean): EditNode {
|
||||
return {
|
||||
key: crypto.randomUUID(),
|
||||
key: uuid(),
|
||||
keyName,
|
||||
kind: "value",
|
||||
valueText: "",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { RefreshCw, Trash2, Plus, Save, ChevronLeft, ChevronRight } from "lucide-vue-next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -130,7 +131,7 @@ function cancelEdit() {
|
||||
function createEditNode(keyName: string, value: unknown, readonlyKey: boolean, readonlyValue: boolean): EditNode {
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
key: crypto.randomUUID(),
|
||||
key: uuid(),
|
||||
keyName,
|
||||
kind: "array",
|
||||
valueText: "",
|
||||
@@ -142,7 +143,7 @@ function createEditNode(keyName: string, value: unknown, readonlyKey: boolean, r
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
return {
|
||||
key: crypto.randomUUID(),
|
||||
key: uuid(),
|
||||
keyName,
|
||||
kind: "object",
|
||||
valueText: "",
|
||||
@@ -155,7 +156,7 @@ function createEditNode(keyName: string, value: unknown, readonlyKey: boolean, r
|
||||
}
|
||||
|
||||
return {
|
||||
key: crypto.randomUUID(),
|
||||
key: uuid(),
|
||||
keyName,
|
||||
kind: "value",
|
||||
valueText: formatForEdit(value),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { AlertCircle, Loader2, Search, Square, Table2 } from "lucide-vue-next";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -129,7 +130,7 @@ function filterSearchSchemas(schemas: string[]): string[] {
|
||||
}
|
||||
|
||||
function makeExecutionId(): string {
|
||||
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return `database-search-${crypto.randomUUID()}`;
|
||||
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return `database-search-${uuid()}`;
|
||||
return `database-search-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { isTauriRuntime } from "@/lib/tauriRuntime";
|
||||
import { Dialog, DialogFooter, DialogHeader, DialogScrollContent, DialogTitle } from "@/components/ui/dialog";
|
||||
@@ -234,7 +235,7 @@ async function selectFile() {
|
||||
async function startExecution() {
|
||||
if (!canStart.value || !preview.value) return;
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
const id = uuid();
|
||||
executionId.value = id;
|
||||
running.value = true;
|
||||
cancelling.value = false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Dialog, DialogFooter, DialogHeader, DialogScrollContent, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -167,7 +168,7 @@ async function loadStructure() {
|
||||
|
||||
function addColumn() {
|
||||
columns.value.push({
|
||||
id: `new:${crypto.randomUUID()}`,
|
||||
id: `new:${uuid()}`,
|
||||
name: "",
|
||||
dataType: "varchar(255)",
|
||||
isNullable: true,
|
||||
@@ -189,7 +190,7 @@ function toggleDropColumn(column: EditableStructureColumn) {
|
||||
|
||||
function addIndex() {
|
||||
indexes.value.push({
|
||||
id: `new:${crypto.randomUUID()}`,
|
||||
id: `new:${uuid()}`,
|
||||
name: "",
|
||||
columns: [],
|
||||
isUnique: false,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Dialog, DialogHeader, DialogTitle, DialogFooter, DialogScrollContent } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -204,7 +205,7 @@ async function startTransfer() {
|
||||
overallCancelled.value = false;
|
||||
transferProgress.value.clear();
|
||||
|
||||
transferId.value = crypto.randomUUID();
|
||||
transferId.value = uuid();
|
||||
|
||||
// Auto-detect target schema
|
||||
const targetConfig = store.getConfig(targetConnectionId.value);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
@@ -41,7 +42,7 @@ export function useFileDrop() {
|
||||
const dataQuery = getDataFileQuery(path);
|
||||
if (dataQuery) {
|
||||
const config: ConnectionConfig = {
|
||||
id: crypto.randomUUID(),
|
||||
id: uuid(),
|
||||
name: `[Preview] ${name}`,
|
||||
db_type: "duckdb",
|
||||
driver_profile: "duckdb",
|
||||
@@ -64,7 +65,7 @@ export function useFileDrop() {
|
||||
const dbType = getDbType(path);
|
||||
if (!dbType) continue;
|
||||
const config: ConnectionConfig = {
|
||||
id: crypto.randomUUID(),
|
||||
id: uuid(),
|
||||
name,
|
||||
db_type: dbType,
|
||||
driver_profile: dbType,
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import type { AiConfig } from "@/stores/settingsStore";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import type {
|
||||
ColumnInfo,
|
||||
ConnectionConfig,
|
||||
@@ -111,7 +112,7 @@ export async function runAiStream(
|
||||
|
||||
const messages: api.AiMessage[] = [...(history || []), { role: "user", content: userPrompt }];
|
||||
|
||||
const sid = sessionId || crypto.randomUUID();
|
||||
const sid = sessionId || uuid();
|
||||
const params = actionParams(input.action);
|
||||
|
||||
await api.aiStream(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ConnectionConfig, ConnectionGroup, SidebarLayout, SidebarOrderEntry, TreeNode } from "@/types/database";
|
||||
import { uuid } from "@/lib/utils";
|
||||
|
||||
export function emptyLayout(): SidebarLayout {
|
||||
return { groups: [], order: [] };
|
||||
@@ -234,7 +235,7 @@ function reorderConnection(
|
||||
}
|
||||
|
||||
export function createGroup(layout: SidebarLayout, name: string): { layout: SidebarLayout; groupId: string } {
|
||||
const groupId = crypto.randomUUID();
|
||||
const groupId = uuid();
|
||||
const group: ConnectionGroup = { id: groupId, name, collapsed: false };
|
||||
return {
|
||||
groupId,
|
||||
|
||||
@@ -5,3 +5,13 @@ import { twMerge } from "tailwind-merge";
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function uuid(): string {
|
||||
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { ref, watch } from "vue";
|
||||
import type { ColumnInfo, ConnectionConfig, SidebarLayout, TreeNode } from "@/types/database";
|
||||
import { orderPinnedFirst } from "@/lib/pinnedItems";
|
||||
@@ -866,7 +867,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
(c) => c.name === config.name && c.host === config.host && c.port === config.port,
|
||||
);
|
||||
if (!duplicate) {
|
||||
config.id = crypto.randomUUID();
|
||||
config.id = uuid();
|
||||
const normalized = normalizeConnection(config);
|
||||
await addConnection(normalized);
|
||||
count++;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { ref } from "vue";
|
||||
import * as api from "@/lib/api";
|
||||
import type { HistoryEntry } from "@/lib/api";
|
||||
@@ -19,7 +20,7 @@ export const useHistoryStore = defineStore("history", () => {
|
||||
async function add(entry: Omit<HistoryEntry, "id" | "executed_at">) {
|
||||
const full: HistoryEntry = {
|
||||
...entry,
|
||||
id: crypto.randomUUID(),
|
||||
id: uuid(),
|
||||
executed_at: new Date().toISOString(),
|
||||
};
|
||||
await api.saveHistory(full);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { ref, watch } from "vue";
|
||||
import type { DatabaseType, QueryTab } from "@/types/database";
|
||||
import { orderPinnedFirst } from "@/lib/pinnedItems";
|
||||
@@ -87,7 +88,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
}
|
||||
}
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
const id = uuid();
|
||||
const tab: QueryTab = {
|
||||
id,
|
||||
title: title || `Query ${tabs.value.length + 1}`,
|
||||
@@ -222,7 +223,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const tab = tabs.value.find((t) => t.id === id);
|
||||
if (!tab || !sql.trim()) return;
|
||||
|
||||
const executionId = crypto.randomUUID();
|
||||
const executionId = uuid();
|
||||
tab.isExecuting = true;
|
||||
tab.isCancelling = false;
|
||||
tab.executionId = executionId;
|
||||
@@ -270,7 +271,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
return built;
|
||||
}
|
||||
|
||||
const executionId = crypto.randomUUID();
|
||||
const executionId = uuid();
|
||||
tab.isExplaining = true;
|
||||
tab.explainExecutionId = executionId;
|
||||
tab.explainError = undefined;
|
||||
|
||||
Reference in New Issue
Block a user