Fork project

This commit is contained in:
2025-09-19 15:59:08 +08:00
commit 2f921b6209
52 changed files with 4012 additions and 0 deletions

150
utils/cache.py Normal file
View File

@ -0,0 +1,150 @@
import ipaddress
import warnings
from CTFd.cache import cache
from CTFd.utils import get_config
from flask_redis import FlaskRedis
from redis.exceptions import LockError
from .db import DBContainer
class CacheProvider:
def __init__(self, app, *args, **kwargs):
if app.config['CACHE_TYPE'] == 'redis':
self.provider = RedisCacheProvider(app, *args, **kwargs)
elif app.config['CACHE_TYPE'] in ['filesystem', 'simple']:
if not hasattr(CacheProvider, 'cache'):
CacheProvider.cache = {}
self.provider = FilesystemCacheProvider(app, *args, **kwargs)
self.init_port_sets()
def init_port_sets(self):
self.clear()
containers = DBContainer.get_all_container()
used_port_list = []
for container in containers:
if container.port != 0:
used_port_list.append(container.port)
for port in range(int(get_config("whale:frp_direct_port_minimum", 29000)),
int(get_config("whale:frp_direct_port_maximum", 28000)) + 1):
if port not in used_port_list:
self.add_available_port(port)
from .docker import get_docker_client
client = get_docker_client()
docker_subnet = get_config("whale:docker_subnet", "174.1.0.0/16")
docker_subnet_new_prefix = int(
get_config("whale:docker_subnet_new_prefix", "24"))
exist_networks = []
available_networks = []
for network in client.networks.list(filters={'label': 'prefix'}):
exist_networks.append(str(network.attrs['Labels']['prefix']))
for network in list(ipaddress.ip_network(docker_subnet).subnets(new_prefix=docker_subnet_new_prefix)):
if str(network) not in exist_networks:
available_networks.append(str(network))
self.add_available_network_range(*set(available_networks))
def __getattr__(self, name):
return self.provider.__getattribute__(name)
class FilesystemCacheProvider:
def __init__(self, app, *args, **kwargs):
warnings.warn(
'\n[CTFd Whale] Warning: looks like you are using filesystem cache. '
'\nThis is for TESTING purposes only, DO NOT USE on production sites.',
RuntimeWarning
)
self.key = 'ctfd_whale_lock-' + str(kwargs.get('user_id', 0))
self.global_port_key = "ctfd_whale-port-set"
self.global_network_key = "ctfd_whale-network-set"
def clear(self):
cache.set(self.global_port_key, set())
cache.set(self.global_network_key, set())
def add_available_network_range(self, *ranges):
s = cache.get(self.global_network_key)
s.update(ranges)
cache.set(self.global_network_key, s)
def get_available_network_range(self):
try:
s = cache.get(self.global_network_key)
r = s.pop()
cache.set(self.global_network_key, s)
return r
except KeyError:
return None
def add_available_port(self, port):
s = cache.get(self.global_port_key)
s.add(port)
cache.set(self.global_port_key, s)
def get_available_port(self):
try:
s = cache.get(self.global_port_key)
r = s.pop()
cache.set(self.global_port_key, s)
return r
except KeyError:
return None
def acquire_lock(self):
# for testing purposes only, so no need to set this limit
return True
def release_lock(self):
return True
class RedisCacheProvider(FlaskRedis):
def __init__(self, app, *args, **kwargs):
super().__init__(app)
self.key = 'ctfd_whale_lock-' + str(kwargs.get('user_id', 0))
self.current_lock = None
self.global_port_key = "ctfd_whale-port-set"
self.global_network_key = "ctfd_whale-network-set"
def clear(self):
self.delete(self.global_port_key)
self.delete(self.global_network_key)
def add_available_network_range(self, *ranges):
self.sadd(self.global_network_key, *ranges)
def get_available_network_range(self):
return self.spop(self.global_network_key).decode()
def add_available_port(self, port):
self.sadd(self.global_port_key, str(port))
def get_available_port(self):
return int(self.spop(self.global_port_key))
def acquire_lock(self):
lock = self.lock(name=self.key, timeout=10)
if not lock.acquire(blocking=True, blocking_timeout=2.0):
return False
self.current_lock = lock
return True
def release_lock(self):
if self.current_lock is None:
return False
try:
self.current_lock.release()
return True
except LockError:
return False