Compare commits
2 Commits
main
...
feature/ro
| Author | SHA1 | Date | |
|---|---|---|---|
| 8630a3e2c5 | |||
| 8fe130f918 |
6
.env
Normal file
6
.env
Normal file
@ -0,0 +1,6 @@
|
||||
VITE_API_URL=http://localhost:3000/api/v1
|
||||
|
||||
NODE_ENV=development
|
||||
|
||||
#Database Configuration
|
||||
DB_NAME=ve_router_db
|
||||
@ -4,35 +4,40 @@ services:
|
||||
frontend:
|
||||
build:
|
||||
context: ./router-dashboard
|
||||
dockerfile: Dockerfile
|
||||
dockerfile: dockerfile
|
||||
ports:
|
||||
- "5173:5173"
|
||||
environment:
|
||||
- VITE_API_URL=http://localhost:3001/api/v1
|
||||
- VITE_API_URL=${VITE_API_URL}
|
||||
restart: always
|
||||
depends_on:
|
||||
- backend
|
||||
volumes:
|
||||
- ./router-dashboard:/app
|
||||
- /app/node_modules
|
||||
backend:
|
||||
condition: service_healthy
|
||||
container_name: router_dashboard_frontend
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./ve-router-backend
|
||||
dockerfile: Dockerfile
|
||||
dockerfile: dockerfile
|
||||
ports:
|
||||
- "3001:3000"
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- NODE_ENV=${NODE_ENV}
|
||||
- DB_HOST=host.docker.internal
|
||||
- DB_PORT=3307
|
||||
- DB_PORT=3306
|
||||
- DB_USER=ve_router_user
|
||||
- DB_PASSWORD=ve_router_password
|
||||
- DB_NAME=ve_router_db
|
||||
- DB_NAME=${DB_NAME}
|
||||
restart: always
|
||||
depends_on:
|
||||
- mysql
|
||||
volumes:
|
||||
- ./ve-router-backend:/app
|
||||
- /app/node_modules
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "-z", "localhost", "3000"] # Netcat check to see if port 3000 is open
|
||||
interval: 30s # Check every 30 seconds
|
||||
retries: 3 # Retry 3 times before marking unhealthy
|
||||
start_period: 30s # Wait 30 seconds before starting health checks
|
||||
timeout: 10s # Wait for 10 seconds for each health check to respond
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
@ -44,11 +49,11 @@ services:
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
# Correct paths for init scripts
|
||||
- ./router-dashboard/sql/init.sql:/docker-entrypoint-initdb.d/01-init.sql
|
||||
- ./router-dashboard/sql/seed_data.sql:/docker-entrypoint-initdb.d/02-seed_data.sql
|
||||
- ./sql:/docker-entrypoint-initdb.d
|
||||
ports:
|
||||
- "3307:3306"
|
||||
- "3306:3306"
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "ve_router_user", "-pve_router_password"]
|
||||
interval: 10s
|
||||
@ -57,4 +62,4 @@ services:
|
||||
start_period: 30s
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
mysql_data:
|
||||
18
readme.txt
Normal file
18
readme.txt
Normal file
@ -0,0 +1,18 @@
|
||||
1. Go to router-dashboard directory
|
||||
2. Run below command to build the docker images, create sql schema, insert data and start containers
|
||||
docker-compose up --build -d
|
||||
3. When code changes done, then just run above command mentioned in point 2,
|
||||
it will udpate the changes and restart containers for which code changed
|
||||
4. Open below URL in web browser to verify UI
|
||||
http://localhost:5173
|
||||
5. Open mysql workbench/any tool to view schema details
|
||||
database:ve_router_db
|
||||
host: localhost
|
||||
port:3306
|
||||
user/password: ve_router_user/ve_router_password
|
||||
|
||||
6. Run below command to stop and remove containers
|
||||
docker-compose down
|
||||
7. Run below command to stop, remove and delete all the volumes
|
||||
Caution : if mysql has volumes then all the existing data will be erasesd
|
||||
docker-compose down -v
|
||||
@ -1,2 +1,2 @@
|
||||
VITE_API_URL=http://localhost:3001/api/v1
|
||||
VITE_API_URL=http://localhost:3000/api/v1
|
||||
VITE_NODE_ENV=development
|
||||
@ -8,7 +8,7 @@ RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
ENV VITE_API_URL=http://localhost:3001/api/v1
|
||||
ENV VITE_API_URL=http://localhost:3000/api/v1
|
||||
|
||||
EXPOSE 5173
|
||||
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
||||
<link rel="icon" type="image/png" href="ve.png" >
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<!-- <title>Vite + React + TS </title> -->
|
||||
<title>Router Management</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
BIN
router-dashboard/public/ve.png
Normal file
BIN
router-dashboard/public/ve.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 905 B |
1
router-dashboard/src/assets/images/react-logo-tm.svg
Normal file
1
router-dashboard/src/assets/images/react-logo-tm.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@ -1 +1,99 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 225.9 45.8" style="enable-background:new 0 0 225.9 45.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#231F52;}
|
||||
.st1{fill:#05887A;}
|
||||
.st2{fill:#87C35F;}
|
||||
.st3{fill:#68C7D7;}
|
||||
.st4{fill:#24767B;}
|
||||
.st5{fill:#2377B5;}
|
||||
.st6{fill:#069666;}
|
||||
.st7{fill:#60B046;}
|
||||
.st8{fill:#ECE843;}
|
||||
.st9{fill:#D8DF25;}
|
||||
.st10{fill:#204780;}
|
||||
.st11{fill:#81BC42;}
|
||||
.st12{fill:#244087;}
|
||||
.st13{fill:#224480;}
|
||||
.st14{fill:#148745;}
|
||||
.st15{fill:#298DC9;}
|
||||
.st16{fill:#17464A;}
|
||||
.st17{fill:#2B67A8;}
|
||||
.st18{fill:#25AC6D;}
|
||||
.st19{fill:#276780;}
|
||||
.st20{fill:#2783C1;}
|
||||
.st21{fill:#2894CE;}
|
||||
</style>
|
||||
<g id="VitalEngine_Horizontal_4c" transform="translate(-135.213 -220.46)">
|
||||
<g id="Group_3" transform="translate(188.083 239.605)">
|
||||
<g id="Group_2" transform="translate(67.686)">
|
||||
<g id="Group_1">
|
||||
<path id="Path_1" class="st0" d="M14.4,22H0V0.9h14.4v2.9h-11v5.8h9.1v2.9H3.4v6.6h11V22z"/>
|
||||
<path id="Path_2" class="st0" d="M18.2,22V5.7h3.1v2c2-1.7,2.7-2,4.1-2h2.6c1-0.1,2,0.2,2.8,0.9c0.6,0.6,1,1.4,1,3.7V22h-3.1
|
||||
V10.5c0.1-0.6-0.1-1.1-0.4-1.6c-0.3-0.3-0.6-0.5-1.7-0.5h-2.1c-1.1,0-2.1,0.4-3,0.9V22L18.2,22L18.2,22z"/>
|
||||
<path id="Path_3" class="st0" d="M39.8,26.5c-1,0.1-1.9-0.2-2.7-0.8c-0.6-0.6-0.9-1.4-0.9-3.2h3.1c0,0.7,0.1,1,0.3,1.2
|
||||
c0.2,0.2,0.4,0.2,1.2,0.2h3.9c0.9,0,1.2-0.1,1.4-0.3c0.2-0.2,0.3-0.5,0.3-2v-3.4c-2.1,1.7-2.7,2-4.1,2h-2.2
|
||||
c-1.7,0-2.6-0.3-3.3-1c-0.9-0.9-1.3-1.8-1.3-6.3s0.4-5.4,1.3-6.3c0.7-0.7,1.6-1,3.3-1h2.2c1.3,0,2,0.3,4.1,2v-2h3.1v15.5
|
||||
c0,2.9-0.3,3.7-1,4.4c-0.5,0.5-1.3,0.9-3.2,0.9L39.8,26.5L39.8,26.5z M46.6,16.5V9.4c-1-0.6-2-0.9-3.2-1h-2.3
|
||||
c-1,0-1.4,0.1-1.7,0.4C39,9.3,38.9,9.7,38.9,13s0.1,3.6,0.6,4.1c0.3,0.3,0.7,0.4,1.7,0.4h2.4C44.7,17.4,45.7,17.1,46.6,16.5
|
||||
L46.6,16.5z"/>
|
||||
<path id="Path_4" class="st0" d="M57.7,3.4h-3.6V0h3.6V3.4z M57.5,22h-3.1V5.7h3.1V22z"/>
|
||||
<path id="Path_5" class="st0" d="M62.1,22V5.7h3.1v2c2-1.7,2.7-2,4.1-2h2.6c1-0.1,2,0.2,2.8,0.9c0.6,0.6,1,1.4,1,3.7V22h-3.1
|
||||
V10.5c0.1-0.6-0.1-1.1-0.4-1.6c-0.3-0.3-0.6-0.5-1.7-0.5h-2.1c-1.1,0-2.1,0.4-3,0.9V22L62.1,22L62.1,22z"/>
|
||||
<path id="Path_6" class="st0" d="M82.7,14.9c0,3.2,0.2,3.8,0.5,4.1s0.5,0.3,1.4,0.3h4.2c0.7,0,0.9-0.1,1.1-0.3
|
||||
c0.2-0.2,0.3-0.6,0.3-2h3c-0.1,2.5-0.3,3.4-1.1,4.2c-0.7,0.6-1.7,0.9-2.7,0.8h-5.3c-1.2,0.1-2.3-0.2-3.2-0.9
|
||||
c-1.1-1.1-1.4-2.3-1.4-7.2s0.3-6.1,1.4-7.2c0.7-0.7,1.6-0.9,3.2-0.9h4.5c1.2-0.1,2.3,0.2,3.2,0.9c1.1,1.1,1.4,2.3,1.4,7.1v0.8
|
||||
c0,0.2-0.1,0.4-0.4,0.4L82.7,14.9L82.7,14.9z M82.7,12.4h7.4c0-2.7-0.2-3.3-0.5-3.6c-0.2-0.2-0.5-0.3-1.4-0.3h-3.7
|
||||
c-0.9,0-1.2,0.1-1.4,0.3C82.9,9.1,82.8,9.6,82.7,12.4z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Path_7" class="st0" d="M20.8,3.4h3.6V0h-3.6V3.4z M21,22h3.1V5.7H21L21,22z M9.4,18.7H9L3.6,0.9H0l6.4,20.4
|
||||
c0.2,0.7,0.5,0.8,1.2,0.8h2.9c0.8,0,1-0.1,1.2-0.8l6.4-20.4h-3.4L9.4,18.7z M49.1,5.7H44c-1-0.1-2,0.2-2.7,0.9
|
||||
c-0.6,0.6-0.9,1.4-1,4h3.1c0.1-1.2,0.1-1.8,0.4-2c0.2-0.2,0.5-0.3,1.3-0.3h3.2c0.9,0,1.2,0.1,1.4,0.3c0.2,0.2,0.4,0.7,0.4,2.6v1.8
|
||||
c-1.3-0.3-2.7-0.5-4-0.5c-3.9,0-4.7,0.5-5.4,1.2c-0.6,0.6-1,1.9-1,3.8c0,2,0.4,3,1,3.6c0.7,0.7,1.4,0.8,2.9,0.8h2.5
|
||||
c1.4,0,2-0.4,4-2v2h3.1V11c0-2.9-0.2-3.7-0.9-4.4C51.7,6.1,50.9,5.7,49.1,5.7z M50.1,18.3c-1,0.7-2.2,1-3.4,1h-2.3
|
||||
c-0.7,0-1.1,0-1.3-0.2c-0.2-0.2-0.3-0.8-0.3-2c0-1.1,0.1-1.4,0.4-1.7c0.3-0.3,0.7-0.4,2.7-0.4h4.2L50.1,18.3L50.1,18.3z M33,1.7
|
||||
h-3.2v4h-2.8v2.7h2.8v10.4c-0.1,0.9,0.1,1.8,0.7,2.5c0.7,0.6,1.6,0.9,2.4,0.8c0.7,0,1.4-0.1,2-0.2l1.8-0.5v-2.1h-2.5
|
||||
c-0.8,0-1.1,0-1.2-0.1C33.1,18.9,33,18.7,33,18V8.5h3.9V5.7H33L33,1.7z M62.2,19.2c-0.8,0-1.1,0-1.2-0.1c-0.1-0.1-0.2-0.4-0.2-1.1
|
||||
V8.5l0,0V0.3h-3.1v8.2h0v10.4c-0.1,0.9,0.1,1.8,0.7,2.5c0.7,0.6,1.6,0.9,2.4,0.8c0.7,0,1.4-0.1,2-0.2l1.8-0.5v-2.1H62.2L62.2,19.2
|
||||
z"/>
|
||||
</g>
|
||||
<g id="Group_4" transform="translate(135.213 220.46)">
|
||||
<path id="Path_8" class="st1" d="M29.6,25.9l-4.1,7.6L17,18.4h8.4L29.6,25.9z"/>
|
||||
<path id="Path_9" class="st2" d="M25.4,18.4l5-9.2h10.1l-4.2,7.6c0,0-1,0-1.5,0c-0.2,0-0.4,0.1-0.4,0.2c-0.2,0.4-0.8,1.3-0.8,1.3
|
||||
L25.4,18.4z"/>
|
||||
<path id="Path_10" class="st3" d="M11.9,9.3l-1.8-3.2L9.2,7.5L5.1,0l9.8,0c0.2,0,0.4,0.1,0.5,0.3c1.3,2.3,5,8.9,5,8.9L11.9,9.3z"
|
||||
/>
|
||||
<path id="Path_11" class="st4" d="M23.6,36.6l1.8,3.2l0.8-1.4l4.2,7.4c0,0,0,0-0.2,0c-3.2,0-6.4,0-9.7,0c-0.1,0-0.3-0.1-0.3-0.2
|
||||
l-5-9L23.6,36.6z"/>
|
||||
<path id="Path_12" class="st5" d="M5.1,0l4.2,7.5l-1,1.8c0,0-7.7,0-8.3,0c0-0.1,0-0.1,0-0.2c0.3-0.5,4.1-7.5,4.9-8.9
|
||||
C4.9,0.2,4.9,0.1,5.1,0C5,0,5,0,5.1,0z"/>
|
||||
<path id="Path_13" class="st6" d="M40.6,27.5l-5.1,9.1h-8.3l5-9.1L40.6,27.5z"/>
|
||||
<path id="Path_14" class="st7" d="M30.4,9.2c0,0,4.4-8,4.8-8.8c0.1-0.1,0.2-0.2,0.3-0.3l5.1,9.1L30.4,9.2z"/>
|
||||
<path id="Path_15" class="st8" d="M40.6,9.2c0,0,5-9,5.1-9.1c1.5,2.8,5,9.1,5,9.1c0,0,0,0,0,0C50.6,9.2,43.9,9.2,40.6,9.2z"/>
|
||||
<path id="Path_16" class="st9" d="M45.7,0.1l-5.1,9.1l-5.1-9.1c0,0,0-0.1,0.3-0.1c3.2,0,6.3,0,9.5,0C45.5,0,45.6,0,45.7,0.1z"/>
|
||||
<path id="Path_17" class="st10" d="M27.2,36.7h8.3c0,0-4.6,8.4-5,9c0,0-0.1,0.1-0.1,0.1l-4.2-7.4L27.2,36.7z"/>
|
||||
<path id="Path_18" class="st11" d="M50.7,9.2c0,0-2.8,5.2-4,7.4c-0.1,0.2-0.2,0.3-0.4,0.2c-0.5,0-1.5,0-1.5,0l-4.2-7.6L50.7,9.2z"
|
||||
/>
|
||||
<path id="Path_19" class="st12" d="M19.5,29.2l-4.2,7.4l-5.1-9.1l8.3,0L19.5,29.2z"/>
|
||||
<path id="Path_20" class="st13" d="M25.4,18.4H17l-0.9-1.6l4.2-7.6L25.4,18.4z"/>
|
||||
<path id="Path_21" class="st14" d="M40.6,27.5L36.4,20c0.1-0.1,0.2-0.1,0.3-0.1h8.1L40.6,27.5z"/>
|
||||
<path id="Path_22" class="st7" d="M44.8,16.8h-8.4l4.2-7.6L44.8,16.8z"/>
|
||||
<path id="Path_23" class="st15" d="M16.1,16.8l-4.2-7.5l8.4-0.1L16.1,16.8z"/>
|
||||
<path id="Path_24" class="st16" d="M36.4,20l4.2,7.5l-8.4,0c0,0,2.9-5.3,4-7.3C36.3,20.1,36.3,20.1,36.4,20z"/>
|
||||
<path id="Path_25" class="st17" d="M14.4,20c0.2,0.4,4.2,7.5,4.2,7.5l-8.3,0C10.2,27.5,14.3,20.1,14.4,20z"/>
|
||||
<path id="Path_26" class="st18" d="M25.4,18.4l8.3,0l-4.1,7.5L25.4,18.4z"/>
|
||||
<path id="Path_27" class="st19" d="M19.5,29.2l4.1,7.4l-8.3,0L19.5,29.2z"/>
|
||||
<path id="Path_28" class="st20" d="M6,20l4.2,7.5l4.2-7.5L6,20z"/>
|
||||
<path id="Path_29" class="st21" d="M0,9.2l8.3,0l-4.1,7.5L0,9.2z"/>
|
||||
<path id="Path_30" class="st3" d="M14.4,20L6,20l4.1-7.6L14.4,20z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st0" d="M218.2,16.4h-2v5.5h-1.4v-5.5h-2v-1.1h5.4V16.4z"/>
|
||||
<path class="st0" d="M220.7,15.3l1.7,4.8l1.7-4.8h1.8v6.6h-1.4v-1.8l0.1-3.1l-1.8,4.9h-0.9l-1.8-4.9l0.1,3.1v1.8h-1.4v-6.6H220.7z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 6.5 KiB |
@ -40,7 +40,7 @@ const Dashboard = () => {
|
||||
router.diskStatus === 'DISK_CRITICAL'
|
||||
).length,
|
||||
diskWarnings: data.filter(router =>
|
||||
router.diskUsage >= 80
|
||||
router.diskUsage >= 70
|
||||
).length
|
||||
};
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import React from 'react';
|
||||
import { ChevronRight, ChevronDown } from 'lucide-react';
|
||||
import { RouterData } from '../../types';
|
||||
import { STATUS_COLORS, formatStatus, getStatusColor } from '../../utils/statusHelpers';
|
||||
import { STATUS_COLORS, formatStatus, getStatusColor, getVMStatus, getSystemStatus } from '../../utils/statusHelpers';
|
||||
|
||||
interface RouterTableRowProps {
|
||||
router: RouterData;
|
||||
@ -88,6 +88,12 @@ export const RouterTableRow: React.FC<RouterTableRowProps> = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<h4 className="font-semibold mb-2">VM Status</h4>
|
||||
<span className={`px-2 py-1 rounded-full text-sm ${getStatusColor(getVMStatus(router.lastSeen))}`}>
|
||||
{formatStatus(getVMStatus(router.lastSeen))}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-2">VPN Status</h4>
|
||||
<span className={`px-2 py-1 rounded-full text-sm ${getStatusColor(router.systemStatus.vpnStatus)}`}>
|
||||
@ -96,22 +102,22 @@ export const RouterTableRow: React.FC<RouterTableRowProps> = ({
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-2">App Status</h4>
|
||||
<span className={`px-2 py-1 rounded-full text-sm ${getStatusColor(router.diskStatus)}`}>
|
||||
{formatStatus(router.diskStatus)}
|
||||
<span className={`px-2 py-1 rounded-full text-sm ${getStatusColor(router.systemStatus.appStatus)}`}>
|
||||
{formatStatus(router.systemStatus.appStatus)}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-2">VM Status</h4>
|
||||
{router.systemStatus.vms.length > 0 ? (
|
||||
router.systemStatus.vms.map((vm, idx) => (
|
||||
<h4 className="font-semibold mb-2">Container Status</h4>
|
||||
{router.systemStatus.containers.length > 0 ? (
|
||||
router.systemStatus.containers.map((container, idx) => (
|
||||
<div key={idx} className="mb-1">
|
||||
<span className={`px-2 py-1 rounded-full text-sm ${getStatusColor(vm.status)}`}>
|
||||
VM {vm.id}: {formatStatus(vm.status)}
|
||||
<span className={`px-2 py-1 rounded-full text-sm ${getStatusColor(container.status)}`}>
|
||||
{container.container_name}: {formatStatus(container.status)}
|
||||
</span>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<span className="text-gray-500">No VMs configured</span>
|
||||
<span className="text-gray-500">No Containers configured</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@ interface Config {
|
||||
}
|
||||
|
||||
const config: Config = {
|
||||
apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3001/api/v1',
|
||||
apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
|
||||
environment: import.meta.env.VITE_NODE_ENV || 'development',
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// router-dashboard/src/services/api.service.ts
|
||||
import { RouterData, FilterType, BackendRouter } from '../types';
|
||||
|
||||
const API_BASE_URL = 'http://localhost:3001/api/v1';
|
||||
const API_BASE_URL = 'http://localhost:3000/api/v1';
|
||||
|
||||
// Default request options for all API calls
|
||||
const DEFAULT_OPTIONS = {
|
||||
@ -84,6 +84,12 @@ class ApiService {
|
||||
id: vm.id,
|
||||
status: vm.status
|
||||
}))
|
||||
: [],
|
||||
containers: Array.isArray(router.systemStatus?.containers)
|
||||
? router.systemStatus.containers.map((container: any) => ({
|
||||
container_name: container.container_name,
|
||||
status: container.status_code
|
||||
}))
|
||||
: []
|
||||
}
|
||||
};
|
||||
|
||||
@ -14,6 +14,11 @@ export interface VM {
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface Container {
|
||||
container_name: string;
|
||||
status_code: string;
|
||||
}
|
||||
|
||||
export type FilterType = 'all' | 'active' | 'critical' | 'diskAlert';
|
||||
|
||||
export interface RouterData {
|
||||
@ -34,6 +39,7 @@ export interface RouterData {
|
||||
vpnStatus: string;
|
||||
appStatus: string;
|
||||
vms: VM[];
|
||||
containers: Container[];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// src/utils/statusHelpers.ts
|
||||
|
||||
// Define all possible status values
|
||||
// Below's are for demo purpose
|
||||
|
||||
export type StatusType =
|
||||
| 'RUNNING'
|
||||
| 'STOPPED'
|
||||
@ -8,11 +9,16 @@ export type StatusType =
|
||||
| 'CONNECTED'
|
||||
| 'DISCONNECTED'
|
||||
| 'ERROR'
|
||||
| 'UNKNOWN';
|
||||
| 'UNKNOWN'
|
||||
| 'ONLINE'
|
||||
| 'OFFLINE';
|
||||
|
||||
|
||||
export const STATUS_COLORS: Record<StatusType | string, string> = {
|
||||
'RUNNING': 'bg-green-100 text-green-700',
|
||||
'CONNECTED': 'bg-green-100 text-green-700',
|
||||
'ONLINE': 'bg-green-100 text-green-700',
|
||||
'OFFLINE': 'bg-red-100 text-red-700',
|
||||
'STOPPED': 'bg-red-100 text-red-700',
|
||||
'DISCONNECTED': 'bg-red-100 text-red-700',
|
||||
'WARNING': 'bg-yellow-100 text-yellow-700',
|
||||
@ -20,11 +26,55 @@ export const STATUS_COLORS: Record<StatusType | string, string> = {
|
||||
'UNKNOWN': 'bg-gray-100 text-gray-700'
|
||||
};
|
||||
|
||||
export const formatStatus = (status: string): string => {
|
||||
return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
|
||||
// Add this helper function
|
||||
|
||||
function getStatusAfterUnderscore(status: string): string {
|
||||
if (status.includes('_')) {
|
||||
const parts = status.split('_');
|
||||
return parts[1] || ''; // Get the part after the underscore or return an empty string
|
||||
}
|
||||
return status; // Return the original string if no underscore is present
|
||||
}
|
||||
|
||||
export const getStatus = (status: string): string => {
|
||||
const keywords = ['CONNECTED', 'ONLINE', 'RUNNING'];
|
||||
return keywords.some((keyword) => status === keyword) ? 'Up' : 'Down';
|
||||
};
|
||||
|
||||
// Add this helper function
|
||||
export const getStatusColor = (status: string): string => {
|
||||
return STATUS_COLORS[status] || STATUS_COLORS['UNKNOWN'];
|
||||
return STATUS_COLORS[getStatusAfterUnderscore(status)] || STATUS_COLORS['UNKNOWN'];
|
||||
};
|
||||
|
||||
export const formatStatus = (status: string): string => {
|
||||
return getStatus(getStatusAfterUnderscore(status));
|
||||
};
|
||||
|
||||
export const getVMStatus = (lastSeen: string | number | Date) => {
|
||||
const currentTime = new Date();
|
||||
const lastSeenTime = new Date(lastSeen);
|
||||
|
||||
// Use getTime() to get timestamps in milliseconds
|
||||
const diffInMinutes = (currentTime.getTime() - lastSeenTime.getTime()) / (1000 * 60);
|
||||
|
||||
return diffInMinutes > 1 ? 'NET_ONLINE' : 'NET_ONLINE'; //demo purpose returning only online
|
||||
};
|
||||
|
||||
export const getSystemStatus = (lastSeen: string | number | Date, vpnStatus: string, appStatus:string) => {
|
||||
const vmStatus = getVMStatus(lastSeen);
|
||||
|
||||
const expectedStatuses = {
|
||||
VPN_CONNECTED: 'VPN_CONNECTED',
|
||||
CONTAINER_RUNNING: 'CONTAINER_RUNNING',
|
||||
NET_ONLINE: 'NET_ONLINE',
|
||||
};
|
||||
|
||||
if (
|
||||
vpnStatus === expectedStatuses.VPN_CONNECTED &&
|
||||
appStatus === expectedStatuses.CONTAINER_RUNNING &&
|
||||
vmStatus === expectedStatuses.NET_ONLINE
|
||||
) {
|
||||
return 'CONNECTED';
|
||||
}
|
||||
return 'DISCONNECTED';
|
||||
|
||||
};
|
||||
@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS routers (
|
||||
last_seen TIMESTAMP NOT NULL,
|
||||
vpn_status_code VARCHAR(50) NOT NULL,
|
||||
disk_status_code VARCHAR(50) NOT NULL,
|
||||
app_status_code VARCHAR(50) NOT NULL,
|
||||
license_status ENUM('active', 'inactive', 'suspended') NOT NULL DEFAULT 'inactive',
|
||||
free_disk BIGINT NOT NULL CHECK (free_disk >= 0),
|
||||
total_disk BIGINT NOT NULL CHECK (total_disk > 0),
|
||||
@ -63,33 +64,15 @@ CREATE TABLE IF NOT EXISTS user_sessions (
|
||||
CONSTRAINT unique_refresh_token UNIQUE(refresh_token)
|
||||
);
|
||||
|
||||
-- System status table
|
||||
CREATE TABLE IF NOT EXISTS system_status (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
router_id INT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Container status table
|
||||
CREATE TABLE IF NOT EXISTS container_status (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
system_status_id INT NOT NULL,
|
||||
container_number INT NOT NULL CHECK (container_number BETWEEN 1 AND 10),
|
||||
status_code VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- VM details table
|
||||
CREATE TABLE IF NOT EXISTS vm_details (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
router_id INT NOT NULL,
|
||||
vm_number INT NOT NULL CHECK (vm_number > 0),
|
||||
status_code VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT unique_vm_per_router UNIQUE(router_id, vm_number)
|
||||
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
router_id varchar(50) NOT NULL,
|
||||
container_name varchar(50) NOT NULL,
|
||||
status_code varchar(50) NOT NULL,
|
||||
created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE(router_id, container_name)
|
||||
);
|
||||
|
||||
-- DICOM study overview table with router_id as a string reference
|
||||
@ -118,7 +101,7 @@ CREATE TABLE IF NOT EXISTS status_type (
|
||||
category_id VARCHAR(50),
|
||||
name VARCHAR(100),
|
||||
code VARCHAR(100),
|
||||
description VARCHAR(20),
|
||||
description VARCHAR(150),
|
||||
severity INT
|
||||
);
|
||||
|
||||
105
sql/02-seed_data.sql
Normal file
105
sql/02-seed_data.sql
Normal file
@ -0,0 +1,105 @@
|
||||
DELIMITER //
|
||||
|
||||
CREATE PROCEDURE seed_complete_router_system()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT 0;
|
||||
DECLARE table_name VARCHAR(64);
|
||||
DECLARE table_cursor CURSOR FOR
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name IN (
|
||||
'auth_log', 'user_sessions', 'user_router_access', 'users',
|
||||
'container_status_history', 'router_status_history',
|
||||
'container_status', 'dicom_study_overview',
|
||||
'router_settings_history', 'router_settings',
|
||||
'routers', 'status_type', 'status_category'
|
||||
);
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
|
||||
|
||||
-- Disable foreign key checks
|
||||
SET FOREIGN_KEY_CHECKS=0;
|
||||
|
||||
-- Truncate all tables dynamically
|
||||
OPEN table_cursor;
|
||||
truncate_loop: LOOP
|
||||
FETCH table_cursor INTO table_name;
|
||||
IF done THEN
|
||||
LEAVE truncate_loop;
|
||||
END IF;
|
||||
|
||||
SET @query = CONCAT('TRUNCATE TABLE ', table_name);
|
||||
PREPARE stmt FROM @query;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
END LOOP;
|
||||
CLOSE table_cursor;
|
||||
|
||||
-- Re-enable foreign key checks
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
|
||||
-- Insert Status Categories
|
||||
INSERT INTO status_category (name, description)
|
||||
VALUES
|
||||
('Network', 'Network related statuses'),
|
||||
('Disk', 'Disk related statuses'),
|
||||
('VPN', 'VPN connection statuses'),
|
||||
('License', 'License statuses'),
|
||||
('Container', 'Container related statuses')
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert Status Types
|
||||
INSERT INTO status_type (category_id, name, code, description, severity)
|
||||
VALUES
|
||||
(1, 'Online', 'NET_ONLINE', 'System is online', 1),
|
||||
(1, 'Offline', 'NET_OFFLINE', 'System is offline', 5),
|
||||
(2, 'Normal', 'DISK_NORMAL', 'Disk usage is normal', 1),
|
||||
(2, 'Warning', 'DISK_WARNING', 'Disk usage is high', 3),
|
||||
(2, 'Critical', 'DISK_CRITICAL', 'Disk usage is critical', 5),
|
||||
(3, 'Connected', 'VPN_CONNECTED', 'VPN is connected', 1),
|
||||
(3, 'Disconnected', 'VPN_DISCONNECTED', 'VPN is disconnected', 5),
|
||||
(5, 'Running', 'CONTAINER_RUNNING', 'Container is running', 1),
|
||||
(5, 'Stopped', 'CONTAINER_STOPPED', 'Container is stopped', 5)
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert Routers
|
||||
INSERT INTO routers (router_id, facility, router_alias, last_seen, vpn_status_code, disk_status_code, app_status_code, license_status, free_disk, total_disk, disk_usage)
|
||||
VALUES
|
||||
('RTR001', 'Main Hospital', 'MAIN_RAD', NOW(), 'VPN_CONNECTED', 'DISK_NORMAL', 'CONTAINER_RUNNING', 'active', 500000000000, 1000000000000, 50.00),
|
||||
('RTR002', 'Emergency Center', 'ER_RAD', NOW(), 'VPN_CONNECTED', 'DISK_WARNING', 'CONTAINER_RUNNING', 'active', 400000000000, 1000000000000, 60.00),
|
||||
('RTR003', 'Imaging Center', 'IMG_CENTER', NOW(), 'VPN_CONNECTED', 'DISK_NORMAL', 'CONTAINER_RUNNING', 'active', 600000000000, 1000000000000, 40.00)
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Store Router IDs
|
||||
SET @router1_id = (SELECT id FROM routers WHERE router_id = 'RTR001');
|
||||
SET @router2_id = (SELECT id FROM routers WHERE router_id = 'RTR002');
|
||||
SET @router3_id = (SELECT id FROM routers WHERE router_id = 'RTR003');
|
||||
|
||||
-- Insert Container Status
|
||||
INSERT INTO container_status (router_id, container_name, status_code, created_at, updated_at)
|
||||
VALUES
|
||||
(1, 'router-cstore-scp', 'CONTAINER_RUNNING', NOW(), NOW()),
|
||||
(1, 'router-cstore-scu', 'CONTAINER_RUNNING', NOW(), NOW()),
|
||||
(2, 'router-cstore-scp', 'CONTAINER_RUNNING', NOW(), NOW()),
|
||||
(2, 'router-cstore-scu', 'CONTAINER_RUNNING', NOW(), NOW()),
|
||||
(3, 'router-cstore-scp', 'CONTAINER_RUNNING', NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert DICOM Study Overview
|
||||
INSERT INTO dicom_study_overview (
|
||||
router_id, study_instance_uid, patient_id, patient_name,
|
||||
accession_number, study_date, modality, study_description,
|
||||
series_instance_uid, procedure_code, referring_physician_name
|
||||
)
|
||||
VALUES
|
||||
(@router1_id, '1.2.840.113619.2.55.3.283116435.276.1543707218.134', 'P1', 'John Doe', 'ACC1234', '2024-03-15', 'CT', 'Chest CT', '1.2.840.113619.2.55.3.283116435.276.1543707219.135', 'CT001', 'Dr. Smith'),
|
||||
(@router2_id, '1.2.840.113619.2.55.3.283116435.276.1543707218.136', 'P2', 'Jane Doe', 'ACC1235', '2024-03-15', 'MR', 'Brain MRI', '1.2.840.113619.2.55.3.283116435.276.1543707219.137', 'MR001', 'Dr. Johnson')
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
END //
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- Automatically call the procedure after creation
|
||||
CALL seed_complete_router_system();
|
||||
@ -1,267 +0,0 @@
|
||||
DELIMITER //
|
||||
|
||||
CREATE PROCEDURE seed_complete_router_system()
|
||||
BEGIN
|
||||
-- Disable foreign key checks and start fresh
|
||||
SET FOREIGN_KEY_CHECKS=0;
|
||||
|
||||
-- Conditionally clear existing data, only if the table exists
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'auth_log') THEN
|
||||
TRUNCATE TABLE auth_log;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'user_sessions') THEN
|
||||
TRUNCATE TABLE user_sessions;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'user_router_access') THEN
|
||||
TRUNCATE TABLE user_router_access;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'users') THEN
|
||||
TRUNCATE TABLE users;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'container_status_history') THEN
|
||||
TRUNCATE TABLE container_status_history;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'router_status_history') THEN
|
||||
TRUNCATE TABLE router_status_history;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'container_status') THEN
|
||||
TRUNCATE TABLE container_status;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'vm_details') THEN
|
||||
TRUNCATE TABLE vm_details;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'dicom_study_overview') THEN
|
||||
TRUNCATE TABLE dicom_study_overview;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'system_status') THEN
|
||||
TRUNCATE TABLE system_status;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'router_settings_history') THEN
|
||||
TRUNCATE TABLE router_settings_history;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'router_settings') THEN
|
||||
TRUNCATE TABLE router_settings;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'routers') THEN
|
||||
TRUNCATE TABLE routers;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'status_type') THEN
|
||||
TRUNCATE TABLE status_type;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'status_category') THEN
|
||||
TRUNCATE TABLE status_category;
|
||||
END IF;
|
||||
|
||||
-- Re-enable foreign key checks
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
|
||||
-- Insert status categories
|
||||
INSERT INTO status_category (name, description)
|
||||
VALUES
|
||||
('Network', 'Network related statuses'),
|
||||
('Disk', 'Disk related statuses'),
|
||||
('VPN', 'VPN connection statuses'),
|
||||
('License', 'License statuses'),
|
||||
('Container', 'Container related statuses')
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert status types
|
||||
INSERT INTO status_type (category_id, name, code, description, severity)
|
||||
VALUES
|
||||
(1, 'Online', 'NET_ONLINE', 'System is online', 1),
|
||||
(1, 'Offline', 'NET_OFFLINE', 'System is offline', 5),
|
||||
(2, 'Normal', 'DISK_NORMAL', 'Disk usage is normal', 1),
|
||||
(2, 'Warning', 'DISK_WARNING', 'Disk usage is high', 3),
|
||||
(2, 'Critical', 'DISK_CRITICAL', 'Disk usage is critical', 5),
|
||||
(3, 'Connected', 'VPN_CONNECTED', 'VPN is connected', 1),
|
||||
(3, 'Disconnected', 'VPN_DISCONNECTED', 'VPN is disconnected', 5),
|
||||
(5, 'Running', 'CONTAINER_RUNNING', 'Container is running', 1),
|
||||
(5, 'Stopped', 'CONTAINER_STOPPED', 'Container is stopped', 5)
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert routers
|
||||
INSERT INTO routers (router_id, facility, router_alias, last_seen, vpn_status_code, disk_status_code, license_status, free_disk, total_disk, disk_usage)
|
||||
VALUES
|
||||
('RTR001', 'Main Hospital', 'MAIN_RAD', NOW(), 'VPN_CONNECTED', 'DISK_NORMAL', 'active', 500000000000, 1000000000000, 50.00),
|
||||
('RTR002', 'Emergency Center', 'ER_RAD', NOW(), 'VPN_CONNECTED', 'DISK_WARNING', 'active', 400000000000, 1000000000000, 60.00),
|
||||
('RTR003', 'Imaging Center', 'IMG_CENTER', NOW(), 'VPN_CONNECTED', 'DISK_NORMAL', 'active', 600000000000, 1000000000000, 40.00)
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Store router IDs for later use
|
||||
SET @router1_id = (SELECT id FROM routers WHERE router_id = 'RTR001');
|
||||
SET @router2_id = (SELECT id FROM routers WHERE router_id = 'RTR002');
|
||||
SET @router3_id = (SELECT id FROM routers WHERE router_id = 'RTR003');
|
||||
|
||||
-- Insert system status
|
||||
INSERT INTO system_status (router_id)
|
||||
VALUES
|
||||
(@router1_id),
|
||||
(@router2_id),
|
||||
(@router3_id)
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert container status
|
||||
INSERT INTO container_status (system_status_id, container_number, status_code)
|
||||
VALUES
|
||||
(1, 1, 'CONTAINER_RUNNING'),
|
||||
(1, 2, 'CONTAINER_RUNNING'),
|
||||
(2, 1, 'CONTAINER_RUNNING'),
|
||||
(2, 2, 'CONTAINER_STOPPED'),
|
||||
(3, 1, 'CONTAINER_RUNNING')
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert VM details
|
||||
INSERT INTO vm_details (router_id, vm_number, status_code)
|
||||
VALUES
|
||||
(@router1_id, 1, 'NET_ONLINE'),
|
||||
(@router2_id, 1, 'NET_ONLINE'),
|
||||
(@router3_id, 1, 'NET_ONLINE')
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert DICOM studies
|
||||
INSERT INTO dicom_study_overview (
|
||||
router_id,
|
||||
study_instance_uid,
|
||||
patient_id,
|
||||
patient_name,
|
||||
accession_number,
|
||||
study_date,
|
||||
modality,
|
||||
study_description,
|
||||
series_instance_uid,
|
||||
procedure_code,
|
||||
referring_physician_name
|
||||
)
|
||||
VALUES
|
||||
(@router1_id, '1.2.840.113619.2.55.3.283116435.276.1543707218.134', 'P1', 'John Doe', 'ACC1234', '2024-03-15', 'CT', 'Chest CT', '1.2.840.113619.2.55.3.283116435.276.1543707219.135', 'CT001', 'Dr. Smith'),
|
||||
(@router2_id, '1.2.840.113619.2.55.3.283116435.276.1543707218.136', 'P2', 'Jane Doe', 'ACC1235', '2024-03-15', 'MR', 'Brain MRI', '1.2.840.113619.2.55.3.283116435.276.1543707219.137', 'MR001', 'Dr. Johnson')
|
||||
ON DUPLICATE KEY UPDATE id = id;
|
||||
|
||||
-- Insert router settings for each router (calls to upsert_router_settings are disabled)
|
||||
-- Main Hospital Router
|
||||
-- CALL upsert_router_settings(
|
||||
-- @router1_id,
|
||||
-- 'client',
|
||||
-- '{
|
||||
-- "dicom": {
|
||||
-- "local": {
|
||||
-- "aet": "MAIN_RAD",
|
||||
-- "port": 104,
|
||||
-- "file_directory": "/dicom_images",
|
||||
-- "wait_time": 2,
|
||||
-- "receiver_wait_time": 5000
|
||||
-- },
|
||||
-- "association": {
|
||||
-- "acse_timeout": 5,
|
||||
-- "dimse_timeout": 1000,
|
||||
-- "network_timeout": 1000,
|
||||
-- "retry": {
|
||||
-- "attempts": 3,
|
||||
-- "interval": 10
|
||||
-- }
|
||||
-- }
|
||||
-- },
|
||||
-- "rabbitmq": {
|
||||
-- "local": {
|
||||
-- "hostname": "router-rabbitmq",
|
||||
-- "port": 5672,
|
||||
-- "credentials": {
|
||||
-- "username": "vitalengine",
|
||||
-- "password": "vitalengine"
|
||||
-- },
|
||||
-- "settings": {
|
||||
-- "durable": true,
|
||||
-- "auto_delete": false,
|
||||
-- "exchange_type": "direct",
|
||||
-- "heartbeat": 50
|
||||
-- }
|
||||
-- }
|
||||
-- },
|
||||
-- "scp_connections": {
|
||||
-- "pacs_nodes": [
|
||||
-- {
|
||||
-- "host": "pacsmain.example.com",
|
||||
-- "port": 104
|
||||
-- },
|
||||
-- {
|
||||
-- "host": "pacsbackup.example.com",
|
||||
-- "port": 104
|
||||
-- }
|
||||
-- ]
|
||||
-- }
|
||||
-- }',
|
||||
-- 'system',
|
||||
-- 'Initial client configuration for Main Hospital'
|
||||
-- );
|
||||
|
||||
-- Emergency Center Router
|
||||
-- CALL upsert_router_settings(
|
||||
-- @router2_id,
|
||||
-- 'client',
|
||||
-- '{
|
||||
-- "dicom": {
|
||||
-- "local": {
|
||||
-- "aet": "ER_RAD",
|
||||
-- "port": 104,
|
||||
-- "file_directory": "/dicom_images",
|
||||
-- "wait_time": 2,
|
||||
-- "receiver_wait_time": 5000
|
||||
-- },
|
||||
-- "association": {
|
||||
-- "acse_timeout": 5,
|
||||
-- "dimse_timeout": 1000,
|
||||
-- "network_timeout": 1000,
|
||||
-- "retry": {
|
||||
-- "attempts": 3,
|
||||
-- "interval": 10
|
||||
-- }
|
||||
-- }
|
||||
-- },
|
||||
-- "rabbitmq": {
|
||||
-- "local": {
|
||||
-- "hostname": "router-rabbitmq",
|
||||
-- "port": 5672,
|
||||
-- "credentials": {
|
||||
-- "username": "vitalengine",
|
||||
-- "password": "vitalengine"
|
||||
-- },
|
||||
-- "settings": {
|
||||
-- "durable": true,
|
||||
-- "auto_delete": false,
|
||||
-- "exchange_type": "direct",
|
||||
-- "heartbeat": 50
|
||||
-- }
|
||||
-- }
|
||||
-- },
|
||||
-- "scp_connections": {
|
||||
-- "pacs_nodes": [
|
||||
-- {
|
||||
-- "host": "pacsemergency.example.com",
|
||||
-- "port": 104
|
||||
-- }
|
||||
-- ]
|
||||
-- }
|
||||
-- }',
|
||||
-- 'system',
|
||||
-- 'Initial client configuration for Emergency Center'
|
||||
-- );
|
||||
|
||||
-- Insert settings for other routers as needed...
|
||||
|
||||
END //
|
||||
|
||||
DELIMITER ;
|
||||
@ -7,7 +7,7 @@ CORS_ORIGIN=http://localhost:5173,http://localhost:3000
|
||||
|
||||
# Database Configuration
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3307
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=rootpassword
|
||||
DB_NAME=ve_router_db
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { DicomStudyService } from '../services/DicomStudyService';
|
||||
import logger from '../utils/logger';
|
||||
import { Pool } from 'mysql2/promise';
|
||||
|
||||
interface ApiError {
|
||||
message: string;
|
||||
@ -13,8 +14,8 @@ interface ApiError {
|
||||
export class DicomStudyController {
|
||||
private service: DicomStudyService;
|
||||
|
||||
constructor() {
|
||||
this.service = new DicomStudyService();
|
||||
constructor(pool:Pool) {
|
||||
this.service = new DicomStudyService(pool);
|
||||
}
|
||||
|
||||
private handleError(error: unknown, message: string): ApiError {
|
||||
|
||||
@ -1,77 +1,80 @@
|
||||
// src/controllers/RouterController.ts
|
||||
import { Request, Response } from 'express';
|
||||
import { RouterService } from '../services/RouterService';
|
||||
import { RouterService, DicomStudyService, UtilityService } from '../services';
|
||||
import { Pool } from 'mysql2/promise';
|
||||
import { RouterData, VMUpdate, VMUpdateRequest } from '../types';
|
||||
|
||||
import logger from '../utils/logger';
|
||||
|
||||
|
||||
export class RouterController {
|
||||
private service: RouterService;
|
||||
private dicomStudyService: DicomStudyService;
|
||||
private utilityService: UtilityService
|
||||
|
||||
constructor(pool: Pool) {
|
||||
this.service = new RouterService(pool);
|
||||
}
|
||||
this.dicomStudyService = new DicomStudyService(pool);
|
||||
this.utilityService = new UtilityService();
|
||||
}
|
||||
|
||||
// src/controllers/RouterController.ts
|
||||
// src/controllers/RouterController.ts
|
||||
updateRouterVMs = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const routerId = req.query.router_id as string;
|
||||
const { vms } = req.body;
|
||||
|
||||
// Add debugging logs
|
||||
console.log('Received request:');
|
||||
console.log('Router ID:', routerId);
|
||||
console.log('VMs data:', vms);
|
||||
// src/controllers/RouterController.ts
|
||||
updateRouterVMs = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const routerId = req.query.router_id as string;
|
||||
const { vms } = req.body;
|
||||
|
||||
// Add debugging logs
|
||||
console.log('Received request:');
|
||||
console.log('Router ID:', routerId);
|
||||
console.log('VMs data:', vms);
|
||||
|
||||
if (!routerId) {
|
||||
return res.status(400).json({ error: 'router_id is required' });
|
||||
}
|
||||
if (!routerId) {
|
||||
return res.status(400).json({ error: 'router_id is required' });
|
||||
}
|
||||
|
||||
if (!Array.isArray(vms)) {
|
||||
return res.status(400).json({ error: 'VMs must be an array' });
|
||||
}
|
||||
if (!Array.isArray(vms)) {
|
||||
return res.status(400).json({ error: 'VMs must be an array' });
|
||||
}
|
||||
|
||||
const updatedVMs = await this.service.updateRouterVMs(routerId, vms);
|
||||
res.json(updatedVMs);
|
||||
} catch (err) {
|
||||
// Type cast the error
|
||||
const error = err as Error;
|
||||
|
||||
// Enhanced error logging
|
||||
console.error('Error in updateRouterVMs:', {
|
||||
message: error?.message || 'Unknown error',
|
||||
stack: error?.stack,
|
||||
type: error?.constructor.name
|
||||
});
|
||||
const updatedVMs = await this.service.updateRouterVMs(routerId, vms);
|
||||
res.json(updatedVMs);
|
||||
} catch (err) {
|
||||
// Type cast the error
|
||||
const error = err as Error;
|
||||
|
||||
// Enhanced error logging
|
||||
console.error('Error in updateRouterVMs:', {
|
||||
message: error?.message || 'Unknown error',
|
||||
stack: error?.stack,
|
||||
type: error?.constructor.name
|
||||
});
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Failed to update VMs',
|
||||
details: error?.message || 'Unknown error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
getAllRouters = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const routers = await this.service.getAllRouters();
|
||||
res.json(routers);
|
||||
} catch (error: unknown) {
|
||||
if (error && typeof error === 'object' && 'message' in error) {
|
||||
const e = error as { message: string }; // Type assertion here
|
||||
res.status(500).json({ error: e.message });
|
||||
} else {
|
||||
res.status(500).json({ error: 'An unexpected error occurred' });
|
||||
res.status(500).json({
|
||||
error: 'Failed to update VMs',
|
||||
details: error?.message || 'Unknown error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
getAllRouters = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const routers = await this.service.getAllRouters();
|
||||
res.json(routers);
|
||||
} catch (error: unknown) {
|
||||
if (error && typeof error === 'object' && 'message' in error) {
|
||||
const e = error as { message: string }; // Type assertion here
|
||||
res.status(500).json({ error: e.message });
|
||||
} else {
|
||||
res.status(500).json({ error: 'An unexpected error occurred' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getRouterById = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
const id = parseInt(req.params.routerId);
|
||||
const router = await this.service.getRouterById(id);
|
||||
|
||||
if (!router) {
|
||||
@ -86,9 +89,54 @@ getAllRouters = async (req: Request, res: Response) => {
|
||||
|
||||
createRouter = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const router = await this.service.createRouter(req.body);
|
||||
res.status(201).json(router);
|
||||
const routerMetrics = req.body;
|
||||
let routerId: number;
|
||||
|
||||
logger.info(`Initiating to create or update router: ${routerMetrics.routerId}`);
|
||||
|
||||
// Check for existing router
|
||||
const existingRouter = await this.service.getRouterByRouterId(routerMetrics.routerId);
|
||||
if (existingRouter) {
|
||||
// Update existing router
|
||||
const updatedRouter = await this.service.updateRouter(existingRouter.id, routerMetrics);
|
||||
if (!updatedRouter) {
|
||||
return res.status(404).json({ error: 'Failed to update existing router' });
|
||||
}
|
||||
routerId = existingRouter.id;
|
||||
} else {
|
||||
// Create a new router
|
||||
routerMetrics.diskUsage = ((routerMetrics.totalDisk - routerMetrics.freeDisk) / routerMetrics.totalDisk) * 100;
|
||||
// Get disk status
|
||||
routerMetrics.diskStatus = this.utilityService.getDiskStatus(routerMetrics.diskUsage);
|
||||
const router = await this.service.createRouter(routerMetrics);
|
||||
if (!router) {
|
||||
return res.status(404).json({ error: 'Failed to create router' });
|
||||
}
|
||||
routerId = router.id;
|
||||
}
|
||||
|
||||
// Process studies after router processing
|
||||
const studies = routerMetrics.routerActivity?.studies || [];
|
||||
if (Array.isArray(studies) && studies.length > 0) {
|
||||
logger.info(`Processing study for router: ${routerMetrics.routerId}`);
|
||||
await this.dicomStudyService.processStudies(routerId, studies);
|
||||
logger.info(`Successfully processed study for router: ${routerMetrics.routerId}`);
|
||||
} else {
|
||||
logger.info(`No study to process for router: ${routerMetrics.routerId}`);
|
||||
}
|
||||
|
||||
// Process containers after study processing
|
||||
const containers = routerMetrics.systemStatus?.containers || [];
|
||||
if (Array.isArray(containers) && containers.length > 0) {
|
||||
logger.info(`Processing containers for router: ${routerMetrics.routerId}`);
|
||||
const result = await this.service.processContainers(routerId, containers);
|
||||
logger.info(`Successfully processed ${result.affectedRows} containers for router: ${routerId}`
|
||||
);
|
||||
}
|
||||
|
||||
res.status(201).json({message: 'Router created successfully'});
|
||||
} catch (error) {
|
||||
logger.error('Error creating router:', error); // Log error for debugging
|
||||
res.status(500).json({ error: 'Failed to create router' });
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,8 +3,12 @@
|
||||
import { DicomStudy, CreateDicomStudyDTO, UpdateDicomStudyDTO, DBDicomStudy, DicomStudySearchParams } from '../types/dicom';
|
||||
import pool from '../config/db';
|
||||
import logger from '../utils/logger';
|
||||
import { Pool } from 'mysql2/promise';
|
||||
import { RowDataPacket, ResultSetHeader } from 'mysql2';
|
||||
|
||||
export class DicomStudyRepository {
|
||||
constructor(private pool: Pool) {} // Modified constructor
|
||||
|
||||
private async getRouterStringId(numericId: number): Promise<string> {
|
||||
try {
|
||||
const [result] = await pool.query(
|
||||
@ -40,10 +44,9 @@ export class DicomStudyRepository {
|
||||
}
|
||||
|
||||
private async mapDBStudyToDicomStudy(dbStudy: DBDicomStudy): Promise<DicomStudy> {
|
||||
const routerStringId = await this.getRouterStringId(dbStudy.router_id);
|
||||
return {
|
||||
id: dbStudy.id,
|
||||
router_id: routerStringId,
|
||||
router_id: dbStudy.router_id.toString(),
|
||||
study_instance_uid: dbStudy.study_instance_uid,
|
||||
patient_id: dbStudy.patient_id,
|
||||
patient_name: dbStudy.patient_name,
|
||||
@ -63,18 +66,16 @@ export class DicomStudyRepository {
|
||||
|
||||
async create(studyData: CreateDicomStudyDTO): Promise<DicomStudy> {
|
||||
try {
|
||||
// Convert string router_id to numeric id for database
|
||||
const numericRouterId = await this.getRouterNumericId(studyData.router_id);
|
||||
|
||||
const [result] = await pool.query(
|
||||
`INSERT INTO dicom_study_overview (
|
||||
router_id, study_instance_uid, patient_id, patient_name,
|
||||
accession_number, study_date, modality, study_description,
|
||||
series_instance_uid, procedure_code, referring_physician_name,
|
||||
study_status_code, association_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
study_status_code, association_id, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
|
||||
[
|
||||
numericRouterId,
|
||||
studyData.router_id,
|
||||
studyData.study_instance_uid,
|
||||
studyData.patient_id,
|
||||
studyData.patient_name,
|
||||
@ -246,4 +247,26 @@ export class DicomStudyRepository {
|
||||
throw new Error('Failed to search DICOM studies');
|
||||
}
|
||||
}
|
||||
|
||||
async findByStudyInstanceUid(studyInstanceUid: string): Promise<DicomStudy | null> {
|
||||
try {
|
||||
// Use RowDataPacket[] to align with mysql2/promise type expectations
|
||||
const [rows] = await pool.query<DBDicomStudy[] & RowDataPacket[]>(`
|
||||
SELECT * FROM dicom_study_overview WHERE study_instance_uid = ?`,
|
||||
[studyInstanceUid]
|
||||
);
|
||||
|
||||
// Check if rows is empty
|
||||
if (rows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map the first result to your DicomStudy object
|
||||
return await this.mapDBStudyToDicomStudy(rows[0]);
|
||||
} catch (error) {
|
||||
logger.error('Error fetching DICOM study by Study Instance UID:', error);
|
||||
throw new Error('Failed to fetch DICOM study');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
// src/repositories/RouterRepository.ts
|
||||
import { RouterData, Study, VM,VMUpdate } from '../types';
|
||||
import { RouterData, Study, VM, VMUpdate, Container } from '../types';
|
||||
import pool from '../config/db';
|
||||
import { RowDataPacket, ResultSetHeader } from 'mysql2';
|
||||
import logger from '../utils/logger';
|
||||
@ -126,10 +126,32 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
private async getRouterContainers(routerId: number): Promise<Container[]> {
|
||||
try {
|
||||
|
||||
// Then use this to query vm_details
|
||||
const [rows] = await pool.query<RowDataPacket[]>(
|
||||
`SELECT
|
||||
container_name,
|
||||
status_code
|
||||
FROM container_status
|
||||
WHERE router_id = ?`,
|
||||
[routerId]
|
||||
);
|
||||
|
||||
logger.info(`Containers for router ${routerId}:`, rows);
|
||||
return rows as Container[];
|
||||
} catch (error) {
|
||||
logger.error(`Error fetching Containers for router ${routerId}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private async transformDatabaseRouter(dbRouter: any, index: number): Promise<RouterData> {
|
||||
try {
|
||||
const studies = await this.getRouterStudies(dbRouter.id);
|
||||
const vms = await this.getRouterVMs(dbRouter.id);
|
||||
const containers = await this.getRouterContainers(dbRouter.id);
|
||||
|
||||
return {
|
||||
id: dbRouter.id,
|
||||
@ -147,8 +169,9 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
},
|
||||
systemStatus: {
|
||||
vpnStatus: dbRouter.vpn_status_code,
|
||||
appStatus: dbRouter.disk_status_code,
|
||||
vms
|
||||
appStatus: dbRouter.app_status_code,
|
||||
vms,
|
||||
containers
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
@ -184,6 +207,16 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
return this.transformDatabaseRouter(rows[0], 0);
|
||||
}
|
||||
|
||||
async findByRouterId(routerId: string): Promise<RouterData | null> {
|
||||
const [rows] = await pool.query<RowDataPacket[]>(
|
||||
'SELECT * FROM routers WHERE router_id = ?',
|
||||
[routerId]
|
||||
);
|
||||
|
||||
if (!rows.length) return null;
|
||||
return this.transformDatabaseRouter(rows[0], 0);
|
||||
}
|
||||
|
||||
async findByFacility(facility: string): Promise<RouterData[]> {
|
||||
const [rows] = await pool.query<RowDataPacket[]>(
|
||||
'SELECT * FROM routers WHERE facility = ? ORDER BY created_at DESC',
|
||||
@ -198,18 +231,19 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
}
|
||||
|
||||
async create(router: Partial<RouterData>): Promise<RouterData> {
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
`INSERT INTO routers (
|
||||
router_id, facility, router_alias, last_seen,
|
||||
vpn_status_code, disk_status_code, license_status,
|
||||
free_disk, total_disk, disk_usage
|
||||
) VALUES (?, ?, ?, NOW(), ?, ?, 'inactive', ?, ?, ?)`,
|
||||
vpn_status_code, disk_status_code, app_status_code,
|
||||
license_status, free_disk, total_disk, disk_usage
|
||||
) VALUES (?, ?, ?, NOW(), ?, ?, ?, 'inactive', ?, ?, ?)`,
|
||||
[
|
||||
router.routerId,
|
||||
router.facility,
|
||||
router.routerAlias,
|
||||
'unknown',
|
||||
router.systemStatus?.vpnStatus || 'unknown',
|
||||
router.diskStatus || 'unknown',
|
||||
router.systemStatus?.appStatus || 'unknown',
|
||||
router.freeDisk,
|
||||
router.totalDisk,
|
||||
router.diskUsage || 0
|
||||
@ -226,6 +260,9 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
if (router.facility) updates.facility = router.facility;
|
||||
if (router.routerAlias) updates.router_alias = router.routerAlias;
|
||||
if (router.diskStatus) updates.disk_status_code = router.diskStatus;
|
||||
|
||||
if (router.systemStatus?.vpnStatus) updates.vpn_status_code = router.systemStatus?.vpnStatus;
|
||||
if (router.systemStatus?.appStatus) updates.app_status_code = router.systemStatus?.appStatus;
|
||||
|
||||
if (router.freeDisk !== undefined || router.totalDisk !== undefined) {
|
||||
const existingRouter = await this.findById(id);
|
||||
@ -242,7 +279,7 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
.join(', ');
|
||||
|
||||
await pool.query(
|
||||
`UPDATE routers SET ${setClauses}, updated_at = NOW() WHERE id = ?`,
|
||||
`UPDATE routers SET ${setClauses}, last_seen = NOW(), updated_at = NOW() WHERE id = ?`,
|
||||
[...Object.values(updates), id]
|
||||
);
|
||||
}
|
||||
@ -257,4 +294,37 @@ async updateVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
async upsertContainerStatus(routerId: string, containers: Container[]): Promise<ResultSetHeader> {
|
||||
const values = containers.map((container) => [
|
||||
routerId,
|
||||
container.container_name,
|
||||
container.status_code,
|
||||
new Date(), // created_at
|
||||
new Date(), // updated_at
|
||||
]);
|
||||
|
||||
const query = `
|
||||
INSERT INTO container_status (router_id, container_name, status_code, created_at, updated_at)
|
||||
VALUES ${values.map(() => "(?, ?, ?, ?, ?)").join(", ")}
|
||||
ON DUPLICATE KEY UPDATE
|
||||
status_code = VALUES(status_code),
|
||||
updated_at = VALUES(updated_at);
|
||||
`;
|
||||
|
||||
// Flatten the values array manually (compatible with older TypeScript versions)
|
||||
const flattenedValues = values.reduce((acc, val) => acc.concat(val), []);
|
||||
|
||||
try {
|
||||
const [result] = await pool.query<ResultSetHeader>(query, flattenedValues);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Error inserting or updating container status:");
|
||||
logger.error(`Query: ${query}`);
|
||||
logger.error(`Flattened Values: ${JSON.stringify(flattenedValues)}`);
|
||||
logger.error("Error Details:", error);
|
||||
throw new Error("Error inserting or updating container status");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,9 +3,10 @@
|
||||
import express from 'express';
|
||||
import { DicomStudyController } from '../controllers/DicomStudyController';
|
||||
import logger from '../utils/logger';
|
||||
import pool from '../config/db'; // If using default export
|
||||
|
||||
const router = express.Router();
|
||||
const dicomStudyController = new DicomStudyController();
|
||||
const dicomStudyController = new DicomStudyController(pool);
|
||||
|
||||
// Debug logging
|
||||
logger.info('Initializing DICOM routes');
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { DicomStudy, CreateDicomStudyDTO, UpdateDicomStudyDTO, DicomStudySearchParams } from '../types/dicom';
|
||||
import { DicomStudy, CreateDicomStudyDTO, UpdateDicomStudyDTO, DicomStudySearchParams} from '../types/dicom';
|
||||
import { DicomStudyRepository } from '../repositories/DicomStudyRepository';
|
||||
import pool from '../config/db';
|
||||
import logger from '../utils/logger';
|
||||
import { Pool } from 'mysql2/promise';
|
||||
|
||||
export class DicomStudyService {
|
||||
private repository: DicomStudyRepository;
|
||||
|
||||
constructor() {
|
||||
this.repository = new DicomStudyRepository();
|
||||
constructor(pool: Pool) {
|
||||
this.repository = new DicomStudyRepository(pool);
|
||||
}
|
||||
|
||||
private async isValidStatusCode(statusCode: string): Promise<boolean> {
|
||||
@ -39,21 +40,18 @@ export class DicomStudyService {
|
||||
];
|
||||
|
||||
for (const field of requiredFields) {
|
||||
if (!studyData[field as keyof CreateDicomStudyDTO]) {
|
||||
// Check for undefined or null only (allow empty strings)
|
||||
if (studyData[field as keyof CreateDicomStudyDTO] == null) {
|
||||
throw new Error(`Missing required field: ${field}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Commented, currently this field is inserted with active/idle
|
||||
// Validate status code
|
||||
const isValidStatus = await this.isValidStatusCode(studyData.study_status_code);
|
||||
if (!isValidStatus) {
|
||||
throw new Error(`Invalid study status code: ${studyData.study_status_code}. Must be one of: NEW, IN_PROGRESS, COMPLETED, FAILED, CANCELLED, ON_HOLD`);
|
||||
}
|
||||
|
||||
// Validate date format
|
||||
if (!this.isValidDate(studyData.study_date)) {
|
||||
throw new Error('Invalid study date format. Use YYYY-MM-DD');
|
||||
}
|
||||
//const isValidStatus = await this.isValidStatusCode(studyData.study_status_code);
|
||||
//if (!isValidStatus) {
|
||||
//throw new Error(`Invalid study status code: ${studyData.study_status_code}. Must be one of: NEW, IN_PROGRESS, COMPLETED, FAILED, CANCELLED, ON_HOLD`);
|
||||
//}
|
||||
|
||||
logger.info('Creating new study', { studyData });
|
||||
return await this.repository.create(studyData);
|
||||
@ -151,4 +149,19 @@ export class DicomStudyService {
|
||||
throw new Error('Failed to search studies');
|
||||
}
|
||||
}
|
||||
|
||||
async processStudies(routerId: number, studies: DicomStudy[]): Promise<void> {
|
||||
for (const study of studies) {
|
||||
const existingStudy = await this.repository.findByStudyInstanceUid(study.study_instance_uid);
|
||||
if (existingStudy) {
|
||||
study.router_id = existingStudy.router_id;
|
||||
await this.updateStudy(existingStudy.id, study);
|
||||
} else {
|
||||
study.router_id = routerId.toString();
|
||||
logger.info(`Inserting study for router: ${routerId}`);
|
||||
await this.createStudy(study);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
// src/services/RouterService.ts
|
||||
import { RouterRepository } from '../repositories/RouterRepository';
|
||||
import { RouterData,VMUpdate} from '../types';
|
||||
import { Container, RouterData,VMUpdate} from '../types';
|
||||
import { Pool } from 'mysql2/promise';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
|
||||
export class RouterService {
|
||||
@ -30,6 +31,10 @@ async updateRouterVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
return this.repository.findById(id);
|
||||
}
|
||||
|
||||
async getRouterByRouterId(routerId: string): Promise<RouterData | null> {
|
||||
return this.repository.findByRouterId(routerId);
|
||||
}
|
||||
|
||||
async getRoutersByFacility(facility: string): Promise<RouterData[]> {
|
||||
return this.repository.findByFacility(facility);
|
||||
}
|
||||
@ -45,5 +50,10 @@ async updateRouterVMs(routerId: string, vms: VMUpdate[]): Promise<any> {
|
||||
async deleteRouter(id: number): Promise<boolean> {
|
||||
return this.repository.delete(id);
|
||||
}
|
||||
|
||||
async processContainers(routerId: number, containers: Container[]): Promise<any> {
|
||||
return this.repository.upsertContainerStatus(routerId.toString(), containers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
17
ve-router-backend/src/services/UtilityService.ts
Normal file
17
ve-router-backend/src/services/UtilityService.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import logger from '../utils/logger';
|
||||
|
||||
export class UtilityService {
|
||||
|
||||
// Get disk status based on disk usage (handles float values like 10.25)
|
||||
getDiskStatus(diskUsage: number): string {
|
||||
if (diskUsage >= 90) {
|
||||
return 'DISK_CRITICAL'; // Critical disk status
|
||||
} else if (diskUsage >= 70) {
|
||||
return 'DISK_WARNING'; // Warning disk status
|
||||
} else {
|
||||
return 'DISK_NORMAL'; // Normal disk status
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from './RouterService';
|
||||
export * from './DicomStudyService';
|
||||
export * from './UtilityService';
|
||||
// Add more service exports as needed
|
||||
|
||||
@ -71,4 +71,19 @@ export interface DicomStudySearchParams {
|
||||
endDate?: string;
|
||||
modality?: string;
|
||||
patientName?: string;
|
||||
}
|
||||
|
||||
export interface Study {
|
||||
patientId: string;
|
||||
patientName: string;
|
||||
siuid: string;
|
||||
accessionNumber: string;
|
||||
studyDate: string;
|
||||
modality: string;
|
||||
studyDescription: string;
|
||||
seriesInstanceUid: string;
|
||||
procedureCode: string;
|
||||
referringPhysicianName: string;
|
||||
associationId: string;
|
||||
studyStatusCode: string;
|
||||
}
|
||||
@ -15,19 +15,25 @@ export interface RouterData {
|
||||
};
|
||||
systemStatus: {
|
||||
vpnStatus: string; // maps to backend 'vpn_status_code'
|
||||
appStatus: string; // maps to backend 'disk_status_code'
|
||||
appStatus: string; // maps to backend 'app_status_code'
|
||||
vms: VM[];
|
||||
containers: Container[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface Study {
|
||||
siuid: string;
|
||||
patientId: string;
|
||||
accessionNumber: string;
|
||||
patientName: string;
|
||||
siuid: string;
|
||||
accessionNumber: string;
|
||||
studyDate: string;
|
||||
modality: string;
|
||||
studyDescription: string;
|
||||
seriesInstanceUid: string;
|
||||
procedureCode: string;
|
||||
referringPhysicianName: string;
|
||||
associationId: string;
|
||||
studyStatusCode: string;
|
||||
}
|
||||
|
||||
export interface VM {
|
||||
@ -43,4 +49,9 @@ export interface VMUpdate {
|
||||
|
||||
export interface VMUpdateRequest {
|
||||
vms: VMUpdate[];
|
||||
}
|
||||
|
||||
export interface Container {
|
||||
container_name: string;
|
||||
status_code: string;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user