15
15
from urllib .parse import urljoin
16
16
17
17
try :
18
- from typing import Annotated
18
+ from typing import Annotated , Any
19
19
except ImportError :
20
20
# Only for Python 3.8
21
21
# To be removed when Python 3.8 support will be dropped
22
22
from typing_extensions import Annotated
23
23
24
24
from glances import __apiversion__ , __version__
25
+ from glances .globals import json_dumps
25
26
from glances .logger import logger
26
27
from glances .password import GlancesPassword
27
28
from glances .timer import Timer
31
32
from fastapi import APIRouter , Depends , FastAPI , HTTPException , Request , status
32
33
from fastapi .middleware .cors import CORSMiddleware
33
34
from fastapi .middleware .gzip import GZipMiddleware
34
- from fastapi .responses import HTMLResponse , ORJSONResponse
35
+ from fastapi .responses import HTMLResponse , JSONResponse
35
36
from fastapi .security import HTTPBasic , HTTPBasicCredentials
36
37
from fastapi .staticfiles import StaticFiles
37
38
from fastapi .templating import Jinja2Templates
50
51
import threading
51
52
import time
52
53
54
+
55
+ class GlancesJSONResponse (JSONResponse ):
56
+ """
57
+ Glances impl of fastapi's JSONResponse to use internal JSON Serialization features
58
+
59
+ Ref: https://fastapi.tiangolo.com/advanced/custom-response/
60
+ """
61
+
62
+ def render (self , content : Any ) -> bytes :
63
+ return json_dumps (content )
64
+
65
+
53
66
security = HTTPBasic ()
54
67
55
68
@@ -106,11 +119,11 @@ def __init__(self, config=None, args=None):
106
119
107
120
# FastAPI Init
108
121
if self .args .password :
109
- self ._app = FastAPI (dependencies = [Depends (self .authentication )])
122
+ self ._app = FastAPI (default_response_class = GlancesJSONResponse , dependencies = [Depends (self .authentication )])
110
123
self ._password = GlancesPassword (username = args .username , config = config )
111
124
112
125
else :
113
- self ._app = FastAPI ()
126
+ self ._app = FastAPI (default_response_class = GlancesJSONResponse )
114
127
self ._password = None
115
128
116
129
# Set path for WebUI
@@ -178,102 +191,49 @@ def authentication(self, creds: Annotated[HTTPBasicCredentials, Depends(security
178
191
headers = {"WWW-Authenticate" : "Basic" },
179
192
)
180
193
181
- def _router (self ):
194
+ def _router (self ) -> APIRouter :
182
195
"""Define a custom router for Glances path."""
183
- # Create une main router
196
+ base_path = f'/api/{ self .API_VERSION } '
197
+ plugin_path = f"{ base_path } /{{plugin}}"
198
+
199
+ # Create the main router
184
200
router = APIRouter (prefix = self .url_prefix )
185
201
186
202
# REST API
187
- router .add_api_route (
188
- f'/api/{ self .API_VERSION } /status' ,
189
- status_code = status .HTTP_200_OK ,
190
- methods = ['HEAD' , 'GET' ],
191
- response_class = ORJSONResponse ,
192
- endpoint = self ._api_status ,
193
- )
194
-
195
- router .add_api_route (
196
- f'/api/{ self .API_VERSION } /config' , response_class = ORJSONResponse , endpoint = self ._api_config
197
- )
198
- router .add_api_route (
199
- f'/api/{ self .API_VERSION } /config/{{section}}' ,
200
- response_class = ORJSONResponse ,
201
- endpoint = self ._api_config_section ,
202
- )
203
- router .add_api_route (
204
- f'/api/{ self .API_VERSION } /config/{{section}}/{{item}}' ,
205
- response_class = ORJSONResponse ,
206
- endpoint = self ._api_config_section_item ,
207
- )
208
-
209
- router .add_api_route (f'/api/{ self .API_VERSION } /args' , response_class = ORJSONResponse , endpoint = self ._api_args )
210
- router .add_api_route (
211
- f'/api/{ self .API_VERSION } /args/{{item}}' , response_class = ORJSONResponse , endpoint = self ._api_args_item
212
- )
213
-
214
- router .add_api_route (
215
- f'/api/{ self .API_VERSION } /pluginslist' , response_class = ORJSONResponse , endpoint = self ._api_plugins
216
- )
217
- router .add_api_route (f'/api/{ self .API_VERSION } /all' , response_class = ORJSONResponse , endpoint = self ._api_all )
218
- router .add_api_route (
219
- f'/api/{ self .API_VERSION } /all/limits' , response_class = ORJSONResponse , endpoint = self ._api_all_limits
220
- )
221
- router .add_api_route (
222
- f'/api/{ self .API_VERSION } /all/views' , response_class = ORJSONResponse , endpoint = self ._api_all_views
223
- )
224
-
225
- router .add_api_route (f'/api/{ self .API_VERSION } /help' , response_class = ORJSONResponse , endpoint = self ._api_help )
226
- router .add_api_route (f'/api/{ self .API_VERSION } /{{plugin}}' , response_class = ORJSONResponse , endpoint = self ._api )
227
- router .add_api_route (
228
- f'/api/{ self .API_VERSION } /{{plugin}}/history' , response_class = ORJSONResponse , endpoint = self ._api_history
229
- )
230
- router .add_api_route (
231
- f'/api/{ self .API_VERSION } /{{plugin}}/history/{{nb}}' ,
232
- response_class = ORJSONResponse ,
233
- endpoint = self ._api_history ,
234
- )
235
- router .add_api_route (
236
- f'/api/{ self .API_VERSION } /{{plugin}}/top/{{nb}}' , response_class = ORJSONResponse , endpoint = self ._api_top
237
- )
238
- router .add_api_route (
239
- f'/api/{ self .API_VERSION } /{{plugin}}/limits' , response_class = ORJSONResponse , endpoint = self ._api_limits
240
- )
241
- router .add_api_route (
242
- f'/api/{ self .API_VERSION } /{{plugin}}/views' , response_class = ORJSONResponse , endpoint = self ._api_views
243
- )
244
- router .add_api_route (
245
- f'/api/{ self .API_VERSION } /{{plugin}}/{{item}}' , response_class = ORJSONResponse , endpoint = self ._api_item
246
- )
247
- router .add_api_route (
248
- f'/api/{ self .API_VERSION } /{{plugin}}/{{item}}/history' ,
249
- response_class = ORJSONResponse ,
250
- endpoint = self ._api_item_history ,
251
- )
252
- router .add_api_route (
253
- f'/api/{ self .API_VERSION } /{{plugin}}/{{item}}/history/{{nb}}' ,
254
- response_class = ORJSONResponse ,
255
- endpoint = self ._api_item_history ,
256
- )
257
- router .add_api_route (
258
- f'/api/{ self .API_VERSION } /{{plugin}}/{{item}}/description' ,
259
- response_class = ORJSONResponse ,
260
- endpoint = self ._api_item_description ,
261
- )
262
- router .add_api_route (
263
- f'/api/{ self .API_VERSION } /{{plugin}}/{{item}}/unit' ,
264
- response_class = ORJSONResponse ,
265
- endpoint = self ._api_item_unit ,
266
- )
267
- router .add_api_route (
268
- f'/api/{ self .API_VERSION } /{{plugin}}/{{item}}/{{value:path}}' ,
269
- response_class = ORJSONResponse ,
270
- endpoint = self ._api_value ,
271
- )
203
+ router .add_api_route (f'{ base_path } /status' , self ._api_status , methods = ['HEAD' , 'GET' ])
204
+
205
+ route_mapping = {
206
+ f'{ base_path } /config' : self ._api_config ,
207
+ f'{ base_path } /config/{{section}}' : self ._api_config_section ,
208
+ f'{ base_path } /config/{{section}}/{{item}}' : self ._api_config_section_item ,
209
+ f'{ base_path } /args' : self ._api_args ,
210
+ f'{ base_path } /args/{{item}}' : self ._api_args_item ,
211
+ f'{ base_path } /help' : self ._api_help ,
212
+ f'{ base_path } /all' : self ._api_all ,
213
+ f'{ base_path } /all/limits' : self ._api_all_limits ,
214
+ f'{ base_path } /all/views' : self ._api_all_views ,
215
+ f'{ base_path } /pluginslist' : self ._api_plugins ,
216
+ f'{ plugin_path } ' : self ._api ,
217
+ f'{ plugin_path } /history' : self ._api_history ,
218
+ f'{ plugin_path } /history/{{nb}}' : self ._api_history ,
219
+ f'{ plugin_path } /top/{{nb}}' : self ._api_top ,
220
+ f'{ plugin_path } /limits' : self ._api_limits ,
221
+ f'{ plugin_path } /views' : self ._api_views ,
222
+ f'{ plugin_path } /{{item}}' : self ._api_item ,
223
+ f'{ plugin_path } /{{item}}/history' : self ._api_item_history ,
224
+ f'{ plugin_path } /{{item}}/history/{{nb}}' : self ._api_item_history ,
225
+ f'{ plugin_path } /{{item}}/description' : self ._api_item_description ,
226
+ f'{ plugin_path } /{{item}}/unit' : self ._api_item_unit ,
227
+ f'{ plugin_path } /{{item}}/{{value:path}}' : self ._api_value ,
228
+ }
229
+
230
+ for path , endpoint in route_mapping .items ():
231
+ router .add_api_route (path , endpoint )
272
232
273
233
# WEB UI
274
234
if not self .args .disable_webui :
275
235
# Template for the root index.html file
276
- router .add_api_route ('/' , response_class = HTMLResponse , endpoint = self ._index )
236
+ router .add_api_route ('/' , self ._index , response_class = HTMLResponse )
277
237
278
238
# Statics files
279
239
self ._app .mount (self .url_prefix + '/static' , StaticFiles (directory = self .STATIC_PATH ), name = "static" )
@@ -361,7 +321,7 @@ def _api_status(self):
361
321
See related issue: Web server health check endpoint #1988
362
322
"""
363
323
364
- return ORJSONResponse ({'version' : __version__ })
324
+ return GlancesJSONResponse ({'version' : __version__ })
365
325
366
326
def _api_help (self ):
367
327
"""Glances API RESTful implementation.
@@ -373,7 +333,7 @@ def _api_help(self):
373
333
except Exception as e :
374
334
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get help view data ({ str (e )} )" )
375
335
376
- return ORJSONResponse (plist )
336
+ return GlancesJSONResponse (plist )
377
337
378
338
def _api_plugins (self ):
379
339
"""Glances API RESTFul implementation.
@@ -409,7 +369,7 @@ def _api_plugins(self):
409
369
except Exception as e :
410
370
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get plugin list ({ str (e )} )" )
411
371
412
- return ORJSONResponse (plist )
372
+ return GlancesJSONResponse (plist )
413
373
414
374
def _api_all (self ):
415
375
"""Glances API RESTful implementation.
@@ -436,7 +396,7 @@ def _api_all(self):
436
396
except Exception as e :
437
397
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get stats ({ str (e )} )" )
438
398
439
- return ORJSONResponse (statval )
399
+ return GlancesJSONResponse (statval )
440
400
441
401
def _api_all_limits (self ):
442
402
"""Glances API RESTful implementation.
@@ -452,7 +412,7 @@ def _api_all_limits(self):
452
412
except Exception as e :
453
413
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get limits ({ str (e )} )" )
454
414
455
- return ORJSONResponse (limits )
415
+ return GlancesJSONResponse (limits )
456
416
457
417
def _api_all_views (self ):
458
418
"""Glances API RESTful implementation.
@@ -468,7 +428,7 @@ def _api_all_views(self):
468
428
except Exception as e :
469
429
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get views ({ str (e )} )" )
470
430
471
- return ORJSONResponse (limits )
431
+ return GlancesJSONResponse (limits )
472
432
473
433
def _api (self , plugin ):
474
434
"""Glances API RESTful implementation.
@@ -493,7 +453,7 @@ def _api(self, plugin):
493
453
except Exception as e :
494
454
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get plugin { plugin } ({ str (e )} )" )
495
455
496
- return ORJSONResponse (statval )
456
+ return GlancesJSONResponse (statval )
497
457
498
458
def _api_top (self , plugin , nb : int = 0 ):
499
459
"""Glances API RESTful implementation.
@@ -525,7 +485,7 @@ def _api_top(self, plugin, nb: int = 0):
525
485
if isinstance (statval , list ):
526
486
statval = statval [:nb ]
527
487
528
- return ORJSONResponse (statval )
488
+ return GlancesJSONResponse (statval )
529
489
530
490
def _api_history (self , plugin , nb : int = 0 ):
531
491
"""Glances API RESTful implementation.
@@ -577,7 +537,7 @@ def _api_limits(self, plugin):
577
537
status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get limits for plugin { plugin } ({ str (e )} )"
578
538
)
579
539
580
- return ORJSONResponse (ret )
540
+ return GlancesJSONResponse (ret )
581
541
582
542
def _api_views (self , plugin ):
583
543
"""Glances API RESTful implementation.
@@ -601,7 +561,7 @@ def _api_views(self, plugin):
601
561
status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get views for plugin { plugin } ({ str (e )} )"
602
562
)
603
563
604
- return ORJSONResponse (ret )
564
+ return GlancesJSONResponse (ret )
605
565
606
566
def _api_item (self , plugin , item ):
607
567
"""Glances API RESTful implementation.
@@ -629,7 +589,7 @@ def _api_item(self, plugin, item):
629
589
detail = f"Cannot get item { item } in plugin { plugin } ({ str (e )} )" ,
630
590
)
631
591
632
- return ORJSONResponse (ret )
592
+ return GlancesJSONResponse (ret )
633
593
634
594
def _api_item_history (self , plugin , item , nb : int = 0 ):
635
595
"""Glances API RESTful implementation.
@@ -657,7 +617,7 @@ def _api_item_history(self, plugin, item, nb: int = 0):
657
617
status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get history for plugin { plugin } ({ str (e )} )"
658
618
)
659
619
else :
660
- return ORJSONResponse (ret )
620
+ return GlancesJSONResponse (ret )
661
621
662
622
def _api_item_description (self , plugin , item ):
663
623
"""Glances API RESTful implementation.
@@ -682,7 +642,7 @@ def _api_item_description(self, plugin, item):
682
642
detail = f"Cannot get { item } description for plugin { plugin } ({ str (e )} )" ,
683
643
)
684
644
else :
685
- return ORJSONResponse (ret )
645
+ return GlancesJSONResponse (ret )
686
646
687
647
def _api_item_unit (self , plugin , item ):
688
648
"""Glances API RESTful implementation.
@@ -707,7 +667,7 @@ def _api_item_unit(self, plugin, item):
707
667
detail = f"Cannot get { item } unit for plugin { plugin } ({ str (e )} )" ,
708
668
)
709
669
else :
710
- return ORJSONResponse (ret )
670
+ return GlancesJSONResponse (ret )
711
671
712
672
def _api_value (self , plugin , item , value ):
713
673
"""Glances API RESTful implementation.
@@ -735,7 +695,7 @@ def _api_value(self, plugin, item, value):
735
695
detail = f"Cannot get { item } = { value } for plugin { plugin } ({ str (e )} )" ,
736
696
)
737
697
else :
738
- return ORJSONResponse (ret )
698
+ return GlancesJSONResponse (ret )
739
699
740
700
def _api_config (self ):
741
701
"""Glances API RESTful implementation.
@@ -750,7 +710,7 @@ def _api_config(self):
750
710
except Exception as e :
751
711
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get config ({ str (e )} )" )
752
712
else :
753
- return ORJSONResponse (args_json )
713
+ return GlancesJSONResponse (args_json )
754
714
755
715
def _api_config_section (self , section ):
756
716
"""Glances API RESTful implementation.
@@ -772,7 +732,7 @@ def _api_config_section(self, section):
772
732
status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get config section { section } ({ str (e )} )"
773
733
)
774
734
775
- return ORJSONResponse (ret_section )
735
+ return GlancesJSONResponse (ret_section )
776
736
777
737
def _api_config_section_item (self , section , item ):
778
738
"""Glances API RESTful implementation.
@@ -803,7 +763,7 @@ def _api_config_section_item(self, section, item):
803
763
detail = f"Cannot get item { item } in config section { section } ({ str (e )} )" ,
804
764
)
805
765
806
- return ORJSONResponse (ret_item )
766
+ return GlancesJSONResponse (ret_item )
807
767
808
768
def _api_args (self ):
809
769
"""Glances API RESTful implementation.
@@ -820,7 +780,7 @@ def _api_args(self):
820
780
except Exception as e :
821
781
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get args ({ str (e )} )" )
822
782
823
- return ORJSONResponse (args_json )
783
+ return GlancesJSONResponse (args_json )
824
784
825
785
def _api_args_item (self , item ):
826
786
"""Glances API RESTful implementation.
@@ -841,4 +801,4 @@ def _api_args_item(self, item):
841
801
except Exception as e :
842
802
raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Cannot get args item ({ str (e )} )" )
843
803
844
- return ORJSONResponse (args_json )
804
+ return GlancesJSONResponse (args_json )
0 commit comments