Files
CTFd-Whale/__init__.py
2025-09-19 22:16:58 +08:00

131 lines
4.5 KiB
Python

import fcntl
import warnings
import requests
from flask import Blueprint, render_template, session, current_app, request
from flask_apscheduler import APScheduler
from CTFd.api import CTFd_API_v1
from CTFd.plugins import (
register_plugin_assets_directory,
register_admin_plugin_menu_bar,
)
from CTFd.plugins.challenges import CHALLENGE_CLASSES
from CTFd.utils import get_config, set_config
from CTFd.utils.decorators import admins_only
from .api import user_namespace, admin_namespace, AdminContainers
from .challenge_type import DynamicValueDockerChallenge
from .utils.checks import WhaleChecks
from .utils.control import ControlUtil
from .utils.db import DBContainer
from .utils.docker import DockerUtils
from .utils.exceptions import WhaleWarning
from .utils.setup import setup_default_configs
from .utils.routers import Router
def load(app):
app.config['RESTX_ERROR_404_HELP'] = False
# upgrade()
plugin_name = __name__.split('.')[-1]
set_config('whale:plugin_name', plugin_name)
app.db.create_all()
if not get_config("whale:setup"):
setup_default_configs()
register_plugin_assets_directory(
app, base_path=f"/plugins/{plugin_name}/assets",
endpoint='plugins.ctfd-whale.assets'
)
register_admin_plugin_menu_bar(
title='Whale',
route='/plugins/ctfd-whale/admin/settings'
)
DynamicValueDockerChallenge.templates = {
"create": f"/plugins/{plugin_name}/assets/create.html",
"update": f"/plugins/{plugin_name}/assets/update.html",
"view": f"/plugins/{plugin_name}/assets/view.html",
}
DynamicValueDockerChallenge.scripts = {
"create": "/plugins/ctfd-whale/assets/create.js",
"update": "/plugins/ctfd-whale/assets/update.js",
"view": "/plugins/ctfd-whale/assets/view.js",
}
CHALLENGE_CLASSES["dynamic_docker"] = DynamicValueDockerChallenge
page_blueprint = Blueprint(
"ctfd-whale",
__name__,
template_folder="templates",
static_folder="assets",
url_prefix="/plugins/ctfd-whale"
)
CTFd_API_v1.add_namespace(admin_namespace, path="/plugins/ctfd-whale/admin")
CTFd_API_v1.add_namespace(user_namespace, path="/plugins/ctfd-whale")
worker_config_commit = None
@page_blueprint.route('/admin/settings')
@admins_only
def admin_list_configs():
nonlocal worker_config_commit
errors = WhaleChecks.perform()
if not errors and get_config("whale:refresh") != worker_config_commit:
worker_config_commit = get_config("whale:refresh")
DockerUtils.init()
Router.reset()
set_config("whale:refresh", "false")
return render_template('whale_config.html', errors=errors)
@page_blueprint.route("/admin/containers")
@admins_only
def admin_list_containers():
page_type = request.args.get("type", "instances")
if page_type == "logs":
result = AdminContainers.logs()
session['page_type'] = 'logs'
else:
result = AdminContainers.get()
session['page_type'] = 'containers'
view_mode = request.args.get('mode', session.get('view_mode', 'list'))
session['view_mode'] = view_mode
return render_template("whale_containers.html",
plugin_name=plugin_name,
containers=result['data']['containers'],
pages=result['data']['pages'],
curr_page=abs(request.args.get("page", 1, type=int)),
curr_page_start=result['data']['page_start'])
def auto_clean_container():
with app.app_context():
results = DBContainer.get_all_expired_container()
for r in results:
ControlUtil.try_remove_container(r.user_id)
app.register_blueprint(page_blueprint)
try:
Router.check_availability()
DockerUtils.init()
except Exception:
warnings.warn("Initialization Failed. Please check your configs.", WhaleWarning)
try:
lock_file = open("/tmp/ctfd_whale.lock", "w")
lock_fd = lock_file.fileno()
fcntl.lockf(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
scheduler = APScheduler()
scheduler.init_app(app)
scheduler.start()
scheduler.add_job(
id='whale-auto-clean', func=auto_clean_container,
trigger="interval", seconds=10
)
print("[CTFd Whale] Started successfully")
except IOError:
pass