Compare commits

..

2 commits

Author SHA1 Message Date
Sami Mokaddem
4529017748 chg: [app] Replaced _.throttle by _.debounce to have the desired effect 2024-07-02 09:39:19 +02:00
Sami Mokaddem
0fe9d9183a chg: [app] Performance improvements 2024-07-02 09:23:08 +02:00
12 changed files with 889 additions and 838 deletions

778
dist/assets/index-CG7kEUoH.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View file

@ -5,8 +5,8 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<script type="module" crossorigin src="/assets/index-D76yy8_K.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CvXX7jiP.css">
<script type="module" crossorigin src="/assets/index-CG7kEUoH.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DlglK08D.css">
</head>
<body>
<div id="app"></div>

View file

@ -9,6 +9,7 @@ from urllib.parse import parse_qs
VERBOSE_MODE = False
NOTIFICATION_COUNT = 1
def set_verbose_mode(enabled: bool):
@ -107,6 +108,9 @@ def is_api_request(data: dict) -> bool:
def get_notification_message(data: dict) -> dict:
global NOTIFICATION_COUNT
id = NOTIFICATION_COUNT
NOTIFICATION_COUNT += 1
user = db.USER_ID_TO_EMAIL_MAPPING.get(int(data['user_id']), '?')
time = data['created'].split(' ')[1].split('.')[0]
url = data['url']
@ -117,6 +121,7 @@ def get_notification_message(data: dict) -> dict:
http_method = 'DELETE' if (http_method == 'POST' or http_method == 'PUT') and action == 'delete' else http_method # small override for UI
payload = get_request_post_body(data)
return {
'id': id,
'user': user,
'time': time,
'url': url,

7
package-lock.json generated
View file

@ -13,6 +13,7 @@
"@fortawesome/free-regular-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/vue-fontawesome": "^3.0.8",
"lodash.debounce": "^4.0.8",
"vue": "^3.4.29"
},
"devDependencies": {
@ -2347,6 +2348,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",

View file

@ -16,6 +16,7 @@
"@fortawesome/free-regular-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/vue-fontawesome": "^3.0.8",
"lodash.debounce": "^4.0.8",
"vue": "^3.4.29"
},
"devDependencies": {

View file

@ -16,6 +16,7 @@ onMounted(() => {
<template>
<main>
<h1 class="text-2xl text-center text-slate-500 dark:text-slate-400 absolute top-1 left-1">MISP Exercise Dashboard</h1>
<div class="absolute top-1 right-1">
<div class="flex gap-2">
<TheThemeButton></TheThemeButton>

View file

@ -19,7 +19,6 @@ onMounted(() => {
</script>
<template>
<h1 class="text-3xl font-bold text-center text-slate-600 dark:text-slate-300">MISP Exercise Dashboard</h1>
<TheScores></TheScores>
<TheLiveLogs></TheLiveLogs>
</template>

View file

@ -1,6 +1,6 @@
<script setup>
import { ref, computed, onMounted } from 'vue'
import { exercises, selected_exercises, diagnostic, resetAllExerciseProgress, resetLiveLoggs, changeExerciseSelection, fetchDiagnostic } from "@/socket";
import { exercises, selected_exercises, diagnostic, resetAllExerciseProgress, resetLiveLogs, changeExerciseSelection, debouncedGetDiangostic } from "@/socket";
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faScrewdriverWrench, faTrash, faSuitcaseMedical, faGraduationCap, faBan } from '@fortawesome/free-solid-svg-icons'
@ -17,7 +17,7 @@
function showTheModal() {
admin_modal.value.showModal()
fetchDiagnostic()
debouncedGetDiangostic()
}
</script>
@ -52,7 +52,7 @@
Reset All Exercises
</button>
<button
@click="resetLiveLoggs()"
@click="resetLiveLogs()"
class="h-10 min-h-10 px-2 py-1 font-semibold bg-amber-600 text-slate-200 hover:bg-amber-700 btn btn-sm"
>
<FontAwesomeIcon :icon="faBan" class=""></FontAwesomeIcon>

View file

@ -79,7 +79,7 @@
</td>
</tr>
<template v-else>
<tr v-for="(notification, index) in notifications" :key="index">
<tr v-for="notification in notifications" :key="notification.id">
<td
class="border-b border-slate-100 dark:border-slate-700 text-slate-600 dark:text-slate-400 p-1 pl-2 w-12 whitespace-nowrap"
>

View file

@ -1,5 +1,6 @@
import { reactive, computed } from "vue";
import { io } from "socket.io-client";
import debounce from 'lodash.debounce'
// "undefined" means the URL will be computed from the `window.location` object
const URL = process.env.NODE_ENV === "production" ? undefined : "http://localhost:3000";
@ -20,6 +21,15 @@ const connectionState = reactive({
connected: false
})
const socket = io(URL, {
autoConnect: true
});
/* Public */
/* ------ */
export const exercises = computed(() => state.exercises)
export const selected_exercises = computed(() => state.selected_exercises)
export const active_exercises = computed(() => state.exercises.filter((exercise) => state.selected_exercises.includes(exercise.uuid)))
@ -36,25 +46,10 @@ export function resetState() {
}
export function fullReload() {
socket.emit("get_exercises", (all_exercises) => {
state.exercises = all_exercises
})
socket.emit("get_selected_exercises", (all_selected_exercises) => {
state.selected_exercises = all_selected_exercises
})
socket.emit("get_notifications", (all_notifications) => {
state.notificationEvents = all_notifications
})
socket.emit("get_progress", (all_progress) => {
state.progresses = all_progress
})
}
export function fetchDiagnostic() {
state.diagnostic = {}
socket.emit("get_diagnostic", (diagnostic) => {
state.diagnostic = diagnostic
})
getExercises()
getSelectedExercises()
getNotifications()
getProgress()
}
export function setCompletedState(completed, user_id, exec_uuid, task_uuid) {
@ -63,28 +58,15 @@ export function setCompletedState(completed, user_id, exec_uuid, task_uuid) {
exercise_uuid: exec_uuid,
task_uuid: task_uuid,
}
const event_name = !completed ? "mark_task_completed": "mark_task_incomplete"
socket.emit(event_name, payload, () => {
socket.emit("get_progress", (all_progress) => {
state.progresses = all_progress
})
})
sendCompletedState(completed, payload)
}
export function resetAllExerciseProgress() {
socket.emit("reset_all_exercise_progress", () => {
socket.emit("get_progress", (all_progress) => {
state.progresses = all_progress
})
})
sendResetAllExerciseProgress()
}
export function resetLiveLoggs() {
socket.emit("reset_notifications", () => {
socket.emit("get_notifications", (all_notifications) => {
state.notificationEvents = all_notifications
})
})
export function resetLiveLogs() {
sendResetLiveLogs()
}
export function changeExerciseSelection(exec_uuid, state_enabled) {
@ -92,24 +74,84 @@ export function changeExerciseSelection(exec_uuid, state_enabled) {
exercise_uuid: exec_uuid,
selected: state_enabled,
}
socket.emit("change_exercise_selection", payload, () => {
socket.emit("get_selected_exercises", (all_selected_exercises) => {
state.selected_exercises = all_selected_exercises
})
})
sendChangeExerciseSelection(payload)
}
export function toggleVerboseMode(enabled) {
sendToggleVerboseMode(enabled)
}
export const debouncedGetProgress = debounce(getProgress, 200, {leading: true})
export const debouncedGetDiangostic = debounce(getDiangostic, 1000, {leading: true})
/* Private */
/* ------- */
function getExercises() {
socket.emit("get_exercises", (all_exercises) => {
state.exercises = all_exercises
})
}
function getSelectedExercises() {
socket.emit("get_selected_exercises", (all_selected_exercises) => {
state.selected_exercises = all_selected_exercises
})
}
function getNotifications() {
socket.emit("get_notifications", (all_notifications) => {
state.notificationEvents = all_notifications
})
}
function getProgress() {
socket.emit("get_progress", (all_progress) => {
state.progresses = all_progress
})
}
function getDiangostic() {
state.diagnostic = {}
socket.emit("get_diagnostic", (diagnostic) => {
state.diagnostic = diagnostic
})
}
function sendCompletedState(completed, payload) {
const event_name = !completed ? "mark_task_completed": "mark_task_incomplete"
socket.emit(event_name, payload, () => {
getProgress()
})
}
function sendResetAllExerciseProgress() {
socket.emit("reset_all_exercise_progress", () => {
getProgress()
})
}
function sendResetLiveLogs() {
socket.emit("reset_notifications", () => {
getNotifications()
})
}
function sendChangeExerciseSelection(payload) {
socket.emit("change_exercise_selection", payload, () => {
getSelectedExercises()
})
}
function sendToggleVerboseMode(enabled) {
const payload = {
verbose: enabled
}
socket.emit("toggle_verbose_mode", payload, () => {})
}
const socket = io(URL, {
autoConnect: true
});
/* Event listener */
socket.on("connect", () => {
connectionState.connected = true;
@ -128,15 +170,11 @@ socket.on("notification", (message) => {
});
socket.on("new_user", (new_user) => {
socket.emit("get_progress", (all_progress) => {
state.progresses = all_progress
})
debouncedGetProgress()
});
socket.on("refresh_score", (new_user) => {
socket.emit("get_progress", (all_progress) => {
state.progresses = all_progress
})
debouncedGetProgress()
});
function addLimited(target, message, maxCount) {