diff --git a/feat.txt b/feat.txt index 64c51cb..a4af5f4 100644 --- a/feat.txt +++ b/feat.txt @@ -92,3 +92,9 @@ DASHBOARD | **RAM Usage** | Server memory pressure | % | | **Disk Usage** | Remaining DB server disk | % | | **Disk I/O** | Storage read/write activity | MB/s | + + + +SPOKE (LIMIT) +CONNECTIVE (LIMIT) +PGEDGE (ENTRIPRISE VS OPENSOURCE) \ No newline at end of file diff --git a/instructions.txt b/instructions.txt index 9680ab6..da9197e 100644 --- a/instructions.txt +++ b/instructions.txt @@ -19,4 +19,14 @@ source /home/george/Desktop/PROJECT26/psqlDB/venv/bin/activate INSTALL IN DB, IF NOT DONE!!! -CREATE EXTENSION IF NOT EXISTS pg_stat_statements; \ No newline at end of file +CREATE EXTENSION IF NOT EXISTS pg_stat_statements; + +psutil.cpu_percent() +psutil.virtual_memory() +psutil.disk_usage() + +const token = localStorage.getItem("token"); + +const socket = new WebSocket( + `ws://172.16.10.178:8000/ws/node/replication-health?token=${encodeURIComponent(token)}` +); \ No newline at end of file diff --git a/main.py b/main.py index 804082e..aff70d4 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ import os import uvicorn from dotenv import load_dotenv -from fastapi import FastAPI,Depends, Header, HTTPException, status, WebSocket, WebSocketDisconnect +from fastapi import FastAPI,Depends, Header, HTTPException, status, WebSocket, WebSocketDisconnect, WebSocketException import psycopg2 from jwt import ExpiredSignatureError, InvalidTokenError import jwt @@ -89,8 +89,42 @@ def verify_token( ) +# JWT TOKEN VERIFICATION FOR WEBSOCKET CONNECTIONS +async def verify_websocket_token(websocket: WebSocket): + + token = websocket.query_params.get("token") + + if not token: + raise WebSocketException( + code=status.WS_1008_POLICY_VIOLATION, + reason="Authentication token missing" + ) + + try: + + payload = jwt.decode( + token, + SECRET_KEY, + algorithms=[ALGORITHM], + ) + + return payload + + except ExpiredSignatureError: + raise WebSocketException( + code=status.WS_1008_POLICY_VIOLATION, + reason="Token expired" + ) + + except InvalidTokenError: + + + raise WebSocketException( + code=status.WS_1008_POLICY_VIOLATION, + reason="Invalid token" + ) @app.get("/") @@ -215,9 +249,12 @@ def get_replication_health(data: NodeStatusRequest): # "user": "postgres", # "password": "postgres" # } - +#DONE @app.websocket("/ws/node/replication-health") async def replication_monitor(websocket: WebSocket): + token_data = await verify_websocket_token(websocket) + # if token_data is None: + # return await websocket.accept() try: raw_data = await websocket.receive_json() @@ -344,10 +381,12 @@ def get_database_uptime( if conn: conn.close() - +# DONE @app.websocket("/ws/database/status") async def database_status_monitor(websocket: WebSocket): - + token_data = await verify_websocket_token(websocket) + # if token_data is None: + # return await websocket.accept() try: @@ -508,9 +547,12 @@ def get_connection_usage(data: DatabaseRequest): conn.close() # WEB SOCKETS FOR ACTIVE CONNECTIONS MONITORING +# DONE @app.websocket("/ws/database/active-connections") async def database_active_connections_monitor(websocket: WebSocket): - + token_data = await verify_websocket_token(websocket) + # if token_data is None: + # return await websocket.accept() try: @@ -725,9 +767,12 @@ def get_cache_hit_ratio(data: DatabaseRequest): conn.close() #TPS AND CACHE HIT RATIO WEB SOCKET +# DONE @app.websocket("/ws/database/performance") async def database_performance_monitor(websocket: WebSocket): - + token_data = await verify_websocket_token(websocket) + # if token_data is None: + # return await websocket.accept() try: @@ -911,7 +956,9 @@ def get_database_deadlocks(data: DatabaseRequest): # LOCKS AND DEADLOCKS WEB SOCKET @app.websocket("/ws/database/locks-deadlocks") async def database_locks_deadlocks_monitor(websocket: WebSocket): - + token_data = await verify_websocket_token(websocket) + # if token_data is None: + # return await websocket.accept() try: @@ -1040,6 +1087,7 @@ def get_database_size(data: DatabaseRequest): # API ENDPOINTS FOR DATABASE SIZE +#DONE @app.post("/database/size") def database_size( data: DatabaseRequest, @@ -1136,6 +1184,7 @@ def get_largest_tables( # SHOWS THE LIST OF TABLES IN THE DATABASE SORTED BY SIZE +# DONE @app.post("/database/largest-tables") def database_largest_tables( data: DatabaseRequest, @@ -1216,6 +1265,7 @@ def get_index_size(data: DatabaseRequest): # API ENDPOINT FOR INDEX SIZE REST API +# DONE @app.post("/database/index-size") def database_index_size( data: DatabaseRequest, @@ -1338,9 +1388,12 @@ def get_tuple_reads_writes(data: DatabaseRequest): conn.close() # TUPLE READS AND WRITES PER SECOND WEB SOCKET +# DONE @app.websocket("/ws/database/tuple-activity") async def database_tuple_activity_monitor(websocket: WebSocket): - + token_data = await verify_websocket_token(websocket) + if token_data is None: + return await websocket.accept() try: @@ -1429,11 +1482,25 @@ def get_dead_tuples(data: DatabaseRequest): n_live_tup, n_dead_tup, last_vacuum, - last_autovacuum + last_autovacuum, + + pg_total_relation_size( + quote_ident(schemaname) || '.' || + quote_ident(relname) + ) AS size_bytes, + + ROUND( + pg_total_relation_size( + quote_ident(schemaname) || '.' || + quote_ident(relname) + )::numeric + / 1024 / 1024, + 2 + ) AS size_mb FROM pg_stat_user_tables - WHERE n_dead_tup > 0 - ORDER BY n_dead_tup DESC; + + ORDER BY size_bytes DESC; """) rows = cursor.fetchall() @@ -1443,17 +1510,40 @@ def get_dead_tuples(data: DatabaseRequest): for row in rows ) + total_size_bytes = sum( + row["size_bytes"] or 0 + for row in rows + ) + + total_size_mb = round( + total_size_bytes / 1024 / 1024, + 2 + ) + return { "database_name": data.dbname, + + "table_count": len(rows), + "total_dead_tuples": total_dead_tuples, + "total_table_size_bytes": total_size_bytes, + "total_table_size_mb": total_size_mb, + "tables": [ { "schema": row["schemaname"], + "table_name": row["table_name"], + "live_tuples": row["n_live_tup"], + "dead_tuples": row["n_dead_tup"], + "size_bytes": row["size_bytes"], + + "size_mb": float(row["size_mb"]), + "last_vacuum": row["last_vacuum"].isoformat() if row["last_vacuum"] @@ -1472,8 +1562,17 @@ def get_dead_tuples(data: DatabaseRequest): return { "database_name": data.dbname, + + "table_count": None, + "total_dead_tuples": None, + + "total_table_size_bytes": None, + + "total_table_size_mb": None, + "tables": [], + "error": str(e) }