diff --git a/.gitignore b/.gitignore index 2911c18..5dd71aa 100644 --- a/.gitignore +++ b/.gitignore @@ -265,3 +265,4 @@ tags # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) main.py +hsman/.flaskenv diff --git a/hsapi/hsapi/__init__.py b/hsapi/hsapi/__init__.py index 1b77937..8c4c99b 100644 --- a/hsapi/hsapi/__init__.py +++ b/hsapi/hsapi/__init__.py @@ -2,3 +2,4 @@ from .apikeys import APIKey from .nodes import Node from .users import User from .routes import Route +from .preauthkeys import PreAuthKey, v1ListPreAuthKeyRequest diff --git a/hsapi/hsapi/config.py b/hsapi/hsapi/config.py index 14d9d57..31fed30 100644 --- a/hsapi/hsapi/config.py +++ b/hsapi/hsapi/config.py @@ -5,14 +5,14 @@ from pydantic_settings import BaseSettings, SettingsConfigDict class APISettings(BaseSettings): - model_config = SettingsConfigDict(env_prefix='HS_') + model_config = SettingsConfigDict(env_prefix='HSAPI_') server: str = "http://localhost:8080" api_path: str = "/api/v1" api_token: Union[str, None] = None ssl_verify: Union[bool, str] = True def refresh_api_token(self): - self.api_token = os.environ.get('HS_API_TOKEN', 'default') + self.api_token = os.environ.get('HSAPI_API_TOKEN', 'default') class HTTPException(Exception): diff --git a/hsman/app/__init__.py b/hsman/app/__init__.py index 73c4b19..2084a3e 100644 --- a/hsman/app/__init__.py +++ b/hsman/app/__init__.py @@ -2,19 +2,19 @@ from flask import Flask, render_template from werkzeug.exceptions import HTTPException from flask_mobility import Mobility -from flask_pyoidc import OIDCAuthentication +# from flask_pyoidc import OIDCAuthentication from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata from . import filters +from .lib import OIDCAuthentication import os mobility = Mobility() client_metadata = ClientMetadata( client_id='***REMOVED***', - client_secret='***REMOVED***', - post_logout_redirect_uris=['https://example.com/logout']) + client_secret='***REMOVED***') provider_config = ProviderConfiguration(issuer='***REMOVED***', @@ -55,8 +55,7 @@ def create_app(environment='development'): filters.init_app(app) app.config.update( - OIDC_REDIRECT_URI='http://localhost:5000/redirect', - SECRET_KEY="secreto" + OIDC_REDIRECT_URI='http://localhost:5000/auth', ) auth.init_app(app) diff --git a/hsman/app/lib.py b/hsman/app/lib.py index 2c5375a..3f0439f 100644 --- a/hsman/app/lib.py +++ b/hsman/app/lib.py @@ -1,21 +1,152 @@ -from app import models import os -from flask import request +import functools + +from flask import request, abort, current_app +from flask import session as flask_session +from flask_pyoidc import OIDCAuthentication as _OIDCAuth +from flask_pyoidc.user_session import UserSession + +from typing import Callable, List import logging log = logging.getLogger(__name__) -def remote_ip(): +def remote_ip() -> str: if 'HTTP_X_FORWARDED_FOR' in request.environ: xff_parts = request.environ.get('HTTP_X_FORWARDED_FOR').split(',') return xff_parts[0] else: - return request.environ.get('REMOTE_ADDR') + return str(request.environ.get('REMOTE_ADDR')) -def webMode(): +def webMode() -> bool: is_gunicorn = "gunicorn" in os.environ.get('SERVER_SOFTWARE', '') is_werkzeug = os.environ.get('WERKZEUG_RUN_MAIN', False) == "true" return is_gunicorn or is_werkzeug + + +class OIDCAuthentication(_OIDCAuth): + + def authorize(self, provider_name: str, authz_fn: Callable, **kwargs): + if provider_name not in self._provider_configurations: + raise ValueError( + f"Provider name '{provider_name}' not in configured providers: { + self._provider_configurations.keys()}." + ) + + # We save args with which we have been called + external_args = kwargs + + # Decorator + def oidc_decorator(view_func): + @functools.wraps(view_func) + def wrapper(*args, **kwargs): + # Retrieve session and client + session = UserSession(flask_session, provider_name) + client = self.clients[session.current_provider] + + # Check session validity + if session.should_refresh(client.session_refresh_interval_seconds): + log.debug('user auth will be refreshed "silently"') + return self._authenticate(client, interactive=False) + elif session.is_authenticated(): + log.debug('user is already authenticated') + else: + log.debug('user not authenticated, start flow') + return self._authenticate(client) + + # Call authorization function that must return true or false + authorized = authz_fn(session, **external_args) + if authorized: + return view_func(*args, **kwargs) + else: + return abort(403) + return wrapper + + return oidc_decorator + + def authorize_domains(self, + provider_name: str, + domains: List[str]): + """ + Authorize a user if the email domain is in a list of domains + """ + + def _authz_fn(session, domains) -> bool: + email = session.userinfo.get('email', "") + domain = email.split('@')[-1] + + if domain in domains: + return True + return False + + return self.authorize(provider_name, + authz_fn=_authz_fn, + domains=domains) + + def authorize_users(self, provider_name: str, users: List[str]): + """ + Authorize a user if the username of the user part of the email + is in a list of usernames + """ + + def _authz_fn(session, users) -> bool: + username = session.userinfo.get('preferred_username', "") + email = session.userinfo.get('email', "") + email_user = email.split('@')[0] + + if username in users or email_user in users: + return True + return False + + return self.authorize(provider_name, + authz_fn=_authz_fn, + users=users) + + def authorize_groups(self, provider_name: str, groups: List[str]): + """ + Authorize members of a list of groups + """ + + def _authz_fn(session, groups) -> bool: + user_groups = session.userinfo.get('groups', []) + + if len(set(groups).intersection(user_groups)): + return True + return False + + return self.authorize(provider_name, + authz_fn=_authz_fn, + groups=groups) + + def authorize_admins(self, provider_name: str): + """ + Authorize admins. + Admins are taken from the app config: + - members of groups in ADMIN_GROUPS + - users in ADMIN_USERS + """ + + def _authz_fn(session) -> bool: + user_groups = session.userinfo.get('groups', []) + username = session.userinfo.get('preferred_username', "") + with current_app.app_context(): + admin_groups = current_app.config.get('ADMIN_GROUPS', []) + admin_users = current_app.config.get('ADMIN_USERS', []) + + authorized_groups = set(admin_groups).intersection(user_groups) + + if len(authorized_groups): + log.debug(f"'{username}' is a member of { + authorized_groups}") + return True + + if username in admin_users: + log.debug(f"'{username}' is an admin user") + return True + return False + + return self.authorize(provider_name, + authz_fn=_authz_fn) diff --git a/hsman/app/static/main.css b/hsman/app/static/main.css index d136ecf..4404482 100644 --- a/hsman/app/static/main.css +++ b/hsman/app/static/main.css @@ -35,34 +35,38 @@ body > .container-fluid { padding-right: 1rem; } -.theme-icon { - width: 1.3em; - height: 1.3em; - margin-right: 10px; -} -a.plain:link { +a:link { color: unset; text-decoration-line: underline; - text-decoration-style: dotted; + /* text-decoration-style: dotted; */ + /* text-decoration-style: dashed; */ } -a.plain:visited { +a:visited { color: unset; text-decoration-line: underline; - text-decoration-style: dotted; + /* text-decoration-style: dotted; */ + /* text-decoration-style: dashed; */ } -a.plain:hover { +a:hover { color: unset; - text-decoration: underline; + text-decoration-line: unset; + /* text-decoration: underline; */ + /* text-decoration-style: dotted; */ } -a.plain:active { +a:active { color: unset; text-decoration: none; } +a.nodeco, a.nav-link, a.navbar-brand, a.routeToggle { + text-decoration-line: none; + text-decoration-style: unset; +} + table.dataTable tbody tr:hover { background-color: #444; } @@ -71,7 +75,27 @@ table.dataTable tbody tr:hover > .sorting_1 { background-color: #444; } -.data:hover{ +div.data:hover{ /* background-color: rgba(63, 140, 211, 0.808); */ background-color: #444; } + +/* primary route */ +a.route.primary { + font-weight: bold; +} +/* route enabled */ +a.route.True { + font-style: italic; + color: #bbbbbb; +} +/* route disabled */ +a.route.False { + font-style: italic; + text-decoration-line: line-through underline; + color: #888; +} + +div.dt-container div.dt-scroll-body { + border-bottom: none !important; +} diff --git a/hsman/app/static/main.js b/hsman/app/static/main.js index 765298f..97f7753 100644 --- a/hsman/app/static/main.js +++ b/hsman/app/static/main.js @@ -1,55 +1,30 @@ -// custom javascript -var sun_icon = ` -
- - - -
-`; - -var moon_icon = ` -
- - - -
-`; - -function getCookie(cookie) { - let name = cookie + "="; - let ca = document.cookie.split(";"); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) == " ") { - c = c.substring(1); - } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return ""; +function renameNode(nodeId) { + var newName = $("#newName").val(); + var url = "/node/" + nodeId + "/rename/" + newName; + $.ajax({ + url: url, + xhrFields: { + withCredentials: true, + }, + success: function (data) { + $("#renameModal").modal("hide"); + $("#givenName").html(data.newName); + }, + }); } -function setCookie(cname, cvalue) { - const d = new Date(); - d.setTime(d.getTime() + 3650 * 24 * 60 * 60 * 1000); // 10 years cookie - let expires = "expires=" + d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; -} - -function toggleTheme(obj) { - console.log(getCookie("theme")); - console.log(document.cookie); - if (getCookie("theme") == "light") { - setCookie("theme", "dark"); - obj.html(sun_icon); - $("body").addClass("bootstrap-dark"); - } else { - // switch to light mode - setCookie("theme", "light"); - obj.html(moon_icon); - $("body").removeClass("bootstrap-dark"); +function copyToClipboard(obj) { + var span = $(obj); + var value = span.attr("data-original-title"); + var original = span.html(); + try { + navigator.clipboard.writeText(value); + span.html("copied!"); + setTimeout(function () { + span.html(original); + }, 500); + } catch (error) { + span.html("error"); + console.error(error); } } diff --git a/hsman/app/templates/base.html b/hsman/app/templates/base.html index 16a28a5..7ba2288 100644 --- a/hsman/app/templates/base.html +++ b/hsman/app/templates/base.html @@ -22,6 +22,8 @@ + + {% block links %}{% endblock %} @@ -59,13 +61,14 @@ routes - - --> + @@ -92,8 +95,8 @@ - diff --git a/hsman/app/templates/index.html b/hsman/app/templates/index.html index ed26721..294ab23 100644 --- a/hsman/app/templates/index.html +++ b/hsman/app/templates/index.html @@ -1,12 +1,71 @@ {% extends "base.html" %} {% block content %} -

-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Egestas tellus rutrum tellus pellentesque eu tincidunt. Aenean et tortor at risus viverra adipiscing. Et malesuada fames ac turpis egestas sed tempus. Amet commodo nulla facilisi nullam vehicula ipsum a arcu. Quam viverra orci sagittis eu volutpat odio. Elit ut aliquam purus sit amet luctus. Vestibulum mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare massa. Ultrices gravida dictum fusce ut placerat orci nulla. Sed faucibus turpis in eu mi bibendum. Vel facilisis volutpat est velit egestas dui id ornare arcu. Scelerisque eu ultrices vitae auctor. Sem nulla pharetra diam sit amet. Lectus proin nibh nisl condimentum id venenatis a condimentum vitae. -

-

+

Welcome, {{ session.userinfo.name }}


-

-Auctor elit sed vulputate mi sit amet mauris. Tincidunt eget nullam non nisi est. Leo vel orci porta non pulvinar neque laoreet suspendisse interdum. Volutpat commodo sed egestas egestas fringilla phasellus faucibus scelerisque. Ipsum dolor sit amet consectetur adipiscing elit pellentesque habitant morbi. Ultricies mi quis hendrerit dolor magna. Porta nibh venenatis cras sed felis eget. Quam vulputate dignissim suspendisse in. Pellentesque dignissim enim sit amet venenatis urna cursus eget nunc. Nunc lobortis mattis aliquam faucibus purus in. -

+

authentication info

+
+
+ email +
+
+ {{ session.userinfo.email }} + +
+
+
+
+ username +
+
+ {{ session.userinfo.preferred_username }} +
+
+
+
+ groups +
+
+ + {{ session.userinfo.groups[0]}} +
+
+ {% for group in session.userinfo.groups[1:] |sort %} +
+
+   +
+
+ {{ group }} +
+
+ {% endfor %} +
+

your devices

+
+
+
registered
+
last event
+
online
+
+{% for node in userNodeList %} +
+
+ {{ node.givenName}} +
+
+ + {{node.createdAt | htime_dt }} + +
+
+ + {{node.lastSeen | htime_dt }} + +
+
+ {{node.online | fancyBool | safe }} +
+
+{% endfor %} {% endblock %} diff --git a/hsman/app/templates/node.html b/hsman/app/templates/node.html index 562f5f3..9359f6a 100644 --- a/hsman/app/templates/node.html +++ b/hsman/app/templates/node.html @@ -6,7 +6,15 @@ name
- {{ node.givenName }} + {{ node.givenName }} + + + + +
@@ -28,6 +36,11 @@ {{ node.expiry | htime_dt }} + + + + +
@@ -130,17 +143,17 @@

+
-
- routes +
routes {% if isExitNode %} Exit Node {% endif %}
- +{% if routes %}
prefix @@ -165,4 +178,33 @@
{% endfor %} +{% else %} +
+
+

No routes announced

+
+
+ + + + +{% endif %} {% endblock %} diff --git a/hsman/app/templates/nodes.html b/hsman/app/templates/nodes.html index e31bf8f..5453ad1 100644 --- a/hsman/app/templates/nodes.html +++ b/hsman/app/templates/nodes.html @@ -10,9 +10,10 @@ name user - registered on - last connect + registered + last event online +   @@ -23,7 +24,11 @@ {{node.givenName}} - {{node.user.name}} + + + {{node.user.name}} + + {{node.createdAt | htime_dt }} @@ -37,6 +42,18 @@ {{node.online | fancyBool | safe}} + + + + + + + + + + + + {% endfor %} @@ -55,6 +72,9 @@ fixedHeader: false, select: false, keys: false, + aoColumnDefs: [ + { 'bSortable': false, 'aTargets': [ -1 ] } + ], }); }) diff --git a/hsman/app/templates/routes.html b/hsman/app/templates/routes.html index 683c22f..e1f998c 100644 --- a/hsman/app/templates/routes.html +++ b/hsman/app/templates/routes.html @@ -2,6 +2,15 @@ {% block content %} +
+ Routing table + + click on the icon in enabled column to toggle route status + +
+
+

+
Exit nodes
@@ -31,7 +40,7 @@ enabled
- primary + active
@@ -39,23 +48,21 @@ {{ prefix}}
- - - - - {{ rts[0].node.givenName}} - - - - + + + {{ rts[0].node.givenName}} + + +
+
+ + {{ rts[0].enabled | fancyBool | safe}} +
{{ rts[0].isPrimary | fancyBool | safe}}
-
- {{ rts[0].enabled | fancyBool | safe}} -
{% for rt in rts[1:] %}
@@ -63,27 +70,17 @@  
- {% if not rt.enabled %} - - - - {{ rt.node.givenName}} - - - - {% else %} - - - - {{ rt.node.givenName}} - - - {% endif %} + + + {{ rt.node.givenName}} + +
+ {{ rt.enabled | fancyBool | safe}} +
{{ rt.isPrimary | fancyBool | safe}} diff --git a/hsman/app/templates/user.html b/hsman/app/templates/user.html index a27c847..daee58e 100644 --- a/hsman/app/templates/user.html +++ b/hsman/app/templates/user.html @@ -1,4 +1,8 @@ {% extends "base.html" %} +{% block links %} + + +{% endblock %} {% block content %} @@ -21,40 +25,130 @@

-
-
-
nodes
-
-
-
-
- name -
-
- last connect -
-
- online -
-
-{% for node in userNodeList %} -
- -
- - {{node.lastSeen | htime_dt }} - -
-
- {{node.online | fancyBool | safe}} -
-
+
nodes
+ + + + + + + + + +{% for node in userNodeList %} + + + + + {% endfor %} - + +
namelast connectonline
+ + {{ node.givenName }} + + + + {{node.lastSeen | htime_dt }} + + + {{node.online | fancyBool | safe}} +
+

+
pre auth keys
+ + + + + + + + + + + +{% for key in preauthKeys %} + + + + + + + +{% endfor %} + +
namecreatedexpirationattributes
+ {{ key.key[:10] }}… + + + {{key.createdAt | htime_dt }} + + + + {{key.expiration | htime_dt }} + + + {% if key.ephemeral %} + ephemereal + {% endif %} + {% if key.reusable %} + reusable + {% endif %} + {% if key.used %} + used + {% endif %} +
{% endblock %} + +{% block scripts %} + + +{% endblock %} diff --git a/hsman/app/templates/users.html b/hsman/app/templates/users.html index f2079e6..cd3562a 100644 --- a/hsman/app/templates/users.html +++ b/hsman/app/templates/users.html @@ -11,6 +11,7 @@ name registered on online +   @@ -29,6 +30,13 @@ {{online[user.name] | fancyBool | safe }} + + + + + + + {% endfor %} @@ -47,6 +55,9 @@ fixedHeader: false, select: false, keys: false, + aoColumnDefs: [ + { 'bSortable': false, 'aTargets': [ -1 ] } + ], }); }) diff --git a/hsman/app/views.py b/hsman/app/views.py index 8dbe380..d042f12 100644 --- a/hsman/app/views.py +++ b/hsman/app/views.py @@ -1,11 +1,13 @@ -from flask import render_template, Blueprint, current_app, g -from flask import request, after_this_request, redirect, session +from flask import render_template, Blueprint +from flask import redirect, session, url_for from app import auth from flask import jsonify from flask_pyoidc.user_session import UserSession -from hsapi import Node, User, Route +from hsapi import Node, User, Route, PreAuthKey, v1ListPreAuthKeyRequest + +from .lib import remote_ip import logging log = logging.getLogger() @@ -13,18 +15,28 @@ log = logging.getLogger() main_blueprint = Blueprint('main', __name__) -# @main_blueprint.before_request -# def set_theme(): -# g.theme = request.cookies.get('theme', 'light') - - @main_blueprint.route('/', methods=['GET', 'POST']) -@auth.oidc_auth('default') +@auth.access_control('default') def index(): user_session = UserSession(session) - return jsonify(access_token=user_session.access_token, - id_token=user_session.id_token, - userinfo=user_session.userinfo) + hs_user = user_session.userinfo['email'].split('@')[0] + userNodeList = [n for n in Node().list().nodes if n.user.name == hs_user] + return render_template('index.html', + userNodeList=userNodeList, + session=user_session) + + +@main_blueprint.route('/token', methods=['GET', 'POST']) +@auth.access_control('default') +def token(): + user_session = UserSession(session) + return jsonify(user_session.userinfo) + + +@main_blueprint.route('/call', methods=['GET', 'POST']) +@auth.access_control('default') +def call(): + return "CALL OK" @main_blueprint.route('/logout') @@ -34,16 +46,15 @@ def logout(): @main_blueprint.route('/nodes', methods=['GET']) +@auth.authorize_admins('default') def nodes(): - nodelist = Node().list() - log.debug(nodelist) - return render_template('nodes.html', nodes=nodelist.nodes) @main_blueprint.route('/node/', methods=['GET']) +@auth.authorize_admins('default') def node(nodeId): # There is a bug in HS api with retrieving a single node # and we added a workaround to hsapi, so node.get() returns a @@ -52,24 +63,21 @@ def node(nodeId): node = Node().get(nodeId) routes = Node().routes(nodeId) isExitNode = any((r for r in routes.routes if r.prefix.endswith('/0'))) - log.debug(node) return render_template("node.html", routes=routes.routes, isExitNode=isExitNode, - node=node.node) + node=node) @main_blueprint.route('/users', methods=['GET']) +@auth.authorize_admins('default') def users(): - userList = User().list() # Get online status of devices of the user online = {} nodeList = Node().list() for user in userList.users: - log.debug(user) userNodeList = [n for n in nodeList.nodes if n.user.name == user.name] - log.debug(userNodeList) online[user.name] = any(map(lambda x: x.online, userNodeList)) return render_template('users.html', @@ -78,23 +86,25 @@ def users(): @main_blueprint.route('/user/', methods=['GET']) +@auth.authorize_admins('default') def user(userName): - # There is a bug in HS api with retrieving a single node - # and we added a workaround to hsapi, so node.get() returns a - # v1Node object instead of v1NodeResponse, so we access directly - # `node`, instead of `node.node` user = User().get(userName) - log.debug(user) userNodeList = [n for n in Node().list().nodes if n.user.name == userName] + preauthkeyreq = v1ListPreAuthKeyRequest(user=userName) + + preauthKeys = PreAuthKey().list(preauthkeyreq) + validpak = [k for k in preauthKeys.preAuthKeys if not k.expired] + return render_template("user.html", user=user.user, + preauthKeys=validpak, userNodeList=userNodeList) @main_blueprint.route('/routes', methods=['GET']) +@auth.authorize_admins('default') def routes(): routes = Route().list() - log.debug(routes) prefixes = set( (r.prefix for r in routes.routes if not r.prefix.endswith('/0'))) @@ -105,7 +115,59 @@ def routes(): for prefix in prefixes: rrp = [x for x in routes.routes if x.prefix == prefix] final[prefix] = sorted(rrp, key=lambda x: x.isPrimary, reverse=True) - log.debug(final) return render_template("routes.html", exitNodes=exitNodes, routes=final) + + +@main_blueprint.route('/routeToggle/', methods=['GET']) +@auth.authorize_admins('default') +def routeToggle(routeId: int): + routes = Route().list() + route = [r for r in routes.routes if r.id == routeId] + if route: + route = route[0] + if route.enabled: + Route().disable(routeId) + else: + Route().enable(routeId) + return redirect(url_for("main.routes")) + + +@main_blueprint.route('/node//expire', methods=['GET']) +@auth.authorize_admins('default') +def expireNode(nodeId: int): + Node().expire(nodeId) + return redirect(url_for("main.node", nodeId=nodeId)) + + +@main_blueprint.route('/node//list-expire', methods=['GET']) +@auth.authorize_admins('default') +def expireNodeList(nodeId: int): + Node().expire(nodeId) + return redirect(url_for("main.nodes")) + + +@main_blueprint.route('/node//delete', methods=['GET']) +@auth.authorize_admins('default') +def deleteNode(nodeId: int): + Node().delete(nodeId) + return redirect(url_for("main.nodes")) + + +@main_blueprint.route('/node//rename/', methods=['GET']) +@auth.authorize_admins('default') +def renameNode(nodeId: int, newName: str): + Node().rename(nodeId, newName) + return jsonify(dict(newName=newName)) + + +@main_blueprint.route('/user//delete', methods=['GET']) +@auth.authorize_admins('default') +def deleteUser(userName: str): + nodes = Node().byUser(userName) + for node in nodes.nodes: + Node().expire(node.id) + Node().delete(node.id) + User().delete(userName) + return redirect(url_for("main.users")) diff --git a/hsman/poetry.lock b/hsman/poetry.lock index f1d21dd..4a12d93 100644 --- a/hsman/poetry.lock +++ b/hsman/poetry.lock @@ -447,11 +447,11 @@ tornado = ["tornado (>=0.2)"] [[package]] name = "hsapi" version = "0.9.0" -description = "Headscale API module" +description = "Headscale API client" optional = false python-versions = ">=3.11,<4.0" files = [ - {file = "hsapi-0.9.0-py3-none-any.whl", hash = "sha256:71697fca037bbebf6b6daf233b1ee7c43c52732f5780a9543a2dac154aef556b"}, + {file = "hsapi-0.9.0-py3-none-any.whl", hash = "sha256:51b0a2424d6a93451710129f6232aa1552a83402f59e3e9a2cb7525000886d54"}, ] [package.dependencies] @@ -851,109 +851,121 @@ files = [ [[package]] name = "pydantic" -version = "2.7.4" +version = "2.8.0" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, - {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, + {file = "pydantic-2.8.0-py3-none-any.whl", hash = "sha256:ead4f3a1e92386a734ca1411cb25d94147cf8778ed5be6b56749047676d6364e"}, + {file = "pydantic-2.8.0.tar.gz", hash = "sha256:d970ffb9d030b710795878940bd0489842c638e7252fc4a19c3ae2f7da4d6141"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.4" -typing-extensions = ">=4.6.1" +pydantic-core = "2.20.0" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] [package.extras] email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.20.0" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, - {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, - {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, + {file = "pydantic_core-2.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e9dcd7fb34f7bfb239b5fa420033642fff0ad676b765559c3737b91f664d4fa9"}, + {file = "pydantic_core-2.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:649a764d9b0da29816889424697b2a3746963ad36d3e0968784ceed6e40c6355"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7701df088d0b05f3460f7ba15aec81ac8b0fb5690367dfd072a6c38cf5b7fdb5"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab760f17c3e792225cdaef31ca23c0aea45c14ce80d8eff62503f86a5ab76bff"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb1ad5b4d73cde784cf64580166568074f5ccd2548d765e690546cff3d80937d"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b81ec2efc04fc1dbf400647d4357d64fb25543bae38d2d19787d69360aad21c9"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4a9732a5cad764ba37f3aa873dccb41b584f69c347a57323eda0930deec8e10"}, + {file = "pydantic_core-2.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6dc85b9e10cc21d9c1055f15684f76fa4facadddcb6cd63abab702eb93c98943"}, + {file = "pydantic_core-2.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:21d9f7e24f63fdc7118e6cc49defaab8c1d27570782f7e5256169d77498cf7c7"}, + {file = "pydantic_core-2.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8b315685832ab9287e6124b5d74fc12dda31e6421d7f6b08525791452844bc2d"}, + {file = "pydantic_core-2.20.0-cp310-none-win32.whl", hash = "sha256:c3dc8ec8b87c7ad534c75b8855168a08a7036fdb9deeeed5705ba9410721c84d"}, + {file = "pydantic_core-2.20.0-cp310-none-win_amd64.whl", hash = "sha256:85770b4b37bb36ef93a6122601795231225641003e0318d23c6233c59b424279"}, + {file = "pydantic_core-2.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:58e251bb5a5998f7226dc90b0b753eeffa720bd66664eba51927c2a7a2d5f32c"}, + {file = "pydantic_core-2.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:78d584caac52c24240ef9ecd75de64c760bbd0e20dbf6973631815e3ef16ef8b"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5084ec9721f82bef5ff7c4d1ee65e1626783abb585f8c0993833490b63fe1792"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d0f52684868db7c218437d260e14d37948b094493f2646f22d3dda7229bbe3f"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1def125d59a87fe451212a72ab9ed34c118ff771e5473fef4f2f95d8ede26d75"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b34480fd6778ab356abf1e9086a4ced95002a1e195e8d2fd182b0def9d944d11"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d42669d319db366cb567c3b444f43caa7ffb779bf9530692c6f244fc635a41eb"}, + {file = "pydantic_core-2.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53b06aea7a48919a254b32107647be9128c066aaa6ee6d5d08222325f25ef175"}, + {file = "pydantic_core-2.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1f038156b696a1c39d763b2080aeefa87ddb4162c10aa9fabfefffc3dd8180fa"}, + {file = "pydantic_core-2.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3f0f3a4a23717280a5ee3ac4fb1f81d6fde604c9ec5100f7f6f987716bb8c137"}, + {file = "pydantic_core-2.20.0-cp311-none-win32.whl", hash = "sha256:316fe7c3fec017affd916a0c83d6f1ec697cbbbdf1124769fa73328e7907cc2e"}, + {file = "pydantic_core-2.20.0-cp311-none-win_amd64.whl", hash = "sha256:2d06a7fa437f93782e3f32d739c3ec189f82fca74336c08255f9e20cea1ed378"}, + {file = "pydantic_core-2.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d6f8c49657f3eb7720ed4c9b26624063da14937fc94d1812f1e04a2204db3e17"}, + {file = "pydantic_core-2.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad1bd2f377f56fec11d5cfd0977c30061cd19f4fa199bf138b200ec0d5e27eeb"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed741183719a5271f97d93bbcc45ed64619fa38068aaa6e90027d1d17e30dc8d"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d82e5ed3a05f2dcb89c6ead2fd0dbff7ac09bc02c1b4028ece2d3a3854d049ce"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2ba34a099576234671f2e4274e5bc6813b22e28778c216d680eabd0db3f7dad"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:879ae6bb08a063b3e1b7ac8c860096d8fd6b48dd9b2690b7f2738b8c835e744b"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b0eefc7633a04c0694340aad91fbfd1986fe1a1e0c63a22793ba40a18fcbdc8"}, + {file = "pydantic_core-2.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73deadd6fd8a23e2f40b412b3ac617a112143c8989a4fe265050fd91ba5c0608"}, + {file = "pydantic_core-2.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:35681445dc85446fb105943d81ae7569aa7e89de80d1ca4ac3229e05c311bdb1"}, + {file = "pydantic_core-2.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0f6dd3612a3b9f91f2e63924ea18a4476656c6d01843ca20a4c09e00422195af"}, + {file = "pydantic_core-2.20.0-cp312-none-win32.whl", hash = "sha256:7e37b6bb6e90c2b8412b06373c6978d9d81e7199a40e24a6ef480e8acdeaf918"}, + {file = "pydantic_core-2.20.0-cp312-none-win_amd64.whl", hash = "sha256:7d4df13d1c55e84351fab51383520b84f490740a9f1fec905362aa64590b7a5d"}, + {file = "pydantic_core-2.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d43e7ab3b65e4dc35a7612cfff7b0fd62dce5bc11a7cd198310b57f39847fd6c"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6a24d7b5893392f2b8e3b7a0031ae3b14c6c1942a4615f0d8794fdeeefb08b"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b2f13c3e955a087c3ec86f97661d9f72a76e221281b2262956af381224cfc243"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72432fd6e868c8d0a6849869e004b8bcae233a3c56383954c228316694920b38"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d70a8ff2d4953afb4cbe6211f17268ad29c0b47e73d3372f40e7775904bc28fc"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e49524917b8d3c2f42cd0d2df61178e08e50f5f029f9af1f402b3ee64574392"}, + {file = "pydantic_core-2.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4f0f71653b1c1bad0350bc0b4cc057ab87b438ff18fa6392533811ebd01439c"}, + {file = "pydantic_core-2.20.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:16197e6f4fdecb9892ed2436e507e44f0a1aa2cff3b9306d1c879ea2f9200997"}, + {file = "pydantic_core-2.20.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:763602504bf640b3ded3bba3f8ed8a1cc2fc6a87b8d55c1c5689f428c49c947e"}, + {file = "pydantic_core-2.20.0-cp313-none-win32.whl", hash = "sha256:a3f243f318bd9523277fa123b3163f4c005a3e8619d4b867064de02f287a564d"}, + {file = "pydantic_core-2.20.0-cp313-none-win_amd64.whl", hash = "sha256:03aceaf6a5adaad3bec2233edc5a7905026553916615888e53154807e404545c"}, + {file = "pydantic_core-2.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d6f2d8b8da1f03f577243b07bbdd3412eee3d37d1f2fd71d1513cbc76a8c1239"}, + {file = "pydantic_core-2.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a272785a226869416c6b3c1b7e450506152d3844207331f02f27173562c917e0"}, + {file = "pydantic_core-2.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efbb412d55a4ffe73963fed95c09ccb83647ec63b711c4b3752be10a56f0090b"}, + {file = "pydantic_core-2.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e4f46189d8740561b43655263a41aac75ff0388febcb2c9ec4f1b60a0ec12f3"}, + {file = "pydantic_core-2.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3df115f4a3c8c5e4d5acf067d399c6466d7e604fc9ee9acbe6f0c88a0c3cf"}, + {file = "pydantic_core-2.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a340d2bdebe819d08f605e9705ed551c3feb97e4fd71822d7147c1e4bdbb9508"}, + {file = "pydantic_core-2.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:616b9c2f882393d422ba11b40e72382fe975e806ad693095e9a3b67c59ea6150"}, + {file = "pydantic_core-2.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25c46bb2ff6084859bbcfdf4f1a63004b98e88b6d04053e8bf324e115398e9e7"}, + {file = "pydantic_core-2.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:23425eccef8f2c342f78d3a238c824623836c6c874d93c726673dbf7e56c78c0"}, + {file = "pydantic_core-2.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:52527e8f223ba29608d999d65b204676398009725007c9336651c2ec2d93cffc"}, + {file = "pydantic_core-2.20.0-cp38-none-win32.whl", hash = "sha256:1c3c5b7f70dd19a6845292b0775295ea81c61540f68671ae06bfe4421b3222c2"}, + {file = "pydantic_core-2.20.0-cp38-none-win_amd64.whl", hash = "sha256:8093473d7b9e908af1cef30025609afc8f5fd2a16ff07f97440fd911421e4432"}, + {file = "pydantic_core-2.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ee7785938e407418795e4399b2bf5b5f3cf6cf728077a7f26973220d58d885cf"}, + {file = "pydantic_core-2.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e75794883d635071cf6b4ed2a5d7a1e50672ab7a051454c76446ef1ebcdcc91"}, + {file = "pydantic_core-2.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:344e352c96e53b4f56b53d24728217c69399b8129c16789f70236083c6ceb2ac"}, + {file = "pydantic_core-2.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:978d4123ad1e605daf1ba5e01d4f235bcf7b6e340ef07e7122e8e9cfe3eb61ab"}, + {file = "pydantic_core-2.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c05eaf6c863781eb834ab41f5963604ab92855822a2062897958089d1335dad"}, + {file = "pydantic_core-2.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc7e43b4a528ffca8c9151b6a2ca34482c2fdc05e6aa24a84b7f475c896fc51d"}, + {file = "pydantic_core-2.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:658287a29351166510ebbe0a75c373600cc4367a3d9337b964dada8d38bcc0f4"}, + {file = "pydantic_core-2.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1dacf660d6de692fe351e8c806e7efccf09ee5184865893afbe8e59be4920b4a"}, + {file = "pydantic_core-2.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3e147fc6e27b9a487320d78515c5f29798b539179f7777018cedf51b7749e4f4"}, + {file = "pydantic_core-2.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c867230d715a3dd1d962c8d9bef0d3168994ed663e21bf748b6e3a529a129aab"}, + {file = "pydantic_core-2.20.0-cp39-none-win32.whl", hash = "sha256:22b813baf0dbf612752d8143a2dbf8e33ccb850656b7850e009bad2e101fc377"}, + {file = "pydantic_core-2.20.0-cp39-none-win_amd64.whl", hash = "sha256:3a7235b46c1bbe201f09b6f0f5e6c36b16bad3d0532a10493742f91fbdc8035f"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cafde15a6f7feaec2f570646e2ffc5b73412295d29134a29067e70740ec6ee20"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2aec8eeea0b08fd6bc2213d8e86811a07491849fd3d79955b62d83e32fa2ad5f"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:840200827984f1c4e114008abc2f5ede362d6e11ed0b5931681884dd41852ff1"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ea1d8b7df522e5ced34993c423c3bf3735c53df8b2a15688a2f03a7d678800"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5b8376a867047bf08910573deb95d3c8dfb976eb014ee24f3b5a61ccc5bee1b"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d08264b4460326cefacc179fc1411304d5af388a79910832835e6f641512358b"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7a3639011c2e8a9628466f616ed7fb413f30032b891898e10895a0a8b5857d6c"}, + {file = "pydantic_core-2.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05e83ce2f7eba29e627dd8066aa6c4c0269b2d4f889c0eba157233a353053cea"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:603a843fea76a595c8f661cd4da4d2281dff1e38c4a836a928eac1a2f8fe88e4"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac76f30d5d3454f4c28826d891fe74d25121a346c69523c9810ebba43f3b1cec"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e3b1d4b1b3f6082849f9b28427ef147a5b46a6132a3dbaf9ca1baa40c88609"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2761f71faed820e25ec62eacba670d1b5c2709bb131a19fcdbfbb09884593e5a"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a0586cddbf4380e24569b8a05f234e7305717cc8323f50114dfb2051fcbce2a3"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b8c46a8cf53e849eea7090f331ae2202cd0f1ceb090b00f5902c423bd1e11805"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b4a085bd04af7245e140d1b95619fe8abb445a3d7fdf219b3f80c940853268ef"}, + {file = "pydantic_core-2.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:116b326ac82c8b315e7348390f6d30bcfe6e688a7d3f1de50ff7bcc2042a23c2"}, + {file = "pydantic_core-2.20.0.tar.gz", hash = "sha256:366be8e64e0cb63d87cf79b4e1765c0703dd6313c729b22e7b9e378db6b96877"}, ] [package.dependencies] diff --git a/hsman/wsgi.py b/hsman/wsgi.py index 6404fa0..b4d9604 100644 --- a/hsman/wsgi.py +++ b/hsman/wsgi.py @@ -11,7 +11,6 @@ logging.config.fileConfig(logconffile, disable_existing_loggers=True) log = logging.getLogger(__name__) - app = create_app() log.debug(f"Running in web mode: {lib.webMode()}")