From 548fb94932072bdcbbfe39e209dde1bd78565618 Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Sun, 1 Feb 2026 13:16:58 +0000 Subject: [PATCH] feat: allow setting API key via file path Signed-off-by: Nikolaos Karaolidis --- package-lock.json | 38 ++++++++++++++++++-- src/geosearch.ts | 15 +++++--- src/settings.ts | 3 ++ src/settingsTab.ts | 87 +++++++++++++++++++++++++++++++++++++++++---- src/urlConvertor.ts | 9 +++-- 5 files changed, 135 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b6279f..9084692 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-map-view", - "version": "6.1.3", + "version": "6.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-map-view", - "version": "6.1.3", + "version": "6.1.4", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.1", @@ -238,6 +238,7 @@ "integrity": "sha512-y1IOpG6OSmTpGg/CT0YBb/EAhR2nsC18QWp9Jy8HO9iGySpcwaTvs5kHa17daP3BMTwWyaX9/1tDTDQshZzXdg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-common": "5.49.2", "@algolia/requester-browser-xhr": "5.49.2", @@ -623,6 +624,7 @@ "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -633,6 +635,7 @@ "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -728,6 +731,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -768,6 +772,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -1753,6 +1758,7 @@ "integrity": "sha512-7199re3wvMAlVqXLaCyAr8IkJSXqkeVAxcYyB2rBu4Id5m2hhlGX1dQsdMBiCXLwu6/LLVqDvJggSNVQBzL6ZQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/css-font-loading-module": "^0.0.7" }, @@ -1802,6 +1808,7 @@ "integrity": "sha512-0XtvrfxHlS2T+beBBSpo7GI8+QLyyTqMVQpNmPqB4woYxzrOEJ9JaUFBaBfCvycLeUkfVih1u6HAbtF+2d1EjQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@pixi/color": "7.2.4", "@pixi/constants": "7.2.4", @@ -1824,6 +1831,7 @@ "integrity": "sha512-w5tqb8cWEO5qIDaO9GEqRvxYhL0iMk0Wsngw23bbLm1gLEQmrFkB2tpJlRAqd7H82C3DrDDeWvkrrxW6+m4apg==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@pixi/core": "7.2.4" } @@ -1834,6 +1842,7 @@ "integrity": "sha512-/JtmoB98fzIU8giN9xvlRvmvOi6u4MaD2DnKNOMHkQ1MBraj3pmrXM9fZ0JbNzi+324GraAAY76QidgHjIYoYQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@pixi/core": "7.2.4", "@pixi/display": "7.2.4" @@ -1922,6 +1931,7 @@ "integrity": "sha512-3A2EumTjWJgXlDLOyuBrl9b6v1Za/E+/IjOGUIX843HH4NYaf1a2sfDfljx6r3oiDvy+VhuBFmgynRcV5IyA0Q==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@pixi/core": "7.2.4", "@pixi/display": "7.2.4", @@ -1941,6 +1951,7 @@ "integrity": "sha512-wiALIqcRKib2BqeH9kOA5fOKWN352nqAspgbDa8gA7OyWzmNwqIedIlElixd0oLFOrIN5jOZAdzeKnoYQlt9Aw==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@pixi/core": "7.2.4", "@pixi/display": "7.2.4" @@ -2047,6 +2058,7 @@ "integrity": "sha512-DhR1B+/d0eXpxHIesJMXcVPrKFwQ+zRA1LvEIFfzewqfaRN3X6PMIuoKX8SIb6tl+Hq8Ba9Pe28zI7d2rmRzrA==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@pixi/core": "7.2.4", "@pixi/display": "7.2.4" @@ -2092,6 +2104,7 @@ "integrity": "sha512-DGu7ktpe+zHhqR2sG9NsJt4mgvSObv5EqXTtUxD4Z0li1gmqF7uktpLyn5I6vSg1TTEL4TECClRDClVDGiykWw==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@pixi/core": "7.2.4", "@pixi/sprite": "7.2.4" @@ -2142,6 +2155,7 @@ "integrity": "sha512-VUGQHBOINIS4ePzoqafwxaGPVRTa3oM/mEutIIHbNGI3b+QvSO+1Dnk40M0zcH6Bo+MxQZbOZK5X/wO9oU5+LQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@pixi/color": "7.2.4", "@pixi/constants": "7.2.4", @@ -4578,6 +4592,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4628,6 +4643,7 @@ "integrity": "sha512-1K0wtDaRONwfhL4h8bbJ9qTjmY6rhGgRvvagXkMBsAOMNr+3Q2SffHECh9DIuNVrMA1JwA0zCwhyepgBZVakng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@algolia/abtesting": "1.15.2", "@algolia/client-abtesting": "5.49.2", @@ -4910,6 +4926,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5758,6 +5775,7 @@ "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -6192,6 +6210,7 @@ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -7002,6 +7021,7 @@ "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "tabbable": "^6.4.0" } @@ -8026,6 +8046,7 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -8184,7 +8205,8 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/leaflet-extra-markers": { "version": "1.2.2", @@ -8401,6 +8423,7 @@ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", + "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -10576,6 +10599,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11238,6 +11262,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11750,6 +11775,7 @@ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -12792,6 +12818,7 @@ "integrity": "sha512-GYmqRjRhJYLQBonfdfGAt28gkfWEShrtXKGXcFGneXi502aBE+I1dJcs/YQriByvP6xqXRz/OdBGC6tfvUQHyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -13280,6 +13307,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13544,6 +13572,7 @@ "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", @@ -14109,6 +14138,7 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -14169,6 +14199,7 @@ "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.1.0", "@vitest/mocker": "4.1.0", @@ -14306,6 +14337,7 @@ "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.30", "@vue/compiler-sfc": "3.5.30", diff --git a/src/geosearch.ts b/src/geosearch.ts index 7222b3e..78744e6 100644 --- a/src/geosearch.ts +++ b/src/geosearch.ts @@ -2,7 +2,7 @@ import { request, App, Notice } from 'obsidian'; import * as geosearch from 'leaflet-geosearch'; import * as leaflet from 'leaflet'; import queryString from 'query-string'; - +import { readFileSync } from 'fs'; import { type PluginSettings } from 'src/settings'; import { UrlConvertor } from 'src/urlConvertor'; import { FileMarker } from 'src/fileMarker'; @@ -45,9 +45,12 @@ export class GeoSearcher { }, }); } else if (settings.searchProvider == 'google') { - this.searchProvider = new geosearch.GoogleProvider({ - apiKey: settings.geocodingApiKey, - }); + const apiKey = + settings.geocodingApiMethod === 'key' + ? settings.geocodingApiKey + : readFileSync(settings.geocodingApiPath, 'utf-8').trim(); + + this.searchProvider = new geosearch.GoogleProvider({ apiKey }); } } @@ -132,7 +135,9 @@ export async function googlePlacesSearch( ): Promise { if (settings.searchProvider != 'google' || !settings.useGooglePlacesNew2025) return []; - const googleApiKey = settings.geocodingApiKey; + const googleApiKey = settings.geocodingApiMethod === 'key' + ? settings.geocodingApiKey + : readFileSync(settings.geocodingApiPath, 'utf-8').trim(); // Request body for the new Places API const requestBody = { diff --git a/src/settings.ts b/src/settings.ts index dc99599..8d4be0d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -52,7 +52,9 @@ export type PluginSettings = { searchProvider: 'osm' | 'google'; osmUser: string; searchDelayMs: number; + geocodingApiMethod?: 'key' | 'path'; geocodingApiKey: string; + geocodingApiPath?: string; useGooglePlacesNew2025: boolean; googlePlacesDataFields: string; saveHistory: boolean; @@ -284,6 +286,7 @@ export const DEFAULT_SETTINGS: PluginSettings = { searchProvider: 'osm', osmUser: '', searchDelayMs: 250, + geocodingApiMethod: 'key', geocodingApiKey: '', useGooglePlacesNew2025: false, googlePlacesDataFields: '', diff --git a/src/settingsTab.ts b/src/settingsTab.ts index 767825c..d730e4e 100644 --- a/src/settingsTab.ts +++ b/src/settingsTab.ts @@ -84,7 +84,9 @@ export class SettingsTab extends PluginSettingTab { }); }); + let apiMethodControl: Setting = null; let apiKeyControl: Setting = null; + let apiPathControl: Setting = null; let osmUser: Setting = null; new Setting(containerEl) .setName('Geocoding search provider') @@ -105,8 +107,18 @@ export class SettingsTab extends PluginSettingTab { this.refreshPluginOnHide = true; osmUser.settingEl.style.display = value === 'osm' ? '' : 'none'; - apiKeyControl.settingEl.style.display = + apiMethodControl.settingEl.style.display = value === 'google' ? '' : 'none'; + apiKeyControl.settingEl.style.display = + value === 'google' && + this.plugin.settings.geocodingApiMethod === 'key' + ? '' + : 'none'; + apiPathControl.settingEl.style.display = + value === 'google' && + this.plugin.settings.geocodingApiMethod === 'path' + ? '' + : 'none'; googlePlacesControl.settingEl.style.display = this.plugin.settings.searchProvider === 'google' ? '' @@ -137,8 +149,40 @@ export class SettingsTab extends PluginSettingTab { : 'red'; }); + osmUser.settingEl.style.display = + this.plugin.settings.searchProvider === 'osm' ? '' : 'none'; + + apiMethodControl = new Setting(containerEl) + .setName('Geocoding API Method') + .setDesc( + 'Choose whether to provide the API key directly or via a file path.' + ) + .addDropdown((component) => { + component + .addOption('key', 'API Key') + .addOption('path', 'API Key Path') + .setValue(this.plugin.settings.geocodingApiMethod || 'key') + .onChange(async (value: 'key' | 'path') => { + this.plugin.settings.geocodingApiMethod = value; + await this.plugin.saveSettings(); + apiKeyControl.settingEl.style.display = + value === 'key' && + this.plugin.settings.searchProvider === 'google' + ? '' + : 'none'; + apiPathControl.settingEl.style.display = + value === 'path' && + this.plugin.settings.searchProvider === 'google' + ? '' + : 'none'; + }); + }); + + apiMethodControl.settingEl.style.display = + this.plugin.settings.searchProvider === 'google' ? '' : 'none'; + apiKeyControl = new Setting(containerEl) - .setName('Gecoding API key') + .setName('Geocoding API key') .setDesc( 'If using Google as the geocoding search provider, paste the API key here. See the plugin documentation for more details. Changes are applied after restart.', ) @@ -157,6 +201,40 @@ export class SettingsTab extends PluginSettingTab { ? '' : 'red'; }); + + apiKeyControl.settingEl.style.display = + this.plugin.settings.searchProvider === 'google' && + this.plugin.settings.geocodingApiMethod === 'key' + ? '' + : 'none'; + + apiPathControl = new Setting(containerEl) + .setName('Geocoding API key path') + .setDesc( + 'If using Google as the geocoding search provider and using a path to the API key, enter the file path here. See the plugin documentation for more details. Changes are applied after restart.' + ) + .addText((component) => { + component + .setValue(this.plugin.settings.geocodingApiPath) + .onChange(async (value) => { + this.plugin.settings.geocodingApiPath = value; + await this.plugin.saveSettings(); + component.inputEl.style.borderColor = value + ? '' + : 'red'; + }); + component.inputEl.style.borderColor = this.plugin.settings + .geocodingApiPath + ? '' + : 'red'; + }); + + apiPathControl.settingEl.style.display = + this.plugin.settings.searchProvider === 'google' && + this.plugin.settings.geocodingApiMethod === 'path' + ? '' + : 'none'; + let googlePlacesControl = new Setting(containerEl) .setName('Use Google Places for searches') .setDesc( @@ -190,11 +268,6 @@ export class SettingsTab extends PluginSettingTab { }); }); - // Display the user or API key control only if the search provider requires it - osmUser.settingEl.style.display = - this.plugin.settings.searchProvider === 'osm' ? '' : 'none'; - apiKeyControl.settingEl.style.display = - this.plugin.settings.searchProvider === 'google' ? '' : 'none'; googlePlacesControl.settingEl.style.display = this.plugin.settings.searchProvider === 'google' ? '' : 'none'; googlePlacesDataFields.settingEl.style.display = diff --git a/src/urlConvertor.ts b/src/urlConvertor.ts index 365f49b..014b3b8 100644 --- a/src/urlConvertor.ts +++ b/src/urlConvertor.ts @@ -1,6 +1,6 @@ import { App, Editor, TFile, request } from 'obsidian'; import queryString from 'query-string'; - +import { readFileSync } from 'fs'; import * as leaflet from 'leaflet'; import { type PluginSettings, type UrlParsingRule } from 'src/settings'; import * as utils from 'src/utils'; @@ -136,7 +136,12 @@ export class UrlConvertor { const placeName = placeNameMatch[1]; if (this.settings.debug) console.log('Google link: found place name = ', placeName); - const googleApiKey = settings.geocodingApiKey; + + const googleApiKey = + settings.geocodingApiMethod === 'key' + ? settings.geocodingApiKey + : readFileSync(settings.geocodingApiPath, 'utf-8').trim(); + const params = { query: placeName, key: googleApiKey,