diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d59dc9..dd8f1211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Confirmation dialogs for deep link queries, connection imports, and pre-connect scripts + ## [0.25.0] - 2026-03-27 ### Added diff --git a/TablePro/AppDelegate+FileOpen.swift b/TablePro/AppDelegate+FileOpen.swift index a36aed20..d20ccc40 100644 --- a/TablePro/AppDelegate+FileOpen.swift +++ b/TablePro/AppDelegate+FileOpen.swift @@ -37,7 +37,7 @@ extension AppDelegate { let deeplinks = urls.filter { $0.scheme == "tablepro" } if !deeplinks.isEmpty { Task { @MainActor in - for url in deeplinks { self.handleDeeplink(url) } + for url in deeplinks { await self.handleDeeplink(url) } } } @@ -115,7 +115,7 @@ extension AppDelegate { // MARK: - Deeplink Handling - private func handleDeeplink(_ url: URL) { + private func handleDeeplink(_ url: URL) async { guard let action = DeeplinkHandler.parse(url) else { return } switch action { @@ -129,14 +129,23 @@ extension AppDelegate { } case .openQuery(let name, let sql): + let preview = (sql as NSString).length > 300 ? String(sql.prefix(300)) + "…" : sql + let confirmed = await AlertHelper.confirmDestructive( + title: String(localized: "Open Query from Link"), + message: String(localized: "An external link wants to open a query on connection \"\(name)\":\n\n\(preview)"), + confirmButton: String(localized: "Open Query"), + cancelButton: String(localized: "Cancel"), + window: NSApp.keyWindow + ) + guard confirmed else { return } connectViaDeeplink(connectionName: name) { connectionId in EditorTabPayload(connectionId: connectionId, tabType: .query, initialQuery: sql) } case .importConnection(let name, let host, let port, let type, let username, let database): - handleImportDeeplink(name: name, host: host, port: port, type: type, - username: username, database: database) + await handleImportDeeplink(name: name, host: host, port: port, type: type, + username: username, database: database) } } @@ -193,7 +202,18 @@ extension AppDelegate { private func handleImportDeeplink( name: String, host: String, port: Int, type: DatabaseType, username: String, database: String - ) { + ) async { + let userPart = username.isEmpty ? "" : "\(username)@" + let details = "\(type.rawValue)://\(userPart)\(host):\(port)/\(database)" + let confirmed = await AlertHelper.confirmDestructive( + title: String(localized: "Import Connection from Link"), + message: String(localized: "An external link wants to add a database connection:\n\nName: \(name)\n\(details)"), + confirmButton: String(localized: "Add Connection"), + cancelButton: String(localized: "Cancel"), + window: NSApp.keyWindow + ) + guard confirmed else { return } + let connection = DatabaseConnection( name: name, host: host, port: port, database: database, username: username, type: type diff --git a/TablePro/Core/Database/DatabaseManager.swift b/TablePro/Core/Database/DatabaseManager.swift index 5aa8b7e3..f9112058 100644 --- a/TablePro/Core/Database/DatabaseManager.swift +++ b/TablePro/Core/Database/DatabaseManager.swift @@ -5,6 +5,7 @@ // Created by Ngo Quoc Dat on 16/12/25. // +import AppKit import Foundation import Observation import os @@ -123,6 +124,22 @@ final class DatabaseManager { if let script = resolvedConnection.preConnectScript, !script.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + let confirmed = await AlertHelper.confirmDestructive( + title: String(localized: "Pre-Connect Script"), + message: String(localized: "Connection \"\(connection.name)\" has a script that will run before connecting:\n\n\(script)"), + confirmButton: String(localized: "Run Script"), + cancelButton: String(localized: "Cancel"), + window: NSApp.keyWindow + ) + + guard confirmed else { + removeSessionEntry(for: connection.id) + currentSessionId = nil + throw PreConnectHookRunner.HookError.scriptFailed( + exitCode: 1, stderr: "User cancelled pre-connect script" + ) + } + do { try await PreConnectHookRunner.run(script: script) } catch { diff --git a/docs/features/deep-links.mdx b/docs/features/deep-links.mdx index 8dee17f7..18aee69c 100644 --- a/docs/features/deep-links.mdx +++ b/docs/features/deep-links.mdx @@ -111,7 +111,7 @@ open "tablepro://connect/Production/database/analytics/table/events" tablepro://connect/{name}/query?sql={encoded_sql} ``` -Connects and opens a new query tab with the SQL pre-filled. The SQL must be percent-encoded. +Connects and opens a new query tab with the SQL pre-filled. The SQL must be percent-encoded. A confirmation dialog shows the SQL before opening, so you can verify the query is safe. ```bash open "tablepro://connect/Production/query?sql=SELECT%20*%20FROM%20users%20LIMIT%2010" @@ -123,7 +123,7 @@ open "tablepro://connect/Production/query?sql=SELECT%20*%20FROM%20users%20LIMIT% tablepro://import?name={n}&host={h}&port={p}&type={t}&username={u}&database={db} ``` -Creates a new saved connection and opens the connection form for review. You can add a password before connecting. +Creates a new saved connection and opens the connection form for review. A confirmation dialog shows the connection details before adding, so you can reject unexpected imports. You can add a password before connecting. ```bash open "tablepro://import?name=Staging&host=db.example.com&port=5432&type=postgresql&username=admin&database=mydb"