Skip to content

Commit 79a2316

Browse files
authored
Merge pull request #123 from bfritscher/copilot/add-connection-timeout-config
Add connectionTimeoutSeconds configuration via UI and config file fixes #122
2 parents f4105eb + 8810ceb commit 79a2316

File tree

8 files changed

+610
-726
lines changed

8 files changed

+610
-726
lines changed

config.json.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"path": "",
88
"tls": false
99
},
10+
"connectionTimeoutSeconds": 10,
1011
"ui": {
1112
"hideProjectInfo": true
1213
},

package-lock.json

Lines changed: 503 additions & 694 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "typesense-dashboard",
3-
"version": "2.4.5",
3+
"version": "2.4.6",
44
"description": "A Typesense Dashboard to manage and browse collections.",
55
"productName": "Typesense-Dashboard",
66
"author": "Boris Fritscher <boris@fritscher.ch>",
@@ -19,39 +19,39 @@
1919
},
2020
"dependencies": {
2121
"@quasar/extras": "1.17.0",
22-
"axios": "1.13.2",
22+
"axios": "1.13.5",
2323
"file-saver": "2.0.5",
24-
"instantsearch.css": "8.9.0",
24+
"instantsearch.css": "8.11.0",
2525
"monaco-editor": "0.55.1",
2626
"nanoid": "5.1.6",
2727
"pinia": "3.0.4",
2828
"pretty-bytes": "7.1.0",
2929
"quasar": "2.18.6",
3030
"typesense": "2.1.0",
3131
"typesense-instantsearch-adapter": "2.9.0",
32-
"vue": "3.5.26",
33-
"vue-instantsearch": "4.22.6",
32+
"vue": "3.5.29",
33+
"vue-instantsearch": "4.24.0",
3434
"vue-router": "4.6.4"
3535
},
3636
"devDependencies": {
3737
"@electron/packager": "18.4.4",
3838
"@eslint/js": "9.39.2",
3939
"@quasar/app-vite": "2.4.0",
4040
"@types/file-saver": "2.0.7",
41-
"@types/node": "25.0.3",
42-
"@vue/devtools": "8.0.5",
41+
"@types/node": "25.3.2",
42+
"@vue/devtools": "8.0.6",
4343
"@vue/eslint-config-prettier": "10.2.0",
44-
"@vue/eslint-config-typescript": "14.6.0",
45-
"autoprefixer": "10.4.23",
44+
"@vue/eslint-config-typescript": "14.7.0",
45+
"autoprefixer": "10.4.27",
4646
"electron": "39.2.7",
4747
"eslint": "9.39.2",
48-
"eslint-plugin-vue": "10.6.2",
49-
"globals": "16.5.0",
50-
"prettier": "3.7.4",
48+
"eslint-plugin-vue": "10.8.0",
49+
"globals": "17.3.0",
50+
"prettier": "3.8.1",
5151
"typescript": "5.9.3",
5252
"vite-plugin-checker": "0.12.0",
53-
"vue-eslint-parser": "10.2.0",
54-
"vue-tsc": "3.1.8"
53+
"vue-eslint-parser": "10.4.0",
54+
"vue-tsc": "3.2.5"
5555
},
5656
"engines": {
5757
"node": "^28 || ^26 || ^24 || ^22 || ^20",

src/pages/ClusterStatus.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ function isCurrent(entry: NodeLoginDataInterface) {
4040
4141
function connect(entry: NodeLoginDataInterface) {
4242
// Do not redirect to home from the cluster page
43-
store.login({ node: entry.node, apiKey: entry.apiKey });
43+
const payload: Parameters<typeof store.login>[0] = {
44+
node: entry.node,
45+
apiKey: entry.apiKey,
46+
};
47+
if (entry.connectionTimeoutSeconds !== undefined) {
48+
payload.connectionTimeoutSeconds = entry.connectionTimeoutSeconds;
49+
}
50+
store.login(payload);
4451
}
4552
</script>

src/pages/Login.vue

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@
3333
label="path"
3434
hint="optional: leave blank or start with / and end without /"
3535
/>
36+
<q-expansion-item
37+
v-model="showAdvancedSettings"
38+
dense
39+
dense-toggle
40+
label="Advanced settings"
41+
class="text-left"
42+
>
43+
<q-input
44+
v-model.number="connectionTimeoutSeconds"
45+
class="q-mt-sm"
46+
filled
47+
type="number"
48+
label="Connection Timeout (seconds)"
49+
hint="optional: leave blank to use default timeout"
50+
clearable
51+
/>
52+
</q-expansion-item>
3653
<div class="text-left">
3754
<q-toggle
3855
v-if="$q.platform.is.electron && store.currentNodeConfig.protocol === 'https'"
@@ -75,15 +92,23 @@ const store = useNodeStore();
7592
7693
const protocolOptions = ['http', 'https'];
7794
const apiKey = ref('');
95+
const showAdvancedSettings = ref(false);
96+
const connectionTimeoutSeconds = ref<number | null>(
97+
store.loginData?.connectionTimeoutSeconds ?? null,
98+
);
7899
79100
onMounted(() => {
80101
void store.connectionCheck();
81102
});
82103
83104
function login() {
84-
void store.login({
105+
const payload: Parameters<typeof store.login>[0] = {
85106
apiKey: apiKey.value,
86107
node: store.currentNodeConfig,
87-
});
108+
};
109+
if (connectionTimeoutSeconds.value !== null) {
110+
payload.connectionTimeoutSeconds = connectionTimeoutSeconds.value;
111+
}
112+
void store.login(payload);
88113
}
89114
</script>

src/pages/ServerStatus.vue

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
<div>Protocol: {{ store.loginData?.node.protocol }}</div>
164164
<div>Host: {{ store.loginData?.node.host }}</div>
165165
<div>Port: {{ store.loginData?.node.port }}</div>
166+
<div>Connection Timeout: {{ connectionTimeoutDisplay }}</div>
166167
<div v-if="store.data.debug.version">Version: {{ store.data.debug.version }}</div>
167168
<div v-if="Object.hasOwnProperty.call(store.data.debug, 'state')">
168169
Role:
@@ -405,7 +406,20 @@ const systemNetworkSentLabel = computed(() => {
405406
return v === null ? '' : prettyBytes(v);
406407
});
407408
409+
const connectionTimeoutDisplay = computed(() => {
410+
const timeout = store.loginData?.connectionTimeoutSeconds;
411+
return timeout !== undefined ? `${timeout}s` : 'Default';
412+
});
413+
408414
function connectTo(member: NodeLoginDataInterface) {
409-
void store.login({ apiKey: member.apiKey, node: member.node, forceHomeRedirect: true });
415+
const payload: Parameters<typeof store.login>[0] = {
416+
apiKey: member.apiKey,
417+
node: member.node,
418+
forceHomeRedirect: true,
419+
};
420+
if (member.connectionTimeoutSeconds !== undefined) {
421+
payload.connectionTimeoutSeconds = member.connectionTimeoutSeconds;
422+
}
423+
void store.login(payload);
410424
}
411425
</script>

src/shared/api.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import * as Typesense from 'typesense';
44
import type { CollectionAliasSchema } from 'typesense/lib/Typesense/Aliases';
55
import type { CollectionCreateSchema } from 'typesense/lib/Typesense/Collections';
66
import type { CollectionUpdateSchema } from 'typesense/lib/Typesense/Collection';
7-
import type { NodeConfiguration } from 'typesense/lib/Typesense/Configuration';
7+
import type {
8+
ConfigurationOptions,
9+
NodeConfiguration,
10+
} from 'typesense/lib/Typesense/Configuration';
811
import type { SearchParams } from 'typesense/lib/Typesense/Documents';
912
import type { KeyCreateSchema } from 'typesense/lib/Typesense/Key';
1013
import type { OverrideSchema } from 'typesense/lib/Typesense/Override';
@@ -19,20 +22,27 @@ export class Api {
1922
public axiosClient?: AxiosInstance;
2023
private typesenseClient?: Typesense.Client;
2124

22-
public init({ node, apiKey }: { node: NodeConfiguration; apiKey: string }): void {
25+
public init({
26+
node,
27+
apiKey,
28+
connectionTimeoutSeconds,
29+
}: {
30+
node: NodeConfiguration;
31+
apiKey: string;
32+
connectionTimeoutSeconds?: number;
33+
}): void {
2334
this.axiosClient = axios.create({
2435
baseURL: `${node.protocol}://${node.host}:${node.port}${node.path || ''}`,
2536
headers: { 'x-typesense-api-key': apiKey },
2637
});
27-
this.typesenseClient = new Typesense.Client({
28-
nodes: [
29-
{
30-
...node,
31-
},
32-
],
38+
const clientConfig: ConfigurationOptions = {
39+
nodes: [{ ...node }],
3340
apiKey,
34-
//connection_timeout_seconds: 3600,
35-
});
41+
};
42+
if (connectionTimeoutSeconds !== undefined) {
43+
clientConfig.connectionTimeoutSeconds = connectionTimeoutSeconds;
44+
}
45+
this.typesenseClient = new Typesense.Client(clientConfig);
3646
}
3747

3848
public getDebug() {

src/stores/node.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export interface NodeLoginDataInterface {
6464
node: CustomNodeConfiguration;
6565
apiKey: string;
6666
clusterTag?: string;
67+
connectionTimeoutSeconds?: number;
6768
}
6869

6970
export interface NodeLoginPayloadInterface extends NodeLoginDataInterface {
@@ -179,10 +180,14 @@ export const useNodeStore = defineStore('node', {
179180
api = electron;
180181
(electron as any).rejectTLS(Number(state.loginData.node.tls));
181182
}
182-
api.init({
183+
const initConfig: Parameters<typeof api.init>[0] = {
183184
node: { ...state.loginData.node },
184185
apiKey: state.loginData.apiKey,
185-
});
186+
};
187+
if (state.loginData.connectionTimeoutSeconds !== undefined) {
188+
initConfig.connectionTimeoutSeconds = state.loginData.connectionTimeoutSeconds;
189+
}
190+
api.init(initConfig);
186191
return api;
187192
}
188193
},
@@ -474,7 +479,7 @@ export const useNodeStore = defineStore('node', {
474479
});
475480
},
476481
login(loginData: NodeLoginPayloadInterface) {
477-
const { apiKey, node, forceHomeRedirect = false } = loginData;
482+
const { apiKey, node, forceHomeRedirect = false, connectionTimeoutSeconds } = loginData;
478483
let { clusterTag } = loginData;
479484
// Recover clusterTag from history if not provided
480485
if (!clusterTag) {
@@ -493,7 +498,14 @@ export const useNodeStore = defineStore('node', {
493498
}
494499
this.setForceRedirect(forceHomeRedirect);
495500
this.setCurrentNodeConfig(node);
496-
this.setNodeData({ apiKey, node, clusterTag } as NodeLoginDataInterface);
501+
const nodeData: NodeLoginDataInterface = { apiKey, node };
502+
if (clusterTag !== undefined) {
503+
nodeData.clusterTag = clusterTag;
504+
}
505+
if (connectionTimeoutSeconds !== undefined) {
506+
nodeData.connectionTimeoutSeconds = connectionTimeoutSeconds;
507+
}
508+
this.setNodeData(nodeData);
497509
void this.connectionCheck();
498510
},
499511
logout() {
@@ -898,6 +910,9 @@ export const useNodeStore = defineStore('node', {
898910
apiKey: parsed.apiKey,
899911
clusterTag: tag,
900912
};
913+
if (parsed.connectionTimeoutSeconds !== undefined) {
914+
updated.connectionTimeoutSeconds = parsed.connectionTimeoutSeconds;
915+
}
901916
this.loginHistory.splice(index, 1, JSON.stringify(updated));
902917
LocalStorage.set(STORAGE_KEY_LOGIN_HISTORY, this.loginHistory);
903918
if (this.loginData) {
@@ -908,6 +923,9 @@ export const useNodeStore = defineStore('node', {
908923
if (index < 0 || index >= this.loginHistoryParsed.length) return;
909924
const base = this.loginHistoryParsed[index] as NodeLoginDataInterface;
910925
const noTag: NodeLoginDataInterface = { node: base.node, apiKey: base.apiKey };
926+
if (base.connectionTimeoutSeconds !== undefined) {
927+
noTag.connectionTimeoutSeconds = base.connectionTimeoutSeconds;
928+
}
911929
this.loginHistory.splice(index, 1, JSON.stringify(noTag));
912930
LocalStorage.set(STORAGE_KEY_LOGIN_HISTORY, this.loginHistory);
913931
if (this.loginData) {

0 commit comments

Comments
 (0)