Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
44c99c3
[ADD] init commit - passing data to svelte
Rumburaq2 Feb 19, 2025
e2e0ad6
[IMP] passing data - shift due to timezones not yet fixed
Rumburaq2 Feb 19, 2025
d28c229
debug cosole.logs
Rumburaq2 Feb 19, 2025
ad9e3ed
[ADD] working persistence of SLA elapsed time
Rumburaq2 Feb 20, 2025
ad612b8
[IMP] rerender alert upon alert refresh (not just /alets page refresh)
Rumburaq2 Feb 20, 2025
35a7ee9
[ADD] Styling of SLA plugin
Rumburaq2 Feb 21, 2025
5700772
[IMP] persistence of completed state
Rumburaq2 Feb 21, 2025
958e184
[IMP] turn progress bar red upon SLA breach
Rumburaq2 Feb 21, 2025
4939738
[IMP] printing alert_status_id
Rumburaq2 Feb 21, 2025
8f1e65f
[IMP] complete SLA when ticket state becomes In Progress
Rumburaq2 Feb 21, 2025
12943f3
[IMP] Working SLA severity based on alert sev.
Rumburaq2 Feb 21, 2025
a4bfeda
Merge pull request #729 from dfir-iris/develop
whikernel Feb 27, 2025
1e91f03
[ADD] SLA configured per customer via Customers page
Rumburaq2 Feb 28, 2025
2c137f9
[ADD] comments + added alertID to svelte frontend
Rumburaq2 Mar 5, 2025
dedf5eb
[ADD] fetching the DB from Svelte
Rumburaq2 Mar 5, 2025
a4e18d6
[ADD] added column alert_elapsed_sla to table Alert
Rumburaq2 Mar 5, 2025
727a516
[ADD] fetching data from column alert_elapsed_sla from Svelte
Rumburaq2 Mar 5, 2025
262c24c
[DEL] removing old debug code and comments
Rumburaq2 Mar 5, 2025
d4f66ed
[IMP] moved fetching of elapsed_sla to ProgressBar.svelte
Rumburaq2 Mar 7, 2025
b4e984f
[IMP] calling start() from onMount funciton not retarded Svelte react…
Rumburaq2 Mar 7, 2025
9a41e34
[ADD] getting elapsed sla from looping interval function
Rumburaq2 Mar 7, 2025
47abef8
[ADD] clicking the complete button will save the SLA in the db
Rumburaq2 Mar 8, 2025
8eb4909
[DEL] decomission of Local storage code
Rumburaq2 Mar 8, 2025
f3b2548
[ADD] giving alertID info to dispatch event in updateAlerts not just …
Rumburaq2 Mar 8, 2025
229580b
[DEL] removing old unused code and comments
Rumburaq2 Mar 8, 2025
91b4097
[IMP] clearInterval after SLA is completed so that loop is not called…
Rumburaq2 Mar 8, 2025
a1a8931
[IMP] refactoring code - indetation, removing unused code
Rumburaq2 Mar 8, 2025
b571e34
[IMP] some more code refactoring
Rumburaq2 Mar 8, 2025
c09012d
[DEL] removing debug HTML content
Rumburaq2 Mar 14, 2025
5b0a6bc
[DEL] removing some more debug HTML content
Rumburaq2 Mar 15, 2025
ddf9ab9
[DEL] deleting unused code from alerts_routes.py
Rumburaq2 Mar 15, 2025
04fcab9
[IMP] code refactoring - del unused prints, etc
Rumburaq2 Apr 20, 2025
d674931
Merge branch 'dfir-iris:master' into SLA-feature_v2
Rumburaq2 May 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions source/app/blueprints/pages/alerts/alerts_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
from app.blueprints.responses import response_error
from app.blueprints.access_controls import ac_requires


from app.datamgmt.client.client_db import get_clients_sla
from app.blueprints.responses import response_success

from app.blueprints.access_controls import ac_api_requires

from app.datamgmt.alerts.alerts_db import get_elapsed_sla

from app.datamgmt.alerts.alerts_db import set_elapsed_sla

alerts_blueprint = Blueprint(
'alerts',
__name__,
Expand All @@ -57,6 +67,39 @@ def alerts_list_view_route(caseid, url_redir) -> Union[str, Response]:

return render_template('alerts.html', caseid=caseid, form=form)

#later move this to source/app/blueprints/rest/alerts_routes.py
@alerts_blueprint.route('/alerts/api/set_elapsed_sla_api/<int:alert_id>/<int:new_elapsed_sla>', methods=['GET'])
@ac_api_requires(Permissions.alerts_write)
def set_elapsed_sla_api(alert_id: int, new_elapsed_sla: int):
updated_alert = set_elapsed_sla(alert_id, new_elapsed_sla)
return response_success(data=updated_alert)


@alerts_blueprint.route('/alerts/api/get_clients_sla_api', methods=['GET'])
@ac_api_requires()
def get_clients_sla_api():
rows= get_clients_sla()
customers = [dict(row._mapping) for row in rows]
output = {
"customers_sla": customers
}

return response_success(data=output)

#later move this to source/app/blueprints/rest/alerts_routes.py
@alerts_blueprint.route('/alerts/api/get_elapsed_sla_api/<int:alert_id>', methods=['GET'])
@ac_api_requires()
def get_elapsed_sla_api(alert_id: int):
elapsed_sla = get_elapsed_sla(alert_id)
#output = {
# "elapsed_sla": elapsed_sla
#}

return response_success(data=elapsed_sla)





@alerts_blueprint.route('/alerts/<int:cur_id>/comments/modal', methods=['GET'])
@ac_requires(Permissions.alerts_read, no_cid_required=True)
Expand Down
1 change: 1 addition & 0 deletions source/app/blueprints/pages/alerts/templates/alerts.html
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ <h5 class="modal-title" id="closeAlertModalLabel"></h5>
<script src="/static/assets/js/plugin/vis/vis.min.js"></script>
<script src="/static/assets/js/plugin/vis/vis-network.min.js"></script>
<script src="/static/assets/js/iris/alerts.js"></script>
<script type="module" src="/static/assets/js/iris/SLA.js" defer></script>
<script src="/static/assets/js/iris/comments.js"></script>
<script src="/static/assets/js/core/socket.io.js"></script>
{% endblock javascripts %}
Expand Down
17 changes: 17 additions & 0 deletions source/app/datamgmt/alerts/alerts_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -1566,3 +1566,20 @@ def get_alert_status_by_name(name: str) -> AlertStatus:
"""
return AlertStatus.query.filter(AlertStatus.status_name == name).first()


def get_elapsed_sla(alert_id: int) -> Alert:
elapsed_sla = Alert.query.filter(Alert.alert_id == alert_id).first()
return elapsed_sla

def set_elapsed_sla(alert_id: int, new_elapsed_sla: int):
alert = Alert.query.filter(Alert.alert_id == alert_id).first()
if alert:
# Update the column with the new value
alert.alert_elapsed_sla = new_elapsed_sla
# Commit the changes to the database
db.session.commit()
return alert
else:
# Optionally handle the case where no alert is found
return None

4 changes: 4 additions & 0 deletions source/app/datamgmt/client/client_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ def get_client_list(current_user_id: int = None,

return output

def get_clients_sla():
client_data = Client.query.with_entities(Client.sla, Client.client_id).all()
return client_data


def get_client(client_id: int) -> Client:
client = Client.query.filter(Client.client_id == client_id).first()
Expand Down
1 change: 1 addition & 0 deletions source/app/models/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Alert(db.Model):
alert_customer_id = Column(ForeignKey('client.client_id'), nullable=False)
alert_classification_id = Column(ForeignKey('case_classification.id'))
alert_resolution_status_id = Column(ForeignKey('alert_resolution_status.resolution_status_id'), nullable=True)
alert_elapsed_sla = Column(Integer, default=-1)

owner = relationship('User', foreign_keys=[alert_owner_id])
severity = relationship('Severity')
Expand Down
247 changes: 247 additions & 0 deletions ui/src/pages/ProgressBar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
<script lang="ts">
import {onDestroy, onMount} from 'svelte';

export let alertStatusID;
export let alertID;

let dbElapsedSla;
let currstate;

onMount(async () => {
console.log("I JUST MOUNTED AGAIN MOTHERFUCKER");
dbElapsedSla = await fetchAlertSla();
console.log("DB_Data: ", dbElapsedSla);

currstate = MyState.RUNNING;

start();

cleanupEffect = () => clearInterval(interval);
});

const MyState ={
NEW: 0,
RUNNING: 1,
PAUSED: 2,
};


//let { startDateTime = "01/02/2025 14:30", endDateTime = "01/02/2025 14:31" } = $props();
export let startDateTime = "01/02/2025 14:30";
export let endDateTime = "01/02/2025 14:30";
let timecomputed = calculateDuration(); //calculate duration betwwen two timestamps in seconds
$: elapsed = 0;
$: duration = timecomputed;
let interval: number
let oldElapsedTime = 0;
let currTimeEpoch = 0;
let startTimeEpoch = getSecondsSinceEpoch(startDateTime);
$: SLAbreached = false;

function start() {
interval = setInterval(() => {
if(currstate === MyState.RUNNING) {
//calculate curr time
currTimeEpoch = Math.floor((Date.now()) / (1000 * 60)) * 60;
if(dbElapsedSla < 0){ //SLA not yet completed
elapsed = currTimeEpoch - startTimeEpoch + oldElapsedTime;
console.log("elapsed: "+elapsed);
}
else { //SLA is not -1 therefore was completed sometime before
elapsed = dbElapsedSla;
clearInterval(interval)
currstate = MyState.PAUSED;
}

if (elapsed > duration) {
elapsed = duration
clearInterval(interval)
SLAbreached = true;
}
if (alertStatusID === 4){
complete();
}
}
}, 1000)
}

function getSecondsSinceEpoch(dateString) {
// Split the input string into date and time parts
const [datePart, timePart] = dateString.split(' ');
// Split the date part into day, month, and year
const [day, month, year] = datePart.split('/');
// Split the time part into hours and minutes
const [hours, minutes] = timePart.split(':');
// Create a Date object (note: months are 0-based in JavaScript)
const date = new Date(year, month - 1, day, hours, minutes);
// Get the number of seconds since the Unix epoch
const secondsSinceEpoch = Math.floor(date.getTime() / 1000);
return secondsSinceEpoch;
}


//this should actually fetch for column "alert_elapsed_sla"
// it will either have -1 or other int value for SLA completed seconds
async function fetchAlertSla() {
try {
const response = await fetch(`alerts/api/get_elapsed_sla_api/${alertID}`);
const data = await response.json();

// Declare the variable properly
const elapsedSla = data.data.alert_elapsed_sla;

return elapsedSla;
}
catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}

async function writeSlaData() {
try {
const response = await fetch(`alerts/api/set_elapsed_sla_api/${alertID}/${elapsed}`);

const result = await response.json();
console.log('Update result:', result);
}
catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}


function complete() {
clearInterval(interval)
currstate = MyState.PAUSED;
if (elapsed < 0){
elapsed = 0;
}
console.log("elapsedFromComplete "+elapsed);
writeSlaData();
}

function pause() {
currstate = MyState.PAUSED;
console.log("Paused!");
oldElapsedTime = elapsed;
}

function resume() {
currstate = MyState.RUNNING;
startTimeEpoch = (Math.floor((Date.now()) / (1000 * 60)) * 60);
console.log("Runing!");
}

let cleanupEffect;

onDestroy(() => {
if (cleanupEffect) cleanupEffect();
});

function calculateDuration() {
try {
// Extract date and time components
let [date1, time1] = startDateTime.split(" ");
let [date2, time2] = endDateTime.split(" ");
let [d1, m1, y1] = date1.split("/").map(Number);
let [d2, m2, y2] = date2.split("/").map(Number);
let [h1, min1] = time1 ? time1.split(":").map(Number) : [0, 0];
let [h2, min2] = time2 ? time2.split(":").map(Number) : [0, 0];
// Convert to JS Date objects (JS months are 0-based)
let start = new Date(y1, m1 - 1, d1, h1, min1);
let end = new Date(y2, m2 - 1, d2, h2, min2);
let totalTime = 0;
// Calculate the difference in milliseconds
let diffMs = end - start;
if (diffMs < 0) {
totalTime = 0;
return;
}
// Convert milliseconds to days, hours, minutes, and seconds
let totalSeconds = Math.floor(diffMs / 1000);
//let days = Math.floor(totalSeconds / 86400);
//let hours = Math.floor((totalSeconds % 86400) / 3600);
//let minutes = Math.floor((totalSeconds % 3600) / 60);
//let seconds = totalSeconds % 60;
totalTime = totalSeconds;
return totalTime;
} catch (error) {
console.error("Invalid date format:", error);
}
}
console.log(calculateDuration());
</script>

<div class="grid-gap">
<div class="progress-container col-md-12" style="display: flex;justify-content: center;">
<div class="col-md-12" style="padding-top: 5px;">
<div class="row col-md-12" style="display: flex;justify-content: center;">
<p>{startDateTime} - {endDateTime}</p>
</div>

<div class="row col-md-12" style="display: flex;justify-content: center; padding-bottom: 5px;">
<span>Elapsed time:</span>
</div>

<label class="row col-md-12" style="display: flex;justify-content: center;">
<progress class:completed={currstate === MyState.PAUSED} class:breached={SLAbreached} max={duration} value={elapsed}></progress>
</label>

<div class="row col-md-12" style="display: flex;justify-content: center;">
<p>{elapsed.toFixed(1)}s</p>
</div>
</div>
</div>

{#if duration === elapsed}
<p class="row col-md-12" style="display: flex;justify-content: center;"><strong>SLA breached!</strong></p>
{/if}
</div>


<style>
.progress-container {
width: 100%;
padding: 5px;
border: 2px solid grey;
border-radius: 10px;
background-color: #f3f3f3;
}

progress {
width: 90%;
height: 10px;
-webkit-appearance: none;
appearance: none;
}

p {
height: 10px;
}

progress::-webkit-progress-bar {
background-color: #ffffff;
border-radius: 10px;
}

progress::-webkit-progress-value {
background-color: #00b5f1;
border-radius: 10px;
}

/* When complete, change the progress bar's value color */
.completed::-webkit-progress-value {
background-color: green;
}

.breached::-webkit-progress-value {
background-color: red;
}

progress::-moz-progress-bar {
background-color: lightblue;
border-radius: 10px;
}
</style>
33 changes: 33 additions & 0 deletions ui/src/pages/SLA.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import App from './SLAcontainer.svelte';

export let app; // Declare at the top level

document.addEventListener('alertRendered', (event) => {
const containers = document.querySelectorAll('.SLAcontainer');
if (containers) {
containers.forEach((container) => {
// Check if the container already has the "mounted" class
if (container.classList.contains("mounted")) {
console.log('SLA component already mounted on this container');
return;
}
app = new App({
target: container,
props: {
IRIStime: event.detail.IRIStime,
alertStatusID: event.detail.alertStatusID,
alertSevID: event.detail.alertSevID,
alertCustomerID: event.detail.alertCustomerID,
SLA: event.detail.SLA,
alertID: event.detail.alertID
}
});
console.log('Svelte component mounted:', app);
// Mark the container by adding a "mounted" class so we don't mount again.
container.classList.add("mounted");
});
}
});



Loading