mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-29 17:27:17 +00:00
chg: [dashboard] add objects tooltip
This commit is contained in:
parent
e0ae20968d
commit
3d14bac434
7 changed files with 320 additions and 7 deletions
|
@ -709,7 +709,15 @@ def add_object_tag(tag, obj_type, obj_id, subtype=''):
|
|||
else:
|
||||
r_tags.sadd(f'{obj_type}:{subtype}:{tag}', obj_id)
|
||||
|
||||
# STATS
|
||||
r_tags.hincrby(f'daily_tags:{datetime.date.today().strftime("%Y%m%d")}', tag, 1)
|
||||
mess = f'{int(time.time())}:{obj_type}:{subtype}:{obj_id}'
|
||||
r_tags.lpush('dashboard:tags', mess)
|
||||
r_tags.ltrim('dashboard:tags', 0, 19)
|
||||
|
||||
def get_tags_dashboard():
|
||||
return r_tags.lrange('dashboard:tags', 0, -1)
|
||||
|
||||
|
||||
# obj -> Object()
|
||||
def confirm_tag(tag, obj):
|
||||
|
|
|
@ -15,6 +15,8 @@ sys.path.append(os.environ['AIL_BIN'])
|
|||
##################################
|
||||
from lib.ConfigLoader import ConfigLoader
|
||||
from lib.objects import ail_objects
|
||||
from lib import Tag
|
||||
from lib import Tracker
|
||||
|
||||
|
||||
# Config
|
||||
|
@ -93,5 +95,20 @@ def get_nb_objs_dashboard():
|
|||
date = datetime.date.today().strftime("%Y%m%d")
|
||||
return ail_objects.get_nb_objects_dashboard(date)
|
||||
|
||||
def get_tagged_objs_dashboard():
|
||||
tagged_objs = []
|
||||
for tagged_obj in Tag.get_tags_dashboard():
|
||||
timestamp, obj_gid = tagged_obj.split(':', 1)
|
||||
timestamp = datetime.datetime.utcfromtimestamp(int(timestamp)).strftime('%H:%M:%S')
|
||||
obj_meta = ail_objects.get_obj_basic_meta(ail_objects.get_obj_from_global_id(obj_gid), flask_context=True)
|
||||
obj_meta['date_tag'] = timestamp
|
||||
tagged_objs.append(obj_meta)
|
||||
return tagged_objs
|
||||
|
||||
def get_tracked_objs_dashboard(user_org, user_id):
|
||||
trackers = Tracker.get_trackers_dashboard(user_org, user_id)
|
||||
for t in trackers:
|
||||
t['obj'] = ail_objects.get_obj_basic_meta(ail_objects.get_obj_from_global_id(t['obj']))
|
||||
return trackers
|
||||
|
||||
|
||||
|
|
|
@ -258,6 +258,12 @@ def add_obj_tags(obj_type, subtype, id, tags):
|
|||
|
||||
#### OBJ META ####
|
||||
|
||||
def get_obj_basic_meta(obj, flask_context=False):
|
||||
meta = obj.get_default_meta(tags=True)
|
||||
meta['icon'] = obj.get_svg_icon()
|
||||
meta['link'] = obj.get_link(flask_context=flask_context)
|
||||
meta['gid'] = obj.get_global_id()
|
||||
return meta
|
||||
|
||||
def get_object_meta(obj_type, subtype, id, options=set(), flask_context=False):
|
||||
obj = get_object(obj_type, subtype, id)
|
||||
|
|
|
@ -302,7 +302,8 @@ sock = Sock(app)
|
|||
@login_required
|
||||
@sock.route('/ws/dashboard')
|
||||
def ws_dashboard(ws):
|
||||
# TODO wait %30
|
||||
user_org = current_user.get_org()
|
||||
user_id = current_user.get_user_id()
|
||||
next_feeders = ail_stats.get_next_feeder_timestamp(int(time.time())) + 1
|
||||
try:
|
||||
while True:
|
||||
|
@ -313,7 +314,9 @@ def ws_dashboard(ws):
|
|||
if int(time.time()) >= next_feeders:
|
||||
feeders = ail_stats.get_feeders_dashboard()
|
||||
objs = ail_stats.get_nb_objs_today()
|
||||
ws.send(json.dumps({'feeders': feeders, 'objs': objs}))
|
||||
tags = ail_stats.get_tagged_objs_dashboard()
|
||||
trackers = ail_stats.get_tracked_objs_dashboard(user_org, user_id)
|
||||
ws.send(json.dumps({'feeders': feeders, 'objs': objs, 'tags': tags, 'trackers': trackers}))
|
||||
next_feeders = next_feeders + 30
|
||||
time.sleep(1)
|
||||
except Exception as e: # ConnectionClosed ?
|
||||
|
|
|
@ -10,7 +10,7 @@ import sys
|
|||
import json
|
||||
|
||||
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response, abort, send_file, stream_with_context
|
||||
from flask_login import login_required
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
# Import Role_Manager
|
||||
from Role_Manager import login_admin, login_read_only
|
||||
|
@ -37,10 +37,15 @@ bootstrap_label = ['primary', 'success', 'danger', 'warning', 'info']
|
|||
@login_required
|
||||
@login_read_only
|
||||
def objects():
|
||||
user_org = current_user.get_org()
|
||||
user_id = current_user.get_user_id()
|
||||
nb_objects = ail_stats.get_nb_objs_dashboard()
|
||||
print(nb_objects)
|
||||
feeders_dashboard = ail_stats.get_feeders_dashboard_full()
|
||||
return render_template("objs_dashboard.html", feeders_dashboard=feeders_dashboard, nb_objects=nb_objects)
|
||||
trackers = ail_stats.get_tracked_objs_dashboard(user_org, user_id)
|
||||
tagged_objs = ail_stats.get_tagged_objs_dashboard()
|
||||
return render_template("objs_dashboard.html", feeders_dashboard=feeders_dashboard,
|
||||
nb_objects=nb_objects, trackers=trackers, tagged_objs=tagged_objs,
|
||||
bootstrap_label=bootstrap_label)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,3 +2,124 @@
|
|||
function sanitize_text(str_to_sanitize) {
|
||||
return $("<span\>").text(str_to_sanitize).html()
|
||||
}
|
||||
|
||||
// REQUIRE var url_obj_description
|
||||
function show_obj_tooltip(container, obj_gid) {
|
||||
container = $(container);
|
||||
|
||||
if (container.data('bs.popover')) {
|
||||
container.popover('show');
|
||||
} else {
|
||||
let pop_header = "<div class=\"card text-white\"><div class=\"card-header bg-dark pb-0\">" + sanitize_text(obj_gid) + "/div>";
|
||||
let spinner = "<div class=\"card-body bg-dark pt-0\"><div class=\"spinner-border text-warning\" role=\"status\"></div> Loading...</div>";
|
||||
|
||||
container.popover({
|
||||
title: pop_header,
|
||||
content: spinner,
|
||||
html: true,
|
||||
container: container,
|
||||
})
|
||||
container.popover('show');
|
||||
|
||||
let popoverInstance = container.data('bs.popover');
|
||||
|
||||
|
||||
$.getJSON(url_obj_description + obj_gid, function (data) {
|
||||
|
||||
let desc = "<div class=\"card-body bg-dark text-white pb-1 pt-2\"><dl class=\"row py-0 my-0\">"
|
||||
Object.keys(data).forEach(function(key) {
|
||||
if (key=="status") {
|
||||
desc = desc + "<dt class=\"col-sm-3 px-0\">status</dt><dd class=\"col-sm-9 px-0\"><div class=\"badge badge-pill badge-light flex-row-reverse\" style=\"color:"
|
||||
if (data["status"]) {
|
||||
desc = desc + "Green"
|
||||
} else {
|
||||
desc = desc + "Red"
|
||||
}
|
||||
desc = desc + ";\"><i class=\"fas "
|
||||
if (data["status"]) {
|
||||
desc = desc + "fa-check-circle\"></i>UP"
|
||||
} else {
|
||||
desc = desc + "fa-times-circle\"></i>DOWN"
|
||||
}
|
||||
desc = desc + "</div></dd>"
|
||||
} else if (key!=="tags" && key!=="id" && key!=="img" && key!=="svg_icon" && key!=="icon" && key!=="link" && key!=="type") {
|
||||
if (data[key]) {
|
||||
if ((key==="first_seen" || key==="last_seen") && data[key].length===8) {
|
||||
let date = sanitize_text(data[key])
|
||||
desc = desc + "<dt class=\"col-sm-3 px-0\">" + sanitize_text(key) + "</dt><dd class=\"col-sm-9 px-0 mb-1\">" + date.slice(0, 4) + "-" + date.slice(4, 6) + "-" + date.slice(6, 8) + "</dd>"
|
||||
} else {
|
||||
desc = desc + "<dt class=\"col-sm-3 px-0\">" + sanitize_text(key) + "</dt><dd class=\"col-sm-9 px-0 mb-1\">" + sanitize_text(data[key]) + "</dd>"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
desc = desc + "</dl>"
|
||||
|
||||
if (data["tags"]) {
|
||||
data["tags"].forEach(function(tag) {
|
||||
desc = desc + "<span class=\"badge badge-warning\">"+ sanitize_text(tag) +"</span>";
|
||||
});
|
||||
}
|
||||
|
||||
/*if (data["img"]) {
|
||||
if (data["tags_safe"]) {
|
||||
if (data["type"] === "screenshot") {
|
||||
desc = desc + "<img src={{ url_for('objects_item.screenshot', filename="") }}"
|
||||
} else if (data["type"] === "favicon") {
|
||||
desc = desc + "<img src={{ url_for('objects_favicon.favicon', filename="") }}"
|
||||
} else {
|
||||
desc = desc + "<img src={{ url_for('objects_image.image', filename="") }}"
|
||||
}
|
||||
desc = desc + data["img"] +" class=\"img-thumbnail blured\" id=\"tooltip_screenshot_correlation\" style=\"\"/>";
|
||||
} else {
|
||||
desc = desc + "<span class=\"my-2 fa-stack fa-4x\"><i class=\"fas fa-stack-1x fa-image\"></i><i class=\"fas fa-stack-2x fa-ban\" style=\"color:Red\"></i></span>";
|
||||
}
|
||||
}*/
|
||||
|
||||
desc = desc + "</div></div>"
|
||||
//div.html(desc)
|
||||
// .style("left", (d3_pageX) + "px")
|
||||
// .style("top", (d3_pageY - 28) + "px");
|
||||
//d.popover = desc
|
||||
|
||||
if (data["img"]) {
|
||||
blur_tooltip();
|
||||
}
|
||||
|
||||
popoverInstance.config.content = desc;
|
||||
popoverInstance.setContent();
|
||||
popoverInstance.update();
|
||||
|
||||
|
||||
//let popoverid = container.attr('aria-describedby');
|
||||
//$('#' + popoverid).find('.popover-header').html(newTitle);
|
||||
//$('#' + popoverid).find('.popover-body').html('newContesssnt');
|
||||
|
||||
}).fail(function(error) {
|
||||
let desc = "<div class=\"card-body bg-dark text-white pt-0\"><i class=\"fas fa-3x fa-times text-danger\"></i>"+ error.statusText +"</div>"
|
||||
popoverInstance.config.content = desc;
|
||||
popoverInstance.setContent();
|
||||
popoverInstance.update();
|
||||
|
||||
});
|
||||
|
||||
container.popover('hide');
|
||||
container.popover('show');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function hide_obj_tooltip(container) {
|
||||
container = $(container);
|
||||
container.popover('hide')
|
||||
|
||||
}
|
||||
|
||||
function blur_tooltip(){
|
||||
var image = $('#tooltip_screenshot_correlation')[0];
|
||||
if (image) {
|
||||
let blurValue = $('#blur-slider-correlation').val();
|
||||
blurValue = 15 - blurValue;
|
||||
image.style.filter = "blur(" + blurValue + "px)";
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
<!-- JS -->
|
||||
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/helper.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/echarts.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
|
||||
|
||||
|
@ -24,6 +26,10 @@
|
|||
|
||||
|
||||
<style>
|
||||
.popover {
|
||||
max-width: none;
|
||||
width: 500px;
|
||||
}
|
||||
.icon-button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@ -34,7 +40,6 @@
|
|||
cursor: pointer;
|
||||
outline: inherit;
|
||||
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
|
@ -83,6 +88,83 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-12 col-xl-6">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><i class="fa-solid fa-crosshairs"></i></th>
|
||||
<th>tracker</th>
|
||||
<th class="text-center"><i class="fas fa-cube"></i></th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tracked_objs">
|
||||
{% for tracker in trackers %}
|
||||
<tr>
|
||||
<td>{{ tracker['type'] }}</td>
|
||||
<td style="word-break: break-all;">
|
||||
<a href="{{ url_for('hunters.show_tracker') }}?uuid={{ tracker['uuid'] }}">{{ tracker['tracked'] }}</a>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{{ tracker['obj']['link'] }}" onmouseenter="show_obj_tooltip(this, '{{ tracker['obj']['gid'] }}')" onmouseleave="hide_obj_tooltip(this)">
|
||||
<svg height="26" width="26" style="pointer-events:none;">
|
||||
<g class="nodes">
|
||||
<circle cx="13" cy="13" r="13" fill="{{ tracker['obj']['icon']['color'] }}"></circle>
|
||||
<text x="13" y="13" text-anchor="middle" dominant-baseline="central" class="{{ tracker['obj']['icon']['style'] }}" font-size="16px">{{ tracker['obj']['icon']['icon'] }}</text>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ tracker['timestamp'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-6">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"><i class="fas fa-cube"></i></th>
|
||||
<th>ID</th>
|
||||
<th><i class="fas fa-tag"></i> Tags</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="t_tags_objs">
|
||||
{% for t_obj in tagged_objs %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ t_obj['link'] }}" onmouseenter="show_obj_tooltip(this, '{{ t_obj['gid'] }}')" onmouseleave="hide_obj_tooltip(this)">
|
||||
<svg height="26" width="26">
|
||||
<g class="nodes">
|
||||
<circle cx="13" cy="13" r="13" fill="{{ t_obj['icon']['color'] }}"></circle>
|
||||
<text x="13" y="13" text-anchor="middle" dominant-baseline="central" class="{{ t_obj['icon']['style'] }}" font-size="16px">{{ t_obj['icon']['icon'] }}</text>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
</td>
|
||||
<td style="word-break: break-all;">
|
||||
<a href="{{ t_obj['link'] }}">{{ t_obj['id'] }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{% for tag in t_obj['tags'] %}
|
||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>{{ t_obj['date_tag'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -102,6 +184,9 @@
|
|||
</script>
|
||||
|
||||
<script>
|
||||
var tags_colors = ['primary', 'success', 'danger', 'warning', 'info'];
|
||||
var url_obj_description = "{{ url_for('correlation.get_description') }}?object_id=";
|
||||
|
||||
// Init Dashboard
|
||||
var feederChart = echarts.init(document.getElementById('feeders_dashboard'));
|
||||
window.addEventListener('resize', function() {
|
||||
|
@ -201,22 +286,90 @@ function updateNbObjects(data) {
|
|||
}
|
||||
}
|
||||
|
||||
///////
|
||||
|
||||
function create_obj_svg(container, obj_gid, url, color, fa_style, icon) {
|
||||
var svg_obj = '<svg height="26" width="26"> <g class="nodes"> <circle cx="13" cy="13" r="13" fill="' + color + '"></circle> <text x="13" y="13" text-anchor="middle" dominant-baseline="central" class="' + fa_style + '" font-size="16px">' + icon + '</text> </g> </svg>';
|
||||
var obj_link = $('<a>');
|
||||
obj_link.on('mouseenter', function () {
|
||||
show_obj_tooltip(this, obj_gid);
|
||||
});
|
||||
obj_link.on('mouseleave', function () {
|
||||
hide_obj_tooltip(this);
|
||||
});
|
||||
obj_link.attr('href', url);
|
||||
obj_link.html(svg_obj);
|
||||
container.append(obj_link);
|
||||
return container
|
||||
}
|
||||
|
||||
function create_tags_badges(container, tags) {
|
||||
var tspan = '';
|
||||
for (const i in tags) {
|
||||
tspan = tspan + '<span class="badge badge-' + 'primary' + '">' + sanitize_text(tags[i]) + '</span>'
|
||||
}
|
||||
return tspan;
|
||||
}
|
||||
|
||||
function update_tags_obj_dashboard(data) {
|
||||
// delete table
|
||||
var tbody = $("#t_tags_objs");
|
||||
var ntd;
|
||||
tbody.empty();
|
||||
for (const elem in data) {
|
||||
let obj = data[elem];
|
||||
var row = $('<tr></tr>');
|
||||
ntd = $('<td></td>');
|
||||
row.append(create_obj_svg(ntd, obj['gid'], obj.link, obj['icon']['color'], obj['icon']['style'], obj['icon']['icon']))
|
||||
row.append('<td style="word-break: break-all;"><a href="'+ obj['link'] + '">' + sanitize_text(obj['id']) + '</a></td>');
|
||||
ntd = $('<td></td>');
|
||||
row.append(create_tags_badges(ntd, obj['tags']));
|
||||
row.append('<td>' + obj['date_tag'] + '</td>');
|
||||
tbody.append(row);
|
||||
|
||||
|
||||
{# {% for tag in t_obj['tags'] %}#}
|
||||
{# <span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">{{ tag }}</span>#}
|
||||
{# {% endfor %}#}
|
||||
{#</td>#}
|
||||
}
|
||||
}
|
||||
|
||||
function update_tracked_objs_dashboard(data) {
|
||||
// delete table
|
||||
var tbody = $("#tracked_objs");
|
||||
var ntd;
|
||||
tbody.empty();
|
||||
for (const elem in data) {
|
||||
let tracker = data[elem];
|
||||
var row = $('<tr></tr>');
|
||||
row.append('<td>' + tracker['type'] + '</td>');
|
||||
row.append('<td style="word-break: break-all;"><a href="{{ url_for('hunters.show_tracker') }}?uuid=' + tracker['uuid'] + '">' + sanitize_text(tracker['tracked']) + '</a></td>');
|
||||
ntd = $('<td class="text-center"></td>');
|
||||
row.append(create_obj_svg(ntd, tracker['obj']['gid'], tracker['obj']['link'], tracker['obj']['icon']['color'], tracker['obj']['icon']['style'], tracker['obj']['icon']['icon']));
|
||||
row.append('<td>' + tracker['timestamp'] + '</td>');
|
||||
tbody.append(row);
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket
|
||||
var socket = new WebSocket("{{ url_for('ws_dashboard') }}");
|
||||
socket.wsocket = function(event) {
|
||||
console.log("WebSocket connection opened:", event);
|
||||
};
|
||||
socket.onmessage = function(event) {
|
||||
$("[data-toggle='popover']").popover('destroy');
|
||||
let data = JSON.parse(event.data);
|
||||
updateFeederChart(data['feeders']);
|
||||
updateNbObjects(data['objs']);
|
||||
update_tracked_objs_dashboard(data['trackers']);
|
||||
update_tags_obj_dashboard(data['tags']);
|
||||
};
|
||||
socket.onerror = function(error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
socket.onclose = function(event) {
|
||||
console.log('WebSocket connection closed:', event);
|
||||
// Optionally implement reconnection logic here
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue