@@ -52,6 +52,7 @@ class ServerStore {
5252 private _error = $state < string | null > ( null ) ;
5353 private _serverWarning = $state < string | null > ( null ) ;
5454 private _slotsEndpointAvailable = $state < boolean | null > ( null ) ;
55+ private fetchServerPropsPromise : Promise < void > | null = null ;
5556
5657 private readCachedServerProps ( ) : ApiLlamaCppServerProps | null {
5758 if ( ! browser ) return null ;
@@ -171,88 +172,132 @@ class ServerStore {
171172 /**
172173 * Fetches server properties from the server
173174 */
174- async fetchServerProps ( ) : Promise < void > {
175- this . _loading = true ;
176- this . _error = null ;
177- this . _serverWarning = null ;
175+ async fetchServerProps ( options : { silent ?: boolean } = { } ) : Promise < void > {
176+ const { silent = false } = options ;
177+ const isSilent = silent && this . _serverProps !== null ;
178178
179- try {
180- console . log ( 'Fetching server properties...' ) ;
181- const props = await ChatService . getServerProps ( ) ;
182- this . _serverProps = props ;
183- this . persistServerProps ( props ) ;
184- console . log ( 'Server properties loaded:' , props ) ;
185-
186- // Check slots endpoint availability after server props are loaded
187- await this . checkSlotsEndpointAvailability ( ) ;
188- } catch ( error ) {
189- const hadCachedProps = this . _serverProps !== null ;
190- let errorMessage = 'Failed to connect to server' ;
191- let isOfflineLikeError = false ;
192- let isServerSideError = false ;
193-
194- if ( error instanceof Error ) {
195- // Handle specific error types with user-friendly messages
196- if ( error . name === 'TypeError' && error . message . includes ( 'fetch' ) ) {
197- errorMessage = 'Server is not running or unreachable' ;
198- isOfflineLikeError = true ;
199- } else if ( error . message . includes ( 'ECONNREFUSED' ) ) {
200- errorMessage = 'Connection refused - server may be offline' ;
201- isOfflineLikeError = true ;
202- } else if ( error . message . includes ( 'ENOTFOUND' ) ) {
203- errorMessage = 'Server not found - check server address' ;
204- isOfflineLikeError = true ;
205- } else if ( error . message . includes ( 'ETIMEDOUT' ) ) {
206- errorMessage = 'Request timed out - the server took too long to respond' ;
207- isOfflineLikeError = true ;
208- } else if ( error . message . includes ( '503' ) ) {
209- errorMessage = 'Server temporarily unavailable - try again shortly' ;
210- isServerSideError = true ;
211- } else if ( error . message . includes ( '500' ) ) {
212- errorMessage = 'Server error - check server logs' ;
213- isServerSideError = true ;
214- } else if ( error . message . includes ( '404' ) ) {
215- errorMessage = 'Server endpoint not found' ;
216- } else if ( error . message . includes ( '403' ) || error . message . includes ( '401' ) ) {
217- errorMessage = 'Access denied' ;
179+ if ( this . fetchServerPropsPromise ) {
180+ return this . fetchServerPropsPromise ;
181+ }
182+
183+ if ( ! isSilent ) {
184+ this . _loading = true ;
185+ this . _error = null ;
186+ this . _serverWarning = null ;
187+ }
188+
189+ const hadProps = this . _serverProps !== null ;
190+
191+ const fetchPromise = ( async ( ) => {
192+ try {
193+ const props = await ChatService . getServerProps ( ) ;
194+ this . _serverProps = props ;
195+ this . persistServerProps ( props ) ;
196+ this . _error = null ;
197+ this . _serverWarning = null ;
198+ await this . checkSlotsEndpointAvailability ( ) ;
199+ } catch ( error ) {
200+ if ( isSilent && hadProps ) {
201+ console . warn ( 'Silent server props refresh failed, keeping cached data:' , error ) ;
202+ return ;
203+ }
204+
205+ this . handleFetchServerPropsError ( error , hadProps ) ;
206+ } finally {
207+ if ( ! isSilent ) {
208+ this . _loading = false ;
218209 }
210+
211+ this . fetchServerPropsPromise = null ;
219212 }
213+ } ) ( ) ;
214+
215+ this . fetchServerPropsPromise = fetchPromise ;
216+
217+ await fetchPromise ;
218+ }
220219
221- let cachedProps : ApiLlamaCppServerProps | null = null ;
220+ /**
221+ * Handles fetch failures by attempting to recover cached server props and
222+ * updating the user-facing error or warning state appropriately.
223+ */
224+ private handleFetchServerPropsError ( error : unknown , hadProps : boolean ) : void {
225+ const { errorMessage, isOfflineLikeError, isServerSideError } = this . normalizeFetchError ( error ) ;
222226
223- if ( ! hadCachedProps ) {
224- cachedProps = this . readCachedServerProps ( ) ;
225- if ( cachedProps ) {
226- this . _serverProps = cachedProps ;
227- this . _error = null ;
227+ let cachedProps : ApiLlamaCppServerProps | null = null ;
228228
229- if ( isOfflineLikeError || isServerSideError ) {
230- this . _serverWarning = errorMessage ;
231- }
229+ if ( ! hadProps ) {
230+ cachedProps = this . readCachedServerProps ( ) ;
232231
233- console . warn (
234- 'Failed to refresh server properties, using cached values from localStorage:' ,
235- errorMessage
236- ) ;
237- } else {
238- this . _error = errorMessage ;
239- }
240- } else {
232+ if ( cachedProps ) {
233+ this . _serverProps = cachedProps ;
241234 this . _error = null ;
242235
243236 if ( isOfflineLikeError || isServerSideError ) {
244237 this . _serverWarning = errorMessage ;
245238 }
246239
247240 console . warn (
248- 'Failed to refresh server properties, continuing with cached values:' ,
241+ 'Failed to refresh server properties, using cached values from localStorage :' ,
249242 errorMessage
250243 ) ;
244+ } else {
245+ this . _error = errorMessage ;
246+ }
247+ } else {
248+ this . _error = null ;
249+
250+ if ( isOfflineLikeError || isServerSideError ) {
251+ this . _serverWarning = errorMessage ;
251252 }
252- console . error ( 'Error fetching server properties:' , error ) ;
253- } finally {
254- this . _loading = false ;
253+
254+ console . warn (
255+ 'Failed to refresh server properties, continuing with cached values:' ,
256+ errorMessage
257+ ) ;
255258 }
259+
260+ console . error ( 'Error fetching server properties:' , error ) ;
261+ }
262+
263+ private normalizeFetchError ( error : unknown ) : {
264+ errorMessage : string ;
265+ isOfflineLikeError : boolean ;
266+ isServerSideError : boolean ;
267+ } {
268+ let errorMessage = 'Failed to connect to server' ;
269+ let isOfflineLikeError = false ;
270+ let isServerSideError = false ;
271+
272+ if ( error instanceof Error ) {
273+ const message = error . message || '' ;
274+
275+ if ( error . name === 'TypeError' && message . includes ( 'fetch' ) ) {
276+ errorMessage = 'Server is not running or unreachable' ;
277+ isOfflineLikeError = true ;
278+ } else if ( message . includes ( 'ECONNREFUSED' ) ) {
279+ errorMessage = 'Connection refused - server may be offline' ;
280+ isOfflineLikeError = true ;
281+ } else if ( message . includes ( 'ENOTFOUND' ) ) {
282+ errorMessage = 'Server not found - check server address' ;
283+ isOfflineLikeError = true ;
284+ } else if ( message . includes ( 'ETIMEDOUT' ) ) {
285+ errorMessage = 'Request timed out - the server took too long to respond' ;
286+ isOfflineLikeError = true ;
287+ } else if ( message . includes ( '503' ) ) {
288+ errorMessage = 'Server temporarily unavailable - try again shortly' ;
289+ isServerSideError = true ;
290+ } else if ( message . includes ( '500' ) ) {
291+ errorMessage = 'Server error - check server logs' ;
292+ isServerSideError = true ;
293+ } else if ( message . includes ( '404' ) ) {
294+ errorMessage = 'Server endpoint not found' ;
295+ } else if ( message . includes ( '403' ) || message . includes ( '401' ) ) {
296+ errorMessage = 'Access denied' ;
297+ }
298+ }
299+
300+ return { errorMessage, isOfflineLikeError, isServerSideError } ;
256301 }
257302
258303 /**
@@ -264,6 +309,7 @@ class ServerStore {
264309 this . _serverWarning = null ;
265310 this . _loading = false ;
266311 this . _slotsEndpointAvailable = null ;
312+ this . fetchServerPropsPromise = null ;
267313 this . persistServerProps ( null ) ;
268314 }
269315}
0 commit comments