QOL patch

added easter egg on home page
reformatted homepage
added IG loading animation and console output to the animation
This commit is contained in:
Joshua Hare 2025-04-30 22:13:14 +10:00
parent cfe4f3dd4f
commit 89ef0de20c
28 changed files with 40649 additions and 428 deletions

55
app.py
View File

@ -3,6 +3,7 @@ import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
import datetime
import shutil
import queue
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, Response, current_app, session, send_file, make_response
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
@ -17,7 +18,7 @@ import logging
import requests
import re
import services # Restore full module import
from services import services_bp, construct_tgz_filename, parse_package_filename # Keep Blueprint import
from services import services_bp, construct_tgz_filename, parse_package_filename, import_package_and_dependencies # Keep Blueprint import
from forms import IgImportForm, ValidationForm, FSHConverterForm, TestDataUploadForm
from wtforms import SubmitField
#from models import ProcessedIg
@ -53,6 +54,23 @@ class CustomFormDataParser(FormDataParser):
super().__init__(*args, max_form_parts=2000, **kwargs) # Example: Allow 2000 parts
# --- END NEW ---
# Custom logging handler to capture INFO logs from services module
log_queue = queue.Queue()
class StreamLogHandler(logging.Handler):
def __init__(self):
super().__init__(level=logging.INFO)
self.formatter = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
def emit(self, record):
if record.name == 'services' and record.levelno == logging.INFO:
msg = self.format(record)
log_queue.put(msg)
# Add custom handler to services logger
services_logger = logging.getLogger('services')
stream_handler = StreamLogHandler()
services_logger.addHandler(stream_handler)
# <<< ADD THIS CONTEXT PROCESSOR >>>
@app.context_processor
def inject_app_mode():
@ -182,12 +200,19 @@ def index():
@app.route('/import-ig', methods=['GET', 'POST'])
def import_ig():
form = IgImportForm()
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
if form.validate_on_submit():
name = form.package_name.data
version = form.package_version.data
dependency_mode = form.dependency_mode.data
# Clear log queue for this request
while not log_queue.empty():
log_queue.get()
try:
result = services.import_package_and_dependencies(name, version, dependency_mode=dependency_mode)
result = import_package_and_dependencies(name, version, dependency_mode=dependency_mode)
if result['errors'] and not result['downloaded']:
error_msg = result['errors'][0]
simplified_msg = error_msg
@ -199,6 +224,8 @@ def import_ig():
simplified_msg = "Could not connect to the FHIR package registry."
flash(f"Failed to import {name}#{version}: {simplified_msg}", "error")
logger.error(f"Import failed critically for {name}#{version}: {error_msg}")
if is_ajax:
return jsonify({"status": "error", "message": simplified_msg}), 400
else:
if result['errors']:
flash(f"Partially imported {name}#{version} with errors during dependency processing. Check logs.", "warning")
@ -206,16 +233,40 @@ def import_ig():
logger.warning(f"Import warning for {name}#{version}: {err}")
else:
flash(f"Successfully downloaded {name}#{version} and dependencies! Mode: {dependency_mode}", "success")
if is_ajax:
return jsonify({"status": "success", "message": f"Imported {name}#{version}", "redirect": url_for('view_igs')})
return redirect(url_for('view_igs'))
except Exception as e:
logger.error(f"Unexpected error during IG import: {str(e)}", exc_info=True)
flash(f"An unexpected error occurred downloading the IG: {str(e)}", "error")
if is_ajax:
return jsonify({"status": "error", "message": str(e)}), 500
else:
for field, errors in form.errors.items():
for error in errors:
flash(f"Error in {getattr(form, field).label.text}: {error}", "danger")
if is_ajax:
return jsonify({"status": "error", "message": "Form validation failed", "errors": form.errors}), 400
return render_template('import_ig.html', form=form, site_name='FHIRFLARE IG Toolkit', now=datetime.datetime.now())
@app.route('/stream-import-logs')
def stream_import_logs():
logger.debug("Accessing stream-import-logs endpoint")
def generate():
while True:
try:
msg = log_queue.get(timeout=1)
if 'Downloaded' in msg or 'Processing' in msg:
clean_msg = msg.replace('INFO:services:', '').strip()
yield f"data: {clean_msg}\n\n"
except queue.Empty:
yield ": keepalive\n\n"
except GeneratorExit:
break
yield "data: [DONE]\n\n"
return Response(generate(), mimetype='text/event-stream')
@app.route('/view-igs')
def view_igs():
form = FlaskForm()

Binary file not shown.

View File

@ -18,5 +18,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:44.307543+00:00"
"timestamp": "2025-04-30T10:56:12.567847+00:00"
}

View File

@ -30,5 +30,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:01.788251+00:00"
"timestamp": "2025-04-30T10:55:42.789700+00:00"
}

View File

@ -5,5 +5,5 @@
"imported_dependencies": [],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:30.920844+00:00"
"timestamp": "2025-04-30T10:55:57.375318+00:00"
}

View File

@ -10,5 +10,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:40.785525+00:00"
"timestamp": "2025-04-30T10:56:08.781015+00:00"
}

View File

@ -18,5 +18,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:48.451981+00:00"
"timestamp": "2025-04-30T10:56:16.763567+00:00"
}

View File

@ -10,5 +10,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:54.450933+00:00"
"timestamp": "2025-04-30T10:56:23.123899+00:00"
}

View File

@ -14,5 +14,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:46.089555+00:00"
"timestamp": "2025-04-30T10:56:14.378183+00:00"
}

View File

@ -10,5 +10,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:52.965617+00:00"
"timestamp": "2025-04-30T10:56:21.577007+00:00"
}

View File

@ -10,5 +10,5 @@
],
"complies_with_profiles": [],
"imposed_profiles": [],
"timestamp": "2025-04-26T06:09:36.852059+00:00"
"timestamp": "2025-04-30T10:56:04.853495+00:00"
}

View File

@ -1,6 +1,6 @@
#FileLock
#Sat Apr 26 12:19:30 UTC 2025
server=172.18.0.2\:38385
hostName=18113a0f20a7
#Wed Apr 30 12:04:24 UTC 2025
server=172.19.0.2\:43507
hostName=999cce957cc1
method=file
id=1967209d290a29cebe723a4809b58c3cf6c80af4585
id=196869576a9d4c77e61cf01b462a28e4b182a3e00bd

Binary file not shown.

File diff suppressed because it is too large Load Diff

92
logs/flask.log Normal file
View File

@ -0,0 +1,92 @@
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off
* Serving Flask app 'app'
* Debug mode: off

24648
logs/flask_err.log Normal file

File diff suppressed because it is too large Load Diff

607
logs/supervisord.log Normal file
View File

@ -0,0 +1,607 @@
2025-04-24 12:53:20,569 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 12:53:20,575 INFO supervisord started with pid 1
2025-04-24 12:53:21,596 INFO spawned: 'flask' with pid 7
2025-04-24 12:53:21,602 INFO spawned: 'tomcat' with pid 8
2025-04-24 12:53:23,413 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:53:24,422 INFO spawned: 'flask' with pid 39
2025-04-24 12:53:25,576 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:53:27,589 INFO spawned: 'flask' with pid 47
2025-04-24 12:53:28,513 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:53:31,542 INFO spawned: 'flask' with pid 48
2025-04-24 12:53:32,775 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:53:33,780 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-24 12:53:52,023 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 12:55:05,132 WARN received SIGTERM indicating exit request
2025-04-24 12:55:05,142 INFO waiting for tomcat to die
2025-04-24 12:55:07,490 WARN stopped: tomcat (exit status 143)
2025-04-24 12:55:19,591 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 12:55:19,604 INFO supervisord started with pid 1
2025-04-24 12:55:20,610 INFO spawned: 'flask' with pid 7
2025-04-24 12:55:20,621 INFO spawned: 'tomcat' with pid 8
2025-04-24 12:55:22,555 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:55:23,565 INFO spawned: 'flask' with pid 39
2025-04-24 12:55:24,225 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:55:26,239 INFO spawned: 'flask' with pid 47
2025-04-24 12:55:27,490 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:55:30,500 INFO spawned: 'flask' with pid 48
2025-04-24 12:55:31,273 WARN exited: flask (exit status 1; not expected)
2025-04-24 12:55:32,275 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-24 12:55:50,730 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 12:59:49,405 WARN received SIGTERM indicating exit request
2025-04-24 12:59:49,408 INFO waiting for tomcat to die
2025-04-24 12:59:52,896 INFO waiting for tomcat to die
2025-04-24 12:59:54,202 WARN stopped: tomcat (exit status 143)
2025-04-24 13:00:22,084 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 13:00:22,099 INFO supervisord started with pid 1
2025-04-24 13:00:23,143 INFO spawned: 'flask' with pid 7
2025-04-24 13:00:23,154 INFO spawned: 'tomcat' with pid 8
2025-04-24 13:00:26,260 WARN exited: flask (exit status 1; not expected)
2025-04-24 13:00:27,270 INFO spawned: 'flask' with pid 39
2025-04-24 13:00:28,548 WARN exited: flask (exit status 1; not expected)
2025-04-24 13:00:30,696 INFO spawned: 'flask' with pid 43
2025-04-24 13:00:33,861 WARN exited: flask (exit status 1; not expected)
2025-04-24 13:00:36,875 INFO spawned: 'flask' with pid 49
2025-04-24 13:00:38,494 WARN exited: flask (exit status 1; not expected)
2025-04-24 13:00:39,499 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-24 13:00:53,383 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 13:01:56,879 WARN received SIGTERM indicating exit request
2025-04-24 13:01:56,880 INFO waiting for tomcat to die
2025-04-24 13:01:59,200 WARN stopped: tomcat (exit status 143)
2025-04-24 13:02:26,588 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 13:02:26,602 INFO supervisord started with pid 1
2025-04-24 13:02:27,616 INFO spawned: 'flask' with pid 7
2025-04-24 13:02:27,625 INFO spawned: 'tomcat' with pid 8
2025-04-24 13:02:37,957 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-24 13:02:58,407 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 13:11:42,994 WARN received SIGTERM indicating exit request
2025-04-24 13:11:43,001 INFO waiting for flask, tomcat to die
2025-04-24 13:11:46,095 INFO waiting for flask, tomcat to die
2025-04-24 13:11:47,585 WARN stopped: tomcat (exit status 143)
2025-04-24 13:11:48,632 WARN stopped: flask (terminated by SIGTERM)
2025-04-24 13:12:07,464 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 13:12:07,475 INFO supervisord started with pid 1
2025-04-24 13:12:08,488 INFO spawned: 'flask' with pid 7
2025-04-24 13:12:08,496 INFO spawned: 'tomcat' with pid 8
2025-04-24 13:12:18,696 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-24 13:12:39,092 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 13:14:59,175 WARN received SIGTERM indicating exit request
2025-04-24 13:14:59,178 INFO waiting for flask, tomcat to die
2025-04-24 13:15:00,617 WARN received SIGTERM indicating exit request
2025-04-24 13:15:02,344 WARN stopped: tomcat (exit status 143)
2025-04-24 13:15:02,347 INFO waiting for flask to die
2025-04-24 13:15:03,354 WARN stopped: flask (terminated by SIGTERM)
2025-04-24 13:15:28,023 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 13:15:28,035 INFO supervisord started with pid 1
2025-04-24 13:15:29,049 INFO spawned: 'flask' with pid 7
2025-04-24 13:15:29,085 INFO spawned: 'tomcat' with pid 8
2025-04-24 13:15:39,240 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-24 13:15:59,284 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 13:17:28,582 WARN received SIGTERM indicating exit request
2025-04-24 13:17:28,583 INFO waiting for flask, tomcat to die
2025-04-24 13:17:29,955 WARN stopped: tomcat (exit status 143)
2025-04-24 13:17:30,969 WARN stopped: flask (terminated by SIGTERM)
2025-04-24 13:17:48,425 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 13:17:48,436 INFO supervisord started with pid 1
2025-04-24 13:17:49,445 INFO spawned: 'flask' with pid 7
2025-04-24 13:17:49,452 INFO spawned: 'tomcat' with pid 8
2025-04-24 13:17:59,706 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-24 13:18:19,818 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-24 13:25:26,199 WARN received SIGTERM indicating exit request
2025-04-24 13:25:26,212 INFO waiting for flask, tomcat to die
2025-04-24 13:25:27,654 WARN stopped: tomcat (exit status 143)
2025-04-24 13:25:28,678 WARN stopped: flask (terminated by SIGTERM)
2025-04-24 13:25:52,381 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-24 13:25:52,388 INFO supervisord started with pid 1
2025-04-24 13:25:53,393 INFO spawned: 'flask' with pid 7
2025-04-24 13:25:53,402 INFO spawned: 'tomcat' with pid 8
2025-04-24 13:26:03,915 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-24 13:26:23,694 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 10:58:58,306 WARN received SIGTERM indicating exit request
2025-04-25 10:58:58,338 INFO waiting for flask, tomcat to die
2025-04-25 10:59:01,538 INFO waiting for flask, tomcat to die
2025-04-25 10:59:04,692 WARN stopped: tomcat (exit status 143)
2025-04-25 10:59:04,740 INFO waiting for flask to die
2025-04-25 10:59:05,802 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 10:59:31,502 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 10:59:31,512 INFO supervisord started with pid 1
2025-04-25 10:59:32,532 INFO spawned: 'flask' with pid 7
2025-04-25 10:59:32,538 INFO spawned: 'tomcat' with pid 8
2025-04-25 10:59:42,663 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:00:03,350 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:13:49,313 WARN received SIGTERM indicating exit request
2025-04-25 11:13:49,316 INFO waiting for flask, tomcat to die
2025-04-25 11:13:52,578 WARN stopped: tomcat (exit status 143)
2025-04-25 11:13:52,582 INFO waiting for flask to die
2025-04-25 11:13:53,593 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:14:17,467 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:14:17,478 INFO supervisord started with pid 1
2025-04-25 11:14:18,487 INFO spawned: 'flask' with pid 7
2025-04-25 11:14:18,494 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:14:20,261 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:14:21,580 INFO spawned: 'flask' with pid 39
2025-04-25 11:14:23,183 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:14:25,231 INFO spawned: 'flask' with pid 48
2025-04-25 11:14:27,998 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:14:31,012 INFO spawned: 'flask' with pid 51
2025-04-25 11:14:32,140 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:14:33,144 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-25 11:14:38,150 WARN received SIGTERM indicating exit request
2025-04-25 11:14:38,151 INFO waiting for tomcat to die
2025-04-25 11:14:39,191 WARN stopped: tomcat (exit status 143)
2025-04-25 11:16:55,759 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:16:55,767 INFO supervisord started with pid 1
2025-04-25 11:16:56,782 INFO spawned: 'flask' with pid 7
2025-04-25 11:16:56,787 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:17:07,143 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:17:26,965 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:22:46,985 WARN received SIGTERM indicating exit request
2025-04-25 11:22:46,986 INFO waiting for flask, tomcat to die
2025-04-25 11:22:48,031 WARN stopped: tomcat (exit status 143)
2025-04-25 11:22:49,048 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:23:06,664 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:23:06,671 INFO supervisord started with pid 1
2025-04-25 11:23:07,675 INFO spawned: 'flask' with pid 7
2025-04-25 11:23:07,686 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:23:17,936 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:23:38,271 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:27:35,932 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:27:35,942 INFO supervisord started with pid 1
2025-04-25 11:27:36,948 INFO spawned: 'flask' with pid 7
2025-04-25 11:27:36,953 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:27:47,019 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:28:07,048 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:31:23,895 WARN received SIGTERM indicating exit request
2025-04-25 11:31:23,896 INFO waiting for flask, tomcat to die
2025-04-25 11:31:25,050 WARN stopped: tomcat (exit status 143)
2025-04-25 11:31:25,054 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:39:03,546 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:39:03,552 INFO supervisord started with pid 1
2025-04-25 11:39:04,569 INFO spawned: 'flask' with pid 7
2025-04-25 11:39:04,572 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:39:14,792 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:39:34,800 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:41:11,436 WARN received SIGTERM indicating exit request
2025-04-25 11:41:11,437 INFO waiting for flask, tomcat to die
2025-04-25 11:41:12,528 WARN stopped: tomcat (exit status 143)
2025-04-25 11:41:12,533 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:41:34,275 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:41:34,285 INFO supervisord started with pid 1
2025-04-25 11:41:35,319 INFO spawned: 'flask' with pid 7
2025-04-25 11:41:35,324 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:41:46,071 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:41:47,900 WARN received SIGTERM indicating exit request
2025-04-25 11:41:47,901 INFO waiting for flask, tomcat to die
2025-04-25 11:41:48,935 WARN stopped: tomcat (exit status 143)
2025-04-25 11:41:48,940 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:42:03,154 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:42:03,159 INFO supervisord started with pid 1
2025-04-25 11:42:04,166 INFO spawned: 'flask' with pid 7
2025-04-25 11:42:04,170 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:42:14,412 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:42:34,252 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:44:19,667 WARN received SIGTERM indicating exit request
2025-04-25 11:44:19,668 INFO waiting for flask, tomcat to die
2025-04-25 11:44:20,808 WARN stopped: tomcat (exit status 143)
2025-04-25 11:44:20,812 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:44:40,799 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:44:40,807 INFO supervisord started with pid 1
2025-04-25 11:44:41,813 INFO spawned: 'flask' with pid 7
2025-04-25 11:44:41,824 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:44:43,064 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:44:44,073 INFO spawned: 'flask' with pid 48
2025-04-25 11:44:45,296 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:44:47,306 INFO spawned: 'flask' with pid 53
2025-04-25 11:44:48,167 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:44:51,178 INFO spawned: 'flask' with pid 55
2025-04-25 11:44:51,934 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:44:52,937 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-25 11:45:11,860 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:47:25,513 WARN received SIGTERM indicating exit request
2025-04-25 11:47:25,513 INFO waiting for tomcat to die
2025-04-25 11:47:26,630 WARN stopped: tomcat (exit status 143)
2025-04-25 11:48:19,775 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:48:19,782 INFO supervisord started with pid 1
2025-04-25 11:48:20,786 INFO spawned: 'flask' with pid 7
2025-04-25 11:48:20,795 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:48:21,467 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:48:22,508 INFO spawned: 'flask' with pid 42
2025-04-25 11:48:23,009 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:48:25,019 INFO spawned: 'flask' with pid 47
2025-04-25 11:48:25,381 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:48:28,401 INFO spawned: 'flask' with pid 59
2025-04-25 11:48:29,045 WARN exited: flask (exit status 1; not expected)
2025-04-25 11:48:30,000 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-25 11:48:51,365 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:53:09,317 WARN received SIGTERM indicating exit request
2025-04-25 11:53:09,319 INFO waiting for tomcat to die
2025-04-25 11:53:11,346 WARN stopped: tomcat (exit status 143)
2025-04-25 11:53:49,588 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:53:49,595 INFO supervisord started with pid 1
2025-04-25 11:53:50,604 INFO spawned: 'flask' with pid 7
2025-04-25 11:53:50,608 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:54:00,901 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:54:20,619 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:55:01,514 WARN received SIGTERM indicating exit request
2025-04-25 11:55:01,515 INFO waiting for flask, tomcat to die
2025-04-25 11:55:02,357 WARN stopped: tomcat (exit status 143)
2025-04-25 11:55:02,361 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:56:05,943 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:56:05,951 INFO supervisord started with pid 1
2025-04-25 11:56:06,957 INFO spawned: 'flask' with pid 7
2025-04-25 11:56:06,961 INFO spawned: 'tomcat' with pid 8
2025-04-25 11:56:17,835 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 11:56:37,396 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 11:59:16,851 WARN received SIGTERM indicating exit request
2025-04-25 11:59:16,852 INFO waiting for flask, tomcat to die
2025-04-25 11:59:18,045 WARN stopped: tomcat (exit status 143)
2025-04-25 11:59:18,051 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 11:59:51,749 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 11:59:51,757 INFO supervisord started with pid 1
2025-04-25 11:59:52,766 INFO spawned: 'flask' with pid 7
2025-04-25 11:59:52,772 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:00:01,958 WARN received SIGTERM indicating exit request
2025-04-25 12:00:01,960 INFO waiting for flask, tomcat to die
2025-04-25 12:00:02,986 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:00:02,987 WARN stopped: tomcat (exit status 143)
2025-04-25 12:00:02,994 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 12:00:25,888 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:00:25,893 INFO supervisord started with pid 1
2025-04-25 12:00:26,906 INFO spawned: 'flask' with pid 8
2025-04-25 12:00:26,909 INFO spawned: 'tomcat' with pid 9
2025-04-25 12:00:37,092 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:00:57,539 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 12:12:29,121 WARN received SIGTERM indicating exit request
2025-04-25 12:12:29,129 INFO waiting for flask, tomcat to die
2025-04-25 12:12:32,499 WARN stopped: tomcat (exit status 143)
2025-04-25 12:12:32,503 INFO waiting for flask to die
2025-04-25 12:12:33,510 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 12:13:13,667 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:13:13,674 INFO supervisord started with pid 1
2025-04-25 12:13:14,680 INFO spawned: 'flask' with pid 7
2025-04-25 12:13:14,685 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:13:16,181 WARN exited: flask (exit status 1; not expected)
2025-04-25 12:13:17,200 INFO spawned: 'flask' with pid 40
2025-04-25 12:13:18,463 WARN exited: flask (exit status 1; not expected)
2025-04-25 12:13:20,487 INFO spawned: 'flask' with pid 48
2025-04-25 12:13:21,529 WARN exited: flask (exit status 1; not expected)
2025-04-25 12:13:24,540 INFO spawned: 'flask' with pid 50
2025-04-25 12:13:25,478 WARN exited: flask (exit status 1; not expected)
2025-04-25 12:13:26,481 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-25 12:13:44,834 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 12:23:53,921 WARN received SIGTERM indicating exit request
2025-04-25 12:23:53,934 INFO waiting for tomcat to die
2025-04-25 12:23:57,299 WARN stopped: tomcat (exit status 143)
2025-04-25 12:24:32,364 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:24:32,376 INFO supervisord started with pid 1
2025-04-25 12:24:33,384 INFO spawned: 'flask' with pid 7
2025-04-25 12:24:33,390 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:24:44,218 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:25:04,301 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 12:31:25,175 WARN received SIGTERM indicating exit request
2025-04-25 12:31:25,177 INFO waiting for flask, tomcat to die
2025-04-25 12:31:28,374 WARN stopped: tomcat (exit status 143)
2025-04-25 12:31:28,379 INFO waiting for flask to die
2025-04-25 12:31:29,389 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 12:31:49,504 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:31:49,512 INFO supervisord started with pid 1
2025-04-25 12:31:50,518 INFO spawned: 'flask' with pid 7
2025-04-25 12:31:50,523 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:32:00,852 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:32:21,057 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 12:49:44,415 WARN received SIGTERM indicating exit request
2025-04-25 12:49:44,432 INFO waiting for flask, tomcat to die
2025-04-25 12:49:48,411 INFO waiting for flask, tomcat to die
2025-04-25 12:49:49,464 WARN stopped: tomcat (exit status 143)
2025-04-25 12:49:50,493 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 12:50:14,025 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:50:14,034 INFO supervisord started with pid 1
2025-04-25 12:50:15,064 INFO spawned: 'flask' with pid 7
2025-04-25 12:50:15,086 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:50:25,385 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:50:30,391 WARN received SIGTERM indicating exit request
2025-04-25 12:50:30,392 INFO waiting for flask, tomcat to die
2025-04-25 12:50:31,427 WARN stopped: tomcat (exit status 143)
2025-04-25 12:50:31,435 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 12:52:24,814 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:52:24,825 INFO supervisord started with pid 1
2025-04-25 12:52:25,833 INFO spawned: 'flask' with pid 7
2025-04-25 12:52:25,839 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:52:35,983 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:52:56,005 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 12:56:07,983 WARN received SIGTERM indicating exit request
2025-04-25 12:56:07,994 INFO waiting for flask, tomcat to die
2025-04-25 12:56:11,273 WARN stopped: tomcat (exit status 143)
2025-04-25 12:56:11,314 INFO waiting for flask to die
2025-04-25 12:56:12,403 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 12:56:48,029 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 12:56:48,037 INFO supervisord started with pid 1
2025-04-25 12:56:49,049 INFO spawned: 'flask' with pid 7
2025-04-25 12:56:49,061 INFO spawned: 'tomcat' with pid 8
2025-04-25 12:56:59,229 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 12:57:19,563 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 13:01:49,576 WARN received SIGTERM indicating exit request
2025-04-25 13:01:49,585 INFO waiting for flask, tomcat to die
2025-04-25 13:01:50,897 WARN stopped: tomcat (exit status 143)
2025-04-25 13:01:51,917 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 13:38:31,018 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 13:38:31,042 INFO supervisord started with pid 1
2025-04-25 13:38:32,075 INFO spawned: 'flask' with pid 7
2025-04-25 13:38:32,125 INFO spawned: 'tomcat' with pid 8
2025-04-25 13:38:42,115 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 13:39:02,430 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 21:37:13,481 WARN received SIGTERM indicating exit request
2025-04-25 21:37:13,512 INFO waiting for flask, tomcat to die
2025-04-25 21:37:16,663 INFO waiting for flask, tomcat to die
2025-04-25 21:37:19,722 INFO waiting for flask, tomcat to die
2025-04-25 21:37:22,844 INFO waiting for flask, tomcat to die
2025-04-25 21:37:57,094 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 21:37:57,107 INFO supervisord started with pid 1
2025-04-25 21:37:58,118 INFO spawned: 'flask' with pid 7
2025-04-25 21:37:58,127 INFO spawned: 'tomcat' with pid 8
2025-04-25 21:38:02,035 WARN exited: flask (exit status 1; not expected)
2025-04-25 21:38:03,047 INFO spawned: 'flask' with pid 41
2025-04-25 21:38:06,099 WARN exited: flask (exit status 1; not expected)
2025-04-25 21:38:08,126 INFO spawned: 'flask' with pid 50
2025-04-25 21:38:11,124 WARN exited: flask (exit status 1; not expected)
2025-04-25 21:38:14,136 INFO spawned: 'flask' with pid 54
2025-04-25 21:38:15,907 WARN exited: flask (exit status 1; not expected)
2025-04-25 21:38:16,911 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-25 21:38:28,264 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 21:44:55,684 WARN received SIGTERM indicating exit request
2025-04-25 21:44:55,709 INFO waiting for tomcat to die
2025-04-25 21:44:58,801 INFO waiting for tomcat to die
2025-04-25 21:44:59,813 WARN stopped: tomcat (exit status 143)
2025-04-25 21:45:23,313 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 21:45:23,323 INFO supervisord started with pid 1
2025-04-25 21:45:24,329 INFO spawned: 'flask' with pid 7
2025-04-25 21:45:24,333 INFO spawned: 'tomcat' with pid 8
2025-04-25 21:45:34,334 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 21:45:54,338 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 21:46:48,550 WARN received SIGTERM indicating exit request
2025-04-25 21:46:48,551 INFO waiting for flask, tomcat to die
2025-04-25 21:46:49,793 WARN stopped: tomcat (exit status 143)
2025-04-25 21:46:49,802 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 21:47:10,753 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 21:47:10,763 INFO supervisord started with pid 1
2025-04-25 21:47:11,769 INFO spawned: 'flask' with pid 8
2025-04-25 21:47:11,777 INFO spawned: 'tomcat' with pid 9
2025-04-25 21:47:21,957 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 21:47:41,876 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 21:53:21,452 WARN received SIGTERM indicating exit request
2025-04-25 21:53:21,476 INFO waiting for flask, tomcat to die
2025-04-25 21:53:24,100 WARN stopped: tomcat (exit status 143)
2025-04-25 21:53:25,125 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 21:53:38,324 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 21:53:38,334 INFO supervisord started with pid 1
2025-04-25 21:53:39,340 INFO spawned: 'flask' with pid 7
2025-04-25 21:53:39,346 INFO spawned: 'tomcat' with pid 8
2025-04-25 22:44:33,924 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 22:44:33,928 INFO supervisord started with pid 1
2025-04-25 22:44:34,941 INFO spawned: 'flask' with pid 7
2025-04-25 22:44:34,946 INFO spawned: 'tomcat' with pid 8
2025-04-25 22:44:44,961 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 22:45:05,700 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-25 22:48:32,580 WARN received SIGTERM indicating exit request
2025-04-25 22:48:32,582 INFO waiting for flask, tomcat to die
2025-04-25 22:48:35,957 WARN stopped: tomcat (exit status 143)
2025-04-25 22:48:35,961 INFO waiting for flask to die
2025-04-25 22:48:36,966 WARN stopped: flask (terminated by SIGTERM)
2025-04-25 22:48:46,205 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-25 22:48:46,215 INFO supervisord started with pid 1
2025-04-25 22:48:47,223 INFO spawned: 'flask' with pid 8
2025-04-25 22:48:47,229 INFO spawned: 'tomcat' with pid 9
2025-04-25 22:48:58,098 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-25 22:49:17,502 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 02:42:59,958 WARN received SIGTERM indicating exit request
2025-04-26 02:42:59,974 INFO waiting for flask, tomcat to die
2025-04-26 02:43:02,041 WARN stopped: tomcat (exit status 143)
2025-04-26 02:43:03,053 WARN stopped: flask (terminated by SIGTERM)
2025-04-26 02:43:12,563 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 02:43:12,567 INFO supervisord started with pid 1
2025-04-26 02:43:13,573 INFO spawned: 'flask' with pid 7
2025-04-26 02:43:13,588 INFO spawned: 'tomcat' with pid 8
2025-04-26 02:43:24,337 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 02:43:44,046 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 02:48:20,402 WARN received SIGTERM indicating exit request
2025-04-26 02:48:20,403 INFO waiting for flask, tomcat to die
2025-04-26 02:48:21,337 WARN stopped: tomcat (exit status 143)
2025-04-26 02:48:21,343 WARN stopped: flask (terminated by SIGTERM)
2025-04-26 02:48:31,678 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 02:48:31,683 INFO supervisord started with pid 1
2025-04-26 02:48:32,689 INFO spawned: 'flask' with pid 7
2025-04-26 02:48:32,693 INFO spawned: 'tomcat' with pid 8
2025-04-26 02:48:43,291 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 02:49:03,175 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 03:11:20,401 WARN received SIGTERM indicating exit request
2025-04-26 03:11:20,402 INFO waiting for flask, tomcat to die
2025-04-26 03:11:23,066 WARN stopped: tomcat (exit status 143)
2025-04-26 03:11:24,075 WARN stopped: flask (terminated by SIGTERM)
2025-04-26 03:11:33,054 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 03:11:33,059 INFO supervisord started with pid 1
2025-04-26 03:11:34,065 INFO spawned: 'flask' with pid 8
2025-04-26 03:11:34,069 INFO spawned: 'tomcat' with pid 9
2025-04-26 03:11:44,220 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 03:12:04,722 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 03:14:42,212 WARN received SIGTERM indicating exit request
2025-04-26 03:14:42,213 INFO waiting for flask, tomcat to die
2025-04-26 03:14:43,411 WARN stopped: tomcat (exit status 143)
2025-04-26 03:14:43,415 WARN stopped: flask (terminated by SIGTERM)
2025-04-26 03:14:54,625 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 03:14:54,632 INFO supervisord started with pid 1
2025-04-26 03:14:55,637 INFO spawned: 'flask' with pid 7
2025-04-26 03:14:55,641 INFO spawned: 'tomcat' with pid 8
2025-04-26 03:15:06,398 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 03:15:25,714 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 04:25:12,125 WARN received SIGTERM indicating exit request
2025-04-26 04:25:12,126 INFO waiting for flask, tomcat to die
2025-04-26 04:25:13,146 WARN stopped: tomcat (exit status 143)
2025-04-26 04:25:13,151 WARN stopped: flask (terminated by SIGTERM)
2025-04-26 04:25:23,591 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 04:25:23,597 INFO supervisord started with pid 1
2025-04-26 04:25:24,601 INFO spawned: 'flask' with pid 7
2025-04-26 04:25:24,605 INFO spawned: 'tomcat' with pid 8
2025-04-26 04:25:35,590 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 04:25:55,281 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 04:28:14,105 WARN received SIGTERM indicating exit request
2025-04-26 04:28:14,106 INFO waiting for flask, tomcat to die
2025-04-26 04:28:15,433 WARN stopped: tomcat (exit status 143)
2025-04-26 04:28:15,440 WARN stopped: flask (terminated by SIGTERM)
2025-04-26 04:28:24,074 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 04:28:24,079 INFO supervisord started with pid 1
2025-04-26 04:28:25,084 INFO spawned: 'flask' with pid 7
2025-04-26 04:28:25,088 INFO spawned: 'tomcat' with pid 8
2025-04-26 04:28:36,046 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 04:28:55,993 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-26 10:03:36,178 WARN received SIGTERM indicating exit request
2025-04-26 10:03:36,237 INFO waiting for flask, tomcat to die
2025-04-26 10:03:39,837 INFO waiting for flask, tomcat to die
2025-04-26 10:03:43,260 INFO waiting for flask, tomcat to die
2025-04-26 12:18:50,367 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-26 12:18:50,378 INFO supervisord started with pid 1
2025-04-26 12:18:51,395 INFO spawned: 'flask' with pid 7
2025-04-26 12:18:51,403 INFO spawned: 'tomcat' with pid 8
2025-04-26 12:19:01,644 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-26 12:19:21,957 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-27 08:17:45,931 WARN received SIGTERM indicating exit request
2025-04-27 08:17:45,961 INFO waiting for flask, tomcat to die
2025-04-27 08:17:49,064 INFO waiting for flask, tomcat to die
2025-04-27 08:17:50,979 WARN stopped: tomcat (exit status 143)
2025-04-27 08:17:52,033 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 10:23:53,793 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 10:23:53,799 INFO supervisord started with pid 1
2025-04-30 10:23:54,803 INFO spawned: 'flask' with pid 7
2025-04-30 10:23:54,809 INFO spawned: 'tomcat' with pid 8
2025-04-30 10:24:05,021 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 10:24:25,099 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 10:38:47,304 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 10:38:47,308 INFO supervisord started with pid 1
2025-04-30 10:38:48,314 INFO spawned: 'flask' with pid 7
2025-04-30 10:38:48,319 INFO spawned: 'tomcat' with pid 8
2025-04-30 10:38:58,312 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 10:39:19,224 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 10:48:30,304 WARN received SIGTERM indicating exit request
2025-04-30 10:48:30,306 INFO waiting for flask, tomcat to die
2025-04-30 10:48:31,221 WARN stopped: tomcat (exit status 143)
2025-04-30 10:48:31,229 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 10:48:48,967 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 10:48:48,973 INFO supervisord started with pid 1
2025-04-30 10:48:49,980 INFO spawned: 'flask' with pid 7
2025-04-30 10:48:49,993 INFO spawned: 'tomcat' with pid 8
2025-04-30 10:48:50,939 WARN exited: flask (exit status 1; not expected)
2025-04-30 10:48:51,948 INFO spawned: 'flask' with pid 42
2025-04-30 10:48:53,630 WARN exited: flask (exit status 1; not expected)
2025-04-30 10:48:55,642 INFO spawned: 'flask' with pid 48
2025-04-30 10:48:56,893 WARN exited: flask (exit status 1; not expected)
2025-04-30 10:48:59,903 INFO spawned: 'flask' with pid 50
2025-04-30 10:49:00,921 WARN exited: flask (exit status 1; not expected)
2025-04-30 10:49:01,925 INFO gave up: flask entered FATAL state, too many start retries too quickly
2025-04-30 10:49:20,108 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 10:50:51,233 WARN received SIGTERM indicating exit request
2025-04-30 10:50:51,234 INFO waiting for tomcat to die
2025-04-30 10:50:52,273 WARN stopped: tomcat (exit status 143)
2025-04-30 10:51:10,940 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 10:51:10,945 INFO supervisord started with pid 1
2025-04-30 10:51:11,950 INFO spawned: 'flask' with pid 7
2025-04-30 10:51:11,961 INFO spawned: 'tomcat' with pid 8
2025-04-30 10:51:22,850 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 10:51:42,564 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 10:54:54,839 WARN received SIGTERM indicating exit request
2025-04-30 10:54:54,840 INFO waiting for flask, tomcat to die
2025-04-30 10:54:56,240 WARN stopped: tomcat (exit status 143)
2025-04-30 10:54:56,248 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 10:55:15,801 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 10:55:15,808 INFO supervisord started with pid 1
2025-04-30 10:55:16,813 INFO spawned: 'flask' with pid 7
2025-04-30 10:55:16,817 INFO spawned: 'tomcat' with pid 8
2025-04-30 10:55:26,889 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 10:55:46,902 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:28:01,890 WARN received SIGTERM indicating exit request
2025-04-30 11:28:01,892 INFO waiting for flask, tomcat to die
2025-04-30 11:28:04,299 WARN stopped: tomcat (exit status 143)
2025-04-30 11:28:05,309 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:28:23,096 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:28:23,104 INFO supervisord started with pid 1
2025-04-30 11:28:24,109 INFO spawned: 'flask' with pid 7
2025-04-30 11:28:24,113 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:28:34,157 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:28:54,148 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:31:15,173 WARN received SIGTERM indicating exit request
2025-04-30 11:31:15,174 INFO waiting for flask, tomcat to die
2025-04-30 11:31:16,320 WARN stopped: tomcat (exit status 143)
2025-04-30 11:31:16,326 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:32:02,823 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:32:02,830 INFO supervisord started with pid 1
2025-04-30 11:32:03,836 INFO spawned: 'flask' with pid 7
2025-04-30 11:32:03,840 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:32:13,957 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:32:34,251 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:39:35,550 WARN received SIGTERM indicating exit request
2025-04-30 11:39:35,551 INFO waiting for flask, tomcat to die
2025-04-30 11:39:36,383 WARN stopped: tomcat (exit status 143)
2025-04-30 11:39:36,389 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:39:54,475 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:39:54,485 INFO supervisord started with pid 1
2025-04-30 11:39:55,643 INFO spawned: 'flask' with pid 7
2025-04-30 11:39:55,686 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:40:06,202 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:40:26,438 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:41:09,107 WARN received SIGTERM indicating exit request
2025-04-30 11:41:09,108 INFO waiting for flask, tomcat to die
2025-04-30 11:41:10,318 WARN stopped: tomcat (exit status 143)
2025-04-30 11:41:10,323 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:41:27,816 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:41:27,821 INFO supervisord started with pid 1
2025-04-30 11:41:28,831 INFO spawned: 'flask' with pid 7
2025-04-30 11:41:28,834 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:41:39,062 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:41:59,574 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:52:23,663 WARN received SIGTERM indicating exit request
2025-04-30 11:52:23,667 INFO waiting for flask, tomcat to die
2025-04-30 11:52:26,278 WARN stopped: tomcat (exit status 143)
2025-04-30 11:52:27,286 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:52:44,337 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:52:44,342 INFO supervisord started with pid 1
2025-04-30 11:52:45,347 INFO spawned: 'flask' with pid 7
2025-04-30 11:52:45,350 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:52:55,759 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:53:16,085 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:56:41,422 WARN received SIGTERM indicating exit request
2025-04-30 11:56:41,423 INFO waiting for flask, tomcat to die
2025-04-30 11:56:42,293 WARN stopped: tomcat (exit status 143)
2025-04-30 11:56:42,299 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:56:56,156 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:56:56,162 INFO supervisord started with pid 1
2025-04-30 11:56:57,166 INFO spawned: 'flask' with pid 7
2025-04-30 11:56:57,170 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:57:07,183 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:57:27,999 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 11:59:08,707 WARN received SIGTERM indicating exit request
2025-04-30 11:59:08,708 INFO waiting for flask, tomcat to die
2025-04-30 11:59:09,919 WARN stopped: tomcat (exit status 143)
2025-04-30 11:59:09,928 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 11:59:27,085 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 11:59:27,092 INFO supervisord started with pid 1
2025-04-30 11:59:28,099 INFO spawned: 'flask' with pid 7
2025-04-30 11:59:28,102 INFO spawned: 'tomcat' with pid 8
2025-04-30 11:59:38,539 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 11:59:58,828 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 12:01:48,503 WARN received SIGTERM indicating exit request
2025-04-30 12:01:48,504 INFO waiting for flask, tomcat to die
2025-04-30 12:01:49,394 WARN stopped: tomcat (exit status 143)
2025-04-30 12:01:49,399 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 12:02:01,864 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 12:02:01,872 INFO supervisord started with pid 1
2025-04-30 12:02:02,878 INFO spawned: 'flask' with pid 7
2025-04-30 12:02:02,882 INFO spawned: 'tomcat' with pid 8
2025-04-30 12:02:13,021 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 12:02:33,482 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)
2025-04-30 12:03:53,485 WARN received SIGTERM indicating exit request
2025-04-30 12:03:53,486 INFO waiting for flask, tomcat to die
2025-04-30 12:03:54,882 WARN stopped: tomcat (exit status 143)
2025-04-30 12:03:54,890 WARN stopped: flask (terminated by SIGTERM)
2025-04-30 12:04:06,241 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
2025-04-30 12:04:06,248 INFO supervisord started with pid 1
2025-04-30 12:04:07,252 INFO spawned: 'flask' with pid 7
2025-04-30 12:04:07,256 INFO spawned: 'tomcat' with pid 8
2025-04-30 12:04:17,410 INFO success: flask entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
2025-04-30 12:04:38,224 INFO success: tomcat entered RUNNING state, process has stayed up for > than 30 seconds (startsecs)

File diff suppressed because it is too large Load Diff

2984
logs/tomcat_err.log Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1617,10 +1617,18 @@ def map_types_to_packages(used_types, all_dependencies, download_dir):
def import_package_and_dependencies(initial_name, initial_version, dependency_mode='recursive'):
"""Orchestrates recursive download and dependency extraction."""
logger.info(f"Starting import for {initial_name}#{initial_version} with dependency_mode={dependency_mode}")
logger.info(f"Starting import of {initial_name}#{initial_version} with mode {dependency_mode}")
download_dir = _get_download_dir()
if not download_dir:
return {'requested': (initial_name, initial_version), 'processed': set(), 'downloaded': {}, 'all_dependencies': {}, 'dependencies': [], 'errors': ['Download directory not accessible']}
logger.error("Download directory not accessible")
return {
'requested': (initial_name, initial_version),
'processed': set(),
'downloaded': {},
'all_dependencies': {},
'dependencies': [],
'errors': ['Download directory not accessible']
}
results = {
'requested': (initial_name, initial_version),
@ -1640,18 +1648,24 @@ def import_package_and_dependencies(initial_name, initial_version, dependency_mo
if package_id_tuple in results['processed']:
logger.debug(f"Skipping already processed package: {name}#{version}")
continue
logger.info(f"Processing package from queue: {name}#{version}")
logger.info(f"Processing package {name}#{version}")
save_path, dl_error = download_package(name, version)
if dl_error:
logger.error(f"Download failed for {name}#{version}: {dl_error}")
results['errors'].append(f"Download failed for {name}#{version}: {dl_error}")
continue
tgz_filename = os.path.basename(save_path)
logger.info(f"Downloaded {tgz_filename}")
results['downloaded'][package_id_tuple] = save_path
logger.info(f"Extracting dependencies from {tgz_filename}")
dependencies, dep_error = extract_dependencies(save_path)
if dep_error:
logger.error(f"Dependency extraction failed for {name}#{version}: {dep_error}")
results['errors'].append(f"Dependency extraction failed for {name}#{version}: {dep_error}")
results['processed'].add(package_id_tuple)
continue
elif dependencies is None:
logger.error(f"Critical error in dependency extraction for {name}#{version}")
results['errors'].append(f"Dependency extraction returned critical error for {name}#{version}.")
results['processed'].add(package_id_tuple)
continue
@ -1669,12 +1683,15 @@ def import_package_and_dependencies(initial_name, initial_version, dependency_mo
should_queue = False
if dependency_mode == 'recursive':
should_queue = True
logger.info(f"Queueing dependency {dep_name}#{dep_version} (recursive mode)")
elif dependency_mode == 'patch-canonical' and dep_tuple == CANONICAL_PACKAGE:
should_queue = True
logger.info(f"Queueing canonical dependency {dep_name}#{dep_version} (patch-canonical mode)")
if should_queue:
logger.debug(f"Adding dependency to queue ({dependency_mode}): {dep_name}#{dep_version}")
pending_queue.append(dep_tuple)
queued_or_processed_lookup.add(dep_tuple)
logger.info(f"Saving metadata for {name}#{version}")
save_package_metadata(name, version, dependency_mode, current_package_deps)
if dependency_mode == 'tree-shaking' and package_id_tuple == (initial_name, initial_version):
logger.info(f"Performing tree-shaking for {initial_name}#{initial_version}")
@ -1684,14 +1701,14 @@ def import_package_and_dependencies(initial_name, initial_version, dependency_mo
tree_shaken_deps = set(type_to_package.values()) - {package_id_tuple}
if CANONICAL_PACKAGE not in tree_shaken_deps:
tree_shaken_deps.add(CANONICAL_PACKAGE)
logger.debug(f"Ensuring canonical package {CANONICAL_PACKAGE} for tree-shaking")
logger.info(f"Ensuring canonical package {CANONICAL_PACKAGE[0]}#{CANONICAL_PACKAGE[1]} for tree-shaking")
for dep_tuple in tree_shaken_deps:
if dep_tuple not in queued_or_processed_lookup:
logger.info(f"Queueing tree-shaken dependency: {dep_tuple[0]}#{dep_tuple[1]}")
logger.info(f"Queueing tree-shaken dependency {dep_tuple[0]}#{dep_tuple[1]}")
pending_queue.append(dep_tuple)
queued_or_processed_lookup.add(dep_tuple)
results['dependencies'] = [{"name": d[0], "version": d[1]} for d in all_found_dependencies]
logger.info(f"Import finished for {initial_name}#{initial_version}. Processed: {len(results['processed'])}, Downloaded: {len(results['downloaded'])}, Errors: {len(results['errors'])}")
logger.info(f"Completed import of {initial_name}#{initial_version}. Processed {len(results['processed'])} packages, downloaded {len(results['downloaded'])}, with {len(results['errors'])} errors")
return results
# --- Validation Route ---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

After

Width:  |  Height:  |  Size: 706 KiB

387
static/css/animation.css Normal file
View File

@ -0,0 +1,387 @@
body {
width: 100%;
overflow: hidden;
height: 100vh;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* Fire animation overlay */
.fire-on {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(#1d4456, #112630);
opacity: 1;
z-index: 1;
transition: all 1200ms linear;
}
.section-center {
position: relative;
width: 300px; /* Reduced size for landing page */
height: 300px;
margin: 0 auto;
display: block;
overflow: hidden;
border: 8px solid rgba(0,0,0,.2);
border-radius: 50%;
z-index: 5;
background-color: #1d4456;
box-shadow: 0 0 50px 5px rgba(255,148,0,.1);
transition: all 500ms linear;
}
/* Wood and star using local images */
.wood {
position: absolute;
z-index: 21;
left: 50%;
bottom: 12%;
width: 80px;
margin-left: -40px;
height: 30px;
background-image: url('{{ url_for('static', filename='img/wood.png') }}');
background-size: 80px 30px;
border-radius: 5px;
}
.star {
z-index: 2;
position: absolute;
top: 138px;
left: 18px;
background-image: url('{{ url_for('static', filename='img/star.png') }}');
background-size: 11px 11px;
width: 11px;
height: 11px;
opacity: 0.4;
animation: starShine 3.5s linear infinite;
transition: all 1200ms linear;
}
.wood-circle {
position: absolute;
z-index: 20;
left: 50%;
bottom: 11%;
width: 100px;
margin-left: -50px;
height: 20px;
border-radius: 100%;
background-color: #0a171d;
}
.circle {
position: absolute;
z-index: 6;
right: -225px;
bottom: -337px;
width: 562px;
height: 525px;
border-radius: 100%;
background-color: #112630;
}
/* Moon */
.moon {
position: absolute;
top: 37px;
left: 86px;
width: 60px;
height: 60px;
background-color: #b2b7bc;
border-radius: 50%;
box-shadow: inset -15px 1.5px 0 0px #c0c3c9, 0 0 7px 3px rgba(228,228,222,.4);
z-index: 1;
animation: brilla-moon 4s alternate infinite;
transition: all 2000ms linear;
}
.moon div:nth-child(1) {
position: absolute;
top: 50%;
left: 10%;
width: 12%;
height: 12%;
border-radius: 50%;
border: 1px solid #adaca2;
box-shadow: inset 1.5px -0.75px 0 0px #85868b;
opacity: 0.4;
}
.moon div:nth-child(2) {
position: absolute;
top: 20%;
left: 38%;
width: 16%;
height: 16%;
border-radius: 50%;
border: 1px solid #adaca2;
box-shadow: inset 1.5px -0.75px 0 0px #85868b;
opacity: 0.4;
}
.moon div:nth-child(3) {
position: absolute;
top: 60%;
left: 45%;
width: 20%;
height: 20%;
border-radius: 50%;
border: 1px solid #adaca2;
box-shadow: inset 1.5px -0.75px 0 0px #85868b;
opacity: 0.4;
}
@keyframes brilla-moon {
0% { box-shadow: inset -15px 1.5px 0 0px #c0c3c9, 0 0 7px 3px rgba(228,228,222,.4); }
50% { box-shadow: inset -15px 1.5px 0 0px #c0c3c9, 0 0 11px 6px rgba(228,228,222,.4); }
}
/* Shooting stars */
.shooting-star {
z-index: 2;
width: 1px;
height: 37px;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
position: absolute;
top: 0;
left: -52px;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), white);
animation: animShootingStar 6s linear infinite;
transition: all 2000ms linear;
}
@keyframes animShootingStar {
from { transform: translateY(0px) translateX(0px) rotate(-45deg); opacity: 1; height: 3px; }
to { transform: translateY(960px) translateX(960px) rotate(-45deg); opacity: 1; height: 600px; }
}
.shooting-star-2 {
z-index: 2;
width: 1px;
height: 37px;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
position: absolute;
top: 0;
left: 150px;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), white);
animation: animShootingStar-2 9s linear infinite;
transition: all 2000ms linear;
}
@keyframes animShootingStar-2 {
from { transform: translateY(0px) translateX(0px) rotate(-45deg); opacity: 1; height: 3px; }
to { transform: translateY(1440px) translateX(1440px) rotate(-45deg); opacity: 1; height: 600px; }
}
/* Stars */
.star.snd { top: 75px; left: 232px; animation-delay: 1s; }
.star.trd { top: 97px; left: 75px; animation-delay: 1.4s; }
.star.fth { top: 15px; left: 150px; animation-delay: 1.8s; }
.star.fith { top: 63px; left: 165px; animation-delay: 2.2s; }
@keyframes starShine {
0% { transform: scale(0.3) rotate(0deg); opacity: 0.4; }
25% { transform: scale(1) rotate(360deg); opacity: 1; }
50% { transform: scale(0.3) rotate(720deg); opacity: 0.4; }
100% { transform: scale(0.3) rotate(0deg); opacity: 0.4; }
}
/* Trees */
.tree-1 {
position: relative;
top: 112px;
left: 37px;
width: 0;
height: 0;
z-index: 8;
border-bottom: 67px solid #0a171d;
border-left: 22px solid transparent;
border-right: 22px solid transparent;
}
.tree-1:before {
position: absolute;
bottom: -82px;
left: 50%;
margin-left: -3px;
width: 6px;
height: 22px;
z-index: 7;
content: '';
background-color: #000;
}
.tree-2 {
position: relative;
top: 0;
left: 187px;
width: 0;
height: 0;
z-index: 8;
border-bottom: 67px solid #0a171d;
border-left: 22px solid transparent;
border-right: 22px solid transparent;
}
.tree-2:before {
position: absolute;
bottom: -82px;
left: 50%;
margin-left: -3px;
width: 6px;
height: 22px;
z-index: 7;
content: '';
background-color: #000;
}
/* Fire */
.fire {
position: absolute;
z-index: 39;
width: 2px;
margin-left: -1px;
left: 50%;
bottom: 60px;
transition: all 1200ms linear;
}
.fire span {
display: block;
position: absolute;
bottom: -11px;
margin-left: -15px;
height: 0;
width: 0;
border: 22px solid #febd08; /* Main flame: yellow-orange */
border-radius: 50%;
border-top-left-radius: 0;
left: -6px;
box-shadow: 0 0 7px 3px rgba(244,110,28,0.8), 0 0 15px 7px rgba(244,110,28,0.6), 0 0 22px 11px rgba(244,110,28,0.3);
transform: scale(0.45, 0.75) rotate(45deg);
animation: brilla-fire 2.5s alternate infinite;
z-index: 9;
transition: all 1200ms linear;
}
.fire span:nth-child(2) {
left: -16px;
border: 22px solid #e63946; /* Outside flame: red */
box-shadow: 0 0 7px 3px rgba(230,57,70,0.8), 0 0 15px 7px rgba(230,57,70,0.6), 0 0 22px 11px rgba(230,57,70,0.3);
transform: scale(0.3, 0.55) rotate(15deg);
z-index: 8;
animation: brilla-fire-red 1.5s alternate infinite;
}
.fire span:nth-child(3) {
left: 3px;
border: 22px solid #e63946; /* Outside flame: red */
box-shadow: 0 0 7px 3px rgba(230,57,70,0.8), 0 0 15px 7px rgba(230,57,70,0.6), 0 0 22px 11px rgba(230,57,70,0.3);
transform: scale(0.3, 0.55) rotate(80deg);
z-index: 8;
animation: brilla-fire-red 2s alternate infinite;
}
.fire span:after {
display: block;
position: absolute;
bottom: -22px;
content: '';
margin-left: -3px;
height: 22px;
width: 9px;
background-color: rgba(244,110,28,0.7);
border-radius: 80px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
box-shadow: 0 0 15px 7px rgba(244,110,28,0.7);
left: -6px;
opacity: 0.8;
transform: rotate(-50deg);
}
.fire span:nth-child(2):after {
background-color: rgba(230,57,70,0.7); /* Match red flame */
box-shadow: 0 0 15px 7px rgba(230,57,70,0.7);
}
.fire span:nth-child(3):after {
background-color: rgba(230,57,70,0.7); /* Match red flame */
box-shadow: 0 0 15px 7px rgba(230,57,70,0.7);
}
@keyframes brilla-fire {
0%, 100% { box-shadow: 0 0 7px 3px rgba(244,110,28,0.8), 0 0 15px 7px rgba(244,110,28,0.6), 0 0 22px 11px rgba(244,110,28,0.3); }
50% { box-shadow: 0 0 10px 5px rgba(244,110,28,0.8), 0 0 21px 10px rgba(244,110,28,0.6), 0 0 31px 15px rgba(244,110,28,0.3); }
}
@keyframes brilla-fire-red {
0%, 100% { box-shadow: 0 0 7px 3px rgba(230,57,70,0.8), 0 0 15px 7px rgba(230,57,70,0.6), 0 0 22px 11px rgba(230,57,70,0.3); }
50% { box-shadow: 0 0 10px 5px rgba(230,57,70,0.8), 0 0 21px 10px rgba(230,57,70,0.6), 0 0 31px 15px rgba(230,57,70,0.3); }
}
/* Smoke */
.smoke {
position: absolute;
z-index: 40;
width: 2px;
margin-left: -1px;
left: 50%;
bottom: 79px;
opacity: 0;
transition: all 800ms linear;
}
.smoke span {
display: block;
position: absolute;
bottom: -26px;
left: 50%;
margin-left: -15px;
height: 0;
width: 0;
border: 22px solid rgba(0, 0, 0, .6);
border-radius: 16px;
border-bottom-left-radius: 0;
border-top-right-radius: 0;
left: -6px;
opacity: 0;
transform: scale(0.2, 0.2) rotate(-45deg);
}
@keyframes smokeLeft {
0% { transform: scale(0.2, 0.2) translate(0, 0) rotate(-45deg); }
10% { opacity: 1; transform: scale(0.2, 0.3) translate(0, -3px) rotate(-45deg); }
60% { opacity: 0.6; transform: scale(0.3, 0.5) translate(-7px, -60px) rotate(-45deg); }
100% { opacity: 0; transform: scale(0.4, 0.8) translate(-15px, -90px) rotate(-45deg); }
}
@keyframes smokeRight {
0% { transform: scale(0.2, 0.2) translate(0, 0) rotate(-45deg); }
10% { opacity: 1; transform: scale(0.2, 0.3) translate(0, -3px) rotate(-45deg); }
60% { opacity: 0.6; transform: scale(0.3, 0.5) translate(7px, -60px) rotate(-45deg); }
100% { opacity: 0; transform: scale(0.4, 0.8) translate(15px, -90px) rotate(-45deg); }
}
.smoke .s-0 { animation: smokeLeft 7s 0s infinite; }
.smoke .s-1 { animation: smokeRight 7s 0.7s infinite; }
.smoke .s-2 { animation: smokeLeft 7s 1.4s infinite; }
.smoke .s-3 { animation: smokeRight 7s 2.1s infinite; }
.smoke .s-4 { animation: smokeLeft 7s 2.8s infinite; }
.smoke .s-5 { animation: smokeRight 7s 3.5s infinite; }
.smoke .s-6 { animation: smokeLeft 7s 4.2s infinite; }
.smoke .s-7 { animation: smokeRight 7s 4.9s infinite; }
.smoke .s-8 { animation: smokeLeft 7s 5.6s infinite; }
.smoke .s-9 { animation: smokeRight 7s 6.3s infinite; }
/* Dark Theme (fire-off) */
html[data-theme="dark"] .fire-on { opacity: 0; }
html[data-theme="dark"] .section-center { box-shadow: 0 0 50px 5px rgba(200,200,200,.2); }
html[data-theme="dark"] .smoke { opacity: 1; transition-delay: 0.8s; }
html[data-theme="dark"] .fire span { bottom: -26px; transform: scale(0.15, 0.15) rotate(45deg); }

View File

@ -0,0 +1,225 @@
/* Scoped to .fire-loading-overlay to prevent conflicts */
.fire-loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(39, 5, 55, 0.8); /* Semi-transparent gradient background */
display: flex;
align-items: center;
justify-content: center;
z-index: 1050; /* Above Bootstrap modals */
}
.fire-loading-overlay .campfire {
position: relative;
width: 400px; /* Reduced size for better fit */
height: 400px;
transform-origin: center center;
transform: scale(0.6); /* Scaled down for responsiveness */
}
.fire-loading-overlay .log {
position: absolute;
width: 238px;
height: 70px;
border-radius: 32px;
background: #781e20;
overflow: hidden;
opacity: 0.99;
}
.fire-loading-overlay .log:before {
content: '';
display: block;
position: absolute;
top: 50%;
left: 35px;
width: 8px;
height: 8px;
border-radius: 32px;
background: #b35050;
transform: translate(-50%, -50%);
z-index: 3;
box-shadow: 0 0 0 2.5px #781e20, 0 0 0 10.5px #b35050, 0 0 0 13px #781e20, 0 0 0 21px #b35050, 0 0 0 23.5px #781e20, 0 0 0 31.5px #b35050;
}
.fire-loading-overlay .streak {
position: absolute;
height: 2px;
border-radius: 20px;
background: #b35050;
}
/* Streak positioning (unchanged from original) */
.fire-loading-overlay .streak:nth-child(1) { top: 10px; width: 90px; }
.fire-loading-overlay .streak:nth-child(2) { top: 10px; left: 100px; width: 80px; }
.fire-loading-overlay .streak:nth-child(3) { top: 10px; left: 190px; width: 30px; }
.fire-loading-overlay .streak:nth-child(4) { top: 22px; width: 132px; }
.fire-loading-overlay .streak:nth-child(5) { top: 22px; left: 142px; width: 48px; }
.fire-loading-overlay .streak:nth-child(6) { top: 22px; left: 200px; width: 28px; }
.fire-loading-overlay .streak:nth-child(7) { top: 34px; left: 74px; width: 160px; }
.fire-loading-overlay .streak:nth-child(8) { top: 46px; left: 110px; width: 40px; }
.fire-loading-overlay .streak:nth-child(9) { top: 46px; left: 170px; width: 54px; }
.fire-loading-overlay .streak:nth-child(10) { top: 58px; left: 90px; width: 110px; }
.fire-loading-overlay .log {
transform-origin: center center;
box-shadow: 0 0 2px 1px rgba(0,0,0,0.15);
}
.fire-loading-overlay .log:nth-child(1) { bottom: 100px; left: 100px; transform: rotate(150deg) scaleX(0.75); z-index: 20; }
.fire-loading-overlay .log:nth-child(2) { bottom: 120px; left: 140px; transform: rotate(110deg) scaleX(0.75); z-index: 10; }
.fire-loading-overlay .log:nth-child(3) { bottom: 98px; left: 68px; transform: rotate(-10deg) scaleX(0.75); }
.fire-loading-overlay .log:nth-child(4) { bottom: 80px; left: 220px; transform: rotate(-120deg) scaleX(0.75); z-index: 26; }
.fire-loading-overlay .log:nth-child(5) { bottom: 75px; left: 210px; transform: rotate(-30deg) scaleX(0.75); z-index: 25; }
.fire-loading-overlay .log:nth-child(6) { bottom: 92px; left: 280px; transform: rotate(35deg) scaleX(0.85); z-index: 30; }
.fire-loading-overlay .log:nth-child(7) { bottom: 70px; left: 300px; transform: rotate(-30deg) scaleX(0.75); z-index: 20; }
.fire-loading-overlay .stick {
position: absolute;
width: 68px;
height: 20px;
border-radius: 10px;
box-shadow: 0 0 2px 1px rgba(0,0,0,0.1);
background: #781e20;
}
.fire-loading-overlay .stick:before {
content: '';
display: block;
position: absolute;
bottom: 100%;
left: 30px;
width: 6px;
height: 20px;
background: #781e20;
border-radius: 10px;
transform: translateY(50%) rotate(32deg);
}
.fire-loading-overlay .stick:after {
content: '';
display: block;
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
background: #b35050;
border-radius: 10px;
}
.fire-loading-overlay .stick {
transform-origin: center center;
}
.fire-loading-overlay .stick:nth-child(1) { left: 158px; bottom: 164px; transform: rotate(-152deg) scaleX(0.8); z-index: 12; }
.fire-loading-overlay .stick:nth-child(2) { left: 180px; bottom: 30px; transform: rotate(20deg) scaleX(0.9); }
.fire-loading-overlay .stick:nth-child(3) { left: 400px; bottom: 38px; transform: rotate(170deg) scaleX(0.9); }
.fire-loading-overlay .stick:nth-child(3):before { display: none; }
.fire-loading-overlay .stick:nth-child(4) { left: 370px; bottom: 150px; transform: rotate(80deg) scaleX(0.9); z-index: 20; }
.fire-loading-overlay .stick:nth-child(4):before { display: none; }
.fire-loading-overlay .fire .flame {
position: absolute;
transform-origin: bottom center;
opacity: 0.9;
}
.fire-loading-overlay .fire__red .flame {
width: 48px;
border-radius: 48px;
background: #e20f00;
box-shadow: 0 0 80px 18px rgba(226,15,0,0.4);
}
.fire-loading-overlay .fire__red .flame:nth-child(1) { left: 138px; height: 160px; bottom: 100px; animation: fire 2s 0.15s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__red .flame:nth-child(2) { left: 186px; height: 240px; bottom: 100px; animation: fire 2s 0.35s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__red .flame:nth-child(3) { left: 234px; height: 300px; bottom: 100px; animation: fire 2s 0.1s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__red .flame:nth-child(4) { left: 282px; height: 360px; bottom: 100px; animation: fire 2s 0s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__red .flame:nth-child(5) { left: 330px; height: 310px; bottom: 100px; animation: fire 2s 0.45s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__red .flame:nth-child(6) { left: 378px; height: 232px; bottom: 100px; animation: fire 2s 0.3s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__red .flame:nth-child(7) { left: 426px; height: 140px; bottom: 100px; animation: fire 2s 0.1s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame {
width: 48px;
border-radius: 48px;
background: #ff9c00;
box-shadow: 0 0 80px 18px rgba(255,156,0,0.4);
}
.fire-loading-overlay .fire__orange .flame:nth-child(1) { left: 138px; height: 140px; bottom: 100px; animation: fire 2s 0.05s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame:nth-child(2) { left: 186px; height: 210px; bottom: 100px; animation: fire 2s 0.1s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame:nth-child(3) { left: 234px; height: 250px; bottom: 100px; animation: fire 2s 0.35s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame:nth-child(4) { left: 282px; height: 300px; bottom: 100px; animation: fire 2s 0.4s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame:nth-child(5) { left: 330px; height: 260px; bottom: 100px; animation: fire 2s 0.5s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame:nth-child(6) { left: 378px; height: 202px; bottom: 100px; animation: fire 2s 0.35s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__orange .flame:nth-child(7) { left: 426px; height: 110px; bottom: 100px; animation: fire 2s 0.1s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__yellow .flame {
width: 48px;
border-radius: 48px;
background: #ffeb6e;
box-shadow: 0 0 80px 18px rgba(255,235,110,0.4);
}
.fire-loading-overlay .fire__yellow .flame:nth-child(1) { left: 186px; height: 140px; bottom: 100px; animation: fire 2s 0.6s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__yellow .flame:nth-child(2) { left: 234px; height: 172px; bottom: 120px; animation: fire 2s 0.4s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__yellow .flame:nth-child(3) { left: 282px; height: 240px; bottom: 100px; animation: fire 2s 0.38s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__yellow .flame:nth-child(4) { left: 330px; height: 200px; bottom: 100px; animation: fire 2s 0.22s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__yellow .flame:nth-child(5) { left: 378px; height: 142px; bottom: 100px; animation: fire 2s 0.18s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame {
width: 48px;
border-radius: 48px;
background: #fef1d9;
box-shadow: 0 0 80px 18px rgba(254,241,217,0.4);
}
.fire-loading-overlay .fire__white .flame:nth-child(1) { left: 156px; width: 32px; height: 100px; bottom: 100px; animation: fire 2s 0.22s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame:nth-child(2) { left: 181px; width: 32px; height: 120px; bottom: 100px; animation: fire 2s 0.42s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame:nth-child(3) { left: 234px; height: 170px; bottom: 100px; animation: fire 2s 0.32s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame:nth-child(4) { left: 282px; height: 210px; bottom: 100px; animation: fire 2s 0.8s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame:nth-child(5) { left: 330px; height: 170px; bottom: 100px; animation: fire 2s 0.85s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame:nth-child(6) { left: 378px; width: 32px; height: 110px; bottom: 100px; animation: fire 2s 0.64s ease-in-out infinite alternate; }
.fire-loading-overlay .fire__white .flame:nth-child(7) { left: 408px; width: 32px; height: 100px; bottom: 100px; animation: fire 2s 0.32s ease-in-out infinite alternate; }
.fire-loading-overlay .spark {
position: absolute;
width: 6px;
height: 20px;
background: #fef1d9;
border-radius: 18px;
z-index: 50;
transform-origin: bottom center;
transform: scaleY(0);
}
.fire-loading-overlay .spark:nth-child(1) { left: 160px; bottom: 212px; animation: spark 1s 0.4s linear infinite; }
.fire-loading-overlay .spark:nth-child(2) { left: 180px; bottom: 240px; animation: spark 1s 1s linear infinite; }
.fire-loading-overlay .spark:nth-child(3) { left: 208px; bottom: 320px; animation: spark 1s 0.8s linear infinite; }
.fire-loading-overlay .spark:nth-child(4) { left: 310px; bottom: 400px; animation: spark 1s 2s linear infinite; }
.fire-loading-overlay .spark:nth-child(5) { left: 360px; bottom: 380px; animation: spark 1s 0.75s linear infinite; }
.fire-loading-overlay .spark:nth-child(6) { left: 390px; bottom: 320px; animation: spark 1s 0.65s linear infinite; }
.fire-loading-overlay .spark:nth-child(7) { left: 400px; bottom: 280px; animation: spark 1s 1s linear infinite; }
.fire-loading-overlay .spark:nth-child(8) { left: 430px; bottom: 210px; animation: spark 1s 1.4s linear infinite; }
@keyframes fire {
0% { transform: scaleY(1); }
28% { transform: scaleY(0.7); }
38% { transform: scaleY(0.8); }
50% { transform: scaleY(0.6); }
70% { transform: scaleY(0.95); }
82% { transform: scaleY(0.58); }
100% { transform: scaleY(1); }
}
@keyframes spark {
0%, 35% { transform: scaleY(0) translateY(0); opacity: 0; }
50% { transform: scaleY(1) translateY(0); opacity: 1; }
70% { transform: scaleY(1) translateY(-10px); opacity: 1; }
75% { transform: scaleY(1) translateY(-10px); opacity: 0; }
100% { transform: scaleY(0) translateY(0); opacity: 0; }
}

BIN
static/img/star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

BIN
static/img/wood.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -8,6 +8,8 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet" />
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/fire-animation.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/animation.css') }}">
<title>{% if app_mode == 'lite' %}(Lite Version) {% endif %}{% if title %}{{ title }} - {% endif %}{{ site_name }}</title>
<style>
/* Default (Light Theme) Styles */
@ -86,7 +88,7 @@
height: 1.25rem !important;
margin-top: 0.25rem;
display: inline-block !important;
border: 1px solid #6c757d;
border: 1px solide #6c757d;
}
.form-check-input:checked {
background-color: #007bff;
@ -177,44 +179,44 @@
background: inherit; /* Ensure background comes from pre or theme */
}
/* General Button Theme (Primary, Secondary) */
.btn-primary {
background-color: #007bff;
border-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #004085;
}
.btn-outline-primary {
color: #007bff;
border-color: #007bff;
}
.btn-outline-primary:hover {
background-color: #007bff;
color: white;
}
/* General Button Theme (Primary, Secondary) */
.btn-primary {
background-color: #007bff;
border-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #004085;
}
.btn-outline-primary {
color: #007bff;
border-color: #007bff;
}
.btn-outline-primary:hover {
background-color: #007bff;
color: white;
}
.btn-secondary {
background-color: #6c757d;
border-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
border-color: #545b62;
}
.btn-outline-secondary {
color: #6c757d;
border-color: #6c757d;
}
.btn-outline-secondary:hover {
background-color: #6c757d;
color: white;
}
.btn-secondary {
background-color: #6c757d;
border-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
border-color: #545b62;
}
.btn-outline-secondary {
color: #6c757d;
border-color: #6c757d;
}
.btn-outline-secondary:hover {
background-color: #6c757d;
color: white;
}
/* --- Prism.js Theme Overrides for Light Mode --- */
/* --- Prism.js Theme Overrides for Light Mode --- */
/* Target code blocks only when NOT in dark theme */
html:not([data-theme="dark"]) .token.punctuation,
@ -256,380 +258,380 @@
/* --- End Prism.js Overrides --- */
/* --- Theme Styles for FSH Code Blocks --- */
pre, pre code {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875em;
white-space: pre-wrap; /* Allow wrapping */
word-break: break-all; /* Break long lines */
}
/* --- Theme Styles for FSH Code Blocks --- */
pre, pre code {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875em;
white-space: pre-wrap; /* Allow wrapping */
word-break: break-all; /* Break long lines */
}
/* Target pre blocks specifically used for FSH output if needed */
/* If the pre block has a specific class or ID, use that */
#fsh-output pre, /* Targets any pre within the output container */
._fsh_output pre /* Targets any pre within the partial template content */
{
background-color: #e9ecef; /* Light theme background */
color: #212529; /* Light theme text */
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
max-height: 600px; /* Adjust as needed */
overflow: auto;
}
/* Target pre blocks specifically used for FSH output if needed */
/* If the pre block has a specific class or ID, use that */
#fsh-output pre, /* Targets any pre within the output container */
._fsh_output pre /* Targets any pre within the partial template content */
{
background-color: #e9ecef; /* Light theme background */
color: #212529; /* Light theme text */
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
max-height: 600px; /* Adjust as needed */
overflow: auto;
}
#fsh-output pre code,
._fsh_output pre code {
background-color: transparent !important; /* Ensure code tag doesn't override pre bg */
padding: 0;
color: inherit; /* Inherit text color from pre */
font-size: inherit; /* Inherit font size from pre */
display: block; /* Make code fill pre */
}
#fsh-output pre code,
._fsh_output pre code {
background-color: transparent !important; /* Ensure code tag doesn't override pre bg */
padding: 0;
color: inherit; /* Inherit text color from pre */
font-size: inherit; /* Inherit font size from pre */
display: block; /* Make code fill pre */
}
/* Dark Theme Override */
html[data-theme="dark"] #fsh-output pre,
html[data-theme="dark"] ._fsh_output pre
{
background-color: #495057; /* Dark theme background */
color: #f8f9fa; /* Dark theme text */
border-color: #6c757d;
}
/* Dark Theme Override */
html[data-theme="dark"] #fsh-output pre,
html[data-theme="dark"] ._fsh_output pre
{
background-color: #495057; /* Dark theme background */
color: #f8f9fa; /* Dark theme text */
border-color: #6c757d;
}
html[data-theme="dark"] #fsh-output pre code,
html[data-theme="dark"] ._fsh_output pre code {
color: inherit;
}
html[data-theme="dark"] #fsh-output pre code,
html[data-theme="dark"] ._fsh_output pre code {
color: inherit;
}
/* --- Theme Styles for FHIR UI Operations (fhir_ui_operations.html) --- */
/* --- Theme Styles for FHIR UI Operations (fhir_ui_operations.html) --- */
/* Operation Block Container */
.opblock {
border: 1px solid #dee2e6; /* Light theme border */
background: #ffffff; /* Light theme background */
color: #212529; /* Light theme text */
}
html[data-theme="dark"] .opblock {
border-color: #495057; /* Dark theme border */
background: #343a40; /* Dark theme background (card bg) */
color: #f8f9fa; /* Dark theme text */
}
/* Operation Block Container */
.opblock {
border: 1px solid #dee2e6; /* Light theme border */
background: #ffffff; /* Light theme background */
color: #212529; /* Light theme text */
}
html[data-theme="dark"] .opblock {
border-color: #495057; /* Dark theme border */
background: #343a40; /* Dark theme background (card bg) */
color: #f8f9fa; /* Dark theme text */
}
/* Operation Summary (Header) */
.opblock-summary {
background: #f8f9fa; /* Light theme summary bg */
border-bottom: 1px solid #dee2e6;
color: #212529; /* Default text color for summary */
}
.opblock-summary:hover {
background: #e9ecef;
}
html[data-theme="dark"] .opblock-summary {
background: #40464c; /* Darker summary bg */
border-bottom-color: #495057;
color: #f8f9fa;
}
html[data-theme="dark"] .opblock-summary:hover {
background: #495057;
}
/* Keep method badge colors fixed - DO NOT override .opblock-summary-method */
.opblock-summary-description {
color: #495057; /* Slightly muted description text */
}
html[data-theme="dark"] .opblock-summary-description {
color: #adb5bd; /* Lighter muted text for dark */
}
/* Operation Summary (Header) */
.opblock-summary {
background: #f8f9fa; /* Light theme summary bg */
border-bottom: 1px solid #dee2e6;
color: #212529; /* Default text color for summary */
}
.opblock-summary:hover {
background: #e9ecef;
}
html[data-theme="dark"] .opblock-summary {
background: #40464c; /* Darker summary bg */
border-bottom-color: #495057;
color: #f8f9fa;
}
html[data-theme="dark"] .opblock-summary:hover {
background: #495057;
}
/* Keep method badge colors fixed - DO NOT override .opblock-summary-method */
.opblock-summary-description {
color: #495057; /* Slightly muted description text */
}
html[data-theme="dark"] .opblock-summary-description {
color: #adb5bd; /* Lighter muted text for dark */
}
/* Operation Body (Expanded Section) */
.opblock-body {
border-top: 1px solid #dee2e6;
}
html[data-theme="dark"] .opblock-body {
border-top-color: #495057;
}
/* Operation Body (Expanded Section) */
.opblock-body {
border-top: 1px solid #dee2e6;
}
html[data-theme="dark"] .opblock-body {
border-top-color: #495057;
}
/* Section Headers within Body */
.opblock-section-header {
border-bottom: 1px solid #eee;
color: inherit; /* Inherit from .opblock */
}
html[data-theme="dark"] .opblock-section-header {
border-bottom-color: #495057;
}
.opblock-title {
color: inherit; /* Inherit from .opblock */
}
/* Section Headers within Body */
.opblock-section-header {
border-bottom: 1px solid #eee;
color: inherit; /* Inherit from .opblock */
}
html[data-theme="dark"] .opblock-section-header {
border-bottom-color: #495057;
}
.opblock-title {
color: inherit; /* Inherit from .opblock */
}
/* Parameters Table */
.parameters-table th,
.parameters-table td {
border: 1px solid #dee2e6;
color: inherit; /* Inherit from .opblock */
}
html[data-theme="dark"] .parameters-table th,
html[data-theme="dark"] .parameters-table td {
border-color: #495057;
}
.parameter__type,
.parameter__in {
color: #6c757d; /* Use standard muted color */
}
html[data-theme="dark"] .parameter__type,
html[data-theme="dark"] .parameter__in {
color: #adb5bd; /* Use standard dark muted color */
}
.parameters-col_description input[type="text"],
.parameters-col_description input[type="number"] {
background-color: #fff; /* Match light form controls */
border: 1px solid #ced4da;
color: #212529;
}
html[data-theme="dark"] .parameters-col_description input[type="text"],
html[data-theme="dark"] .parameters-col_description input[type="number"] {
background-color: #495057; /* Match dark form controls */
border-color: #6c757d;
color: #f8f9fa;
}
/* Parameters Table */
.parameters-table th,
.parameters-table td {
border: 1px solid #dee2e6;
color: inherit; /* Inherit from .opblock */
}
html[data-theme="dark"] .parameters-table th,
html[data-theme="dark"] .parameters-table td {
border-color: #495057;
}
.parameter__type,
.parameter__in {
color: #6c757d; /* Use standard muted color */
}
html[data-theme="dark"] .parameter__type,
html[data-theme="dark"] .parameter__in {
color: #adb5bd; /* Use standard dark muted color */
}
.parameters-col_description input[type="text"],
.parameters-col_description input[type="number"] {
background-color: #fff; /* Match light form controls */
border: 1px solid #ced4da;
color: #212529;
}
html[data-theme="dark"] .parameters-col_description input[type="text"],
html[data-theme="dark"] .parameters-col_description input[type="number"] {
background-color: #495057; /* Match dark form controls */
border-color: #6c757d;
color: #f8f9fa;
}
/* Request Body Textarea */
.request-body-textarea {
background-color: #fff; /* Match light form controls */
border: 1px solid #ced4da;
color: #212529;
}
html[data-theme="dark"] .request-body-textarea {
background-color: #495057; /* Match dark form controls */
border-color: #6c757d;
color: #f8f9fa;
}
/* Request Body Textarea */
.request-body-textarea {
background-color: #fff; /* Match light form controls */
border: 1px solid #ced4da;
color: #212529;
}
html[data-theme="dark"] .request-body-textarea {
background-color: #495057; /* Match dark form controls */
border-color: #6c757d;
color: #f8f9fa;
}
/* Responses Table */
.responses-table th,
.responses-table td {
border: 1px solid #dee2e6;
color: inherit;
}
html[data-theme="dark"] .responses-table th,
html[data-theme="dark"] .responses-table td {
border-color: #495057;
}
/* Responses Table */
.responses-table th,
.responses-table td {
border: 1px solid #dee2e6;
color: inherit;
}
html[data-theme="dark"] .responses-table th,
html[data-theme="dark"] .responses-table td {
border-color: #495057;
}
/* Example/Schema Tabs & Controls */
.response-control-media-type {
background-color: #f8f9fa;
}
html[data-theme="dark"] .response-control-media-type {
background-color: #40464c; /* Match summary header bg */
}
.tablinks.badge { /* Default inactive tab */
background: #dee2e6 !important; /* Light grey inactive */
color: #333 !important;
}
.tablinks.badge.active { /* Default active tab */
background: #007bff !important; /* Use primary color */
color: white !important;
}
html[data-theme="dark"] .tablinks.badge { /* Dark inactive tab */
background: #6c757d !important; /* Dark grey inactive */
color: white !important;
}
html[data-theme="dark"] .tablinks.badge.active { /* Dark active tab */
background: #4dabf7 !important; /* Use dark primary color */
color: #000 !important;
}
/* Example/Schema Tabs & Controls */
.response-control-media-type {
background-color: #f8f9fa;
}
html[data-theme="dark"] .response-control-media-type {
background-color: #40464c; /* Match summary header bg */
}
.tablinks.badge { /* Default inactive tab */
background: #dee2e6 !important; /* Light grey inactive */
color: #333 !important;
}
.tablinks.badge.active { /* Default active tab */
background: #007bff !important; /* Use primary color */
color: white !important;
}
html[data-theme="dark"] .tablinks.badge { /* Dark inactive tab */
background: #6c757d !important; /* Dark grey inactive */
color: white !important;
}
html[data-theme="dark"] .tablinks.badge.active { /* Dark active tab */
background: #4dabf7 !important; /* Use dark primary color */
color: #000 !important;
}
/* Code/Output Blocks */
.highlight-code,
.request-url-output,
.curl-output,
.execute-wrapper pre.response-output-content {
background: #e9ecef; /* Light theme pre background */
color: #212529;
border: 1px solid #dee2e6;
}
html[data-theme="dark"] .highlight-code,
html[data-theme="dark"] .request-url-output,
html[data-theme="dark"] .curl-output,
html[data-theme="dark"] .execute-wrapper pre.response-output-content {
background: #495057; /* Dark theme pre background */
color: #f8f9fa;
border-color: #6c757d;
}
/* Code/Output Blocks */
.highlight-code,
.request-url-output,
.curl-output,
.execute-wrapper pre.response-output-content {
background: #e9ecef; /* Light theme pre background */
color: #212529;
border: 1px solid #dee2e6;
}
html[data-theme="dark"] .highlight-code,
html[data-theme="dark"] .request-url-output,
html[data-theme="dark"] .curl-output,
html[data-theme="dark"] .execute-wrapper pre.response-output-content {
background: #495057; /* Dark theme pre background */
color: #f8f9fa;
border-color: #6c757d;
}
/* Narrative Response Box */
.execute-wrapper div.response-output-narrative {
background: #ffffff;
color: #212529;
border: 1px solid #dee2e6;
}
html[data-theme="dark"] .execute-wrapper div.response-output-narrative {
background: #343a40; /* Match card body */
color: #f8f9fa;
border: 1px solid #495057;
}
/* Narrative Response Box */
.execute-wrapper div.response-output-narrative {
background: #ffffff;
color: #212529;
border: 1px solid #dee2e6;
}
html[data-theme="dark"] .execute-wrapper div.response-output-narrative {
background: #343a40; /* Match card body */
color: #f8f9fa;
border: 1px solid #495057;
}
/* Response Status Text */
.execute-wrapper .response-status[style*="color: green"] { color: #198754 !important; }
.execute-wrapper .response-status[style*="color: red"] { color: #dc3545 !important; }
html[data-theme="dark"] .execute-wrapper .response-status[style*="color: green"] { color: #20c997 !important; }
html[data-theme="dark"] .execute-wrapper .response-status[style*="color: red"] { color: #e63946 !important; }
/* Response Status Text */
.execute-wrapper .response-status[style*="color: green"] { color: #198754 !important; }
.execute-wrapper .response-status[style*="color: red"] { color: #dc3545 !important; }
html[data-theme="dark"] .execute-wrapper .response-status[style*="color: green"] { color: #20c997 !important; }
html[data-theme="dark"] .execute-wrapper .response-status[style*="color: red"] { color: #e63946 !important; }
/* Specific Action Buttons (Success, Danger, Warning, Info) */
.btn-success { background-color: #198754; border-color: #198754; color: white; }
.btn-success:hover { background-color: #157347; border-color: #146c43; }
.btn-outline-success { color: #198754; border-color: #198754; }
.btn-outline-success:hover { background-color: #198754; color: white; }
/* Specific Action Buttons (Success, Danger, Warning, Info) */
.btn-success { background-color: #198754; border-color: #198754; color: white; }
.btn-success:hover { background-color: #157347; border-color: #146c43; }
.btn-outline-success { color: #198754; border-color: #198754; }
.btn-outline-success:hover { background-color: #198754; color: white; }
.btn-danger { background-color: #dc3545; border-color: #dc3545; color: white; }
.btn-danger:hover { background-color: #bb2d3b; border-color: #b02a37; }
.btn-outline-danger { color: #dc3545; border-color: #dc3545; }
.btn-outline-danger:hover { background-color: #dc3545; color: white; }
.btn-danger { background-color: #dc3545; border-color: #dc3545; color: white; }
.btn-danger:hover { background-color: #bb2d3b; border-color: #b02a37; }
.btn-outline-danger { color: #dc3545; border-color: #dc3545; }
.btn-outline-danger:hover { background-color: #dc3545; color: white; }
.btn-warning { background-color: #ffc107; border-color: #ffc107; color: #000; }
.btn-warning:hover { background-color: #ffca2c; border-color: #ffc720; }
.btn-outline-warning { color: #ffc107; border-color: #ffc107; }
.btn-outline-warning:hover { background-color: #ffc107; color: #000; }
.btn-warning { background-color: #ffc107; border-color: #ffc107; color: #000; }
.btn-warning:hover { background-color: #ffca2c; border-color: #ffc720; }
.btn-outline-warning { color: #ffc107; border-color: #ffc107; }
.btn-outline-warning:hover { background-color: #ffc107; color: #000; }
.btn-info { background-color: #0dcaf0; border-color: #0dcaf0; color: #000; }
.btn-info:hover { background-color: #31d2f2; border-color: #25cff2; }
.btn-outline-info { color: #0dcaf0; border-color: #0dcaf0; }
.btn-outline-info:hover { background-color: #0dcaf0; color: #000; }
.btn-info { background-color: #0dcaf0; border-color: #0dcaf0; color: #000; }
.btn-info:hover { background-color: #31d2f2; border-color: #25cff2; }
.btn-outline-info { color: #0dcaf0; border-color: #0dcaf0; }
.btn-outline-info:hover { background-color: #0dcaf0; color: #000; }
/* General Badge Theme */
.badge {
padding: 0.4em 0.6em; /* Slightly larger padding */
font-size: 0.85em;
font-weight: 600;
}
/* Specific Badge Backgrounds (Add more as needed) */
.badge.bg-primary { background-color: #007bff !important; color: white !important; }
.badge.bg-secondary { background-color: #6c757d !important; color: white !important; }
.badge.bg-success { background-color: #198754 !important; color: white !important; }
.badge.bg-danger { background-color: #dc3545 !important; color: white !important; }
.badge.bg-warning { background-color: #ffc107 !important; color: #000 !important; }
.badge.bg-info { background-color: #0dcaf0 !important; color: #000 !important; }
.badge.bg-light { background-color: #f8f9fa !important; color: #000 !important; border: 1px solid #dee2e6; }
.badge.bg-dark { background-color: #343a40 !important; color: white !important; }
/* General Badge Theme */
.badge {
padding: 0.4em 0.6em; /* Slightly larger padding */
font-size: 0.85em;
font-weight: 600;
}
/* Specific Badge Backgrounds (Add more as needed) */
.badge.bg-primary { background-color: #007bff !important; color: white !important; }
.badge.bg-secondary { background-color: #6c757d !important; color: white !important; }
.badge.bg-success { background-color: #198754 !important; color: white !important; }
.badge.bg-danger { background-color: #dc3545 !important; color: white !important; }
.badge.bg-warning { background-color: #ffc107 !important; color: #000 !important; }
.badge.bg-info { background-color: #0dcaf0 !important; color: #000 !important; }
.badge.bg-light { background-color: #f8f9fa !important; color: #000 !important; border: 1px solid #dee2e6; }
.badge.bg-dark { background-color: #343a40 !important; color: white !important; }
/* Copy Button Specific Style */
.btn-copy { /* Optional: Add a specific class if needed, or style .btn-outline-secondary directly */
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
}
/* Copy Button Specific Style */
.btn-copy { /* Optional: Add a specific class if needed, or style .btn-outline-secondary directly */
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
}
/* --- Dark Theme Overrides --- */
html[data-theme="dark"] .btn-primary { background-color: #4dabf7; border-color: #4dabf7; color: #000; }
html[data-theme="dark"] .btn-primary:hover { background-color: #68bcef; border-color: #5fb7ea; }
html[data-theme="dark"] .btn-outline-primary { color: #4dabf7; border-color: #4dabf7; }
html[data-theme="dark"] .btn-outline-primary:hover { background-color: #4dabf7; color: #000; }
/* --- Dark Theme Overrides --- */
html[data-theme="dark"] .btn-primary { background-color: #4dabf7; border-color: #4dabf7; color: #000; }
html[data-theme="dark"] .btn-primary:hover { background-color: #68bcef; border-color: #5fb7ea; }
html[data-theme="dark"] .btn-outline-primary { color: #4dabf7; border-color: #4dabf7; }
html[data-theme="dark"] .btn-outline-primary:hover { background-color: #4dabf7; color: #000; }
html[data-theme="dark"] .btn-secondary { background-color: #6c757d; border-color: #6c757d; color: white; }
html[data-theme="dark"] .btn-secondary:hover { background-color: #5a6268; border-color: #545b62; }
html[data-theme="dark"] .btn-outline-secondary { color: #adb5bd; border-color: #6c757d; } /* Lighter text/border */
html[data-theme="dark"] .btn-outline-secondary:hover { background-color: #6c757d; color: white; }
html[data-theme="dark"] .btn-secondary { background-color: #6c757d; border-color: #6c757d; color: white; }
html[data-theme="dark"] .btn-secondary:hover { background-color: #5a6268; border-color: #545b62; }
html[data-theme="dark"] .btn-outline-secondary { color: #adb5bd; border-color: #6c757d; } /* Lighter text/border */
html[data-theme="dark"] .btn-outline-secondary:hover { background-color: #6c757d; color: white; }
html[data-theme="dark"] .btn-success { background-color: #20c997; border-color: #20c997; color: #000; }
html[data-theme="dark"] .btn-success:hover { background-color: #3ce0ac; border-color: #36d8a3; }
html[data-theme="dark"] .btn-outline-success { color: #20c997; border-color: #20c997; }
html[data-theme="dark"] .btn-outline-success:hover { background-color: #20c997; color: #000; }
html[data-theme="dark"] .btn-success { background-color: #20c997; border-color: #20c997; color: #000; }
html[data-theme="dark"] .btn-success:hover { background-color: #3ce0ac; border-color: #36d8a3; }
html[data-theme="dark"] .btn-outline-success { color: #20c997; border-color: #20c997; }
html[data-theme="dark"] .btn-outline-success:hover { background-color: #20c997; color: #000; }
/* Keep danger as is */
html[data-theme="dark"] .btn-danger { background-color: #e63946; border-color: #e63946; color: white; }
html[data-theme="dark"] .btn-danger:hover { background-color: #c82b39; border-color: #bd2130; }
html[data-theme="dark"] .btn-outline-danger { color: #e63946; border-color: #e63946; }
html[data-theme="dark"] .btn-outline-danger:hover { background-color: #e63946; color: white; }
/* Keep danger as is */
html[data-theme="dark"] .btn-danger { background-color: #e63946; border-color: #e63946; color: white; }
html[data-theme="dark"] .btn-danger:hover { background-color: #c82b39; border-color: #bd2130; }
html[data-theme="dark"] .btn-outline-danger { color: #e63946; border-color: #e63946; }
html[data-theme="dark"] .btn-outline-danger:hover { background-color: #e63946; color: white; }
html[data-theme="dark"] .btn-warning { background-color: #ffca2c; border-color: #ffca2c; color: #000; }
html[data-theme="dark"] .btn-warning:hover { background-color: #ffd24a; border-color: #ffce3d; }
html[data-theme="dark"] .btn-outline-warning { color: #ffca2c; border-color: #ffca2c; }
html[data-theme="dark"] .btn-outline-warning:hover { background-color: #ffca2c; color: #000; }
html[data-theme="dark"] .btn-warning { background-color: #ffca2c; border-color: #ffca2c; color: #000; }
html[data-theme="dark"] .btn-warning:hover { background-color: #ffd24a; border-color: #ffce3d; }
html[data-theme="dark"] .btn-outline-warning { color: #ffca2c; border-color: #ffca2c; }
html[data-theme="dark"] .btn-outline-warning:hover { background-color: #ffca2c; color: #000; }
html[data-theme="dark"] .btn-info { background-color: #22b8cf; border-color: #22b8cf; color: #000; }
html[data-theme="dark"] .btn-info:hover { background-color: #44cee3; border-color: #38cae0; }
html[data-theme="dark"] .btn-outline-info { color: #22b8cf; border-color: #22b8cf; }
html[data-theme="dark"] .btn-outline-info:hover { background-color: #22b8cf; color: #000; }
html[data-theme="dark"] .btn-info { background-color: #22b8cf; border-color: #22b8cf; color: #000; }
html[data-theme="dark"] .btn-info:hover { background-color: #44cee3; border-color: #38cae0; }
html[data-theme="dark"] .btn-outline-info { color: #22b8cf; border-color: #22b8cf; }
html[data-theme="dark"] .btn-outline-info:hover { background-color: #22b8cf; color: #000; }
/* Dark Theme Badges */
html[data-theme="dark"] .badge.bg-primary { background-color: #4dabf7 !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-secondary { background-color: #6c757d !important; color: white !important; }
html[data-theme="dark"] .badge.bg-success { background-color: #20c997 !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-danger { background-color: #e63946 !important; color: white !important; }
html[data-theme="dark"] .badge.bg-warning { background-color: #ffca2c !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-info { background-color: #22b8cf !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-light { background-color: #495057 !important; color: #f8f9fa !important; border: 1px solid #6c757d; }
html[data-theme="dark"] .badge.bg-dark { background-color: #adb5bd !important; color: #000 !important; }
/* Dark Theme Badges */
html[data-theme="dark"] .badge.bg-primary { background-color: #4dabf7 !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-secondary { background-color: #6c757d !important; color: white !important; }
html[data-theme="dark"] .badge.bg-success { background-color: #20c997 !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-danger { background-color: #e63946 !important; color: white !important; }
html[data-theme="dark"] .badge.bg-warning { background-color: #ffca2c !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-info { background-color: #22b8cf !important; color: #000 !important; }
html[data-theme="dark"] .badge.bg-light { background-color: #495057 !important; color: #f8f9fa !important; border: 1px solid #6c757d; }
html[data-theme="dark"] .badge.bg-dark { background-color: #adb5bd !important; color: #000 !important; }
/* --- Themed Backgrounds for Code/Structure View (cp_view_processed_ig.html) --- */
/* --- Themed Backgrounds for Code/Structure View (cp_view_processed_ig.html) --- */
/* Styling for <pre> blocks containing code */
#raw-structure-wrapper pre,
#example-content-wrapper pre {
background-color: #e9ecef; /* Light theme background */
color: #212529; /* Light theme text */
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
max-height: 400px; /* Keep existing max-height */
overflow-y: auto; /* Keep existing overflow */
white-space: pre-wrap; /* Ensure wrapping */
word-break: break-all; /* Break long strings */
}
/* Styling for <pre> blocks containing code */
#raw-structure-wrapper pre,
#example-content-wrapper pre {
background-color: #e9ecef; /* Light theme background */
color: #212529; /* Light theme text */
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
max-height: 400px; /* Keep existing max-height */
overflow-y: auto; /* Keep existing overflow */
white-space: pre-wrap; /* Ensure wrapping */
word-break: break-all; /* Break long strings */
}
/* Ensure code tag inside inherits background and has appropriate font */
#raw-structure-wrapper pre code,
#example-content-wrapper pre code {
background-color: transparent !important; /* Let pre handle background */
padding: 0;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875em;
color: inherit; /* Inherit text color from pre */
display: block; /* Make code block fill pre */
white-space: pre-wrap;
word-break: break-all;
}
/* Ensure code tag inside inherits background and has appropriate font */
#raw-structure-wrapper pre code,
#example-content-wrapper pre code {
background-color: transparent !important; /* Let pre handle background */
padding: 0;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875em;
color: inherit; /* Inherit text color from pre */
display: block; /* Make code block fill pre */
white-space: pre-wrap;
word-break: break-all;
}
/* Styling for the Structure Definition tree list items */
.structure-tree-root .list-group-item {
background-color: #ffffff; /* Light theme default background */
border-color: #dee2e6;
color: #212529;
}
/* Styling for the Structure Definition tree list items */
.structure-tree-root .list-group-item {
background-color: #ffffff; /* Light theme default background */
border-color: #dee2e6;
color: #212529;
}
.structure-tree-root .list-group-item-warning {
background-color: #fff3cd; /* Bootstrap light theme warning */
border-color: #ffeeba;
/*color: #856404; Ensure text is readable */
}
.structure-tree-root .list-group-item-warning {
background-color: #fff3cd; /* Bootstrap light theme warning */
border-color: #ffeeba;
/*color: #856404; Ensure text is readable */
}
/* --- Dark Theme Overrides for Code/Structure View --- */
html[data-theme="dark"] #raw-structure-wrapper pre,
html[data-theme="dark"] #example-content-wrapper pre {
background-color: #495057; /* Dark theme background */
color: #f8f9fa; /* Dark theme text */
border-color: #6c757d;
}
/* --- Dark Theme Overrides for Code/Structure View --- */
html[data-theme="dark"] #raw-structure-wrapper pre,
html[data-theme="dark"] #example-content-wrapper pre {
background-color: #495057; /* Dark theme background */
color: #f8f9fa; /* Dark theme text */
border-color: #6c757d;
}
/* Dark theme code text color already inherited, but explicit doesn't hurt */
html[data-theme="dark"] #raw-structure-wrapper pre code,
html[data-theme="dark"] #example-content-wrapper pre code {
color: #f8f9fa;
}
/* Dark theme code text color already inherited, but explicit doesn't hurt */
html[data-theme="dark"] #raw-structure-wrapper pre code,
html[data-theme="dark"] #example-content-wrapper pre code {
color: #f8f9fa;
}
/* Dark theme structure list items */
html[data-theme="dark"] .structure-tree-root .list-group-item {
background-color: #343a40; /* Dark theme card background */
border-color: #495057;
color: #f8f9fa;
}
/* Dark theme structure list items */
html[data-theme="dark"] .structure-tree-root .list-group-item {
background-color: #343a40; /* Dark theme card background */
border-color: #495057;
color: #f8f9fa;
}
html[data-theme="dark"] .structure-tree-root .list-group-item-warning {
background-color: #664d03; /* Darker warning background */
border-color: #997404;
/*color: #ffecb5; Lighter text for contrast */
}
html[data-theme="dark"] .structure-tree-root .list-group-item-warning {
background-color: #664d03; /* Darker warning background */
border-color: #997404;
/*color: #ffecb5; Lighter text for contrast */
}
/* Dark Theme Styles */
html[data-theme="dark"] body {
@ -780,7 +782,7 @@ html[data-theme="dark"] .structure-tree-root .list-group-item-warning {
<li class="nav-item">
<a class="nav-link {{ 'active' if request.endpoint == 'fhir_ui' else '' }}" href="{{ url_for('fhir_ui') }}"><i class="fas fa-cloud-download-alt me-1"></i> FHIR API Explorer</a>
</li>
<li class="nav-item">
<li class="nav-item">
<a class="nav-link {{ 'active' if request.endpoint == 'fhir_ui_operations' else '' }}" href="{{ url_for('fhir_ui_operations') }}"><i class="bi bi-gear me-1"></i> FHIR UI Operations</a>
</li>
<li class="nav-item">

View File

@ -1,22 +1,162 @@
{% extends "base.html" %}
{% from "_form_helpers.html" import render_field %}
{% block content %}
<div class="px-4 py-5 my-5 text-center">
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE Ig Toolkit" width="192" height="192">
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE IG Toolkit" width="192" height="192">
<h1 class="display-5 fw-bold text-body-emphasis">Import FHIR Implementation Guides</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">
Import new FHIR Implementation Guides to the system for viewing.
</p>
<!-----------------------------------------------------------------remove the buttons-----------------------------------------------------
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
<a href="{{ url_for('index') }}" class="btn btn-primary btn-lg px-4 gap-3">Back to Home</a>
<a href="{{ url_for('view_igs') }}" class="btn btn-outline-secondary btn-lg px-4">View Downloaded IGs</a>
<a href="{{ url_for('push_igs') }}" class="btn btn-outline-secondary btn-lg px-4">Upload IG's</a>
</div>
</div>
<!-- Fire Animation Loading Overlay -->
<div id="fire-loading-overlay" class="fire-loading-overlay d-none">
<div class="campfire">
<div class="sparks">
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
</div>
-----------------------------------------------------------------remove the buttons----------------------------------------------------->
<div class="logs">
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
<div class="log">
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
<div class="streak"></div>
</div>
</div>
<div class="sticks">
<div class="stick"></div>
<div class="stick"></div>
<div class="stick"></div>
<div class="stick"></div>
</div>
<div class="fire">
<div class="fire__red">
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
</div>
<div class="fire__orange">
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
</div>
<div class="fire__yellow">
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
</div>
<div class="fire__white">
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
<div class="flame"></div>
</div>
</div>
</div>
<div class="text-center mt-3">
<p class="text-white mt-3">Importing Implementation Guide... Please wait.</p>
<p id="log-display" class="text-white mb-0"></p>
</div>
</div>
@ -26,21 +166,13 @@
<div class="col-md-6">
<div class="card">
<div class="card-body">
<form method="POST" class="form">
<form id="import-ig-form" method="POST" class="form">
{{ form.hidden_tag() }}
{{ render_field(form.package_name) }}
{{ render_field(form.package_version) }}
<!-- Dependency Pulling Mode Toggle -->
<div class="mb-3">
<label for="dependency_mode" class="form-label">Dependency Pulling Mode:</label>
<select class="form-select" id="dependency_mode" name="dependency_mode">
<option value="recursive" selected>Current Recursive</option>
<option value="patch-canonical">Patch Canonical Versions</option>
<option value="tree-shaking">Tree Shaking (Only Used Dependencies)</option>
</select>
</div>
{{ render_field(form.dependency_mode) }}
<div class="d-grid gap-2 d-sm-flex">
{{ form.submit(class="btn btn-success") }}
{{ form.submit(class="btn btn-success", id="submit-btn") }}
<a href="{{ url_for('index') }}" class="btn btn-secondary">Back</a>
</div>
</form>
@ -49,4 +181,84 @@
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('import-ig-form');
const submitBtn = document.getElementById('submit-btn');
const loadingOverlay = document.getElementById('fire-loading-overlay');
const logDisplay = document.getElementById('log-display');
if (form && submitBtn && loadingOverlay && logDisplay) {
form.addEventListener('submit', async function(event) {
event.preventDefault();
submitBtn.disabled = true;
logDisplay.textContent = 'Starting import...';
loadingOverlay.classList.remove('d-none');
let eventSource;
try {
// Start SSE connection for logs
eventSource = new EventSource('{{ url_for('stream_import_logs') }}');
eventSource.onmessage = function(event) {
if (event.data === '[DONE]') {
eventSource.close();
return;
}
logDisplay.textContent = event.data;
};
eventSource.onerror = function() {
console.error('SSE connection error');
eventSource.close();
logDisplay.textContent = 'Log streaming interrupted. Import may still be in progress.';
};
} catch (e) {
console.error('Failed to initialize SSE:', e);
logDisplay.textContent = 'Unable to stream logs: ' + e.message;
}
// Submit form via AJAX
const formData = new FormData(form);
try {
const response = await fetch('{{ url_for('import_ig') }}', {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
body: formData
});
if (eventSource) eventSource.close();
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `HTTP error: ${response.status}`);
}
const data = await response.json();
if (data.redirect) {
window.location.href = data.redirect;
} else {
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success mt-3';
alertDiv.textContent = data.message || 'Import successful!';
form.parentElement.appendChild(alertDiv);
}
} catch (error) {
console.error('Import error:', error);
if (eventSource) eventSource.close();
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger mt-3';
alertDiv.textContent = `Import failed: ${error.message}`;
form.parentElement.appendChild(alertDiv);
} finally {
loadingOverlay.classList.add('d-none');
submitBtn.disabled = false;
}
});
} else {
console.error('Required elements missing: form, submit button, loading overlay, or log display.');
}
});
</script>
{% endblock %}

View File

@ -2,23 +2,162 @@
{% block content %}
<div class="px-4 py-5 my-5 text-center">
<img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE Ig Toolkit" width="512" height="512">
<h1 class="display-5 fw-bold text-body-emphasis">Welcome to {{ site_name }}</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">
Simple tool for importing, viewing, and validating FHIR Implementation Guides.
</p>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center flex-wrap">
<a href="{{ url_for('import_ig') }}" class="btn btn-primary btn-lg px-4 gap-3 mb-2">Import FHIR IG</a>
<a href="{{ url_for('view_igs') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">Manage FHIR Packages</a>
<a href="{{ url_for('push_igs') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">Push IGs</a>
<a href="{{ url_for('upload_test_data') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">Upload Test Data</a>
<a href="{{ url_for('validate_sample') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">Validate FHIR Sample</a>
<a href="{{ url_for('fhir_ui') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">FHIR API Explorer</a>
<a href="{{ url_for('fhir_ui_operations') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">FHIR UI Operations</a>
<a href="{{ url_for('fsh_converter') }}" class="btn btn-outline-secondary btn-lg px-4 mb-2">FSH Converter</a>
<a href="{{ url_for('about') }}" class="btn btn-outline-info btn-lg px-4 mb-2">About</a>
<div id="logo-container" class="mb-4">
<img class="d-block mx-auto" src="{{ url_for('static', filename='FHIRFLARE.png') }}" alt="FHIRFLARE IG Toolkit" width="256" height="256">
</div>
<div id="animation-container" class="mb-4 d-none">
<div class="section-center">
<div class="moon">
<div></div>
<div></div>
<div></div>
</div>
<div class="shooting-star"></div>
<div class="shooting-star-2"></div>
<div class="star"></div>
<div class="star snd"></div>
<div class="star trd"></div>
<div class="star fth"></div>
<div class="star fith"></div>
<div class="circle"></div>
<div class="wood-circle"></div>
<div class="wood"></div>
<div class="tree-1"></div>
<div class="tree-2"></div>
<div class="fire">
<span></span>
<span></span>
<span></span>
</div>
<div class="smoke">
<span class="s-0"></span>
<span class="s-1"></span>
<span class="s-2"></span>
<span class="s-3"></span>
<span class="s-4"></span>
<span class="s-5"></span>
<span class="s-6"></span>
<span class="s-7"></span>
<span class="s-8"></span>
<span class="s-9"></span>
</div>
</div>
</div>
<h1 class="display-5 fw-bold text-body-emphasis">Welcome to {{ site_name }}</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-2">Simple tool for importing, viewing, and validating FHIR Implementation Guides.</p>
<p class="text-muted">Streamline Your FHIR Workflow</p>
</div>
</div>
<div class="container">
<div class="row g-4">
<!-- IG Management -->
<div class="col-md-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">IG Management</h5>
<p class="card-text">Import and manage FHIR Implementation Guides.</p>
<div class="d-grid gap-2">
<a href="{{ url_for('import_ig') }}" class="btn btn-primary">Import FHIR IG</a>
<a href="{{ url_for('view_igs') }}" class="btn btn-outline-secondary">Manage FHIR Packages</a>
<a href="{{ url_for('push_igs') }}" class="btn btn-outline-secondary">Push IGs</a>
</div>
</div>
</div>
</div>
<!-- Validation & Testing -->
<div class="col-md-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">Validation & Testing</h5>
<p class="card-text">Validate and test FHIR resources.</p>
<div class="d-grid gap-2">
<a href="{{ url_for('upload_test_data') }}" class="btn btn-outline-secondary">Upload Test Data</a>
<a href="{{ url_for('validate_sample') }}" class="btn btn-outline-secondary">Validate FHIR Sample</a>
</div>
</div>
</div>
</div>
<!-- API & Tools -->
<div class="col-md-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title">API & Tools</h5>
<p class="card-text">Explore FHIR APIs and convert resources.</p>
<div class="d-grid gap-2">
<a href="{{ url_for('fhir_ui') }}" class="btn btn-outline-secondary">FHIR API Explorer</a>
<a href="{{ url_for('fhir_ui_operations') }}" class="btn btn-outline-secondary">FHIR UI Operations</a>
<a href="{{ url_for('fsh_converter') }}" class="btn btn-outline-secondary">FSH Converter</a>
</div>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<a href="{{ url_for('about') }}" class="btn btn-outline-info">About FHIRFLARE</a>
</div>
</div>
<style>
#logo-container {
animation: fadeOut 30s forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
95% { opacity: 1; }
100% { opacity: 0; display: none; }
}
#animation-container {
transition: opacity 1s ease;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
const logoContainer = document.getElementById('logo-container');
const animationContainer = document.getElementById('animation-container');
const htmlElement = document.documentElement;
// After 30 seconds, fade out logo and show animation
setTimeout(() => {
logoContainer.style.display = 'none';
animationContainer.classList.remove('d-none');
animationContainer.style.opacity = '1';
}, 30000);
// Sync animation with theme (inverted: light = fire-off, dark = fire-on)
const updateAnimationState = () => {
const isDark = htmlElement.getAttribute('data-theme') === 'dark';
document.body.classList.toggle('fire-off', !isDark); // Light theme: fire-off, Dark theme: fire-on
};
// Initial state
updateAnimationState();
// Override toggleTheme to ensure animation updates
const originalToggleTheme = window.toggleTheme || function() {};
window.toggleTheme = function() {
originalToggleTheme();
updateAnimationState();
};
// Observe changes to data-theme attribute
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'data-theme') {
updateAnimationState();
}
});
});
observer.observe(htmlElement, { attributes: true });
// Clean up observer on page unload
window.addEventListener('unload', () => {
observer.disconnect();
});
});
</script>
{% endblock %}