diff --git a/components.d.ts b/components.d.ts
index f57edea6..82a73b07 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -20,6 +20,8 @@ declare module 'vue' {
NGridItem: typeof import('naive-ui')['NGridItem']
NIcon: typeof import('naive-ui')['NIcon']
NInput: typeof import('naive-ui')['NInput']
+ NInputGroup: typeof import('naive-ui')['NInputGroup']
+ NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
@@ -30,8 +32,10 @@ declare module 'vue' {
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
+ NSwitch: typeof import('naive-ui')['NSwitch']
NTabPane: typeof import('naive-ui')['NTabPane']
NTabs: typeof import('naive-ui')['NTabs']
+ NTooltip: typeof import('naive-ui')['NTooltip']
RouterLink: typeof import('vue-router')['RouterLink']
RouterMain: typeof import('./src/components/RouterMain.vue')['default']
RouterView: typeof import('vue-router')['RouterView']
diff --git a/package-lock.json b/package-lock.json
index c26939bc..259271ee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"electron-squirrel-startup": "^1.0.0",
"electron-store": "^8.1.0",
"monaco-editor": "^0.46.0",
+ "node-fetch": "^2.7.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"update-electron-app": "^3.0.0",
@@ -34,6 +35,7 @@
"@types/debug": "^4.1.12",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.19",
+ "@types/node-fetch": "^2.6.11",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vicons/antd": "^0.12.0",
@@ -3681,6 +3683,16 @@
"undici-types": "~5.26.4"
}
},
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
+ "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
"node_modules/@types/responselike": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
@@ -4580,6 +4592,12 @@
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
"dev": true
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true
+ },
"node_modules/at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
@@ -5408,6 +5426,18 @@
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/commander": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
@@ -5924,6 +5954,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -6605,7 +6644,6 @@
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "dev": true,
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.2"
@@ -6615,7 +6653,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
"optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -7707,6 +7744,20 @@
"is-callable": "^1.1.3"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -10709,7 +10760,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dev": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -12327,7 +12377,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
+ "devOptional": true
},
"node_modules/sass": {
"version": "1.71.0",
@@ -13176,8 +13226,7 @@
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tree-kill": {
"version": "1.2.2",
@@ -14142,8 +14191,7 @@
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/webpack-sources": {
"version": "3.2.3",
@@ -14164,7 +14212,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dev": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
diff --git a/package.json b/package.json
index 8cd346d6..ea29b8ed 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"electron-squirrel-startup": "^1.0.0",
"electron-store": "^8.1.0",
"monaco-editor": "^0.46.0",
+ "node-fetch": "^2.7.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"update-electron-app": "^3.0.0",
@@ -42,11 +43,12 @@
"@electron-forge/maker-squirrel": "^7.2.0",
"@electron-forge/maker-zip": "^7.2.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.2.0",
- "@electron-forge/publisher-github": "^7.2.0",
"@electron-forge/plugin-vite": "^7.2.0",
+ "@electron-forge/publisher-github": "^7.2.0",
"@types/debug": "^4.1.12",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.19",
+ "@types/node-fetch": "^2.6.11",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vicons/antd": "^0.12.0",
diff --git a/src/assets/styles/theme.scss b/src/assets/styles/theme.scss
index 601f4419..39bb3a1c 100644
--- a/src/assets/styles/theme.scss
+++ b/src/assets/styles/theme.scss
@@ -30,4 +30,4 @@ body {
color: var(--text-color);
transition: .3s;
font-size: 14px;
-}
\ No newline at end of file
+}
diff --git a/src/common/httpClient.ts b/src/common/httpClient.ts
index db15e04d..11c1d770 100644
--- a/src/common/httpClient.ts
+++ b/src/common/httpClient.ts
@@ -2,17 +2,16 @@ import { CustomError } from './customError';
import { Buffer } from 'buffer';
import { lang } from '../lang';
-const catchHandler = (err: unknown) => {
- if (err instanceof CustomError) {
- if (err.status === 401) {
- throw new CustomError(err.status, lang.global.t('connection.unAuthorized'));
- }
- throw new CustomError(err.status, err.details);
+const { fetchApi } = window;
+
+const handleFetch = (result: { data: unknown; status: number; details: string }) => {
+ if ([404, 400].includes(result.status) || (result.status >= 200 && result.status < 300)) {
+ return result.data || JSON.parse(result.details);
}
- if (err instanceof Error) {
- throw new CustomError(500, err.message);
+ if (result.status === 401) {
+ throw new CustomError(result.status, lang.global.t('connection.unAuthorized'));
}
- throw new CustomError(500, `unknown error, trace: ${JSON.stringify(err)}`);
+ throw new CustomError(result.status, result.details);
};
const buildURL = (host: string, port: number, path?: string, queryParameters?: string) => {
@@ -31,6 +30,7 @@ const fetchWrapper = async ({
port,
username,
password,
+ ssl,
}: {
method: string;
path?: string;
@@ -40,6 +40,7 @@ const fetchWrapper = async ({
password?: string;
host: string;
port: number;
+ ssl: boolean;
}) => {
const authorization =
username || password
@@ -47,25 +48,21 @@ const fetchWrapper = async ({
: undefined;
const url = buildURL(host, port, path, queryParameters);
- try {
- const result = await fetch(url, {
- method,
- headers: { 'Content-Type': 'application/json', authorization } as unknown as Headers,
- body: payload ? JSON.stringify(payload) : undefined,
- });
- if (result.ok) {
- return await result.json();
- }
- throw new CustomError(result.status, await result.text());
- } catch (e) {
- throw catchHandler(e);
- }
+ const { data, status, details } = await fetchApi.fetch(url, {
+ method,
+ authorization,
+ payload: payload ? JSON.stringify(payload) : undefined,
+ ssl,
+ });
+ return handleFetch({ data, status, details });
};
+
export const loadHttpClient = (con: {
host: string;
port: number;
username?: string;
password?: string;
+ sslCertVerification: boolean;
}) => ({
get: async (path?: string, queryParameters?: string) =>
fetchWrapper({
@@ -73,6 +70,7 @@ export const loadHttpClient = (con: {
method: 'GET',
path,
queryParameters,
+ ssl: con.sslCertVerification,
}),
post: async (path: string, queryParameters?: string, payload?: unknown) =>
fetchWrapper({
@@ -81,6 +79,7 @@ export const loadHttpClient = (con: {
path,
queryParameters,
payload,
+ ssl: con.sslCertVerification,
}),
put: async (path: string, queryParameters?: string, payload?: unknown) =>
fetchWrapper({
@@ -89,6 +88,7 @@ export const loadHttpClient = (con: {
path,
queryParameters,
payload,
+ ssl: con.sslCertVerification,
}),
delete: async (path: string, queryParameters?: string, payload?: unknown) =>
@@ -98,5 +98,6 @@ export const loadHttpClient = (con: {
path,
queryParameters,
payload,
+ ssl: con.sslCertVerification,
}),
});
diff --git a/src/electron/fetchApi.ts b/src/electron/fetchApi.ts
new file mode 100644
index 00000000..fb042965
--- /dev/null
+++ b/src/electron/fetchApi.ts
@@ -0,0 +1,47 @@
+import Electron from 'electron';
+import fetch from 'node-fetch';
+import { CustomError, debug } from '../common';
+import * as https from 'https';
+
+type FetchApiOptions = {
+ method: string;
+ authorization: string;
+ payload: string | undefined;
+ ssl: boolean;
+};
+
+export type FetchApiInput = {
+ method: string;
+ url: string;
+ options: FetchApiOptions;
+};
+
+const fetchApi: { [key: string]: (key: string, val: unknown) => unknown } = {
+ fetch: async (url: string, { method, authorization, payload, ssl }: FetchApiOptions) => {
+ const agent = url.startsWith('https')
+ ? new https.Agent({
+ rejectUnauthorized: ssl,
+ })
+ : undefined;
+ try {
+ const result = await fetch(url, {
+ method,
+ headers: { 'Content-Type': 'application/json', authorization } as unknown as Headers,
+ body: payload,
+ agent,
+ });
+ if (result.ok) {
+ return { status: result.status, data: await result.json() };
+ }
+ throw new CustomError(result.status, await result.text());
+ } catch (e) {
+ debug('error encountered while node-fetch fetch target:', e);
+ return { status: e.status || 500, details: e.details || e.message };
+ }
+ },
+};
+export const registerFetchApiListener = (ipcMain: Electron.IpcMain) => {
+ ipcMain.handle('fetchApi', (_, { method, url, options }: FetchApiInput) =>
+ fetchApi[method.toLowerCase()](url, options),
+ );
+};
diff --git a/src/electron/main.ts b/src/electron/main.ts
index dc68e722..47155d7c 100644
--- a/src/electron/main.ts
+++ b/src/electron/main.ts
@@ -5,6 +5,7 @@ import { debug } from '../common';
import { githubLink } from '../config';
import { registerStoreApiListener } from './storeApi';
import { registerSourceFileApiListener } from './sourceFIleApi';
+import { registerFetchApiListener } from './fetchApi';
declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string;
declare const MAIN_WINDOW_VITE_NAME: string;
@@ -104,6 +105,7 @@ ipcMain.on('open-github', () => {
registerStoreApiListener(ipcMain);
registerSourceFileApiListener(ipcMain);
+registerFetchApiListener(ipcMain);
try {
autoUpdater.setFeedURL({
diff --git a/src/electron/preload.ts b/src/electron/preload.ts
index 63b2649c..65d6c9ca 100644
--- a/src/electron/preload.ts
+++ b/src/electron/preload.ts
@@ -23,3 +23,8 @@ contextBridge.exposeInMainWorld('sourceFileAPI', {
onSaveShortcut: (callback: (value: unknown) => void) =>
ipcRenderer.on('save-shortcout', (_event, value) => callback(value)),
});
+
+contextBridge.exposeInMainWorld('fetchApi', {
+ fetch: async (url: string, options: unknown) =>
+ ipcRenderer.invoke('fetchApi', { method: 'fetch', url, options }),
+});
diff --git a/src/lang/enUS.ts b/src/lang/enUS.ts
index bad41c4d..b2a69bad 100644
--- a/src/lang/enUS.ts
+++ b/src/lang/enUS.ts
@@ -25,6 +25,7 @@ export const enUS = {
username: 'Username',
password: 'Password',
queryParameters: 'query parameters',
+ sslCertVerification: 'SSL Certificate Verification',
add: 'Add connection',
edit: 'Edit connection',
testSuccess: 'connect success',
@@ -32,6 +33,7 @@ export const enUS = {
nameRequired: 'Name is required',
hostRequired: 'Host is required',
portRequired: 'Port is required',
+ sslCertOnlyHttps: 'SSL Certificate Verification can only be enabled under HTTPS connection',
},
operations: {
connect: 'Connect',
diff --git a/src/lang/zhCN.ts b/src/lang/zhCN.ts
index 3b5a939c..83d41ef6 100644
--- a/src/lang/zhCN.ts
+++ b/src/lang/zhCN.ts
@@ -25,6 +25,7 @@ export const zhCN = {
username: '用户名',
password: '密码',
queryParameters: '查询参数',
+ sslCertVerification: 'SSL 证书验证',
add: '添加连接',
edit: '编辑连接',
testSuccess: '连接成功',
@@ -32,6 +33,7 @@ export const zhCN = {
nameRequired: '请输入连接名称',
hostRequired: '请输入主机地址',
portRequired: '请输入端口号',
+ sslCertOnlyHttps: 'SSL 证书验证只能在 HTTPS 连接下开启',
},
operations: {
connect: '连接',
diff --git a/src/store/connectionStore.ts b/src/store/connectionStore.ts
index fefccfb3..2c4ba010 100644
--- a/src/store/connectionStore.ts
+++ b/src/store/connectionStore.ts
@@ -8,6 +8,7 @@ export type Connection = {
host: string;
port: number;
username?: string;
+ sslCertVerification: boolean;
password?: string;
queryParameters?: string;
};
@@ -85,7 +86,9 @@ export const useConnectionStore = defineStore('connectionStore', {
await this.testConnection(connection);
const client = loadHttpClient(connection);
- const data = await client.get('/_cat/indices', 'format=json');
+ const data = (await client.get('/_cat/indices', 'format=json')) as Array<{
+ [key: string]: string;
+ }>;
const indices = data.map((index: { [key: string]: string }) => ({
...index,
docs: {
@@ -99,7 +102,9 @@ export const useConnectionStore = defineStore('connectionStore', {
async fetchIndices() {
if (!this.established) throw new Error('no connection established');
const client = loadHttpClient(this.established as Connection);
- const data = await client.get('/_cat/indices', 'format=json');
+ const data = (await client.get('/_cat/indices', 'format=json')) as Array<{
+ [key: string]: string;
+ }>;
this.established!.indices = data.map((index: { [key: string]: string }) => ({
...index,
docs: {
diff --git a/src/views/connect/components/connect-dialog.vue b/src/views/connect/components/connect-dialog.vue
index b177d5ac..da7206dd 100644
--- a/src/views/connect/components/connect-dialog.vue
+++ b/src/views/connect/components/connect-dialog.vue
@@ -33,12 +33,37 @@
-
-
+
+
+
+
+
+
+
+
+
+ {{ $t('connection.sslCertVerification') }}
+
+
@@ -112,11 +137,12 @@