mirror of https://github.com/CIRCL/AIL-framework
chg: [trackers] filter trackers list by type + minor fix
parent
ad65ed7c8e
commit
0ced25f3be
48
OVERVIEW.md
48
OVERVIEW.md
|
@ -109,63 +109,65 @@ Redis and ARDB overview
|
|||
| **uuid**:ltags | **tag** |
|
||||
| **uuid**:ltagsgalaxies | **tag** |
|
||||
|
||||
## DB2 - New TermFreq:
|
||||
## DB2 - Leak Hunter:
|
||||
|
||||
##### Term Tracker metadata:
|
||||
##### Tracker metadata:
|
||||
| Hset - Key | Field | Value |
|
||||
| ------ | ------ | ------ |
|
||||
| tracked_term:**uuid** | tracked | **tacked word/set/regex** |
|
||||
| | type | **term/set/regex** |
|
||||
| tracker:**uuid** | tracked | **tacked word/set/regex** |
|
||||
| | type | **word/set/regex** |
|
||||
| | date | **date added** |
|
||||
| | user_id | **created by user_id** |
|
||||
| | dashboard | **0/1 Display alert on dashboard** |
|
||||
| | level | **0/1 Tracker visibility** |
|
||||
|
||||
##### Term Tracked by user_id (visibility level: user only):
|
||||
##### Tracker by user_id (visibility level: user only):
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| user:tracked_term:**user_id** | **uuid - tracked term uuid** |
|
||||
| user:tracker:**user_id** | **uuid - tracker uuid** |
|
||||
| user:tracker:**user_id**:**word/set/regex - tracker type** | **uuid - tracker uuid** |
|
||||
|
||||
##### Global Term Tracked (visibility level: all users):
|
||||
##### Global Tracker (visibility level: all users):
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| gobal:tracked_term | **uuid - tracked term uuid** |
|
||||
| gobal:tracker | **uuid - tracker uuid** |
|
||||
| gobal:tracker:**word/set/regex - tracker type** | **uuid - tracker uuid** |
|
||||
|
||||
##### All Term Tracked by type:
|
||||
##### All Tracker by type:
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| all:tracked_term:**word/set/regex - term type** | **tracked term** |
|
||||
| all:tracker:**word/set/regex - tracker type** | **tracked item** |
|
||||
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| all:tracked_term_uuid:**term type**:**tracked term** | **uuid - tracked term uuid** |
|
||||
| all:tracker_uuid:**tracker type**:**tracked item** | **uuid - tracker uuid** |
|
||||
|
||||
##### All Term Tracked items:
|
||||
##### All Tracked items:
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| tracked_term:item:**uuid**:**date** | **item_id** |
|
||||
| tracker:item:**uuid**:**date** | **item_id** |
|
||||
|
||||
##### All Term Tracked tags:
|
||||
##### All Tracked tags:
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| tracked_term:tags:**uuid** | **tag** |
|
||||
| tracker:tags:**uuid** | **tag** |
|
||||
|
||||
##### All Term Tracked tags:
|
||||
##### All Tracked mail:
|
||||
| Set - Key | Value |
|
||||
| ------ | ------ |
|
||||
| tracked_term:mail:**uuid** | **mail** |
|
||||
| tracker:mail:**uuid** | **mail** |
|
||||
|
||||
##### Refresh Tracked term:
|
||||
##### Refresh Tracker:
|
||||
| Key | Value |
|
||||
| ------ | ------ |
|
||||
| tracked_term:refresh:word | **last refreshed epoch** |
|
||||
| tracked_term:refresh:set | - |
|
||||
| tracked_term:refresh:regex | - |
|
||||
| tracker:refresh:word | **last refreshed epoch** |
|
||||
| tracker:refresh:set | - |
|
||||
| tracker:refresh:regex | - |
|
||||
|
||||
##### Zset Stat Tracked term:
|
||||
##### Zset Stat Tracker:
|
||||
| Key | Field | Value |
|
||||
| ------ | ------ | ------ |
|
||||
| tracked_term:stat:**uuid** | **date** | **nb_seen** |
|
||||
| tracker:stat:**uuid** | **date** | **nb_seen** |
|
||||
|
||||
##### Stat token:
|
||||
| Key | Field | Value |
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*-coding:UTF-8 -*
|
||||
"""
|
||||
The TermTracker Module
|
||||
The DbCleaner Module
|
||||
===================
|
||||
|
||||
"""
|
||||
|
@ -36,8 +36,6 @@ if __name__ == "__main__":
|
|||
publisher.channel = "Script"
|
||||
publisher.info("DbCleaner started")
|
||||
|
||||
config_section = 'TermTrackerMod'
|
||||
|
||||
# low priority
|
||||
time.sleep(180)
|
||||
|
||||
|
|
|
@ -60,3 +60,13 @@ def get_date_range(num_day):
|
|||
for i in range(0, num_day+1):
|
||||
date_list.append(date.substract_day(i))
|
||||
return list(reversed(date_list))
|
||||
|
||||
def substract_date(date_from, date_to):
|
||||
date_from = datetime.date(int(date_from[0:4]), int(date_from[4:6]), int(date_from[6:8]))
|
||||
date_to = datetime.date(int(date_to[0:4]), int(date_to[4:6]), int(date_to[6:8]))
|
||||
delta = date_to - date_from # timedelta
|
||||
l_date = []
|
||||
for i in range(delta.days + 1):
|
||||
date = date_from + datetime.timedelta(i)
|
||||
l_date.append( date.strftime('%Y%m%d') )
|
||||
return l_date
|
||||
|
|
|
@ -134,3 +134,13 @@ def get_item_pgp_name(item_id):
|
|||
|
||||
def get_item_pgp_mail(item_id):
|
||||
return _get_item_correlation('pgpdump', 'mail', item_id)
|
||||
|
||||
|
||||
###
|
||||
### GET Internal Module DESC
|
||||
###
|
||||
def get_item_list_desc(list_item_id):
|
||||
desc_list = []
|
||||
for item_id in list_item_id:
|
||||
desc_list.append( {'id': item_id, 'date': get_item_date(item_id), 'tags': Tag.get_item_tags(item_id)} )
|
||||
return desc_list
|
||||
|
|
|
@ -18,6 +18,7 @@ sys.path.append(os.path.join(os.environ['AIL_FLASK'], 'modules'))
|
|||
import Flask_config
|
||||
|
||||
import Date
|
||||
import Item
|
||||
|
||||
r_serv_term = Flask_config.r_serv_term
|
||||
email_regex = Flask_config.email_regex
|
||||
|
@ -47,11 +48,11 @@ def is_in_role(user_id, role):
|
|||
def check_term_uuid_valid_access(term_uuid, user_id):
|
||||
if not is_valid_uuid_v4(term_uuid):
|
||||
return ({"status": "error", "reason": "Invalid uuid"}, 400)
|
||||
level = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'level')
|
||||
level = r_serv_term.hget('tracker:{}'.format(term_uuid), 'level')
|
||||
if not level:
|
||||
return ({"status": "error", "reason": "Unknown uuid"}, 404)
|
||||
if level == 0:
|
||||
if r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'user_id') != user_id:
|
||||
if r_serv_term.hget('tracker:{}'.format(term_uuid), 'user_id') != user_id:
|
||||
if not is_in_role(user_id, 'admin'):
|
||||
return ({"status": "error", "reason": "Unknown uuid"}, 404)
|
||||
return None
|
||||
|
@ -91,10 +92,10 @@ def get_text_word_frequency(item_content, filtering=True):
|
|||
|
||||
# # TODO: create all tracked words
|
||||
def get_tracked_words_list():
|
||||
return list(r_serv_term.smembers('all:tracked_term:word'))
|
||||
return list(r_serv_term.smembers('all:tracker:word'))
|
||||
|
||||
def get_set_tracked_words_list():
|
||||
set_list = r_serv_term.smembers('all:tracked_term:set')
|
||||
set_list = r_serv_term.smembers('all:tracker:set')
|
||||
all_set_list = []
|
||||
for elem in set_list:
|
||||
res = elem.split(';')
|
||||
|
@ -104,7 +105,7 @@ def get_set_tracked_words_list():
|
|||
return all_set_list
|
||||
|
||||
def get_regex_tracked_words_dict():
|
||||
regex_list = r_serv_term.smembers('all:tracked_term:regex')
|
||||
regex_list = r_serv_term.smembers('all:tracker:regex')
|
||||
dict_tracked_regex = {}
|
||||
for regex in regex_list:
|
||||
dict_tracked_regex[regex] = re.compile(regex)
|
||||
|
@ -113,24 +114,24 @@ def get_regex_tracked_words_dict():
|
|||
def get_tracked_term_list_item(term_uuid, date_from, date_to):
|
||||
all_item_id = []
|
||||
if date_from and date_to:
|
||||
for date in r_serv_term.zrangebyscore('tracked_term:stat:{}'.format(term_uuid), int(date_from), int(date_to)):
|
||||
all_item_id = all_item_id + list(r_serv_term.smembers('tracked_term:item:{}:{}'.format(term_uuid, date)))
|
||||
for date in r_serv_term.zrangebyscore('tracker:stat:{}'.format(term_uuid), int(date_from), int(date_to)):
|
||||
all_item_id = all_item_id + list(r_serv_term.smembers('tracker:item:{}:{}'.format(term_uuid, date)))
|
||||
return all_item_id
|
||||
|
||||
def is_term_tracked_in_global_level(term, term_type):
|
||||
res = r_serv_term.smembers('all:tracked_term_uuid:{}:{}'.format(term_type, term))
|
||||
res = r_serv_term.smembers('all:tracker_uuid:{}:{}'.format(term_type, term))
|
||||
if res:
|
||||
for elem_uuid in res:
|
||||
if r_serv_term.hget('tracked_term:{}'.format(elem_uuid), 'level')=='1':
|
||||
if r_serv_term.hget('tracker:{}'.format(elem_uuid), 'level')=='1':
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_term_tracked_in_user_level(term, term_type, user_id):
|
||||
res = r_serv_term.smembers('user:tracked_term:{}'.format(user_id))
|
||||
res = r_serv_term.smembers('user:tracker:{}'.format(user_id))
|
||||
if res:
|
||||
for elem_uuid in res:
|
||||
if r_serv_term.hget('tracked_term:{}'.format(elem_uuid), 'tracked')== term:
|
||||
if r_serv_term.hget('tracked_term:{}'.format(elem_uuid), 'type')== term_type:
|
||||
if r_serv_term.hget('tracker:{}'.format(elem_uuid), 'tracked')== term:
|
||||
if r_serv_term.hget('tracker:{}'.format(elem_uuid), 'type')== term_type:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -220,39 +221,42 @@ def add_tracked_term(term , term_type, user_id, level, tags, mails, dashboard=0)
|
|||
term_uuid = str(uuid.uuid4())
|
||||
|
||||
# create metadata
|
||||
r_serv_term.hset('tracked_term:{}'.format(term_uuid), 'tracked',term)
|
||||
r_serv_term.hset('tracked_term:{}'.format(term_uuid), 'type', term_type)
|
||||
r_serv_term.hset('tracked_term:{}'.format(term_uuid), 'date', datetime.date.today().strftime("%Y%m%d"))
|
||||
r_serv_term.hset('tracked_term:{}'.format(term_uuid), 'user_id', user_id)
|
||||
r_serv_term.hset('tracked_term:{}'.format(term_uuid), 'level', level)
|
||||
r_serv_term.hset('tracked_term:{}'.format(term_uuid), 'dashboard', dashboard)
|
||||
r_serv_term.hset('tracker:{}'.format(term_uuid), 'tracked',term)
|
||||
r_serv_term.hset('tracker:{}'.format(term_uuid), 'type', term_type)
|
||||
r_serv_term.hset('tracker:{}'.format(term_uuid), 'date', datetime.date.today().strftime("%Y%m%d"))
|
||||
r_serv_term.hset('tracker:{}'.format(term_uuid), 'user_id', user_id)
|
||||
r_serv_term.hset('tracker:{}'.format(term_uuid), 'level', level)
|
||||
r_serv_term.hset('tracker:{}'.format(term_uuid), 'dashboard', dashboard)
|
||||
|
||||
# create all term set
|
||||
r_serv_term.sadd('all:tracked_term:{}'.format(term_type), term)
|
||||
r_serv_term.sadd('all:tracker:{}'.format(term_type), term)
|
||||
|
||||
# create term - uuid map
|
||||
r_serv_term.sadd('all:tracked_term_uuid:{}:{}'.format(term_type, term), term_uuid)
|
||||
r_serv_term.sadd('all:tracker_uuid:{}:{}'.format(term_type, term), term_uuid)
|
||||
|
||||
# add display level set
|
||||
if level == 0: # user only
|
||||
r_serv_term.sadd('user:tracked_term:{}'.format(user_id), term_uuid)
|
||||
r_serv_term.sadd('user:tracker:{}'.format(user_id), term_uuid)
|
||||
r_serv_term.sadd('user:tracker:{}:{}'.format(user_id, term_type), term_uuid)
|
||||
elif level == 1: # global
|
||||
r_serv_term.sadd('global:tracked_term', term_uuid)
|
||||
r_serv_term.sadd('global:tracker', term_uuid)
|
||||
r_serv_term.sadd('global:tracker:{}'.format(term_type), term_uuid)
|
||||
|
||||
# create term tags list
|
||||
for tag in tags:
|
||||
r_serv_term.sadd('tracked_term:tags:{}'.format(term_uuid), tag)
|
||||
r_serv_term.sadd('tracker:tags:{}'.format(term_uuid), tag)
|
||||
|
||||
# create term tags mail notification list
|
||||
for mail in mails:
|
||||
r_serv_term.sadd('tracked_term:mail:{}'.format(term_uuid), mail)
|
||||
r_serv_term.sadd('tracker:mail:{}'.format(term_uuid), mail)
|
||||
|
||||
# toggle refresh module tracker list/set
|
||||
r_serv_term.set('tracked_term:refresh:{}'.format(term_type), time.time())
|
||||
r_serv_term.set('tracker:refresh:{}'.format(term_type), time.time())
|
||||
|
||||
return term_uuid
|
||||
|
||||
def parse_tracked_term_to_delete(dict_input, user_id):
|
||||
term_uuid = dict_input.get("uuid", None)
|
||||
res = check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res:
|
||||
return res
|
||||
|
@ -261,66 +265,68 @@ def parse_tracked_term_to_delete(dict_input, user_id):
|
|||
return ({"uuid": term_uuid}, 200)
|
||||
|
||||
def delete_term(term_uuid):
|
||||
term = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'tracked')
|
||||
term_type = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'type')
|
||||
level = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'level')
|
||||
r_serv_term.srem('all:tracked_term_uuid:{}:{}'.format(term_type, term), term_uuid)
|
||||
term = r_serv_term.hget('tracker:{}'.format(term_uuid), 'tracked')
|
||||
term_type = r_serv_term.hget('tracker:{}'.format(term_uuid), 'type')
|
||||
level = r_serv_term.hget('tracker:{}'.format(term_uuid), 'level')
|
||||
r_serv_term.srem('all:tracker_uuid:{}:{}'.format(term_type, term), term_uuid)
|
||||
# Term not tracked by other users
|
||||
if not r_serv_term.exists('all:tracked_term_uuid:{}:{}'.format(term_type, term)):
|
||||
r_serv_term.srem('all:tracked_term:{}'.format(term_type), term)
|
||||
if not r_serv_term.exists('all:tracker_uuid:{}:{}'.format(term_type, term)):
|
||||
r_serv_term.srem('all:tracker:{}'.format(term_type), term)
|
||||
|
||||
# toggle refresh module tracker list/set
|
||||
r_serv_term.set('tracked_term:refresh:{}'.format(term_type), time.time())
|
||||
r_serv_term.set('tracker:refresh:{}'.format(term_type), time.time())
|
||||
|
||||
if level == 0: # user only
|
||||
user_id = term_type = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'user_id')
|
||||
r_serv_term.srem('user:tracked_term:{}'.format(user_id), term_uuid)
|
||||
elif level == 1: # global
|
||||
r_serv_term.srem('global:tracked_term', term_uuid)
|
||||
if level == '0': # user only
|
||||
user_id = term_type = r_serv_term.hget('tracker:{}'.format(term_uuid), 'user_id')
|
||||
r_serv_term.srem('user:tracker:{}'.format(user_id), term_uuid)
|
||||
r_serv_term.srem('user:tracker:{}:{}'.format(user_id, term_type), term_uuid)
|
||||
elif level == '1': # global
|
||||
r_serv_term.srem('global:tracker', term_uuid)
|
||||
r_serv_term.srem('global:tracker:{}'.format(term_type), term_uuid)
|
||||
|
||||
# delete metatadata
|
||||
r_serv_term.delete('tracked_term:{}'.format(term_uuid))
|
||||
r_serv_term.delete('tracker:{}'.format(term_uuid))
|
||||
|
||||
# remove tags
|
||||
r_serv_term.delete('tracked_term:tags:{}'.format(term_uuid))
|
||||
r_serv_term.delete('tracker:tags:{}'.format(term_uuid))
|
||||
|
||||
# remove mails
|
||||
r_serv_term.delete('tracked_term:mail:{}'.format(term_uuid))
|
||||
r_serv_term.delete('tracker:mail:{}'.format(term_uuid))
|
||||
|
||||
# remove item set
|
||||
all_item_date = r_serv_term.zrange('tracked_term:stat:{}'.format(term_uuid), 0, -1)
|
||||
all_item_date = r_serv_term.zrange('tracker:stat:{}'.format(term_uuid), 0, -1)
|
||||
for date in all_item_date:
|
||||
r_serv_term.delete('tracked_term:item:{}:{}'.format(term_uuid, date))
|
||||
r_serv_term.delete('tracked_term:stat:{}'.format(term_uuid))
|
||||
r_serv_term.delete('tracker:item:{}:{}'.format(term_uuid, date))
|
||||
r_serv_term.delete('tracker:stat:{}'.format(term_uuid))
|
||||
|
||||
def replace_tracked_term_tags(term_uuid, tags):
|
||||
r_serv_term.delete('tracked_term:tags:{}'.format(term_uuid))
|
||||
r_serv_term.delete('tracker:tags:{}'.format(term_uuid))
|
||||
for tag in tags:
|
||||
r_serv_term.sadd('tracked_term:tags:{}'.format(term_uuid), tag)
|
||||
r_serv_term.sadd('tracker:tags:{}'.format(term_uuid), tag)
|
||||
|
||||
def replace_tracked_term_mails(term_uuid, mails):
|
||||
res = verify_mail_list(mails)
|
||||
if res:
|
||||
return res
|
||||
else:
|
||||
r_serv_term.delete('tracked_term:mail:{}'.format(term_uuid))
|
||||
r_serv_term.delete('tracker:mail:{}'.format(term_uuid))
|
||||
for mail in mails:
|
||||
r_serv_term.sadd('tracked_term:mail:{}'.format(term_uuid), mail)
|
||||
r_serv_term.sadd('tracker:mail:{}'.format(term_uuid), mail)
|
||||
|
||||
def get_term_uuid_list(term, term_type):
|
||||
return list(r_serv_term.smembers('all:tracked_term_uuid:{}:{}'.format(term_type, term)))
|
||||
return list(r_serv_term.smembers('all:tracker_uuid:{}:{}'.format(term_type, term)))
|
||||
|
||||
def get_term_tags(term_uuid):
|
||||
return list(r_serv_term.smembers('tracked_term:tags:{}'.format(term_uuid)))
|
||||
return list(r_serv_term.smembers('tracker:tags:{}'.format(term_uuid)))
|
||||
|
||||
def get_term_mails(term_uuid):
|
||||
return list(r_serv_term.smembers('tracked_term:mail:{}'.format(term_uuid)))
|
||||
return list(r_serv_term.smembers('tracker:mail:{}'.format(term_uuid)))
|
||||
|
||||
def add_tracked_item(term_uuid, item_id, item_date):
|
||||
# track item
|
||||
r_serv_term.sadd('tracked_term:item:{}:{}'.format(term_uuid, item_date), item_id)
|
||||
r_serv_term.sadd('tracker:item:{}:{}'.format(term_uuid, item_date), item_id)
|
||||
# track nb item by date
|
||||
r_serv_term.zadd('tracked_term:stat:{}'.format(term_uuid), item_date, int(item_date))
|
||||
r_serv_term.zadd('tracker:stat:{}'.format(term_uuid), item_date, int(item_date))
|
||||
|
||||
def create_token_statistics(item_date, word, nb):
|
||||
r_serv_term.zincrby('stat_token_per_item_by_day:{}'.format(item_date), word, 1)
|
||||
|
@ -336,7 +342,7 @@ def get_all_token_stat_history():
|
|||
return r_serv_term.smembers('stat_token_history')
|
||||
|
||||
def get_tracked_term_last_updated_by_type(term_type):
|
||||
epoch_update = r_serv_term.get('tracked_term:refresh:{}'.format(term_type))
|
||||
epoch_update = r_serv_term.get('tracker:refresh:{}'.format(term_type))
|
||||
if not epoch_update:
|
||||
epoch_update = 0
|
||||
return float(epoch_update)
|
||||
|
@ -363,6 +369,7 @@ def parse_get_tracker_term_item(dict_input, user_id):
|
|||
date_from = date_to
|
||||
|
||||
all_item_id = get_tracked_term_list_item(term_uuid, date_from, date_to)
|
||||
all_item_id = Item.get_item_list_desc(all_item_id)
|
||||
|
||||
res_dict = {}
|
||||
res_dict['uuid'] = term_uuid
|
||||
|
@ -372,7 +379,7 @@ def parse_get_tracker_term_item(dict_input, user_id):
|
|||
return (res_dict, 200)
|
||||
|
||||
def get_tracked_term_first_seen(term_uuid):
|
||||
res = r_serv_term.zrange('tracked_term:stat:{}'.format(term_uuid), 0, 0)
|
||||
res = r_serv_term.zrange('tracker:stat:{}'.format(term_uuid), 0, 0)
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
|
@ -380,7 +387,7 @@ def get_tracked_term_first_seen(term_uuid):
|
|||
|
||||
|
||||
def get_tracked_term_last_seen(term_uuid):
|
||||
res = r_serv_term.zrevrange('tracked_term:stat:{}'.format(term_uuid), 0, 0)
|
||||
res = r_serv_term.zrevrange('tracker:stat:{}'.format(term_uuid), 0, 0)
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
|
@ -388,15 +395,15 @@ def get_tracked_term_last_seen(term_uuid):
|
|||
|
||||
def get_term_metedata(term_uuid, user_id=False, level=False, tags=False, mails=False, sparkline=False):
|
||||
dict_uuid = {}
|
||||
dict_uuid['term'] = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'tracked')
|
||||
dict_uuid['type'] = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'type')
|
||||
dict_uuid['date'] = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'date')
|
||||
dict_uuid['term'] = r_serv_term.hget('tracker:{}'.format(term_uuid), 'tracked')
|
||||
dict_uuid['type'] = r_serv_term.hget('tracker:{}'.format(term_uuid), 'type')
|
||||
dict_uuid['date'] = r_serv_term.hget('tracker:{}'.format(term_uuid), 'date')
|
||||
dict_uuid['first_seen'] = get_tracked_term_first_seen(term_uuid)
|
||||
dict_uuid['last_seen'] = get_tracked_term_last_seen(term_uuid)
|
||||
if user_id:
|
||||
dict_uuid['user_id'] = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'user_id')
|
||||
dict_uuid['user_id'] = r_serv_term.hget('tracker:{}'.format(term_uuid), 'user_id')
|
||||
if level:
|
||||
dict_uuid['level'] = r_serv_term.hget('tracked_term:{}'.format(term_uuid), 'level')
|
||||
dict_uuid['level'] = r_serv_term.hget('tracker:{}'.format(term_uuid), 'level')
|
||||
if mails:
|
||||
dict_uuid['mails'] = get_list_trackeed_term_mails(term_uuid)
|
||||
if tags:
|
||||
|
@ -406,48 +413,71 @@ def get_term_metedata(term_uuid, user_id=False, level=False, tags=False, mails=F
|
|||
dict_uuid['uuid'] = term_uuid
|
||||
return dict_uuid
|
||||
|
||||
def get_tracked_term_sparkline(term_uuid, num_day=6):
|
||||
def get_tracked_term_sparkline(tracker_uuid, num_day=6):
|
||||
date_range_sparkline = Date.get_date_range(num_day)
|
||||
sparklines_value = []
|
||||
for date_day in date_range_sparkline:
|
||||
nb_seen_this_day = r_serv_term.zscore('tracked_term:stat:{}'.format(term_uuid), date_day)
|
||||
nb_seen_this_day = r_serv_term.scard('tracker:item:{}:{}'.format(tracker_uuid, date_day))
|
||||
if nb_seen_this_day is None:
|
||||
nb_seen_this_day = 0
|
||||
sparklines_value.append(int(nb_seen_this_day))
|
||||
return sparklines_value
|
||||
|
||||
def get_list_tracked_term_stats_by_day(list_tracker_uuid, num_day=31, date_from=None, date_to=None):
|
||||
if date_from and date_to:
|
||||
date_range = Date.substract_date(date_from, date_to)
|
||||
else:
|
||||
date_range = Date.get_date_range(num_day)
|
||||
list_tracker_stats = []
|
||||
for tracker_uuid in list_tracker_uuid:
|
||||
dict_tracker_data = []
|
||||
tracker = r_serv_term.hget('tracker:{}'.format(tracker_uuid), 'tracked')
|
||||
for date_day in date_range:
|
||||
nb_seen_this_day = r_serv_term.scard('tracker:item:{}:{}'.format(tracker_uuid, date_day))
|
||||
if nb_seen_this_day is None:
|
||||
nb_seen_this_day = 0
|
||||
dict_tracker_data.append({"date": date_day,"value": int(nb_seen_this_day)})
|
||||
list_tracker_stats.append({"name": tracker,"Data": dict_tracker_data})
|
||||
return list_tracker_stats
|
||||
|
||||
def get_list_trackeed_term_tags(term_uuid):
|
||||
res = r_serv_term.smembers('tracked_term:tags:{}'.format(term_uuid))
|
||||
res = r_serv_term.smembers('tracker:tags:{}'.format(term_uuid))
|
||||
if res:
|
||||
return list(res)
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_list_trackeed_term_mails(term_uuid):
|
||||
res = r_serv_term.smembers('tracked_term:mail:{}'.format(term_uuid))
|
||||
res = r_serv_term.smembers('tracker:mail:{}'.format(term_uuid))
|
||||
if res:
|
||||
return list(res)
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_user_tracked_term_uuid(user_id):
|
||||
return list(r_serv_term.smembers('user:tracked_term:{}'.format(user_id)))
|
||||
def get_user_tracked_term_uuid(user_id, filter_type=None):
|
||||
if filter_type:
|
||||
return list(r_serv_term.smembers('user:tracker:{}:{}'.format(user_id,filter_type)))
|
||||
else:
|
||||
return list(r_serv_term.smembers('user:tracker:{}'.format(user_id)))
|
||||
|
||||
def get_global_tracked_term_uuid():
|
||||
return list(r_serv_term.smembers('global:tracked_term'))
|
||||
def get_global_tracked_term_uuid(filter_type=None):
|
||||
if filter_type:
|
||||
return list(r_serv_term.smembers('global:tracker:{}'.format(filter_type)))
|
||||
else:
|
||||
return list(r_serv_term.smembers('global:tracker'))
|
||||
|
||||
def get_all_user_tracked_terms(user_id):
|
||||
def get_all_user_tracked_terms(user_id, filter_type=None):
|
||||
all_user_term = []
|
||||
all_user_term_uuid = get_user_tracked_term_uuid(user_id)
|
||||
all_user_term_uuid = get_user_tracked_term_uuid(user_id, filter_type=filter_type)
|
||||
|
||||
for term_uuid in all_user_term_uuid:
|
||||
all_user_term.append(get_term_metedata(term_uuid, tags=True, mails=True))
|
||||
all_user_term.append(get_term_metedata(term_uuid, tags=True, mails=True, sparkline=True))
|
||||
return all_user_term
|
||||
|
||||
def get_all_global_tracked_terms():
|
||||
def get_all_global_tracked_terms(filter_type=None):
|
||||
all_user_term = []
|
||||
all_user_term_uuid = get_global_tracked_term_uuid()
|
||||
all_user_term_uuid = get_global_tracked_term_uuid(filter_type=filter_type)
|
||||
|
||||
for term_uuid in all_user_term_uuid:
|
||||
all_user_term.append(get_term_metedata(term_uuid, user_id=True, tags=True, mails=True))
|
||||
all_user_term.append(get_term_metedata(term_uuid, user_id=True, tags=True, mails=True, sparkline=True))
|
||||
return all_user_term
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*-coding:UTF-8 -*
|
||||
|
||||
'''
|
||||
Flask functions and routes for tracked items
|
||||
'''
|
||||
import json
|
||||
import redis
|
||||
import datetime
|
||||
import calendar
|
||||
import flask
|
||||
from flask import Flask, render_template, jsonify, request, Blueprint, url_for, redirect, Response
|
||||
|
||||
from Role_Manager import login_admin, login_analyst
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
import re
|
||||
from pprint import pprint
|
||||
import Levenshtein
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
import Paste
|
||||
import Term
|
||||
|
||||
# ============ VARIABLES ============
|
||||
import Flask_config
|
||||
|
||||
app = Flask_config.app
|
||||
cfg = Flask_config.cfg
|
||||
baseUrl = Flask_config.baseUrl
|
||||
r_serv_term = Flask_config.r_serv_term
|
||||
r_serv_cred = Flask_config.r_serv_cred
|
||||
r_serv_db = Flask_config.r_serv_db
|
||||
bootstrap_label = Flask_config.bootstrap_label
|
||||
|
||||
hunter = Blueprint('hunter', __name__, template_folder='templates')
|
||||
|
||||
# ============ FUNCTIONS ============
|
||||
|
||||
# ============ ROUTES ============
|
||||
|
||||
@hunter.route("/trackers")
|
||||
@login_required
|
||||
@login_analyst
|
||||
def tracked_menu():
|
||||
user_id = current_user.get_id()
|
||||
user_term = Term.get_all_user_tracked_terms(user_id)
|
||||
global_term = Term.get_all_global_tracked_terms()
|
||||
return render_template("trackersManagement.html", user_term=user_term, global_term=global_term, bootstrap_label=bootstrap_label)
|
||||
|
||||
@hunter.route("/trackers/word")
|
||||
@login_required
|
||||
@login_analyst
|
||||
def tracked_menu_word():
|
||||
filter_type = 'word'
|
||||
user_id = current_user.get_id()
|
||||
user_term = Term.get_all_user_tracked_terms(user_id, filter_type='word')
|
||||
global_term = Term.get_all_global_tracked_terms(filter_type='word')
|
||||
return render_template("trackersManagement.html", user_term=user_term, global_term=global_term, bootstrap_label=bootstrap_label, filter_type=filter_type)
|
||||
|
||||
@hunter.route("/trackers/set")
|
||||
@login_required
|
||||
@login_analyst
|
||||
def tracked_menu_set():
|
||||
filter_type = 'set'
|
||||
user_id = current_user.get_id()
|
||||
user_term = Term.get_all_user_tracked_terms(user_id, filter_type=filter_type)
|
||||
global_term = Term.get_all_global_tracked_terms(filter_type=filter_type)
|
||||
return render_template("trackersManagement.html", user_term=user_term, global_term=global_term, bootstrap_label=bootstrap_label, filter_type=filter_type)
|
||||
|
||||
@hunter.route("/trackers/regex")
|
||||
@login_required
|
||||
@login_analyst
|
||||
def tracked_menu_regex():
|
||||
filter_type = 'regex'
|
||||
user_id = current_user.get_id()
|
||||
user_term = Term.get_all_user_tracked_terms(user_id, filter_type=filter_type)
|
||||
global_term = Term.get_all_global_tracked_terms(filter_type=filter_type)
|
||||
return render_template("trackersManagement.html", user_term=user_term, global_term=global_term, bootstrap_label=bootstrap_label, filter_type=filter_type)
|
||||
|
||||
|
||||
@hunter.route("/tracker/add", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def add_tracked_menu():
|
||||
if request.method == 'POST':
|
||||
term = request.form.get("term")
|
||||
term_type = request.form.get("tracker_type")
|
||||
nb_words = request.form.get("nb_word", 1)
|
||||
level = request.form.get("level", 0)
|
||||
tags = request.form.get("tags", [])
|
||||
mails = request.form.get("mails", [])
|
||||
|
||||
if level == 'on':
|
||||
level = 1
|
||||
|
||||
if mails:
|
||||
mails = mails.split()
|
||||
if tags:
|
||||
tags = tags.split()
|
||||
|
||||
input_dict = {"term": term, "type": term_type, "nb_words": nb_words, "tags": tags, "mails": mails, "level": level}
|
||||
user_id = current_user.get_id()
|
||||
res = Term.parse_json_term_to_add(input_dict, user_id)
|
||||
if res[1] == 200:
|
||||
return redirect(url_for('hunter.tracked_menu'))
|
||||
else:
|
||||
## TODO: use modal
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
else:
|
||||
return render_template("Add_tracker.html")
|
||||
|
||||
@hunter.route("/tracker/show_tracker")
|
||||
@login_required
|
||||
@login_analyst
|
||||
def show_tracker():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.args.get('uuid', None)
|
||||
res = Term.check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res: # invalid access
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
date_from = request.args.get('date_from')
|
||||
date_to = request.args.get('date_to')
|
||||
|
||||
if date_from:
|
||||
date_from = date_from.replace('-', '')
|
||||
if date_to:
|
||||
date_to = date_to.replace('-', '')
|
||||
|
||||
tracker_metadata = Term.get_term_metedata(term_uuid, user_id=True, level=True, tags=True, mails=True, sparkline=True)
|
||||
|
||||
if date_from:
|
||||
res = Term.parse_get_tracker_term_item({'uuid': term_uuid, 'date_from': date_from, 'date_to': date_to}, user_id)
|
||||
if res[1] !=200:
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
tracker_metadata['items'] = res[0]['items']
|
||||
tracker_metadata['date_from'] = res[0]['date_from']
|
||||
tracker_metadata['date_to'] = res[0]['date_to']
|
||||
else:
|
||||
tracker_metadata['items'] = []
|
||||
tracker_metadata['date_from'] = ''
|
||||
tracker_metadata['date_to'] = ''
|
||||
|
||||
return render_template("showTracker.html", tracker_metadata=tracker_metadata, bootstrap_label=bootstrap_label)
|
||||
|
||||
@hunter.route("/tracker/update_tracker_tags", methods=['POST'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def update_tracker_tags():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.form.get('uuid')
|
||||
res = Term.check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res: # invalid access
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
tags = request.form.get('tags')
|
||||
if tags:
|
||||
tags = tags.split()
|
||||
else:
|
||||
tags = []
|
||||
Term.replace_tracked_term_tags(term_uuid, tags)
|
||||
return redirect(url_for('hunter.show_tracker', uuid=term_uuid))
|
||||
|
||||
@hunter.route("/tracker/update_tracker_mails", methods=['POST'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def update_tracker_mails():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.form.get('uuid')
|
||||
res = Term.check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res: # invalid access
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
mails = request.form.get('mails')
|
||||
if mails:
|
||||
mails = mails.split()
|
||||
else:
|
||||
mails = []
|
||||
res = Term.replace_tracked_term_mails(term_uuid, mails)
|
||||
if res: # invalid mail
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
return redirect(url_for('hunter.show_tracker', uuid=term_uuid))
|
||||
|
||||
@hunter.route("/tracker/delete", methods=['GET'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def delete_tracker():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.args.get('uuid')
|
||||
res = Term.parse_tracked_term_to_delete({'uuid': term_uuid}, user_id)
|
||||
if res[1] !=200:
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
return redirect(url_for('hunter.tracked_menu'))
|
||||
|
||||
@hunter.route("/tracker/get_json_tracker_stats", methods=['GET'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def get_json_tracker_stats():
|
||||
date_from = request.args.get('date_from')
|
||||
date_to = request.args.get('date_to')
|
||||
|
||||
if date_from:
|
||||
date_from = date_from.replace('-', '')
|
||||
if date_to:
|
||||
date_to = date_to.replace('-', '')
|
||||
|
||||
tracker_uuid = request.args.get('uuid')
|
||||
|
||||
if date_from and date_to:
|
||||
res = Term.get_list_tracked_term_stats_by_day([tracker_uuid], date_from=date_from, date_to=date_to)
|
||||
else:
|
||||
res = Term.get_list_tracked_term_stats_by_day([tracker_uuid])
|
||||
return jsonify(res)
|
||||
|
||||
# ========= REGISTRATION =========
|
||||
app.register_blueprint(hunter, url_prefix=baseUrl)
|
|
@ -23,7 +23,7 @@
|
|||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
{% include 'crawler/menu_sidebar.html' %}
|
||||
{% include 'hunter/menu_sidebar.html' %}
|
||||
|
||||
<div class="col-12 col-lg-10" id="core_content">
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
|||
<div class="card-body">
|
||||
<p class="card-text">Enter a domain and choose what kind of data you want.</p>
|
||||
|
||||
<form action="{{ url_for('terms.add_tracked_term_menu') }}" method='post'>
|
||||
<form action="{{ url_for('hunter.add_tracked_menu') }}" method='post'>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-9">
|
|
@ -11,21 +11,21 @@
|
|||
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/daterangepicker.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||
<script language="javascript" src="{{ url_for('static', filename='js/d3.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/plugins/d3/sparklines.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/plugins/d3/graphlinesgroup.js')}}"></script>
|
||||
<script language="javascript" src="{{ url_for('static', filename='js/moment.min.js') }}"></script>
|
||||
<script language="javascript" src="{{ url_for('static', filename='js/jquery.daterangepicker.min.js') }}"></script>
|
||||
|
||||
<style>
|
||||
.line_sparkline {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 2.0px;
|
||||
}
|
||||
.btn-link {
|
||||
color: #000000
|
||||
}
|
||||
|
@ -42,13 +42,13 @@
|
|||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
{% include 'tracker/menu_sidebar.html' %}
|
||||
{% include 'hunter/menu_sidebar.html' %}
|
||||
|
||||
<div class="col-12 col-lg-10" id="core_content">
|
||||
|
||||
<div class="card my-3">
|
||||
<div class="card-header" style="background-color:#d9edf7;font-size: 15px">
|
||||
<h4 class="text-secondary">{{ term_metadata['uuid'] }} </h4>
|
||||
<h4 class="text-secondary">{{ tracker_metadata['uuid'] }} </h4>
|
||||
<ul class="list-group mb-2">
|
||||
<li class="list-group-item py-0">
|
||||
<div class="row">
|
||||
|
@ -57,7 +57,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Tracked</th>
|
||||
<th>Tracker</th>
|
||||
<th>Date added</th>
|
||||
<th>Level</th>
|
||||
<th>Created by</th>
|
||||
|
@ -69,23 +69,23 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ term_metadata['type'] }}</td>
|
||||
<td>{{ term_metadata['term'] }}</td>
|
||||
<td>{{ term_metadata['date'][0:4] }}/{{ term_metadata['date'][4:6] }}/{{ term_metadata['date'][6:8] }}</td>
|
||||
<td>{{ term_metadata['level'] }}</td>
|
||||
<td>{{ term_metadata['user_id'] }}</td>
|
||||
<td>{{ tracker_metadata['type'] }}</td>
|
||||
<td>{{ tracker_metadata['term'] }}</td>
|
||||
<td>{{ tracker_metadata['date'][0:4] }}/{{ tracker_metadata['date'][4:6] }}/{{ tracker_metadata['date'][6:8] }}</td>
|
||||
<td>{{ tracker_metadata['level'] }}</td>
|
||||
<td>{{ tracker_metadata['user_id'] }}</td>
|
||||
<td>
|
||||
{% if term_metadata['first_seen'] %}
|
||||
{{ term_metadata['first_seen'][0:4] }}/{{ term_metadata['first_seen'][4:6] }}/{{ term_metadata['first_seen'][6:8] }}
|
||||
{% if tracker_metadata['first_seen'] %}
|
||||
{{ tracker_metadata['first_seen'][0:4] }}/{{ tracker_metadata['first_seen'][4:6] }}/{{ tracker_metadata['first_seen'][6:8] }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if term_metadata['last_seen'] %}
|
||||
{{ term_metadata['last_seen'][0:4] }}/{{ term_metadata['last_seen'][4:6] }}/{{ term_metadata['last_seen'][6:8] }}
|
||||
{% if tracker_metadata['last_seen'] %}
|
||||
{{ tracker_metadata['last_seen'][0:4] }}/{{ tracker_metadata['last_seen'][4:6] }}/{{ tracker_metadata['last_seen'][6:8] }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for tag in term_metadata['tags'] %}
|
||||
{% for tag in tracker_metadata['tags'] %}
|
||||
<a href="{{ url_for('Tags.Tags_page') }}?ltags={{ tag }}">
|
||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">{{ tag }}</span>
|
||||
</a>
|
||||
|
@ -93,7 +93,7 @@
|
|||
|
||||
</td>
|
||||
<td>
|
||||
{% for mail in term_metadata['mails'] %}
|
||||
{% for mail in tracker_metadata['mails'] %}
|
||||
{{ mail }}<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
|
@ -109,15 +109,15 @@
|
|||
</ul>
|
||||
|
||||
<div id="div_edit_tags">
|
||||
<form action="{{ url_for('terms.update_tracker_tags') }}" method='post'>
|
||||
<input name="uuid" type="text" value="{{term_metadata['uuid']}}" hidden>
|
||||
<form action="{{ url_for('hunter.update_tracker_tags') }}" method='post'>
|
||||
<input name="uuid" type="text" value="{{tracker_metadata['uuid']}}" hidden>
|
||||
<div>All Tags added for this tracker, space separated: </div>
|
||||
<div class="input-group mb-2 mr-sm-2">
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text"><i class="fas fa-tag"></i></div>
|
||||
</div>
|
||||
<input id="tags" name="tags" class="form-control" placeholder="Tags (optional, space separated)" type="text"
|
||||
value="{% for tag in term_metadata['tags'] %}{{tag}} {% endfor %}">
|
||||
value="{% for tag in tracker_metadata['tags'] %}{{tag}} {% endfor %}">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-info">
|
||||
|
@ -128,15 +128,15 @@
|
|||
</div>
|
||||
|
||||
<div id="div_edit_mails">
|
||||
<form action="{{ url_for('terms.update_tracker_mails') }}" method='post'>
|
||||
<input name="uuid" type="text" value="{{term_metadata['uuid']}}" hidden>
|
||||
<form action="{{ url_for('hunter.update_tracker_mails') }}" method='post'>
|
||||
<input name="uuid" type="text" value="{{tracker_metadata['uuid']}}" hidden>
|
||||
<div>All E-Mails to Notify for this tracker, space separated: </div>
|
||||
<div class="input-group mb-2 mr-sm-2">
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text"><i class="fas fa-at"></i></div>
|
||||
</div>
|
||||
<input id="mails" name="mails" class="form-control" placeholder="E-Mails Notification (optional, space separated)" type="text"
|
||||
value="{% for mail in term_metadata['mails'] %}{{mail}} {% endfor %}">
|
||||
value="{% for mail in tracker_metadata['mails'] %}{{mail}} {% endfor %}">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-info">
|
||||
|
@ -146,28 +146,29 @@
|
|||
|
||||
</div>
|
||||
|
||||
<a href="{{ url_for('hashDecoded.downloadHash') }}?hash={{hash}}" target="blank" class="float-right" style="font-size: 15px">
|
||||
<a href="{{ url_for('hunter.delete_tracker') }}?uuid={{tracker_metadata['uuid']}}" class="float-right" style="font-size: 15px">
|
||||
<button class='btn btn-danger'><i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="graphline" class="text-center"></div>
|
||||
|
||||
<div class="card mb-3 mt-1">
|
||||
<div class="card mb-5 mt-1">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group" id="date-range-from">
|
||||
<div class="input-group-prepend"><span class="input-group-text"><i class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
|
||||
<input class="form-control" id="date-range-from-input" placeholder="yyyy-mm-dd" value="{{ term_metadata['date_from'] }}" name="date_from" autocomplete="off">
|
||||
<input class="form-control" id="date-range-from-input" placeholder="yyyy-mm-dd" value="{{ tracker_metadata['date_from'] }}" name="date_from" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group" id="date-range-to">
|
||||
<div class="input-group-prepend"><span class="input-group-text"><i class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
|
||||
<input class="form-control" id="date-range-to-input" placeholder="yyyy-mm-dd" value="{{ term_metadata['date_to'] }}" name="date_to" autocomplete="off">
|
||||
<input class="form-control" id="date-range-to-input" placeholder="yyyy-mm-dd" value="{{ tracker_metadata['date_to'] }}" name="date_to" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -179,27 +180,40 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{%if term_metadata['items']%}
|
||||
{%if tracker_metadata['items']%}
|
||||
<div class="mt-4">
|
||||
<table class="table table-bordered table-hover" id="myTable_">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Date</th>
|
||||
<th>Item Id</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% for item in term_metadata['items'] %}
|
||||
{% for item in tracker_metadata['items'] %}
|
||||
<tr>
|
||||
<td>
|
||||
<a target="_blank" href="{{ url_for('showsavedpastes.showsavedpaste') }}?paste={{item}}">
|
||||
<div style="line-height:0.9;">{{ item }}</div>
|
||||
{{item['date'][0:4]}}/{{item['date'][4:6]}}/{{item['date'][6:8]}}
|
||||
</td>
|
||||
<td>
|
||||
<a class="text-secondary" target="_blank" href="{{ url_for('showsavedpastes.showsavedpaste') }}?paste={{item['id']}}">
|
||||
<div style="line-height:0.9;">{{ item['id'] }}</div>
|
||||
</a>
|
||||
<div class="mb-2">
|
||||
{% for tag in item['tags'] %}
|
||||
<a href="{{ url_for('Tags.Tags_page') }}?ltags={{ tag }}">
|
||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
@ -239,7 +253,18 @@ $(document).ready(function(){
|
|||
}
|
||||
});
|
||||
|
||||
sparklines("sparkline", {{ term_metadata['sparkline'] }});
|
||||
$('#myTable_').DataTable({
|
||||
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
|
||||
"iDisplayLength": 10,
|
||||
"order": [[ 0, "desc" ]]
|
||||
});
|
||||
|
||||
sparkline("sparkline", {{ tracker_metadata['sparkline'] }}, {});
|
||||
let div_width = $("#graphline").width();
|
||||
$.getJSON( "{{ url_for('hunter.get_json_tracker_stats') }}?uuid={{ tracker_metadata['uuid'] }}{%if tracker_metadata['date_from']%}&date_from={{ tracker_metadata['date_from'] }}{%endif%}{%if tracker_metadata['date_to']%}&date_to={{ tracker_metadata['date_to'] }}{%endif%}",
|
||||
function( data ) {multilines_group("graphline", data, {"width": div_width});}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
function toggle_sidebar(){
|
||||
|
@ -269,49 +294,10 @@ function edit_mails(){
|
|||
function getItems() {
|
||||
var date_from = $('#date-range-from-input').val();
|
||||
var date_to =$('#date-range-to-input').val();
|
||||
window.location.replace("{{ url_for('terms.show_term_tracker') }}?uuid={{ term_metadata['uuid'] }}&date_from="+date_from+"&date_to="+date_to);
|
||||
window.location.replace("{{ url_for('hunter.show_tracker') }}?uuid={{ tracker_metadata['uuid'] }}&date_from="+date_from+"&date_to="+date_to);
|
||||
}
|
||||
|
||||
</script>
|
||||
<script>
|
||||
//var data = [6,3,3,2,5,3,9];
|
||||
|
||||
// a sparklines plot
|
||||
function sparklines(id, points) {
|
||||
var width_spark = 100, height_spark = 60;
|
||||
|
||||
var data = []
|
||||
for (i = 0; i < points.length; i++) {
|
||||
data[i] = {
|
||||
'x': i,
|
||||
'y': +points[i]
|
||||
}
|
||||
}
|
||||
|
||||
var x = d3.scaleLinear()
|
||||
.range([0, width_spark - 10])
|
||||
.domain([0,5]);
|
||||
|
||||
var y = d3.scaleLinear()
|
||||
.range([height_spark, 0])
|
||||
.domain([0,10]);
|
||||
|
||||
var line = d3.line()
|
||||
.x(function(d) {return x(d.x)})
|
||||
.y(function(d) {return y(d.y)});
|
||||
|
||||
d3.select("#"+id).append('svg')
|
||||
.attr('width', width_spark)
|
||||
.attr('height', height_spark)
|
||||
.append('path')
|
||||
.attr('class','line_sparkline')
|
||||
.datum(data)
|
||||
.attr('d', line);
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
|
@ -5,7 +5,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Terms Management</title>
|
||||
<title>Tracker Management</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
|
||||
|
||||
<!-- Core CSS -->
|
||||
|
@ -17,6 +17,8 @@
|
|||
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/plugins/d3/sparklines.js')}}"></script>
|
||||
|
||||
<style>
|
||||
|
||||
|
@ -38,23 +40,24 @@
|
|||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
{% include 'tracker/menu_sidebar.html' %}
|
||||
{% include 'hunter/menu_sidebar.html' %}
|
||||
|
||||
<div class="col-12 col-lg-10" id="core_content">
|
||||
|
||||
<div class="card my-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Your Tracked Terms</h5>
|
||||
<h5 class="card-title">Your {{filter_type}} Trackers</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="table_user_terms" class="table table-striped table-bordered">
|
||||
<table id="table_user_trackers" class="table table-striped table-bordered">
|
||||
<thead class="bg-dark text-white">
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Tracked Term</th>
|
||||
<th>Tracker</th>
|
||||
<th>First seen</th>
|
||||
<th>Last seen</th>
|
||||
<th>Email notification</th>
|
||||
<th>sparkline</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="font-size: 15px;">
|
||||
|
@ -62,11 +65,11 @@
|
|||
<tr>
|
||||
<td>{{dict_uuid['type']}}</td>
|
||||
<td>
|
||||
<span><a target="_blank" href="{{ url_for('terms.show_term_tracker') }}?uuid={{ dict_uuid['uuid'] }}">{{dict_uuid['term']}}</a></span>
|
||||
<span><a target="_blank" href="{{ url_for('hunter.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">{{dict_uuid['term']}}</a></span>
|
||||
<div>
|
||||
{% for tag in dict_uuid['tags'] %}
|
||||
<a href="{{ url_for('Tags.Tags_page') }}?ltags={{ tag }}">
|
||||
<span class="label label-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
|
||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
@ -86,6 +89,7 @@
|
|||
{{ mail }}<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td id="sparklines_{{ dict_uuid['uuid'] }}" style="text-align:center;"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -95,17 +99,18 @@
|
|||
|
||||
<div class="card my-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Global Tracked Terms</h5>
|
||||
<h5 class="card-title">Global {{filter_type}} Trackers</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table id="table_global_terms" class="table table-striped table-bordered">
|
||||
<table id="table_global_trackers" class="table table-striped table-bordered">
|
||||
<thead class="bg-dark text-white">
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Tracked Term</th>
|
||||
<th>Tracker</th>
|
||||
<th>First seen</th>
|
||||
<th>Last seen</th>
|
||||
<th>Email notification</th>
|
||||
<th>sparkline</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="font-size: 15px;">
|
||||
|
@ -113,7 +118,7 @@
|
|||
<tr>
|
||||
<td>{{dict_uuid['type']}}</td>
|
||||
<td>
|
||||
<span><a target="_blank" href="{{ url_for('terms.show_term_tracker') }}?uuid={{ dict_uuid['uuid'] }}">{{dict_uuid['term']}}</a></span>
|
||||
<span><a target="_blank" href="{{ url_for('hunter.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">{{dict_uuid['term']}}</a></span>
|
||||
<div>
|
||||
{% for tag in dict_uuid['tags'] %}
|
||||
<a href="{{ url_for('Tags.Tags_page') }}?ltags={{ tag }}">
|
||||
|
@ -137,6 +142,7 @@
|
|||
{{ mail }}<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td id="sparklines_{{ dict_uuid['uuid'] }}" style="text-align:center;"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -150,16 +156,28 @@
|
|||
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('#table_user_terms').DataTable({
|
||||
$("#page-Tracker").addClass("active");
|
||||
$("#nav_tracker_{{filter_type}}").addClass("active");
|
||||
|
||||
$('#table_user_trackers').DataTable({
|
||||
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
|
||||
"iDisplayLength": 10,
|
||||
"order": [[ 0, "desc" ]]
|
||||
});
|
||||
$('#table_global_terms').DataTable({
|
||||
$('#table_global_trackers').DataTable({
|
||||
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
|
||||
"iDisplayLength": 10,
|
||||
"order": [[ 0, "desc" ]]
|
||||
});
|
||||
|
||||
|
||||
{% for dict_uuid in user_term %}
|
||||
sparkline("sparklines_{{ dict_uuid['uuid'] }}", {{ dict_uuid['sparkline'] }}, {height: 40});
|
||||
{% endfor %}
|
||||
{% for dict_uuid in global_term %}
|
||||
sparkline("sparklines_{{ dict_uuid['uuid'] }}", {{ dict_uuid['sparkline'] }}, {height: 40});
|
||||
{% endfor %}
|
||||
|
||||
});
|
||||
|
||||
function toggle_sidebar(){
|
|
@ -151,111 +151,6 @@ def save_tag_to_auto_push(list_tag):
|
|||
|
||||
# ============ ROUTES ============
|
||||
|
||||
@terms.route("/tracker_term")
|
||||
def tracked_term_menu():
|
||||
user_id = current_user.get_id()
|
||||
user_term = Term.get_all_user_tracked_terms(user_id)
|
||||
global_term = Term.get_all_global_tracked_terms()
|
||||
return render_template("tracker_term_management.html", user_term=user_term, global_term=global_term, bootstrap_label=bootstrap_label)
|
||||
|
||||
|
||||
@terms.route("/tracker/add", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def add_tracked_term_menu():
|
||||
if request.method == 'POST':
|
||||
term = request.form.get("term")
|
||||
term_type = request.form.get("tracker_type")
|
||||
nb_words = request.form.get("nb_word", 1)
|
||||
level = request.form.get("level", 1)
|
||||
tags = request.form.get("tags", [])
|
||||
mails = request.form.get("mails", [])
|
||||
|
||||
if mails:
|
||||
mails = mails.split()
|
||||
if tags:
|
||||
tags = tags.split()
|
||||
input_dict = {"term": term, "type": term_type, "nb_words": nb_words, "tags": tags, "mails": mails}
|
||||
user_id = current_user.get_id()
|
||||
res = Term.parse_json_term_to_add(input_dict, user_id)
|
||||
if res[1] == 200:
|
||||
return redirect(url_for('terms.tracked_term_menu'))
|
||||
else:
|
||||
## TODO: use modal
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
else:
|
||||
return render_template("Add_tracker.html")
|
||||
|
||||
@terms.route("/tracker/show_term_tracker")
|
||||
@login_required
|
||||
@login_analyst
|
||||
def show_term_tracker():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.args.get('uuid', None)
|
||||
res = Term.check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res: # invalid access
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
date_from = request.args.get('date_from')
|
||||
date_to = request.args.get('date_to')
|
||||
|
||||
if date_from:
|
||||
date_from = date_from.replace('-', '')
|
||||
if date_to:
|
||||
date_to = date_to.replace('-', '')
|
||||
|
||||
term_metadata = Term.get_term_metedata(term_uuid, user_id=True, level=True, tags=True, mails=True, sparkline=True)
|
||||
|
||||
if date_from:
|
||||
res = Term.parse_get_tracker_term_item({'uuid': term_uuid, 'date_from': date_from, 'date_to': date_to}, user_id)
|
||||
if res[1] !=200:
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
term_metadata['items'] = res[0]['items']
|
||||
term_metadata['date_from'] = res[0]['date_from']
|
||||
term_metadata['date_to'] = res[0]['date_to']
|
||||
else:
|
||||
term_metadata['items'] = []
|
||||
term_metadata['date_from'] = ''
|
||||
term_metadata['date_to'] = ''
|
||||
|
||||
return render_template("showTrackerTerm.html", term_metadata=term_metadata, bootstrap_label=bootstrap_label)
|
||||
|
||||
@terms.route("/tracker/update_tracker_tags", methods=['POST'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def update_tracker_tags():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.form.get('uuid')
|
||||
res = Term.check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res: # invalid access
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
tags = request.form.get('tags')
|
||||
if tags:
|
||||
tags = tags.split()
|
||||
else:
|
||||
tags = []
|
||||
Term.replace_tracked_term_tags(term_uuid, tags)
|
||||
return redirect(url_for('terms.show_term_tracker', uuid=term_uuid))
|
||||
|
||||
@terms.route("/tracker/update_tracker_mails", methods=['POST'])
|
||||
@login_required
|
||||
@login_analyst
|
||||
def update_tracker_mails():
|
||||
user_id = current_user.get_id()
|
||||
term_uuid = request.form.get('uuid')
|
||||
res = Term.check_term_uuid_valid_access(term_uuid, user_id)
|
||||
if res: # invalid access
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
mails = request.form.get('mails')
|
||||
if mails:
|
||||
mails = mails.split()
|
||||
else:
|
||||
mails = []
|
||||
res = Term.replace_tracked_term_mails(term_uuid, mails)
|
||||
if res: # invalid mail
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
return redirect(url_for('terms.show_term_tracker', uuid=term_uuid))
|
||||
|
||||
|
||||
@terms.route("/terms_plot_tool/")
|
||||
@login_required
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<li id='page-termsfrequency'><a class="dropdown-toggle" data-toggle="dropdown" href="{{ url_for('terms.terms_management') }}"><i class="fa fa-eye"></i> Terms frequency
|
||||
<li id='page-termsfrequency'><a class="dropdown-toggle" data-toggle="dropdown" href="{{ url_for('terms.credentials_tracker') }}"><i class="fa fa-eye"></i> Terms frequency
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ url_for('terms.terms_management') }}"><i class="fa fa-gear "> </i> Terms managements</a></li>
|
||||
<li><a href="{{ url_for('terms.credentials_tracker') }}"><i class="glyphicon glyphicon-screenshot"> </i> Credentials seeker</a></li>
|
||||
<li><a href="{{ url_for('terms.terms_plot_top') }}"><i class="glyphicon glyphicon-fire"> </i> Terms plot top</a></li>
|
||||
<li><a href="{{ url_for('terms.terms_plot_tool') }}"><i class="fa fa-wrench"> </i> Terms plot tool</a></li>
|
||||
|
|
|
@ -20,7 +20,7 @@ const sparkline = (container_id, data, options) => {
|
|||
let maxY = d3.max(data, function(d) { return d } );
|
||||
|
||||
let x = d3.scaleLinear()
|
||||
.range([0, width_spark - 10])
|
||||
.range([0, width_spark])
|
||||
.domain([0,maxX]);
|
||||
|
||||
let y = d3.scaleLinear()
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<div class="col-12 col-lg-2 p-0 bg-light border-right" id="side_menu">
|
||||
|
||||
<button type="button" class="btn btn-outline-secondary mt-1 ml-3" onclick="toggle_sidebar()">
|
||||
<i class="fas fa-align-left"></i>
|
||||
<span>Toggle Sidebar</span>
|
||||
</button>
|
||||
|
||||
<nav class="navbar navbar-expand navbar-light bg-light flex-md-column flex-row align-items-start py-2" id="nav_menu">
|
||||
<h5 class="d-flex text-muted w-100">
|
||||
<span>Trackers </span>
|
||||
<a class="ml-auto" href="{{url_for('hunter.add_tracked_menu')}}">
|
||||
<i class="fas fa-plus-circle ml-auto"></i>
|
||||
</a>
|
||||
</h5>
|
||||
<ul class="nav flex-md-column flex-row navbar-nav justify-content-between w-100"> <!--nav-pills-->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{url_for('hunter.tracked_menu')}}" id="nav_tracker_">
|
||||
<i class="fas fa-ruler-combined"></i>
|
||||
<span>All Trackers</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{url_for('hunter.tracked_menu_word')}}" id="nav_tracker_word">
|
||||
<i class="fas fa-font"></i>
|
||||
<span>Tracked Words</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{url_for('hunter.tracked_menu_set')}}" id="nav_tracker_set">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
<span>Tracked Set</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{url_for('hunter.tracked_menu_regex')}}" id="nav_tracker_regex">
|
||||
<i class="fas fa-ruler"></i>
|
||||
<span>Tracked Regex</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
|
@ -19,7 +19,7 @@
|
|||
<a class="nav-link" id="page-Browse-Items" href="{{ url_for('Tags.Tags_page') }}" aria-disabled="true"><i class="fas fa-tag"></i> Browse Items</a>
|
||||
</li>
|
||||
<li class="nav-item mr-3">
|
||||
<a class="nav-link" id="page-Tracker" href="{{ url_for('terms.tracked_term_menu') }}" aria-disabled="true"><i class="fas fa-crosshairs"></i> Leaks Hunter</a>
|
||||
<a class="nav-link" id="page-Tracker" href="{{ url_for('hunter.tracked_menu') }}" aria-disabled="true"><i class="fas fa-crosshairs"></i> Leaks Hunter</a>
|
||||
</li>
|
||||
<li class="nav-item mr-3">
|
||||
<a class="nav-link" id="page-Crawler" href="{{ url_for('hiddenServices.dashboard') }}" tabindex="-1" aria-disabled="true"><i class="fas fa-spider"></i> Crawlers</a>
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<div class="col-12 col-lg-2 p-0 bg-light border-right" id="side_menu">
|
||||
|
||||
<button type="button" class="btn btn-outline-secondary mt-1 ml-3" onclick="toggle_sidebar()">
|
||||
<i class="fas fa-align-left"></i>
|
||||
<span>Toggle Sidebar</span>
|
||||
</button>
|
||||
|
||||
<nav class="navbar navbar-expand navbar-light bg-light flex-md-column flex-row align-items-start py-2" id="nav_menu">
|
||||
<h5 class="d-flex text-muted w-100">
|
||||
<span>Terms Tracker </span>
|
||||
<a class="ml-auto" href="{{url_for('terms.add_tracked_term_menu')}}">
|
||||
<i class="fas fa-plus-circle ml-auto"></i>
|
||||
</a>
|
||||
</h5>
|
||||
<ul class="nav flex-md-column flex-row navbar-nav justify-content-between w-100"> <!--nav-pills-->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{url_for('terms.tracked_term_menu')}}" id="nav_dashboard">
|
||||
<i class="fas fa-search"></i>
|
||||
<span>Tracked Terms</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
Loading…
Reference in New Issue