2018-05-04 13:53:29 +02:00
|
|
|
#!/usr/bin/env python3
|
2014-08-06 11:43:40 +02:00
|
|
|
# -*-coding:UTF-8 -*
|
|
|
|
|
2019-06-19 15:00:25 +02:00
|
|
|
import os
|
|
|
|
import sys
|
2019-06-24 13:43:16 +02:00
|
|
|
import ssl
|
2019-07-31 13:24:43 +02:00
|
|
|
import json
|
2019-06-24 13:43:16 +02:00
|
|
|
import time
|
2020-04-06 11:22:31 +02:00
|
|
|
import uuid
|
2019-05-02 17:31:14 +02:00
|
|
|
import random
|
2019-06-26 16:36:40 +02:00
|
|
|
import logging
|
2023-05-12 15:29:53 +02:00
|
|
|
import logging.config
|
2019-06-24 13:43:16 +02:00
|
|
|
|
2019-07-31 13:24:43 +02:00
|
|
|
from flask import Flask, render_template, jsonify, request, Request, Response, session, redirect, url_for
|
2019-05-02 17:31:14 +02:00
|
|
|
from flask_login import LoginManager, current_user, login_user, logout_user, login_required
|
|
|
|
|
2017-04-19 11:02:03 +02:00
|
|
|
import importlib
|
|
|
|
from os.path import join
|
2019-11-18 09:46:15 +01:00
|
|
|
|
2017-04-19 11:02:03 +02:00
|
|
|
sys.path.append('./modules/')
|
2014-12-24 15:42:20 +01:00
|
|
|
|
2022-07-08 09:47:47 +02:00
|
|
|
sys.path.append(os.environ['AIL_BIN'])
|
|
|
|
##################################
|
|
|
|
# Import Project packages
|
|
|
|
##################################
|
2022-10-25 16:25:19 +02:00
|
|
|
from lib.ConfigLoader import ConfigLoader
|
2024-05-06 16:21:00 +02:00
|
|
|
from lib.ail_users import AILUser, get_session_user
|
2022-09-01 14:04:00 +02:00
|
|
|
from lib import Tag
|
2024-09-16 15:30:12 +02:00
|
|
|
from lib import ail_core
|
2023-05-12 15:29:53 +02:00
|
|
|
from lib import ail_logger
|
2019-05-02 17:31:14 +02:00
|
|
|
|
2024-09-16 15:30:12 +02:00
|
|
|
from packages.git_status import clear_git_meta_cache
|
|
|
|
|
2016-12-09 08:46:37 +01:00
|
|
|
# Import config
|
|
|
|
import Flask_config
|
2016-08-19 13:34:02 +02:00
|
|
|
|
2019-09-25 08:58:18 +02:00
|
|
|
# Import Blueprint
|
|
|
|
from blueprints.root import root
|
2019-10-28 13:48:43 +01:00
|
|
|
from blueprints.crawler_splash import crawler_splash
|
2019-11-14 17:05:58 +01:00
|
|
|
from blueprints.correlation import correlation
|
2024-04-11 12:15:47 +02:00
|
|
|
from blueprints.languages_ui import languages_ui
|
2020-01-07 16:14:56 +01:00
|
|
|
from blueprints.tags_ui import tags_ui
|
2020-02-17 10:52:25 +01:00
|
|
|
from blueprints.import_export import import_export
|
2022-03-07 15:12:01 +01:00
|
|
|
from blueprints.investigations_b import investigations_b
|
2020-10-13 16:02:30 +02:00
|
|
|
from blueprints.objects_item import objects_item
|
2021-07-14 13:58:00 +02:00
|
|
|
from blueprints.hunters import hunters
|
2020-10-13 16:02:30 +02:00
|
|
|
from blueprints.old_endpoints import old_endpoints
|
2021-11-22 23:45:41 +01:00
|
|
|
from blueprints.ail_2_ail_sync import ail_2_ail_sync
|
2022-07-08 09:47:47 +02:00
|
|
|
from blueprints.settings_b import settings_b
|
2022-10-25 16:25:19 +02:00
|
|
|
from blueprints.objects_cve import objects_cve
|
2022-08-19 16:53:31 +02:00
|
|
|
from blueprints.objects_decoded import objects_decoded
|
2023-02-28 11:01:27 +01:00
|
|
|
from blueprints.objects_subtypes import objects_subtypes
|
2023-05-25 14:33:12 +02:00
|
|
|
from blueprints.objects_title import objects_title
|
2023-06-16 15:39:13 +02:00
|
|
|
from blueprints.objects_cookie_name import objects_cookie_name
|
2023-07-06 11:26:32 +02:00
|
|
|
from blueprints.objects_etag import objects_etag
|
2023-07-17 15:47:17 +02:00
|
|
|
from blueprints.objects_hhhash import objects_hhhash
|
2023-11-02 16:28:33 +01:00
|
|
|
from blueprints.chats_explorer import chats_explorer
|
2023-11-15 14:12:50 +01:00
|
|
|
from blueprints.objects_image import objects_image
|
2024-04-08 17:16:07 +02:00
|
|
|
from blueprints.objects_ocr import objects_ocr
|
2024-10-01 15:12:15 +02:00
|
|
|
from blueprints.objects_qrcode import objects_qrcode
|
2024-02-21 14:18:09 +01:00
|
|
|
from blueprints.objects_favicon import objects_favicon
|
2024-02-26 15:35:48 +01:00
|
|
|
from blueprints.api_rest import api_rest
|
|
|
|
|
2020-01-07 16:14:56 +01:00
|
|
|
|
2019-07-30 13:49:21 +02:00
|
|
|
Flask_dir = os.environ['AIL_FLASK']
|
|
|
|
|
2016-12-09 08:46:37 +01:00
|
|
|
# CONFIG #
|
2022-10-25 16:25:19 +02:00
|
|
|
config_loader = ConfigLoader()
|
2019-10-28 13:48:43 +01:00
|
|
|
baseUrl = config_loader.get_config_str("Flask", "baseurl")
|
2020-07-06 17:13:36 +02:00
|
|
|
host = config_loader.get_config_str("Flask", "host")
|
2018-09-20 10:38:19 +02:00
|
|
|
baseUrl = baseUrl.replace('/', '')
|
|
|
|
if baseUrl != '':
|
|
|
|
baseUrl = '/'+baseUrl
|
2016-08-24 18:00:05 +02:00
|
|
|
|
2020-01-21 11:39:08 +01:00
|
|
|
try:
|
|
|
|
FLASK_PORT = config_loader.get_config_int("Flask", "port")
|
|
|
|
except Exception:
|
|
|
|
FLASK_PORT = 7000
|
|
|
|
|
2019-05-03 16:52:05 +02:00
|
|
|
# ========= REDIS =========#
|
2022-09-01 14:04:00 +02:00
|
|
|
r_serv_db = config_loader.get_db_conn("Kvrocks_DB")
|
2019-06-26 16:36:40 +02:00
|
|
|
|
|
|
|
# logs
|
|
|
|
log_dir = os.path.join(os.environ['AIL_HOME'], 'logs')
|
|
|
|
if not os.path.isdir(log_dir):
|
2021-09-29 16:17:08 +02:00
|
|
|
os.makedirs(log_dir)
|
2019-06-26 16:36:40 +02:00
|
|
|
|
2024-07-04 15:52:20 +02:00
|
|
|
# ========= LOGS =========#
|
|
|
|
|
2024-08-19 15:45:20 +02:00
|
|
|
access_logger = ail_logger.get_access_config(create=True)
|
|
|
|
|
2024-07-04 15:52:20 +02:00
|
|
|
class FilterLogErrors(logging.Filter):
|
|
|
|
def filter(self, record):
|
|
|
|
# print(dict(record.__dict__))
|
|
|
|
if record.levelname == 'ERROR':
|
|
|
|
if record.msg.startswith('Error on request:'):
|
|
|
|
if 'ssl.SSLEOFError: EOF occurred in violation of protocol' in record.msg:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2023-05-12 15:29:53 +02:00
|
|
|
logging.config.dictConfig(ail_logger.get_config(name='flask'))
|
2024-07-04 15:52:20 +02:00
|
|
|
flask_logger = logging.getLogger()
|
|
|
|
ignore_filter = FilterLogErrors()
|
|
|
|
for handler in flask_logger.handlers:
|
|
|
|
handler.addFilter(ignore_filter)
|
2019-06-26 16:36:40 +02:00
|
|
|
|
2019-05-03 16:52:05 +02:00
|
|
|
|
2019-06-24 13:43:16 +02:00
|
|
|
# ========= TLS =========#
|
2024-07-04 15:52:20 +02:00
|
|
|
|
|
|
|
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
2019-07-30 13:49:21 +02:00
|
|
|
ssl_context.load_cert_chain(certfile=os.path.join(Flask_dir, 'server.crt'), keyfile=os.path.join(Flask_dir, 'server.key'))
|
2024-07-04 15:52:20 +02:00
|
|
|
ssl_context.suppress_ragged_eofs = True
|
2022-11-22 10:47:15 +01:00
|
|
|
# print(ssl_context.get_ciphers())
|
2019-06-24 13:43:16 +02:00
|
|
|
# ========= =========#
|
|
|
|
|
2018-09-20 10:38:19 +02:00
|
|
|
Flask_config.app = Flask(__name__, static_url_path=baseUrl+'/static/')
|
2016-12-09 08:46:37 +01:00
|
|
|
app = Flask_config.app
|
2018-06-08 16:49:20 +02:00
|
|
|
app.config['MAX_CONTENT_LENGTH'] = 900 * 1024 * 1024
|
2014-08-26 17:33:28 +02:00
|
|
|
|
2019-09-25 08:58:18 +02:00
|
|
|
# ========= BLUEPRINT =========#
|
|
|
|
app.register_blueprint(root, url_prefix=baseUrl)
|
2019-10-28 13:48:43 +01:00
|
|
|
app.register_blueprint(crawler_splash, url_prefix=baseUrl)
|
2019-11-14 17:05:58 +01:00
|
|
|
app.register_blueprint(correlation, url_prefix=baseUrl)
|
2024-04-11 12:15:47 +02:00
|
|
|
app.register_blueprint(languages_ui, url_prefix=baseUrl)
|
2020-01-07 16:14:56 +01:00
|
|
|
app.register_blueprint(tags_ui, url_prefix=baseUrl)
|
2020-02-17 10:52:25 +01:00
|
|
|
app.register_blueprint(import_export, url_prefix=baseUrl)
|
2022-03-07 15:12:01 +01:00
|
|
|
app.register_blueprint(investigations_b, url_prefix=baseUrl)
|
2020-10-13 16:02:30 +02:00
|
|
|
app.register_blueprint(objects_item, url_prefix=baseUrl)
|
2021-07-14 13:58:00 +02:00
|
|
|
app.register_blueprint(hunters, url_prefix=baseUrl)
|
2020-10-13 16:02:30 +02:00
|
|
|
app.register_blueprint(old_endpoints, url_prefix=baseUrl)
|
2021-11-22 23:45:41 +01:00
|
|
|
app.register_blueprint(ail_2_ail_sync, url_prefix=baseUrl)
|
2022-07-08 09:47:47 +02:00
|
|
|
app.register_blueprint(settings_b, url_prefix=baseUrl)
|
2022-10-25 16:25:19 +02:00
|
|
|
app.register_blueprint(objects_cve, url_prefix=baseUrl)
|
2022-08-19 16:53:31 +02:00
|
|
|
app.register_blueprint(objects_decoded, url_prefix=baseUrl)
|
2023-02-28 11:01:27 +01:00
|
|
|
app.register_blueprint(objects_subtypes, url_prefix=baseUrl)
|
2023-05-25 14:33:12 +02:00
|
|
|
app.register_blueprint(objects_title, url_prefix=baseUrl)
|
2023-06-16 15:39:13 +02:00
|
|
|
app.register_blueprint(objects_cookie_name, url_prefix=baseUrl)
|
2023-07-06 11:26:32 +02:00
|
|
|
app.register_blueprint(objects_etag, url_prefix=baseUrl)
|
2023-07-17 15:47:17 +02:00
|
|
|
app.register_blueprint(objects_hhhash, url_prefix=baseUrl)
|
2023-11-02 16:28:33 +01:00
|
|
|
app.register_blueprint(chats_explorer, url_prefix=baseUrl)
|
2023-11-15 14:12:50 +01:00
|
|
|
app.register_blueprint(objects_image, url_prefix=baseUrl)
|
2024-04-08 17:16:07 +02:00
|
|
|
app.register_blueprint(objects_ocr, url_prefix=baseUrl)
|
2024-10-01 15:12:15 +02:00
|
|
|
app.register_blueprint(objects_qrcode, url_prefix=baseUrl)
|
2024-02-21 14:18:09 +01:00
|
|
|
app.register_blueprint(objects_favicon, url_prefix=baseUrl)
|
2024-02-26 15:35:48 +01:00
|
|
|
app.register_blueprint(api_rest, url_prefix=baseUrl)
|
2023-06-16 15:39:13 +02:00
|
|
|
|
2019-09-25 08:58:18 +02:00
|
|
|
# ========= =========#
|
|
|
|
|
2020-01-21 11:39:08 +01:00
|
|
|
# ========= Cookie name ========
|
2024-09-16 15:30:12 +02:00
|
|
|
app.config.update(SESSION_COOKIE_NAME='ail_framework_{}'.format(ail_core.get_ail_uuid_int()))
|
2020-01-21 11:39:08 +01:00
|
|
|
|
2019-05-02 17:31:14 +02:00
|
|
|
# ========= session ========
|
|
|
|
app.secret_key = str(random.getrandbits(256))
|
|
|
|
login_manager = LoginManager()
|
2019-09-25 08:58:18 +02:00
|
|
|
login_manager.login_view = 'root.login'
|
2019-05-02 17:31:14 +02:00
|
|
|
login_manager.init_app(app)
|
|
|
|
|
|
|
|
# ========= LOGIN MANAGER ========
|
|
|
|
|
|
|
|
@login_manager.user_loader
|
2024-05-06 16:21:00 +02:00
|
|
|
def load_user(session_id): # TODO USE Alternative ID
|
2024-07-01 17:31:59 +02:00
|
|
|
# print(session)
|
2024-05-06 16:21:00 +02:00
|
|
|
user_id = get_session_user(session_id)
|
|
|
|
if user_id:
|
|
|
|
user = AILUser.get(user_id)
|
2024-07-01 17:31:59 +02:00
|
|
|
# print(user)
|
2024-05-06 16:21:00 +02:00
|
|
|
return user
|
|
|
|
return None
|
2019-05-02 17:31:14 +02:00
|
|
|
|
2019-09-25 08:58:18 +02:00
|
|
|
# ========= HEADER GENERATION ======== DEPRECATED
|
2017-04-25 12:18:08 +02:00
|
|
|
|
2022-11-22 10:47:15 +01:00
|
|
|
|
2017-04-25 12:18:08 +02:00
|
|
|
# Get headers items that should be ignored (not displayed)
|
|
|
|
toIgnoreModule = set()
|
|
|
|
try:
|
|
|
|
with open('templates/ignored_modules.txt', 'r') as f:
|
|
|
|
lines = f.read().splitlines()
|
|
|
|
for line in lines:
|
|
|
|
toIgnoreModule.add(line)
|
|
|
|
|
|
|
|
except IOError:
|
2019-08-05 16:31:03 +02:00
|
|
|
pass
|
2017-04-25 12:18:08 +02:00
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
# Dynamically import routes and functions from modules
|
|
|
|
# Also, prepare header.html
|
|
|
|
to_add_to_header_dico = {}
|
2019-07-30 13:49:21 +02:00
|
|
|
for root, dirs, files in os.walk(os.path.join(Flask_dir, 'modules')):
|
2017-04-19 15:14:20 +02:00
|
|
|
sys.path.append(join(root))
|
2017-04-25 12:18:08 +02:00
|
|
|
|
|
|
|
# Ignore the module
|
|
|
|
curr_dir = root.split('/')[1]
|
|
|
|
if curr_dir in toIgnoreModule:
|
|
|
|
continue
|
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
for name in files:
|
|
|
|
module_name = root.split('/')[-2]
|
|
|
|
if name.startswith('Flask_') and name.endswith('.py'):
|
|
|
|
if name == 'Flask_config.py':
|
|
|
|
continue
|
|
|
|
name = name.strip('.py')
|
|
|
|
importlib.import_module(name)
|
|
|
|
elif name == 'header_{}.html'.format(module_name):
|
|
|
|
with open(join(root, name), 'r') as f:
|
|
|
|
to_add_to_header_dico[module_name] = f.read()
|
2014-08-26 17:33:28 +02:00
|
|
|
|
2022-10-25 16:25:19 +02:00
|
|
|
# create header.html
|
2019-07-30 13:49:21 +02:00
|
|
|
with open(os.path.join(Flask_dir, 'templates', 'header_base.html'), 'r') as f:
|
2017-04-19 15:14:20 +02:00
|
|
|
complete_header = f.read()
|
|
|
|
modified_header = complete_header
|
|
|
|
|
2022-10-25 16:25:19 +02:00
|
|
|
# Add the header in the supplied order
|
2018-04-17 16:06:32 +02:00
|
|
|
for module_name, txt in list(to_add_to_header_dico.items()):
|
2017-04-19 15:14:20 +02:00
|
|
|
to_replace = '<!--{}-->'.format(module_name)
|
|
|
|
if to_replace in complete_header:
|
|
|
|
modified_header = modified_header.replace(to_replace, txt)
|
|
|
|
del to_add_to_header_dico[module_name]
|
|
|
|
|
2022-10-25 16:25:19 +02:00
|
|
|
# Add the header for no-supplied order
|
2017-04-19 15:14:20 +02:00
|
|
|
to_add_to_header = []
|
|
|
|
for module_name, txt in to_add_to_header_dico.items():
|
|
|
|
to_add_to_header.append(txt)
|
|
|
|
|
|
|
|
modified_header = modified_header.replace('<!--insert here-->', '\n'.join(to_add_to_header))
|
|
|
|
|
2022-10-25 16:25:19 +02:00
|
|
|
# Write the header.html file
|
2019-07-30 13:49:21 +02:00
|
|
|
with open(os.path.join(Flask_dir, 'templates', 'header.html'), 'w') as f:
|
2017-04-19 15:14:20 +02:00
|
|
|
f.write(modified_header)
|
|
|
|
|
|
|
|
# ========= JINJA2 FUNCTIONS ========
|
2016-07-05 16:53:03 +02:00
|
|
|
def list_len(s):
|
|
|
|
return len(s)
|
|
|
|
app.jinja_env.filters['list_len'] = list_len
|
|
|
|
|
2016-08-04 11:55:38 +02:00
|
|
|
|
2016-08-09 11:59:36 +02:00
|
|
|
# ========= CACHE CONTROL ========
|
|
|
|
@app.after_request
|
|
|
|
def add_header(response):
|
|
|
|
"""
|
|
|
|
Add headers to both force latest IE rendering engine or Chrome Frame,
|
|
|
|
and also to cache the rendered page for 10 minutes.
|
|
|
|
"""
|
|
|
|
response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1'
|
2019-09-03 11:58:34 +02:00
|
|
|
if 'Cache-Control' not in response.headers:
|
|
|
|
response.headers['Cache-Control'] = 'private, max-age=0'
|
2016-08-09 11:59:36 +02:00
|
|
|
return response
|
2016-07-21 13:44:22 +02:00
|
|
|
|
2024-07-01 14:54:19 +02:00
|
|
|
# ========== USERS ============
|
|
|
|
@app.before_request
|
|
|
|
def before_request():
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
current_user.update_last_seen()
|
|
|
|
|
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
# ========== ROUTES ============
|
2019-06-06 21:27:13 +02:00
|
|
|
|
2019-09-25 08:58:18 +02:00
|
|
|
#@app.route('/endpoints')
|
|
|
|
#def endpoints():
|
|
|
|
# for rule in app.url_map.iter_rules():
|
|
|
|
# str_endpoint = str(rule)
|
|
|
|
# if len(str_endpoint)>5:
|
|
|
|
# if str_endpoint[0:5]=='/api/': ## add baseUrl ???
|
|
|
|
# print(str_endpoint)
|
|
|
|
# #print(rule.endpoint) #internal endpoint name
|
|
|
|
# #print(rule.methods)
|
|
|
|
# return 'ok'
|
2017-04-19 15:14:20 +02:00
|
|
|
|
2019-06-20 10:11:23 +02:00
|
|
|
# ========== ERROR HANDLER ============
|
|
|
|
|
2019-07-31 13:24:43 +02:00
|
|
|
@app.errorhandler(405)
|
|
|
|
def _handle_client_error(e):
|
2019-10-28 13:48:43 +01:00
|
|
|
if request.path.startswith('/api/'): ## # TODO: add baseUrl
|
2019-08-06 11:29:12 +02:00
|
|
|
res_dict = {"status": "error", "reason": "Method Not Allowed: The method is not allowed for the requested URL"}
|
|
|
|
anchor_id = request.path[8:]
|
|
|
|
anchor_id = anchor_id.replace('/', '_')
|
2020-04-20 17:50:40 +02:00
|
|
|
api_doc_url = 'https://github.com/ail-project/ail-framework/tree/master/doc#{}'.format(anchor_id)
|
2019-08-06 11:29:12 +02:00
|
|
|
res_dict['documentation'] = api_doc_url
|
2024-02-29 14:56:45 +01:00
|
|
|
return Response(json.dumps(res_dict) + '\n', mimetype='application/json'), 405
|
2019-07-31 13:24:43 +02:00
|
|
|
else:
|
|
|
|
return e
|
|
|
|
|
2024-09-06 14:32:25 +02:00
|
|
|
@app.errorhandler(403)
|
|
|
|
def error_page_not_found(e):
|
|
|
|
if request.path.startswith('/api/'): ## # TODO: add baseUrl
|
|
|
|
return Response(json.dumps({"status": "error", "reason": "403 Access Denied"}) + '\n', mimetype='application/json'), 403
|
|
|
|
else:
|
|
|
|
# avoid endpoint enumeration
|
|
|
|
return page_forbidden(e)
|
|
|
|
|
2019-06-20 10:11:23 +02:00
|
|
|
@app.errorhandler(404)
|
2019-07-31 13:24:43 +02:00
|
|
|
def error_page_not_found(e):
|
2019-09-25 08:58:18 +02:00
|
|
|
if request.path.startswith('/api/'): ## # TODO: add baseUrl
|
2024-02-29 14:56:45 +01:00
|
|
|
return Response(json.dumps({"status": "error", "reason": "404 Not Found"}) + '\n', mimetype='application/json'), 404
|
2019-07-31 13:24:43 +02:00
|
|
|
else:
|
|
|
|
# avoid endpoint enumeration
|
|
|
|
return page_not_found(e)
|
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
@app.errorhandler(500)
|
|
|
|
def _handle_client_error(e):
|
|
|
|
if request.path.startswith('/api/'):
|
|
|
|
return Response(json.dumps({"status": "error", "reason": "Server Error"}) + '\n', mimetype='application/json'), 500
|
|
|
|
else:
|
|
|
|
return e
|
|
|
|
|
2024-09-06 14:32:25 +02:00
|
|
|
@login_required
|
|
|
|
def page_forbidden(e):
|
|
|
|
return render_template("error/403.html"), 403
|
|
|
|
|
2019-06-20 10:11:23 +02:00
|
|
|
@login_required
|
|
|
|
def page_not_found(e):
|
2019-06-20 10:56:31 +02:00
|
|
|
# avoid endpoint enumeration
|
2019-06-20 10:11:23 +02:00
|
|
|
return render_template('error/404.html'), 404
|
2017-04-19 15:14:20 +02:00
|
|
|
|
2022-10-25 16:25:19 +02:00
|
|
|
|
2018-05-23 16:58:56 +02:00
|
|
|
# ========== INITIAL taxonomies ============
|
2019-11-18 09:46:15 +01:00
|
|
|
default_taxonomies = ["infoleak", "gdpr", "fpf", "dark-web"]
|
|
|
|
# enable default taxonomies
|
2022-11-22 10:47:15 +01:00
|
|
|
for taxonomy in default_taxonomies:
|
|
|
|
Tag.enable_taxonomy_tags(taxonomy)
|
2018-05-23 16:58:56 +02:00
|
|
|
|
2024-09-16 15:30:12 +02:00
|
|
|
# ========== GIT Cache ============
|
|
|
|
clear_git_meta_cache()
|
|
|
|
|
2024-02-26 15:35:48 +01:00
|
|
|
# rrrr = [str(p) for p in app.url_map.iter_rules()]
|
|
|
|
# for p in rrrr:
|
|
|
|
# print(p)
|
|
|
|
|
2016-12-09 08:46:37 +01:00
|
|
|
# ============ MAIN ============
|
2016-07-08 10:19:24 +02:00
|
|
|
|
2014-08-06 11:43:40 +02:00
|
|
|
if __name__ == "__main__":
|
2020-07-06 17:13:36 +02:00
|
|
|
app.run(host=host, port=FLASK_PORT, threaded=True, ssl_context=ssl_context)
|