mirror of
https://github.com/goauthentik/authentik
synced 2026-05-06 15:12:13 +02:00
Compare commits
1 Commits
minimal-mo
...
flows/conc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43628f308d |
@@ -24,7 +24,6 @@ Makefile @goauthentik/infrastructure
|
||||
.editorconfig @goauthentik/infrastructure
|
||||
CODEOWNERS @goauthentik/infrastructure
|
||||
# Backend packages
|
||||
packages/django-channels-postgres @goauthentik/backend
|
||||
packages/django-postgres-cache @goauthentik/backend
|
||||
packages/django-dramatiq-postgres @goauthentik/backend
|
||||
# Web packages
|
||||
|
||||
@@ -76,7 +76,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.8.24 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.8.22 AS uv
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.7-slim-trixie-fips AS python-base
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ from django.db.models import Model
|
||||
from django.db.models.query_utils import Q
|
||||
from django.db.transaction import atomic
|
||||
from django.db.utils import IntegrityError
|
||||
from django_channels_postgres.models import GroupChannel, Message
|
||||
from guardian.models import UserObjectPermission
|
||||
from guardian.shortcuts import assign_perm
|
||||
from rest_framework.exceptions import ValidationError
|
||||
@@ -138,8 +137,6 @@ def excluded_models() -> list[type[Model]]:
|
||||
DeviceToken,
|
||||
StreamEvent,
|
||||
UserConsent,
|
||||
Message,
|
||||
GroupChannel,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
|
||||
|
||||
class AuthentikCommandsConfig(ManagedAppConfig):
|
||||
name = "authentik.commands"
|
||||
label = "authentik_commands"
|
||||
verbose_name = "authentik Commands"
|
||||
default = True
|
||||
@@ -1,8 +0,0 @@
|
||||
from django.db.migrations.autodetector import MigrationAutodetector as BaseMigrationAutodetector
|
||||
from pgtrigger.migrations import MigrationAutodetectorMixin
|
||||
|
||||
MigrationAutodetector = type(
|
||||
"MigrationAutodetector",
|
||||
(MigrationAutodetectorMixin, BaseMigrationAutodetector),
|
||||
{},
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
from django.core.management.commands.makemigrations import Command as BaseCommand
|
||||
|
||||
from authentik.commands.management.commands import MigrationAutodetector
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
autodetector = MigrationAutodetector
|
||||
@@ -1,7 +0,0 @@
|
||||
from django_tenants.management.commands.migrate import Command as BaseCommand
|
||||
|
||||
from authentik.commands.management.commands import MigrationAutodetector
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
autodetector = MigrationAutodetector # type: ignore[assignment]
|
||||
@@ -1,7 +0,0 @@
|
||||
from django_tenants.management.commands.migrate_schemas import Command as BaseCommand
|
||||
|
||||
from authentik.commands.management.commands import MigrationAutodetector
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
autodetector = MigrationAutodetector # type: ignore[assignment]
|
||||
@@ -1,9 +1,13 @@
|
||||
"""authentik shell command"""
|
||||
|
||||
import code
|
||||
import platform
|
||||
import sys
|
||||
import traceback
|
||||
from pprint import pprint
|
||||
|
||||
from django.core.management.commands.shell import Command as BaseCommand
|
||||
from django.apps import apps
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import Model
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
|
||||
@@ -22,12 +26,29 @@ def get_banner_text(shell_type="shell") -> str:
|
||||
class Command(BaseCommand):
|
||||
"""Start the Django shell with all authentik models already imported"""
|
||||
|
||||
def get_namespace(self, **options):
|
||||
return {
|
||||
**super().get_namespace(**options),
|
||||
django_models = {}
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--command",
|
||||
help="Python code to execute (instead of starting an interactive shell)",
|
||||
)
|
||||
|
||||
def get_namespace(self):
|
||||
"""Prepare namespace with all models"""
|
||||
namespace = {
|
||||
"pprint": pprint,
|
||||
}
|
||||
|
||||
# Gather Django models and constants from each app
|
||||
for app in apps.get_app_configs():
|
||||
# Load models from each app
|
||||
for model in app.get_models():
|
||||
namespace[model.__name__] = model
|
||||
|
||||
return namespace
|
||||
|
||||
@staticmethod
|
||||
def post_save_handler(sender, instance: Model, created: bool, **_):
|
||||
"""Signal handler for all object's post_save"""
|
||||
@@ -58,9 +79,41 @@ class Command(BaseCommand):
|
||||
).save()
|
||||
|
||||
def handle(self, **options):
|
||||
namespace = self.get_namespace()
|
||||
|
||||
post_save.connect(Command.post_save_handler)
|
||||
pre_delete.connect(Command.pre_delete_handler)
|
||||
|
||||
print(get_banner_text())
|
||||
# If Python code has been passed, execute it and exit.
|
||||
if options["command"]:
|
||||
|
||||
super().handle(**options)
|
||||
exec(options["command"], namespace) # nosec # noqa
|
||||
return
|
||||
|
||||
try:
|
||||
hook = sys.__interactivehook__
|
||||
except AttributeError:
|
||||
# Match the behavior of the cpython shell where a missing
|
||||
# sys.__interactivehook__ is ignored.
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
hook()
|
||||
except Exception: # noqa
|
||||
# Match the behavior of the cpython shell where an error in
|
||||
# sys.__interactivehook__ prints a warning and the exception
|
||||
# and continues.
|
||||
print("Failed calling sys.__interactivehook__")
|
||||
traceback.print_exc()
|
||||
# Try to enable tab-complete
|
||||
try:
|
||||
import readline
|
||||
import rlcompleter
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
else:
|
||||
readline.set_completer(rlcompleter.Completer(namespace).complete)
|
||||
readline.parse_and_bind("tab: complete")
|
||||
|
||||
# Run interactive shell
|
||||
code.interact(banner=get_banner_text(), local=namespace)
|
||||
|
||||
@@ -29,7 +29,6 @@ from authentik.blueprints.models import ManagedModel
|
||||
from authentik.core.expression.exceptions import PropertyMappingExpressionException
|
||||
from authentik.core.types import UILoginButton, UserSettingSerializer
|
||||
from authentik.lib.avatars import get_avatar
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.expression.exceptions import ControlFlowException
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.merge import MERGE_LIST_UNIQUE
|
||||
@@ -575,10 +574,8 @@ class Application(SerializerModel, PolicyBindingModel):
|
||||
it is returned as-is"""
|
||||
if not self.meta_icon:
|
||||
return None
|
||||
if self.meta_icon.name.startswith("http"):
|
||||
if "://" in self.meta_icon.name or self.meta_icon.name.startswith("/static"):
|
||||
return self.meta_icon.name
|
||||
if self.meta_icon.name.startswith("/"):
|
||||
return CONFIG.get("web.path", "/")[:-1] + self.meta_icon.name
|
||||
return self.meta_icon.url
|
||||
|
||||
def get_launch_url(self, user: Optional["User"] = None) -> str | None:
|
||||
@@ -780,10 +777,8 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
|
||||
starts with http it is returned as-is"""
|
||||
if not self.icon:
|
||||
return None
|
||||
if self.icon.name.startswith("http"):
|
||||
if "://" in self.icon.name or self.icon.name.startswith("/static"):
|
||||
return self.icon.name
|
||||
if self.icon.name.startswith("/"):
|
||||
return CONFIG.get("web.path", "/")[:-1] + self.icon.name
|
||||
return self.icon.url
|
||||
|
||||
def get_user_path(self) -> str:
|
||||
|
||||
@@ -4,7 +4,6 @@ from datetime import datetime, timedelta
|
||||
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_channels_postgres.models import GroupChannel, Message
|
||||
from django_postgres_cache.tasks import clear_expired_cache
|
||||
from dramatiq.actor import actor
|
||||
from structlog.stdlib import get_logger
|
||||
@@ -35,8 +34,6 @@ def clean_expired_models():
|
||||
LOGGER.debug("Expired models", model=cls, amount=amount)
|
||||
self.info(f"Expired {amount} {cls._meta.verbose_name_plural}")
|
||||
clear_expired_cache()
|
||||
Message.delete_expired()
|
||||
GroupChannel.delete_expired()
|
||||
|
||||
|
||||
@actor(description=_("Remove temporary users created by SAML Sources."))
|
||||
|
||||
@@ -82,51 +82,6 @@ class TestApplicationsAPI(APITestCase):
|
||||
self.assertEqual(self.allowed.get_meta_icon, app["meta_icon"])
|
||||
self.assertEqual(self.allowed.meta_icon.read(), b"text")
|
||||
|
||||
def test_set_icon_relative(self):
|
||||
"""Test set_icon (relative path)"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"authentik_api:application-set-icon-url",
|
||||
kwargs={"slug": self.allowed.slug},
|
||||
),
|
||||
data={"url": "relative/path"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.allowed.refresh_from_db()
|
||||
self.assertEqual(self.allowed.get_meta_icon, "/media/public/relative/path")
|
||||
|
||||
def test_set_icon_absolute(self):
|
||||
"""Test set_icon (absolute path)"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"authentik_api:application-set-icon-url",
|
||||
kwargs={"slug": self.allowed.slug},
|
||||
),
|
||||
data={"url": "/relative/path"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.allowed.refresh_from_db()
|
||||
self.assertEqual(self.allowed.get_meta_icon, "/relative/path")
|
||||
|
||||
def test_set_icon_url(self):
|
||||
"""Test set_icon (url)"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"authentik_api:application-set-icon-url",
|
||||
kwargs={"slug": self.allowed.slug},
|
||||
),
|
||||
data={"url": "https://authentik.company/img.png"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.allowed.refresh_from_db()
|
||||
self.assertEqual(self.allowed.get_meta_icon, "https://authentik.company/img.png")
|
||||
|
||||
def test_check_access(self):
|
||||
"""Test check_access operation"""
|
||||
self.client.force_login(self.user)
|
||||
|
||||
@@ -30,10 +30,7 @@ from authentik.policies.types import PolicyRequest
|
||||
|
||||
# Special keys which are *not* cleaned, even when the default filter
|
||||
# is matched
|
||||
ALLOWED_SPECIAL_KEYS = re.compile(
|
||||
r"passing|password_change_date|^auth_method(_args)?$",
|
||||
flags=re.I,
|
||||
)
|
||||
ALLOWED_SPECIAL_KEYS = re.compile("passing|password_change_date", flags=re.I)
|
||||
|
||||
|
||||
def cleanse_item(key: str, value: Any) -> Any:
|
||||
|
||||
@@ -54,6 +54,7 @@ class Challenge(PassiveSerializer):
|
||||
|
||||
flow_info = ContextualFlowInfo(required=False)
|
||||
component = CharField(default="")
|
||||
xid = CharField(required=False)
|
||||
|
||||
response_errors = DictField(
|
||||
child=ErrorDetailSerializer(many=True), allow_empty=True, required=False
|
||||
|
||||
@@ -190,7 +190,7 @@ class Flow(SerializerModel, PolicyBindingModel):
|
||||
)
|
||||
if self.background.name.startswith("http"):
|
||||
return self.background.name
|
||||
if self.background.name.startswith("/"):
|
||||
if self.background.name.startswith("/static"):
|
||||
return CONFIG.get("web.path", "/")[:-1] + self.background.name
|
||||
return self.background.url
|
||||
|
||||
|
||||
@@ -143,10 +143,12 @@ class FlowPlan:
|
||||
request: HttpRequest,
|
||||
flow: Flow,
|
||||
allowed_silent_types: list["StageView"] | None = None,
|
||||
**get_params,
|
||||
) -> HttpResponse:
|
||||
"""Redirect to the flow executor for this flow plan"""
|
||||
from authentik.flows.views.executor import (
|
||||
SESSION_KEY_PLAN,
|
||||
FlowContainer,
|
||||
FlowExecutorView,
|
||||
)
|
||||
|
||||
@@ -157,6 +159,7 @@ class FlowPlan:
|
||||
# No unskippable stages found, so we can directly return the response of the last stage
|
||||
final_stage: type[StageView] = self.bindings[-1].stage.view
|
||||
temp_exec = FlowExecutorView(flow=flow, request=request, plan=self)
|
||||
temp_exec.container = FlowContainer(request)
|
||||
temp_exec.current_stage = self.bindings[-1].stage
|
||||
temp_exec.current_stage_view = final_stage
|
||||
temp_exec.setup(request, flow.slug)
|
||||
@@ -174,6 +177,9 @@ class FlowPlan:
|
||||
):
|
||||
get_qs["inspector"] = "available"
|
||||
|
||||
for key, value in get_params:
|
||||
get_qs[key] = value
|
||||
|
||||
return redirect_with_qs(
|
||||
"authentik_core:if-flow",
|
||||
get_qs,
|
||||
|
||||
@@ -192,6 +192,7 @@ class ChallengeStageView(StageView):
|
||||
)
|
||||
flow_info.is_valid()
|
||||
challenge.initial_data["flow_info"] = flow_info.data
|
||||
challenge.initial_data["xid"] = self.executor.container.exec_id
|
||||
if isinstance(challenge, WithUserInfoChallenge):
|
||||
# If there's a pending user, update the `username` field
|
||||
# this field is only used by password managers.
|
||||
|
||||
@@ -29,7 +29,7 @@ window.authentik.flow = {
|
||||
{% block body %}
|
||||
<ak-skip-to-content></ak-skip-to-content>
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-flow-executor flowSlug="{{ flow.slug }}">
|
||||
<ak-flow-executor flowSlug="{{ flow.slug }}" xid="{{ xid }}">
|
||||
<ak-loading></ak-loading>
|
||||
</ak-flow-executor>
|
||||
{% endblock %}
|
||||
|
||||
@@ -13,7 +13,6 @@ from authentik.core.models import Group, User
|
||||
from authentik.core.tests.utils import create_test_flow, create_test_user
|
||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||
from authentik.flows.models import (
|
||||
FlowAuthenticationRequirement,
|
||||
FlowDeniedAction,
|
||||
FlowDesignation,
|
||||
FlowStageBinding,
|
||||
@@ -178,25 +177,6 @@ class TestFlowExecutor(FlowTestCase):
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/unique-string")
|
||||
|
||||
@patch(
|
||||
"authentik.flows.views.executor.to_stage_response",
|
||||
TO_STAGE_RESPONSE_MOCK,
|
||||
)
|
||||
def test_valid_flow_redirect_authenticated(self):
|
||||
"""Test valid flow with valid redirect destination, authenticated already"""
|
||||
flow = create_test_flow()
|
||||
flow.designation = FlowDesignation.AUTHENTICATION
|
||||
flow.authentication = FlowAuthenticationRequirement.REQUIRE_UNAUTHENTICATED
|
||||
flow.save()
|
||||
self.client.force_login(create_test_user())
|
||||
|
||||
dest = "/unique-string"
|
||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug})
|
||||
|
||||
response = self.client.get(url + f"?{QS_QUERY}={urlencode({NEXT_ARG_NAME: dest})}")
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/unique-string")
|
||||
|
||||
@patch(
|
||||
"authentik.flows.views.executor.to_stage_response",
|
||||
TO_STAGE_RESPONSE_MOCK,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""authentik multi-stage authentication engine"""
|
||||
|
||||
from copy import deepcopy
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
@@ -63,6 +64,7 @@ from authentik.policies.engine import PolicyEngine
|
||||
LOGGER = get_logger()
|
||||
# Argument used to redirect user after login
|
||||
NEXT_ARG_NAME = "next"
|
||||
SESSION_KEY_PLAN_CONTAINER = "authentik/flows/plan_container/%s"
|
||||
SESSION_KEY_PLAN = "authentik/flows/plan"
|
||||
SESSION_KEY_APPLICATION_PRE = "authentik/flows/application_pre"
|
||||
SESSION_KEY_GET = "authentik/flows/get"
|
||||
@@ -70,6 +72,7 @@ SESSION_KEY_POST = "authentik/flows/post"
|
||||
SESSION_KEY_HISTORY = "authentik/flows/history"
|
||||
QS_KEY_TOKEN = "flow_token" # nosec
|
||||
QS_QUERY = "query"
|
||||
QS_EXEC_ID = "xid"
|
||||
|
||||
|
||||
def challenge_types():
|
||||
@@ -96,6 +99,88 @@ class InvalidStageError(SentryIgnoredException):
|
||||
"""Error raised when a challenge from a stage is not valid"""
|
||||
|
||||
|
||||
class FlowContainer:
|
||||
"""Allow for multiple concurrent flow executions in the same session"""
|
||||
|
||||
def __init__(self, request: HttpRequest, exec_id: str | None = None) -> None:
|
||||
self.request = request
|
||||
self.exec_id = exec_id
|
||||
|
||||
@staticmethod
|
||||
def new(request: HttpRequest):
|
||||
exec_id = str(uuid4())
|
||||
request.session[SESSION_KEY_PLAN_CONTAINER % exec_id] = {}
|
||||
return FlowContainer(request, exec_id)
|
||||
|
||||
def exists(self) -> bool:
|
||||
"""Check if flow exists in container/session"""
|
||||
return SESSION_KEY_PLAN in self.session
|
||||
|
||||
def save(self):
|
||||
self.request.session.modified = True
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
# Backwards compatibility: store session plan/etc directly in session
|
||||
if not self.exec_id:
|
||||
return self.request.session
|
||||
self.request.session.setdefault(SESSION_KEY_PLAN_CONTAINER % self.exec_id, {})
|
||||
return self.request.session.get(SESSION_KEY_PLAN_CONTAINER % self.exec_id, {})
|
||||
|
||||
@property
|
||||
def plan(self) -> FlowPlan:
|
||||
return self.session.get(SESSION_KEY_PLAN)
|
||||
|
||||
def to_redirect(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
flow: Flow,
|
||||
allowed_silent_types: list[StageView] | None = None,
|
||||
**get_params,
|
||||
) -> HttpResponse:
|
||||
get_params[QS_EXEC_ID] = self.exec_id
|
||||
return self.plan.to_redirect(
|
||||
request, flow, allowed_silent_types=allowed_silent_types, **get_params
|
||||
)
|
||||
|
||||
@plan.setter
|
||||
def plan(self, value: FlowPlan):
|
||||
self.session[SESSION_KEY_PLAN] = value
|
||||
self.request.session.modified = True
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def application_pre(self):
|
||||
return self.session.get(SESSION_KEY_APPLICATION_PRE)
|
||||
|
||||
@property
|
||||
def get(self) -> QueryDict:
|
||||
return self.session.get(SESSION_KEY_GET)
|
||||
|
||||
@get.setter
|
||||
def get(self, value: QueryDict):
|
||||
self.session[SESSION_KEY_GET] = value
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def post(self) -> QueryDict:
|
||||
return self.session.get(SESSION_KEY_POST)
|
||||
|
||||
@post.setter
|
||||
def post(self, value: QueryDict):
|
||||
self.session[SESSION_KEY_POST] = value
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def history(self) -> list[FlowPlan]:
|
||||
return self.session.get(SESSION_KEY_HISTORY)
|
||||
|
||||
@history.setter
|
||||
def history(self, value: list[FlowPlan]):
|
||||
self.session[SESSION_KEY_HISTORY] = value
|
||||
self.save()
|
||||
|
||||
|
||||
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
||||
class FlowExecutorView(APIView):
|
||||
"""Flow executor, passing requests to Stage Views"""
|
||||
@@ -103,8 +188,9 @@ class FlowExecutorView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
flow: Flow = None
|
||||
|
||||
plan: FlowPlan | None = None
|
||||
container: FlowContainer
|
||||
|
||||
current_binding: FlowStageBinding | None = None
|
||||
current_stage: Stage
|
||||
current_stage_view: View
|
||||
@@ -160,10 +246,12 @@ class FlowExecutorView(APIView):
|
||||
if QS_KEY_TOKEN in get_params:
|
||||
plan = self._check_flow_token(get_params[QS_KEY_TOKEN])
|
||||
if plan:
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
container = FlowContainer.new(request)
|
||||
container.plan = plan
|
||||
# Early check if there's an active Plan for the current session
|
||||
if SESSION_KEY_PLAN in self.request.session:
|
||||
self.plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
|
||||
self.container = FlowContainer(request, request.GET.get(QS_EXEC_ID))
|
||||
if self.container.exists():
|
||||
self.plan: FlowPlan = self.container.plan
|
||||
if self.plan.flow_pk != self.flow.pk.hex:
|
||||
self._logger.warning(
|
||||
"f(exec): Found existing plan for other flow, deleting plan",
|
||||
@@ -176,21 +264,15 @@ class FlowExecutorView(APIView):
|
||||
self._logger.debug("f(exec): Continuing existing plan")
|
||||
|
||||
# Initial flow request, check if we have an upstream query string passed in
|
||||
request.session[SESSION_KEY_GET] = get_params
|
||||
self.container.get = get_params
|
||||
# Don't check session again as we've either already loaded the plan or we need to plan
|
||||
if not self.plan:
|
||||
request.session[SESSION_KEY_HISTORY] = []
|
||||
self.container.history = []
|
||||
self._logger.debug("f(exec): No active Plan found, initiating planner")
|
||||
try:
|
||||
self.plan = self._initiate_plan()
|
||||
self.container.plan = self.plan
|
||||
except FlowNonApplicableException as exc:
|
||||
# If we're this flow is for authentication and the user is already authenticated
|
||||
# continue to the next URL
|
||||
if (
|
||||
self.flow.designation == FlowDesignation.AUTHENTICATION
|
||||
and self.request.user.is_authenticated
|
||||
):
|
||||
return self._flow_done()
|
||||
self._logger.warning("f(exec): Flow not applicable to current user", exc=exc)
|
||||
return self.handle_invalid_flow(exc)
|
||||
except EmptyFlowException as exc:
|
||||
@@ -262,12 +344,19 @@ class FlowExecutorView(APIView):
|
||||
request=OpenApiTypes.NONE,
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="query",
|
||||
name=QS_QUERY,
|
||||
location=OpenApiParameter.QUERY,
|
||||
required=True,
|
||||
description="Querystring as received",
|
||||
type=OpenApiTypes.STR,
|
||||
)
|
||||
),
|
||||
OpenApiParameter(
|
||||
name=QS_EXEC_ID,
|
||||
location=OpenApiParameter.QUERY,
|
||||
required=False,
|
||||
description="Flow execution ID",
|
||||
type=OpenApiTypes.STR,
|
||||
),
|
||||
],
|
||||
operation_id="flows_executor_get",
|
||||
)
|
||||
@@ -294,8 +383,8 @@ class FlowExecutorView(APIView):
|
||||
span.set_data("authentik Stage", self.current_stage_view)
|
||||
span.set_data("authentik Flow", self.flow.slug)
|
||||
stage_response = self.current_stage_view.dispatch(request)
|
||||
return to_stage_response(request, stage_response)
|
||||
except Exception as exc: # noqa
|
||||
return to_stage_response(request, stage_response, self.container.exec_id)
|
||||
except Exception as exc:
|
||||
return self.handle_exception(exc)
|
||||
|
||||
@extend_schema(
|
||||
@@ -313,12 +402,19 @@ class FlowExecutorView(APIView):
|
||||
),
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="query",
|
||||
name=QS_QUERY,
|
||||
location=OpenApiParameter.QUERY,
|
||||
required=True,
|
||||
description="Querystring as received",
|
||||
type=OpenApiTypes.STR,
|
||||
)
|
||||
),
|
||||
OpenApiParameter(
|
||||
name=QS_EXEC_ID,
|
||||
location=OpenApiParameter.QUERY,
|
||||
required=True,
|
||||
description="Flow execution ID",
|
||||
type=OpenApiTypes.STR,
|
||||
),
|
||||
],
|
||||
operation_id="flows_executor_solve",
|
||||
)
|
||||
@@ -345,14 +441,15 @@ class FlowExecutorView(APIView):
|
||||
span.set_data("authentik Stage", self.current_stage_view)
|
||||
span.set_data("authentik Flow", self.flow.slug)
|
||||
stage_response = self.current_stage_view.dispatch(request)
|
||||
return to_stage_response(request, stage_response)
|
||||
return to_stage_response(request, stage_response, self.container.exec_id)
|
||||
except Exception as exc: # noqa
|
||||
return self.handle_exception(exc)
|
||||
|
||||
def _initiate_plan(self) -> FlowPlan:
|
||||
planner = FlowPlanner(self.flow)
|
||||
plan = planner.plan(self.request)
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
container = FlowContainer.new(self.request)
|
||||
container.plan = plan
|
||||
try:
|
||||
# Call the has_stages getter to check that
|
||||
# there are no issues with the class we might've gotten
|
||||
@@ -376,7 +473,7 @@ class FlowExecutorView(APIView):
|
||||
except FlowNonApplicableException as exc:
|
||||
self._logger.warning("f(exec): Flow restart not applicable to current user", exc=exc)
|
||||
return self.handle_invalid_flow(exc)
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
self.container.plan = plan
|
||||
kwargs = self.kwargs
|
||||
kwargs.update({"flow_slug": self.flow.slug})
|
||||
return redirect_with_qs("authentik_api:flow-executor", self.request.GET, **kwargs)
|
||||
@@ -398,9 +495,13 @@ class FlowExecutorView(APIView):
|
||||
)
|
||||
self.cancel()
|
||||
if next_param and not is_url_absolute(next_param):
|
||||
return to_stage_response(self.request, redirect_with_qs(next_param))
|
||||
return to_stage_response(
|
||||
self.request, redirect_with_qs(next_param), self.container.exec_id
|
||||
)
|
||||
return to_stage_response(
|
||||
self.request, self.stage_invalid(error_message=_("Invalid next URL"))
|
||||
self.request,
|
||||
self.stage_invalid(error_message=_("Invalid next URL")),
|
||||
self.container.exec_id,
|
||||
)
|
||||
|
||||
def stage_ok(self) -> HttpResponse:
|
||||
@@ -414,7 +515,7 @@ class FlowExecutorView(APIView):
|
||||
self.current_stage_view.cleanup()
|
||||
self.request.session.get(SESSION_KEY_HISTORY, []).append(deepcopy(self.plan))
|
||||
self.plan.pop()
|
||||
self.request.session[SESSION_KEY_PLAN] = self.plan
|
||||
self.container.plan = self.plan
|
||||
if self.plan.bindings:
|
||||
self._logger.debug(
|
||||
"f(exec): Continuing with next stage",
|
||||
@@ -457,6 +558,7 @@ class FlowExecutorView(APIView):
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel current flow execution"""
|
||||
# TODO: Clean up container
|
||||
keys_to_delete = [
|
||||
SESSION_KEY_APPLICATION_PRE,
|
||||
SESSION_KEY_PLAN,
|
||||
@@ -479,8 +581,8 @@ class CancelView(View):
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""View which canels the currently active plan"""
|
||||
if SESSION_KEY_PLAN in request.session:
|
||||
del request.session[SESSION_KEY_PLAN]
|
||||
if FlowContainer(request, request.GET.get(QS_EXEC_ID)).exists():
|
||||
del request.session[SESSION_KEY_PLAN_CONTAINER % request.GET.get(QS_EXEC_ID)]
|
||||
LOGGER.debug("Canceled current plan")
|
||||
return redirect("authentik_flows:default-invalidation")
|
||||
|
||||
@@ -528,19 +630,12 @@ class ToDefaultFlow(View):
|
||||
|
||||
def dispatch(self, request: HttpRequest) -> HttpResponse:
|
||||
flow = self.get_flow()
|
||||
# If user already has a pending plan, clear it so we don't have to later.
|
||||
if SESSION_KEY_PLAN in self.request.session:
|
||||
plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
|
||||
if plan.flow_pk != flow.pk.hex:
|
||||
LOGGER.warning(
|
||||
"f(def): Found existing plan for other flow, deleting plan",
|
||||
flow_slug=flow.slug,
|
||||
)
|
||||
del self.request.session[SESSION_KEY_PLAN]
|
||||
return redirect_with_qs("authentik_core:if-flow", request.GET, flow_slug=flow.slug)
|
||||
get_qs = request.GET.copy()
|
||||
get_qs[QS_EXEC_ID] = str(uuid4())
|
||||
return redirect_with_qs("authentik_core:if-flow", get_qs, flow_slug=flow.slug)
|
||||
|
||||
|
||||
def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpResponse:
|
||||
def to_stage_response(request: HttpRequest, source: HttpResponse, xid: str) -> HttpResponse:
|
||||
"""Convert normal HttpResponse into JSON Response"""
|
||||
if (
|
||||
isinstance(source, HttpResponseRedirect)
|
||||
@@ -559,6 +654,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
|
||||
RedirectChallenge(
|
||||
{
|
||||
"to": str(redirect_url),
|
||||
"xid": xid,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -567,6 +663,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
|
||||
ShellChallenge(
|
||||
{
|
||||
"body": source.render().content.decode("utf-8"),
|
||||
"xid": xid,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -576,6 +673,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
|
||||
ShellChallenge(
|
||||
{
|
||||
"body": source.content.decode("utf-8"),
|
||||
"xid": xid,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -607,4 +705,6 @@ class ConfigureFlowInitView(LoginRequiredMixin, View):
|
||||
except FlowNonApplicableException:
|
||||
LOGGER.warning("Flow not applicable to user")
|
||||
raise Http404 from None
|
||||
return plan.to_redirect(request, stage.configure_flow)
|
||||
container = FlowContainer.new(request)
|
||||
container.plan = plan
|
||||
return container.to_redirect(request, stage.configure_flow)
|
||||
|
||||
@@ -7,6 +7,7 @@ from ua_parser.user_agent_parser import Parse
|
||||
|
||||
from authentik.core.views.interface import InterfaceView
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.flows.views.executor import QS_EXEC_ID
|
||||
|
||||
|
||||
class FlowInterfaceView(InterfaceView):
|
||||
@@ -17,6 +18,7 @@ class FlowInterfaceView(InterfaceView):
|
||||
kwargs["flow"] = flow
|
||||
kwargs["flow_background_url"] = flow.background_url(self.request)
|
||||
kwargs["inspector"] = "inspector" in self.request.GET
|
||||
kwargs["xid"] = self.request.GET.get(QS_EXEC_ID)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def compat_needs_sfe(self) -> bool:
|
||||
|
||||
@@ -112,6 +112,7 @@ def get_logger_config():
|
||||
"hpack": "WARNING",
|
||||
"httpx": "WARNING",
|
||||
"azure": "WARNING",
|
||||
"channels_postgres": "WARNING",
|
||||
}
|
||||
for handler_name, level in handler_level_map.items():
|
||||
base_config["loggers"][handler_name] = {
|
||||
|
||||
@@ -68,7 +68,9 @@ class OutgoingSyncProviderStatusMixin:
|
||||
return Response(SyncStatusSerializer(status).data)
|
||||
|
||||
last_task: Task = (
|
||||
sync_schedule.tasks.filter(state__in=(TaskStatus.DONE, TaskStatus.REJECTED))
|
||||
sync_schedule.tasks.exclude(
|
||||
aggregated_status__in=(TaskStatus.CONSUMED, TaskStatus.QUEUED)
|
||||
)
|
||||
.order_by("-mtime")
|
||||
.first()
|
||||
)
|
||||
|
||||
35
authentik/root/channels.py
Normal file
35
authentik/root/channels.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import Any
|
||||
|
||||
from channels_postgres.core import PostgresChannelLayer as BasePostgresChannelLayer
|
||||
from channels_postgres.db import DatabaseLayer as BaseDatabaseLayer
|
||||
from django.conf import settings
|
||||
from psycopg_pool import AsyncConnectionPool
|
||||
|
||||
from authentik.root.db.base import DatabaseWrapper
|
||||
|
||||
|
||||
class DatabaseLayer(BaseDatabaseLayer):
|
||||
async def get_db_pool(self, db_params: dict[str, Any]) -> AsyncConnectionPool:
|
||||
db_wrapper = DatabaseWrapper(settings.CHANNEL_LAYERS["default"]["CONFIG"])
|
||||
db_params = db_wrapper.get_connection_params()
|
||||
db_params.pop("cursor_factory")
|
||||
db_params.pop("context")
|
||||
return await super().get_db_pool(db_params)
|
||||
|
||||
|
||||
class PostgresChannelLayer(BasePostgresChannelLayer):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
self.django_db = DatabaseLayer(self.django_db.psycopg_options, self.db_params)
|
||||
|
||||
@property
|
||||
def db_params(self):
|
||||
db_wrapper = DatabaseWrapper(settings.CHANNEL_LAYERS["default"]["CONFIG"])
|
||||
db_params = db_wrapper.get_connection_params()
|
||||
db_params.pop("cursor_factory")
|
||||
db_params.pop("context")
|
||||
return db_params
|
||||
|
||||
@db_params.setter
|
||||
def db_params(self, value):
|
||||
pass
|
||||
@@ -51,7 +51,6 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
# Application definition
|
||||
SHARED_APPS = [
|
||||
"authentik.commands",
|
||||
"django_tenants",
|
||||
"authentik.tenants",
|
||||
"django.contrib.messages",
|
||||
@@ -65,7 +64,7 @@ SHARED_APPS = [
|
||||
"pgactivity",
|
||||
"pglock",
|
||||
"channels",
|
||||
"django_channels_postgres",
|
||||
"channels_postgres",
|
||||
"django_dramatiq_postgres",
|
||||
"authentik.tasks",
|
||||
]
|
||||
@@ -305,7 +304,11 @@ DATABASE_ROUTERS = (
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "django_channels_postgres.layer.PostgresChannelLayer",
|
||||
"BACKEND": "authentik.root.channels.PostgresChannelLayer",
|
||||
"CONFIG": {
|
||||
**DATABASES["default"],
|
||||
"TIME_ZONE": None,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -391,7 +394,6 @@ DRAMATIQ = {
|
||||
"middlewares": (
|
||||
("django_dramatiq_postgres.middleware.FullyQualifiedActorName", {}),
|
||||
("django_dramatiq_postgres.middleware.DbConnectionMiddleware", {}),
|
||||
("django_dramatiq_postgres.middleware.TaskStateBeforeMiddleware", {}),
|
||||
("dramatiq.middleware.age_limit.AgeLimit", {}),
|
||||
(
|
||||
"dramatiq.middleware.time_limit.TimeLimit",
|
||||
@@ -427,7 +429,6 @@ DRAMATIQ = {
|
||||
"prefix": "authentik",
|
||||
},
|
||||
),
|
||||
("django_dramatiq_postgres.middleware.TaskStateAfterMiddleware", {}),
|
||||
),
|
||||
"test": TEST,
|
||||
}
|
||||
|
||||
@@ -62,6 +62,11 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
||||
"""Configure test environment settings"""
|
||||
settings.TEST = True
|
||||
settings.DRAMATIQ["test"] = True
|
||||
settings.CHANNEL_LAYERS["default"]["CONFIG"] = {
|
||||
**settings.DATABASES["default"],
|
||||
**settings.DATABASES["default"]["TEST"],
|
||||
"TIME_ZONE": None,
|
||||
}
|
||||
|
||||
# Test-specific configuration
|
||||
test_config = {
|
||||
|
||||
@@ -108,7 +108,9 @@ class KerberosSourceViewSet(UsedByMixin, ModelViewSet):
|
||||
return Response(SyncStatusSerializer(status).data)
|
||||
|
||||
last_task: Task = (
|
||||
sync_schedule.tasks.filter(state__in=(TaskStatus.DONE, TaskStatus.REJECTED))
|
||||
sync_schedule.tasks.exclude(
|
||||
aggregated_status__in=(TaskStatus.CONSUMED, TaskStatus.QUEUED)
|
||||
)
|
||||
.order_by("-mtime")
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -183,7 +183,9 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
||||
return Response(SyncStatusSerializer(status).data)
|
||||
|
||||
last_task: Task = (
|
||||
sync_schedule.tasks.filter(state__in=(TaskStatus.DONE, TaskStatus.REJECTED))
|
||||
sync_schedule.tasks.exclude(
|
||||
aggregated_status__in=(TaskStatus.CONSUMED, TaskStatus.QUEUED)
|
||||
)
|
||||
.order_by("-mtime")
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from django.db.models import Count
|
||||
from django_dramatiq_postgres.models import TaskState
|
||||
from django_filters.filters import BooleanFilter, MultipleChoiceFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
@@ -7,9 +6,9 @@ from dramatiq.broker import get_broker
|
||||
from dramatiq.errors import ActorNotFound
|
||||
from dramatiq.message import Message
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import IntegerField, ReadOnlyField, SerializerMethodField
|
||||
from rest_framework.fields import ReadOnlyField, SerializerMethodField
|
||||
from rest_framework.mixins import (
|
||||
ListModelMixin,
|
||||
RetrieveModelMixin,
|
||||
@@ -137,48 +136,3 @@ class TaskViewSet(
|
||||
broker = get_broker()
|
||||
broker.enqueue(Message.decode(task.message))
|
||||
return Response(status=204)
|
||||
|
||||
@extend_schema(
|
||||
request=OpenApiTypes.NONE,
|
||||
responses={
|
||||
200: inline_serializer(
|
||||
"GlobalTaskStatusSerializer",
|
||||
{
|
||||
"queued": IntegerField(read_only=True),
|
||||
"consumed": IntegerField(read_only=True),
|
||||
"preprocess": IntegerField(read_only=True),
|
||||
"running": IntegerField(read_only=True),
|
||||
"postprocess": IntegerField(read_only=True),
|
||||
"rejected": IntegerField(read_only=True),
|
||||
"done": IntegerField(read_only=True),
|
||||
"info": IntegerField(read_only=True),
|
||||
"warning": IntegerField(read_only=True),
|
||||
"error": IntegerField(read_only=True),
|
||||
},
|
||||
),
|
||||
},
|
||||
)
|
||||
@action(detail=False, methods=["GET"], permission_classes=[])
|
||||
def status(self, request: Request) -> Response:
|
||||
"""Global status summary for all tasks"""
|
||||
response = {}
|
||||
for status in (
|
||||
Task.objects.all()
|
||||
.values("aggregated_status")
|
||||
.annotate(count=Count("aggregated_status"))
|
||||
):
|
||||
response[status["aggregated_status"]] = status["count"]
|
||||
return Response(
|
||||
{
|
||||
"queued": response.get("queued", 0),
|
||||
"consumed": response.get("consumed", 0),
|
||||
"preprocess": response.get("preprocess", 0),
|
||||
"running": response.get("running", 0),
|
||||
"postprocess": response.get("postprocess", 0),
|
||||
"rejected": response.get("rejected", 0),
|
||||
"done": response.get("done", 0),
|
||||
"info": response.get("info", 0),
|
||||
"warning": response.get("warning", 0),
|
||||
"error": response.get("error", 0),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -63,7 +63,7 @@ class RelObjMiddleware(Middleware):
|
||||
|
||||
|
||||
class MessagesMiddleware(Middleware):
|
||||
def after_enqueue(self, broker: Broker, message: Message, delay: int | None):
|
||||
def after_enqueue(self, broker: Broker, message: Message, delay: int):
|
||||
task: Task = message.options["task"]
|
||||
task_created: bool = message.options["task_created"]
|
||||
if task_created:
|
||||
@@ -107,13 +107,13 @@ class MessagesMiddleware(Middleware):
|
||||
"Task finished processing without errors",
|
||||
)
|
||||
return
|
||||
if should_ignore_exception(exception):
|
||||
return
|
||||
task.log(
|
||||
class_to_path(type(self)),
|
||||
TaskStatus.ERROR,
|
||||
exception,
|
||||
)
|
||||
if should_ignore_exception(exception):
|
||||
return
|
||||
event_kwargs = {
|
||||
"actor": task.actor_name,
|
||||
}
|
||||
@@ -235,7 +235,6 @@ class WorkerStatusMiddleware(Middleware):
|
||||
sleep(10)
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def keep(status: WorkerStatus):
|
||||
lock_id = f"goauthentik.io/worker/status/{status.pk}"
|
||||
with pglock.advisory(lock_id, side_effect=pglock.Raise):
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Generated by Django 5.2.7 on 2025-10-07 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_tasks", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="task",
|
||||
name="aggregated_status",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("queued", "Queued"),
|
||||
("consumed", "Consumed"),
|
||||
("preprocess", "Preprocess"),
|
||||
("running", "Running"),
|
||||
("postprocess", "Postprocess"),
|
||||
("rejected", "Rejected"),
|
||||
("done", "Done"),
|
||||
("info", "Info"),
|
||||
("warning", "Warning"),
|
||||
("error", "Error"),
|
||||
]
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="task",
|
||||
name="state",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("queued", "Queued"),
|
||||
("consumed", "Consumed"),
|
||||
("preprocess", "Preprocess"),
|
||||
("running", "Running"),
|
||||
("postprocess", "Postprocess"),
|
||||
("rejected", "Rejected"),
|
||||
("done", "Done"),
|
||||
],
|
||||
default="queued",
|
||||
help_text="Task status",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -19,9 +19,6 @@ class TaskStatus(models.TextChoices):
|
||||
|
||||
QUEUED = TaskState.QUEUED
|
||||
CONSUMED = TaskState.CONSUMED
|
||||
PREPROCESS = TaskState.PREPROCESS
|
||||
RUNNING = TaskState.RUNNING
|
||||
POSTPROCESS = TaskState.POSTPROCESS
|
||||
REJECTED = TaskState.REJECTED
|
||||
DONE = TaskState.DONE
|
||||
INFO = "info"
|
||||
|
||||
@@ -7479,7 +7479,6 @@
|
||||
],
|
||||
"enum": [
|
||||
null,
|
||||
"authentik.commands",
|
||||
"authentik.tenants",
|
||||
"authentik.tasks",
|
||||
"authentik.admin",
|
||||
|
||||
6
go.mod
6
go.mod
@@ -8,7 +8,7 @@ require (
|
||||
beryju.io/ldap v0.1.0
|
||||
beryju.io/radius-eap v0.1.0
|
||||
github.com/avast/retry-go/v4 v4.6.1
|
||||
github.com/coreos/go-oidc/v3 v3.16.0
|
||||
github.com/coreos/go-oidc/v3 v3.15.0
|
||||
github.com/getsentry/sentry-go v0.35.3
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.12
|
||||
@@ -51,7 +51,7 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 // indirect
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.24.0 // indirect
|
||||
@@ -96,3 +96,5 @@ require (
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace goauthentik.io/api/v3 => ./gen-go-api
|
||||
|
||||
8
go.sum
8
go.sum
@@ -18,8 +18,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -40,8 +40,8 @@ github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 h1:O6yi4xa9b2D
|
||||
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27/go.mod h1:AYvN8omj7nKLmbcXS2dyABYU6JB1Lz1bHmkkq1kf4I4=
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
|
||||
github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-10-07 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-10-02 00:10+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -4006,22 +4006,6 @@ msgstr ""
|
||||
msgid "Domains"
|
||||
msgstr ""
|
||||
|
||||
#: packages/django-channels-postgres/django_channels_postgres/models.py
|
||||
msgid "Group channel"
|
||||
msgstr ""
|
||||
|
||||
#: packages/django-channels-postgres/django_channels_postgres/models.py
|
||||
msgid "Group channels"
|
||||
msgstr ""
|
||||
|
||||
#: packages/django-channels-postgres/django_channels_postgres/models.py
|
||||
msgid "Message"
|
||||
msgstr ""
|
||||
|
||||
#: packages/django-channels-postgres/django_channels_postgres/models.py
|
||||
msgid "Messages"
|
||||
msgstr ""
|
||||
|
||||
#: packages/django-dramatiq-postgres/django_dramatiq_postgres/models.py
|
||||
msgid "Queue name"
|
||||
msgstr ""
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# django-channels-postgres
|
||||
@@ -1,5 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DjangoChannelsPostgresConfig(AppConfig):
|
||||
name = "django_channels_postgres"
|
||||
@@ -1,524 +0,0 @@
|
||||
import asyncio
|
||||
import functools
|
||||
import types
|
||||
import zlib
|
||||
from base64 import b64decode
|
||||
from contextlib import AbstractAsyncContextManager
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from re import Pattern
|
||||
from typing import Any, cast
|
||||
from uuid import uuid4
|
||||
|
||||
import msgpack
|
||||
from channels.layers import BaseChannelLayer
|
||||
from django.db import DEFAULT_DB_ALIAS, connections
|
||||
from django.utils.timezone import now
|
||||
from psycopg import AsyncConnection, Notify, sql
|
||||
from psycopg.conninfo import make_conninfo
|
||||
from psycopg.errors import Error as PsycopgError
|
||||
from psycopg_pool import AsyncConnectionPool
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from django_channels_postgres.models import NOTIFY_CHANNEL, GroupChannel, Message
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
GROUP_CHANNEL_TABLE = GroupChannel._meta.db_table
|
||||
MESSAGE_TABLE = Message._meta.db_table
|
||||
|
||||
|
||||
async def _async_proxy(
|
||||
obj: "PostgresChannelLayerLoopProxy",
|
||||
name: str,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
# Must be defined as a function and not a method due to
|
||||
# https://bugs.python.org/issue38364
|
||||
layer = obj._get_layer()
|
||||
return await getattr(layer, name)(*args, **kwargs)
|
||||
|
||||
|
||||
def _wrap_close(proxy: "PostgresChannelLayerLoopProxy", loop: asyncio.AbstractEventLoop) -> None:
|
||||
original_impl = loop.close
|
||||
|
||||
def _wrapper(self: asyncio.AbstractEventLoop, *args: Any, **kwargs: Any) -> None:
|
||||
if loop in proxy._layers:
|
||||
layer = proxy._layers[loop]
|
||||
del proxy._layers[loop]
|
||||
loop.run_until_complete(layer.flush())
|
||||
self.close = original_impl # type: ignore[method-assign]
|
||||
return self.close(*args, **kwargs)
|
||||
|
||||
loop.close = types.MethodType(_wrapper, loop) # type: ignore[method-assign]
|
||||
|
||||
|
||||
class PostgresChannelLayerLoopProxy:
|
||||
def __init__(
|
||||
self,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self._args = args
|
||||
self._kwargs = kwargs
|
||||
self._kwargs["channel_layer"] = self
|
||||
self._layers: dict[asyncio.AbstractEventLoop, PostgresChannelLoopLayer] = {}
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
if name in (
|
||||
"new_channel",
|
||||
"send",
|
||||
"receive",
|
||||
"group_add",
|
||||
"group_discard",
|
||||
"group_send",
|
||||
"flush",
|
||||
):
|
||||
return functools.partial(_async_proxy, self, name)
|
||||
else:
|
||||
return getattr(self._get_layer(), name)
|
||||
|
||||
def serialize(self, message: dict[str, Any]) -> bytes:
|
||||
"""Serializes message to a byte string."""
|
||||
m = cast(bytes, msgpack.packb(message, use_bin_type=True))
|
||||
c = zlib.compress(m, 6)
|
||||
return c
|
||||
|
||||
def deserialize(self, message: bytes) -> dict[str, Any]:
|
||||
"""Deserializes from a byte string."""
|
||||
m = zlib.decompress(message)
|
||||
return cast(dict[str, Any], msgpack.unpackb(m, raw=False))
|
||||
|
||||
def _get_layer(self) -> "PostgresChannelLoopLayer":
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
try:
|
||||
layer = self._layers[loop]
|
||||
except KeyError:
|
||||
layer = PostgresChannelLoopLayer(*self._args, **self._kwargs)
|
||||
self._layers[loop] = layer
|
||||
_wrap_close(self, loop)
|
||||
|
||||
return layer
|
||||
|
||||
|
||||
PostgresChannelLayer = PostgresChannelLayerLoopProxy
|
||||
|
||||
|
||||
class PostgresChannelLoopLayer(BaseChannelLayer):
|
||||
"""
|
||||
Postgres channel layer.
|
||||
|
||||
It uses the NOTIFY/LISTEN functionality of postgres to broadcast messages
|
||||
|
||||
It also makes use of an internal message table to overcome the
|
||||
8000bytes limit of Postgres' NOTIFY messages.
|
||||
Which is a far cry from the channels standard of 1MB
|
||||
This table has a trigger that sends out the `NOTIFY` signal.
|
||||
|
||||
Using a database also means messages are durable and will always be
|
||||
available to consumers (as long as they're not expired).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
channel_layer: PostgresChannelLayerLoopProxy,
|
||||
prefix: str = "asgi",
|
||||
expiry: int = 60,
|
||||
group_expiry: int = 86400,
|
||||
capacity: int = 100,
|
||||
channel_capacity: dict[Pattern[str] | str, int] | None = None,
|
||||
using: str = DEFAULT_DB_ALIAS,
|
||||
) -> None:
|
||||
super().__init__(expiry=expiry, capacity=capacity, channel_capacity=channel_capacity)
|
||||
|
||||
self.group_expiry = group_expiry
|
||||
self.prefix = prefix
|
||||
assert isinstance(self.prefix, str), "Prefix must be unicode" # nosec
|
||||
self.channel_layer = channel_layer
|
||||
self.using = using
|
||||
self._pool_lock = asyncio.Lock()
|
||||
|
||||
# Each consumer gets its own *specific* channel, created with the `new_channel()` method.
|
||||
# This dict maps `channel_name` to a queue of messages for that channel.
|
||||
self.channels: dict[str, asyncio.Queue[tuple[str, bytes | None]]] = {}
|
||||
|
||||
self._pool: AsyncConnectionPool | None = None
|
||||
self.receiver = PostgresChannelLayerReceiver(self.using, self)
|
||||
|
||||
def make_conninfo(self) -> str:
|
||||
db_params = connections[self.using].get_connection_params()
|
||||
# Prevent psycopg from using the custom synchronous cursor factory from django
|
||||
db_params.pop("cursor_factory")
|
||||
db_params.pop("context")
|
||||
return make_conninfo(conninfo="", **db_params, connect_timeout=10)
|
||||
|
||||
async def connection(self) -> AbstractAsyncContextManager[AsyncConnection]:
|
||||
if self._pool is None:
|
||||
async with self._pool_lock:
|
||||
|
||||
async def _configure_connection(conn: AsyncConnection) -> None:
|
||||
await conn.set_autocommit(True)
|
||||
conn.prepare_threshold = 0 # All statements should be prepared
|
||||
conn.prepared_max = None # No limit on the number of prepared statements
|
||||
|
||||
self._pool = AsyncConnectionPool(
|
||||
conninfo=self.make_conninfo(),
|
||||
open=False,
|
||||
configure=_configure_connection,
|
||||
min_size=1,
|
||||
max_size=4,
|
||||
)
|
||||
await self._pool.open(wait=True)
|
||||
return self._pool.connection()
|
||||
|
||||
async def _subscribe_to_channel(self, channel: str) -> None:
|
||||
self.channels[channel] = asyncio.Queue()
|
||||
await self.receiver.subscribe(channel)
|
||||
|
||||
extensions = ["groups", "flush"]
|
||||
|
||||
### Channel layer API ###
|
||||
|
||||
async def send(self, channel: str, message: dict[str, Any]) -> None:
|
||||
"""
|
||||
Send a message onto a (general or specific) channel.
|
||||
"""
|
||||
# Typecheck
|
||||
assert isinstance(message, dict), "message is not a dict" # nosec
|
||||
assert self.require_valid_channel_name(channel), "Channel name not valid" # nosec
|
||||
# Make sure the message does not contain reserved keys
|
||||
assert "__asgi_channel__" not in message # nosec
|
||||
|
||||
async with await self.connection() as conn:
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
sql.SQL(
|
||||
"""
|
||||
INSERT INTO {table}
|
||||
({id}, {channel}, {message}, {expires})
|
||||
VALUES (%s, %s, %s, %s)
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(MESSAGE_TABLE),
|
||||
id=sql.Identifier("id"),
|
||||
channel=sql.Identifier("channel"),
|
||||
message=sql.Identifier("message"),
|
||||
expires=sql.Identifier("expires"),
|
||||
),
|
||||
(
|
||||
uuid4(),
|
||||
channel,
|
||||
self.channel_layer.serialize(message),
|
||||
now() + timedelta(seconds=self.expiry),
|
||||
),
|
||||
)
|
||||
|
||||
async def new_channel(self, prefix: str = "specific") -> str:
|
||||
"""
|
||||
Returns a new channel name that can be used by something in our
|
||||
process as a specific channel.
|
||||
"""
|
||||
channel = f"{self.prefix}.{prefix}.{uuid4().hex}"
|
||||
await self._subscribe_to_channel(channel)
|
||||
return channel
|
||||
|
||||
async def receive(self, channel: str) -> dict[str, Any]:
|
||||
"""
|
||||
Receive the first message that arrives on the channel.
|
||||
If more than one coroutine waits on the same channel, the first waiter
|
||||
will be given the message when it arrives.
|
||||
"""
|
||||
if channel not in self.channels:
|
||||
await self._subscribe_to_channel(channel)
|
||||
|
||||
q = self.channels[channel]
|
||||
try:
|
||||
while True:
|
||||
(message_id, message) = await q.get()
|
||||
if message is None:
|
||||
async with await self.connection() as conn:
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
sql.SQL(
|
||||
"""
|
||||
SELECT {table}.{message}
|
||||
FROM {table}
|
||||
WHERE {table}.{id} = %s
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(MESSAGE_TABLE),
|
||||
id=sql.Identifier("id"),
|
||||
message=sql.Identifier("message"),
|
||||
),
|
||||
(message_id,),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
if row is None:
|
||||
continue
|
||||
message = row[0]
|
||||
break
|
||||
except (asyncio.CancelledError, TimeoutError, GeneratorExit):
|
||||
# We assume here that the reason we are cancelled is because the consumer
|
||||
# is exiting, therefore we need to cleanup by unsubscribe below. Indeed,
|
||||
# currently the way that Django Channels works, this is a safe assumption.
|
||||
# In the future, Django Channels could change to call a *new* method that
|
||||
# would serve as the antithesis of `new_channel()`; this new method might
|
||||
# be named `delete_channel()`. If that were the case, we would do the
|
||||
# following cleanup from that new `delete_channel()` method, but, since
|
||||
# that's not how Django Channels works (yet), we do the cleanup below:
|
||||
if channel in self.channels:
|
||||
del self.channels[channel]
|
||||
try:
|
||||
await self.receiver.unsubscribe(channel)
|
||||
except BaseException as exc: # noqa: BLE001
|
||||
LOGGER.warning("Unexpected exception while cleaning-up channel", exc=exc)
|
||||
# We don't re-raise here because we want the CancelledError to be the one
|
||||
# re-raised
|
||||
raise
|
||||
return self.channel_layer.deserialize(message)
|
||||
|
||||
# ==============================================================
|
||||
# Groups extension
|
||||
# ==============================================================
|
||||
|
||||
async def group_add(self, group: str, channel: str) -> None:
|
||||
"""
|
||||
Adds the channel name to a group.
|
||||
"""
|
||||
# Check the inputs
|
||||
assert self.require_valid_group_name(group), "Group name not valid" # nosec
|
||||
assert self.require_valid_channel_name(channel), "Channel name not valid" # nosec
|
||||
|
||||
group_key = self._group_key(group)
|
||||
|
||||
async with await self.connection() as conn:
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
sql.SQL(
|
||||
"""
|
||||
INSERT INTO {table}
|
||||
({id}, {group_key}, {channel}, {expires})
|
||||
VALUES (%s, %s, %s, %s)
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(GROUP_CHANNEL_TABLE),
|
||||
id=sql.Identifier("id"),
|
||||
group_key=sql.Identifier("group_key"),
|
||||
channel=sql.Identifier("channel"),
|
||||
expires=sql.Identifier("expires"),
|
||||
),
|
||||
(
|
||||
uuid4(),
|
||||
group_key,
|
||||
channel,
|
||||
now() + timedelta(seconds=self.group_expiry),
|
||||
),
|
||||
)
|
||||
|
||||
async def group_discard(self, group: str, channel: str) -> None:
|
||||
"""
|
||||
Removes the channel from the named group if it is in the group;
|
||||
does nothing otherwise (does not error)
|
||||
"""
|
||||
# Check the inputs
|
||||
assert self.require_valid_group_name(group), "Group name not valid" # nosec
|
||||
assert self.require_valid_channel_name(channel), "Channel name not valid" # nosec
|
||||
|
||||
group_key = self._group_key(group)
|
||||
|
||||
async with await self.connection() as conn:
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
sql.SQL(
|
||||
"""
|
||||
DELETE
|
||||
FROM {table}
|
||||
WHERE {table}.{group_key} = %s
|
||||
AND {table}.{channel} = %s
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(GROUP_CHANNEL_TABLE),
|
||||
group_key=sql.Identifier("group_key"),
|
||||
channel=sql.Identifier("channel"),
|
||||
),
|
||||
(group_key, channel),
|
||||
)
|
||||
|
||||
async def group_send(self, group: str, message: dict[str, Any]) -> None:
|
||||
"""
|
||||
Sends a message to the entire group.
|
||||
"""
|
||||
assert self.require_valid_group_name(group), "Group name not valid" # nosec
|
||||
|
||||
group_key = self._group_key(group)
|
||||
|
||||
serialized_message = self.channel_layer.serialize(message)
|
||||
|
||||
async with await self.connection() as conn:
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
sql.SQL(
|
||||
"""
|
||||
SELECT DISTINCT {table}.{channel}
|
||||
FROM {table}
|
||||
WHERE {table}.{group_key} = %s
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(GROUP_CHANNEL_TABLE),
|
||||
channel=sql.Identifier("channel"),
|
||||
group_key=sql.Identifier("group_key"),
|
||||
),
|
||||
(group_key,),
|
||||
)
|
||||
channels = [row[0] for row in await cursor.fetchall()]
|
||||
messages = [
|
||||
(uuid4(), channel, serialized_message, now() + timedelta(seconds=self.expiry))
|
||||
for channel in channels
|
||||
]
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.executemany(
|
||||
sql.SQL(
|
||||
"""
|
||||
INSERT INTO {table}
|
||||
({id}, {channel}, {message}, {expires})
|
||||
VALUES (%s, %s, %s, %s)
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(MESSAGE_TABLE),
|
||||
id=sql.Identifier("id"),
|
||||
channel=sql.Identifier("channel"),
|
||||
message=sql.Identifier("message"),
|
||||
expires=sql.Identifier("expires"),
|
||||
),
|
||||
messages,
|
||||
)
|
||||
|
||||
def _group_key(self, group: str) -> str:
|
||||
"""
|
||||
Common function to make the storage key for the group.
|
||||
"""
|
||||
return f"{self.prefix}.group.{group}"
|
||||
|
||||
### Flush extension ###
|
||||
|
||||
async def flush(self) -> None:
|
||||
"""
|
||||
Deletes all messages and groups.
|
||||
"""
|
||||
self.channels = {}
|
||||
await self.receiver.flush()
|
||||
|
||||
|
||||
class PostgresChannelLayerReceiver:
|
||||
def __init__(self, using: str, channel_layer: PostgresChannelLoopLayer) -> None:
|
||||
self.using = using
|
||||
self.channel_layer = channel_layer
|
||||
self._subscribed_to: set[str] = set()
|
||||
self._lock = asyncio.Lock()
|
||||
self._receive_task: asyncio.Task[None] | None = None
|
||||
|
||||
async def subscribe(self, channel: str) -> None:
|
||||
async with self._lock:
|
||||
if channel not in self._subscribed_to:
|
||||
self._ensure_receiver()
|
||||
self._subscribed_to.add(channel)
|
||||
|
||||
async def unsubscribe(self, channel: str) -> None:
|
||||
async with self._lock:
|
||||
if channel in self._subscribed_to:
|
||||
self._ensure_receiver()
|
||||
self._subscribed_to.remove(channel)
|
||||
|
||||
async def flush(self) -> None:
|
||||
async with self._lock:
|
||||
if self._receive_task is not None:
|
||||
self._receive_task.cancel()
|
||||
try:
|
||||
await self._receive_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
self._receive_task = None
|
||||
self._subscribed_to = set()
|
||||
|
||||
async def _do_receiving(self) -> None:
|
||||
while True:
|
||||
try:
|
||||
async with await AsyncConnection.connect(
|
||||
conninfo=self.channel_layer.make_conninfo(),
|
||||
autocommit=True,
|
||||
) as conn:
|
||||
await self._process_backlog(conn)
|
||||
await conn.execute(
|
||||
sql.SQL("LISTEN {channel}").format(channel=sql.Identifier(NOTIFY_CHANNEL))
|
||||
)
|
||||
while True:
|
||||
async for notify in conn.notifies(timeout=30):
|
||||
await self._receive_notify(notify)
|
||||
except (asyncio.CancelledError, TimeoutError, GeneratorExit):
|
||||
raise
|
||||
except PsycopgError as exc:
|
||||
LOGGER.warning("Postgres connection is not healthy", exc=exc)
|
||||
except BaseException as exc: # noqa: BLE001
|
||||
LOGGER.warning("Unexpected exception in receive task", exc=exc, exc_info=True)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def _process_backlog(self, conn: AsyncConnection) -> None:
|
||||
if not self._subscribed_to:
|
||||
return
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
sql.SQL(
|
||||
"""
|
||||
DELETE
|
||||
FROM {table}
|
||||
WHERE {table}.{channel} IN (%s)
|
||||
AND {table}.{expires} >= %s
|
||||
RETURNING {table}.{id}, {table}.{channel}, {table}.{message}
|
||||
"""
|
||||
).format(
|
||||
table=sql.Identifier(MESSAGE_TABLE),
|
||||
id=sql.Identifier("id"),
|
||||
channel=sql.Identifier("channel"),
|
||||
expires=sql.Identifier("expires"),
|
||||
message=sql.Identifier("message"),
|
||||
),
|
||||
(tuple(self._subscribed_to), now()),
|
||||
)
|
||||
async for row in cursor:
|
||||
message_id, channel, message = row
|
||||
self._receive_message(channel, message_id, message)
|
||||
|
||||
async def _receive_notify(self, notify: Notify) -> None:
|
||||
payload = notify.payload
|
||||
split_payload = payload.split(":")
|
||||
message: bytes | None = None
|
||||
match len(split_payload):
|
||||
case 4:
|
||||
message_id, channel, timestamp, base64_message = split_payload
|
||||
if channel not in self._subscribed_to:
|
||||
return
|
||||
expires = datetime.fromtimestamp(float(timestamp), tz=UTC)
|
||||
if expires < now():
|
||||
return
|
||||
message = b64decode(base64_message)
|
||||
case 3:
|
||||
message_id, channel, timestamp = split_payload
|
||||
if channel not in self._subscribed_to:
|
||||
return
|
||||
expires = datetime.fromtimestamp(float(timestamp), tz=UTC)
|
||||
if expires < now():
|
||||
return
|
||||
message = None
|
||||
case _:
|
||||
return
|
||||
self._receive_message(channel, message_id, message)
|
||||
|
||||
def _receive_message(self, channel: str, message_id: str, message: bytes | None) -> None:
|
||||
if (q := self.channel_layer.channels.get(channel)) is not None:
|
||||
q.put_nowait((message_id, message))
|
||||
|
||||
def _ensure_receiver(self) -> None:
|
||||
if self._receive_task is None:
|
||||
self._receive_task = asyncio.ensure_future(self._do_receiving())
|
||||
@@ -1,94 +0,0 @@
|
||||
# Generated by Django 5.1.13 on 2025-10-04 14:35
|
||||
|
||||
import django_channels_postgres.models
|
||||
import pgtrigger.compiler
|
||||
import pgtrigger.migrations
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="GroupChannel",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
("group_key", models.TextField(db_index=True)),
|
||||
("channel", models.TextField(db_index=True)),
|
||||
(
|
||||
"expires",
|
||||
models.DateTimeField(
|
||||
db_index=True, default=django_channels_postgres.models._default_group_expiry
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Group channel",
|
||||
"verbose_name_plural": "Group channels",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["group_key", "channel"], name="django_chan_group_k_173f44_idx"
|
||||
),
|
||||
models.Index(
|
||||
fields=["group_key", "expires"], name="django_chan_group_k_45d2e0_idx"
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Message",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
("channel", models.TextField(db_index=True)),
|
||||
("message", models.BinaryField()),
|
||||
(
|
||||
"expires",
|
||||
models.DateTimeField(
|
||||
db_index=True,
|
||||
default=django_channels_postgres.models._default_message_expiry,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Message",
|
||||
"verbose_name_plural": "Messages",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["channel", "expires"], name="django_chan_channel_e8ca51_idx"
|
||||
)
|
||||
],
|
||||
},
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="message",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="notify_new_channels_message",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
constraint="CONSTRAINT",
|
||||
declare="DECLARE payload text; encoded_message text; epoch text;",
|
||||
func="\n encoded_message := encode(NEW.message, 'base64');\n epoch := extract(epoch from NEW.expires)::text;\n IF octet_length(NEW.id::text) + octet_length(NEW.channel) + octet_length(epoch) + octet_length(encoded_message) + 3 <= 8000 THEN\n payload := NEW.id::text || ':' || NEW.channel || ':' || epoch || ':' || encoded_message;\n ELSE\n payload := NEW.id::text || ':' || NEW.channel || ':' || epoch;\n END IF;\n\n PERFORM pg_notify('channels_messages', payload);\n RETURN NEW;\n ",
|
||||
hash="cf7a665df0bbb7d865cdbc92b63d818fd25733d8",
|
||||
operation="INSERT",
|
||||
pgid="pgtrigger_notify_new_channels_message_d21ae",
|
||||
table="django_channels_postgres_message",
|
||||
timing="DEFERRABLE INITIALLY DEFERRED",
|
||||
when="AFTER",
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,97 +0,0 @@
|
||||
from datetime import datetime, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
import pgtrigger
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def _default_group_expiry() -> datetime:
|
||||
return now() + timedelta(seconds=86400)
|
||||
|
||||
|
||||
def _default_message_expiry() -> datetime:
|
||||
return now() + timedelta(minutes=1)
|
||||
|
||||
|
||||
NOTIFY_CHANNEL = "channels_messages"
|
||||
|
||||
|
||||
class GroupChannel(models.Model):
|
||||
"""
|
||||
A model that represents a group channel.
|
||||
|
||||
Groups are used to send messages to multiple channels.
|
||||
"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
group_key = models.TextField(db_index=True)
|
||||
channel = models.TextField(db_index=True)
|
||||
expires = models.DateTimeField(db_index=True, default=_default_group_expiry)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Group channel")
|
||||
verbose_name_plural = _("Group channels")
|
||||
indexes = (
|
||||
models.Index(fields=("group_key", "channel")),
|
||||
models.Index(fields=("group_key", "expires")),
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Group '{self.group_key}' on channel '{self.channel}'"
|
||||
|
||||
@classmethod
|
||||
def delete_expired(cls) -> None:
|
||||
cls.objects.filter(expires__lt=now()).delete()
|
||||
|
||||
|
||||
class Message(models.Model):
|
||||
"""
|
||||
A model that represents a message.
|
||||
|
||||
Messages are used to send messages to a specific channel.
|
||||
E.g for user to user private messages.
|
||||
"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
channel = models.TextField(db_index=True)
|
||||
message = models.BinaryField()
|
||||
expires = models.DateTimeField(db_index=True, default=_default_message_expiry)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Message")
|
||||
verbose_name_plural = _("Messages")
|
||||
indexes = (models.Index(fields=("channel", "expires")),)
|
||||
triggers = (
|
||||
pgtrigger.Trigger(
|
||||
name="notify_new_channels_message",
|
||||
operation=pgtrigger.Insert,
|
||||
when=pgtrigger.After,
|
||||
timing=pgtrigger.Deferred,
|
||||
declare=[
|
||||
("payload", "text"),
|
||||
("encoded_message", "text"),
|
||||
("epoch", "text"),
|
||||
],
|
||||
func=f"""
|
||||
encoded_message := encode(NEW.message, 'base64');
|
||||
epoch := extract(epoch from NEW.expires)::text;
|
||||
IF octet_length(NEW.id::text) + octet_length(NEW.channel) + octet_length(epoch) + octet_length(encoded_message) + 3 <= 8000 THEN
|
||||
payload := NEW.id::text || ':' || NEW.channel || ':' || epoch || ':' || encoded_message;
|
||||
ELSE
|
||||
payload := NEW.id::text || ':' || NEW.channel || ':' || epoch;
|
||||
END IF;
|
||||
|
||||
PERFORM pg_notify('{NOTIFY_CHANNEL}', payload);
|
||||
RETURN NEW;
|
||||
""", # noqa: E501
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Message '{self.pk}' on channel '{self.channel}'"
|
||||
|
||||
@classmethod
|
||||
def delete_expired(cls) -> None:
|
||||
cls.objects.filter(expires__lt=now()).delete()
|
||||
@@ -1,52 +0,0 @@
|
||||
[project]
|
||||
name = "django-channels-postgres"
|
||||
version = "0.1.0"
|
||||
description = "Django channels layer using PostgreSQL NOTIFY/LISTEN"
|
||||
requires-python = ">=3.9,<3.14"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
authors = [{ name = "Authentik Security Inc.", email = "hello@goauthentik.io" }]
|
||||
keywords = ["django", "channels", "postgres"]
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Web Environment",
|
||||
"Framework :: Django",
|
||||
"Framework :: Django :: 4.2",
|
||||
"Framework :: Django :: 5.0",
|
||||
"Framework :: Django :: 5.1",
|
||||
"Framework :: Django :: 5.2",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: MacOS",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"channels >=4.3,<4.4",
|
||||
"django >=4.2,<6.0",
|
||||
"django-pgtrigger >=4,<5",
|
||||
"msgpack >=1,<2",
|
||||
"psycopg[pool] >=3,<4",
|
||||
"structlog >=25,<26",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/goauthentik/authentik/tree/main/packages/django-channels-postgres"
|
||||
Documentation = "https://github.com/goauthentik/authentik/tree/main/packages/django-channels-postgres"
|
||||
Repository = "https://github.com/goauthentik/authentik/tree/main/packages/django-channels-postgres"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.setuptools.packages]
|
||||
find = {}
|
||||
@@ -209,11 +209,10 @@ class PostgresBroker(Broker):
|
||||
if deadline and time.monotonic() >= deadline:
|
||||
raise QueueJoinTimeout(queue_name) # type: ignore[no-untyped-call]
|
||||
|
||||
if (
|
||||
not self.query_set.filter(queue_name=queue_name)
|
||||
.exclude(state__in=(TaskState.DONE, TaskState.REJECTED))
|
||||
.exists()
|
||||
):
|
||||
if self.query_set.filter(
|
||||
queue_name=queue_name,
|
||||
state__in=(TaskState.QUEUED, TaskState.CONSUMED),
|
||||
).exists():
|
||||
return
|
||||
|
||||
time.sleep(interval / 1000)
|
||||
@@ -289,6 +288,7 @@ class _PostgresConsumer(Consumer):
|
||||
self.query_set.filter(
|
||||
message_id=message.message_id,
|
||||
queue_name=message.queue_name,
|
||||
state=TaskState.CONSUMED,
|
||||
).update(
|
||||
state=TaskState.DONE,
|
||||
message=message.encode(),
|
||||
@@ -303,6 +303,8 @@ class _PostgresConsumer(Consumer):
|
||||
self.query_set.filter(
|
||||
message_id=message.message_id,
|
||||
queue_name=message.queue_name,
|
||||
).exclude(
|
||||
state=TaskState.REJECTED,
|
||||
).update(
|
||||
state=TaskState.REJECTED,
|
||||
message=message.encode(),
|
||||
@@ -326,9 +328,11 @@ class _PostgresConsumer(Consumer):
|
||||
def _fetch_pending_notifies(self) -> list[Notify]:
|
||||
self.logger.debug("Polling for lost messages", queue=self.queue_name)
|
||||
notifies = (
|
||||
self.query_set.filter(queue_name=self.queue_name)
|
||||
self.query_set.filter(
|
||||
state__in=(TaskState.QUEUED, TaskState.CONSUMED),
|
||||
queue_name=self.queue_name,
|
||||
)
|
||||
.exclude(
|
||||
state__in=(TaskState.DONE, TaskState.REJECTED),
|
||||
message_id__in=self.in_processing,
|
||||
)
|
||||
.values_list("message_id", flat=True)
|
||||
@@ -359,8 +363,10 @@ class _PostgresConsumer(Consumer):
|
||||
return False
|
||||
|
||||
result = (
|
||||
self.query_set.filter(message_id=message.message_id)
|
||||
.exclude(state__in=(TaskState.DONE, TaskState.REJECTED))
|
||||
self.query_set.filter(
|
||||
message_id=message.message_id,
|
||||
state__in=(TaskState.QUEUED, TaskState.CONSUMED),
|
||||
)
|
||||
.extra(
|
||||
where=["pg_try_advisory_lock(%s)"],
|
||||
params=[self._get_message_lock_id(message.message_id)],
|
||||
|
||||
@@ -43,14 +43,12 @@ class Conf:
|
||||
"middlewares",
|
||||
(
|
||||
("django_dramatiq_postgres.middleware.DbConnectionMiddleware", {}),
|
||||
("django_dramatiq_postgres.middleware.TaskStateBeforeMiddleware", {}),
|
||||
("dramatiq.middleware.age_limit.AgeLimit", {}),
|
||||
("dramatiq.middleware.time_limit.TimeLimit", {}),
|
||||
("dramatiq.middleware.shutdown.ShutdownNotifications", {}),
|
||||
("dramatiq.middleware.callbacks.Callbacks", {}),
|
||||
("dramatiq.middleware.pipelines.Pipelines", {}),
|
||||
("dramatiq.middleware.retries.Retries", {}),
|
||||
("django_dramatiq_postgres.middleware.TaskStateAfterMiddleware", {}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Callable
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from http.server import HTTPServer as BaseHTTPServer
|
||||
from ipaddress import IPv6Address, ip_address
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from typing import Any, cast
|
||||
|
||||
from django.db import DatabaseError, close_old_connections, connections
|
||||
from dramatiq.actor import Actor
|
||||
@@ -16,10 +16,7 @@ from dramatiq.middleware.middleware import Middleware
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from django_dramatiq_postgres.conf import Conf
|
||||
from django_dramatiq_postgres.models import TaskBase, TaskState
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django_dramatiq_postgres.broker import PostgresBroker
|
||||
from django_dramatiq_postgres.models import TaskBase
|
||||
|
||||
|
||||
class HTTPServer(BaseHTTPServer):
|
||||
@@ -70,47 +67,6 @@ class DbConnectionMiddleware(Middleware):
|
||||
before_worker_shutdown = _close_connections
|
||||
|
||||
|
||||
class TaskStateBeforeMiddleware(Middleware):
|
||||
def before_process_message(self, broker: "PostgresBroker", message: Message[Any]) -> None:
|
||||
broker.query_set.filter(
|
||||
message_id=message.message_id,
|
||||
queue_name=message.queue_name,
|
||||
state=TaskState.CONSUMED,
|
||||
).update(
|
||||
state=TaskState.PREPROCESS,
|
||||
)
|
||||
|
||||
|
||||
class TaskStateAfterMiddleware(Middleware):
|
||||
def before_process_message(self, broker: "PostgresBroker", message: Message[Any]) -> None:
|
||||
broker.query_set.filter(
|
||||
message_id=message.message_id,
|
||||
queue_name=message.queue_name,
|
||||
state=TaskState.PREPROCESS,
|
||||
).update(
|
||||
state=TaskState.RUNNING,
|
||||
)
|
||||
|
||||
def after_skip_message(self, broker: "PostgresBroker", message: Message[Any]) -> None:
|
||||
broker.query_set.filter(
|
||||
message_id=message.message_id,
|
||||
queue_name=message.queue_name,
|
||||
state=TaskState.RUNNING,
|
||||
).update(
|
||||
state=TaskState.POSTPROCESS,
|
||||
)
|
||||
|
||||
def after_process_message(
|
||||
self,
|
||||
broker: "PostgresBroker",
|
||||
message: Message[Any],
|
||||
*,
|
||||
result: Any | None = None,
|
||||
exception: Exception | None = None,
|
||||
) -> None:
|
||||
self.after_skip_message(broker, message)
|
||||
|
||||
|
||||
class FullyQualifiedActorName(Middleware):
|
||||
def before_declare_actor(self, broker: Broker, actor: Actor[Any, Any]) -> None:
|
||||
actor.actor_name = f"{actor.fn.__module__}.{actor.fn.__name__}"
|
||||
|
||||
@@ -31,9 +31,6 @@ class TaskState(models.TextChoices):
|
||||
|
||||
QUEUED = "queued"
|
||||
CONSUMED = "consumed"
|
||||
PREPROCESS = "preprocess"
|
||||
RUNNING = "running"
|
||||
POSTPROCESS = "postprocess"
|
||||
REJECTED = "rejected"
|
||||
DONE = "done"
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ classifiers = [
|
||||
dependencies = [
|
||||
"cron-converter >=1,<2",
|
||||
"django >=4.2,<6.0",
|
||||
"django-pglock >=1.7,<2",
|
||||
"django-pgtrigger >=4,<5",
|
||||
"dramatiq[watch] >=1.17,<1.18",
|
||||
"tenacity >=9,<10",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/node": "^24.3.1",
|
||||
"esbuild": "^0.25.9",
|
||||
"pino": "^10.0.0",
|
||||
"pino": "^9.9.5",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-packagejson": "^2.5.19",
|
||||
"typedoc": "^0.28.12",
|
||||
@@ -905,13 +905,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz",
|
||||
"integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==",
|
||||
"version": "24.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz",
|
||||
"integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.14.0"
|
||||
"undici-types": "~7.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
@@ -2294,9 +2294,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-10.0.0.tgz",
|
||||
"integrity": "sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==",
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.12.0.tgz",
|
||||
"integrity": "sha512-0Gd0OezGvqtqMwgYxpL7P0pSHHzTJ0Lx992h+mNlMtRVfNnqweWmf0JmRWk5gJzHalyd2mxTzKjhiNbGS2Ztfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2904,9 +2904,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
|
||||
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
|
||||
"version": "7.13.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz",
|
||||
"integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/node": "^24.3.1",
|
||||
"esbuild": "^0.25.9",
|
||||
"pino": "^10.0.0",
|
||||
"pino": "^9.9.5",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-packagejson": "^2.5.19",
|
||||
"typedoc": "^0.28.12",
|
||||
|
||||
330
packages/eslint-config/package-lock.json
generated
330
packages/eslint-config/package-lock.json
generated
@@ -122,6 +122,18 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-annotate-as-pure": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
|
||||
"integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.27.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
|
||||
@@ -138,6 +150,27 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
|
||||
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||
"@babel/helper-member-expression-to-functions": "^7.27.1",
|
||||
"@babel/helper-optimise-call-expression": "^7.27.1",
|
||||
"@babel/helper-replace-supers": "^7.27.1",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.3",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-globals": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
||||
@@ -147,6 +180,19 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-member-expression-to-functions": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
|
||||
"integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/types": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
||||
@@ -177,6 +223,57 @@
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-optimise-call-expression": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
|
||||
"integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
|
||||
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-replace-supers": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz",
|
||||
"integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-member-expression-to-functions": "^7.27.1",
|
||||
"@babel/helper-optimise-call-expression": "^7.27.1",
|
||||
"@babel/traverse": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
|
||||
"integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/types": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
@@ -232,6 +329,23 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-proposal-private-methods": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
|
||||
"integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
|
||||
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.18.6",
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
@@ -382,21 +496,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
|
||||
"integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
|
||||
"integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
|
||||
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
|
||||
"version": "0.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
|
||||
"integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
@@ -429,9 +540,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
|
||||
"integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
|
||||
"version": "9.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
|
||||
"integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -449,12 +560,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
|
||||
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
|
||||
"integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.16.0",
|
||||
"@eslint/core": "^0.15.2",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -729,17 +840,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
|
||||
"integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz",
|
||||
"integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.46.0",
|
||||
"@typescript-eslint/type-utils": "8.46.0",
|
||||
"@typescript-eslint/utils": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0",
|
||||
"@typescript-eslint/scope-manager": "8.45.0",
|
||||
"@typescript-eslint/type-utils": "8.45.0",
|
||||
"@typescript-eslint/utils": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -753,7 +864,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.46.0",
|
||||
"@typescript-eslint/parser": "^8.45.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
@@ -769,16 +880,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz",
|
||||
"integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz",
|
||||
"integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.46.0",
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0",
|
||||
"@typescript-eslint/scope-manager": "8.45.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -794,14 +905,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz",
|
||||
"integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz",
|
||||
"integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.46.0",
|
||||
"@typescript-eslint/types": "^8.46.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.45.0",
|
||||
"@typescript-eslint/types": "^8.45.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -816,14 +927,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz",
|
||||
"integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz",
|
||||
"integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0"
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -834,9 +945,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz",
|
||||
"integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz",
|
||||
"integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -851,15 +962,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz",
|
||||
"integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz",
|
||||
"integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0",
|
||||
"@typescript-eslint/utils": "8.46.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0",
|
||||
"@typescript-eslint/utils": "8.45.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@@ -876,9 +987,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz",
|
||||
"integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz",
|
||||
"integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -890,16 +1001,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz",
|
||||
"integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz",
|
||||
"integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.46.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.46.0",
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0",
|
||||
"@typescript-eslint/project-service": "8.45.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.45.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -958,16 +1069,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz",
|
||||
"integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz",
|
||||
"integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.46.0",
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0"
|
||||
"@typescript-eslint/scope-manager": "8.45.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -982,13 +1093,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz",
|
||||
"integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz",
|
||||
"integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1332,9 +1443,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.12",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.12.tgz",
|
||||
"integrity": "sha512-vAPMQdnyKCBtkmQA6FMCBvU9qFIppS3nzyXnEM+Lo2IAhG4Mpjv9cCxMudhgV3YdNNJv6TNqXy97dfRVL2LmaQ==",
|
||||
"version": "2.8.10",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz",
|
||||
"integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.js"
|
||||
@@ -1466,9 +1577,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001748",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz",
|
||||
"integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==",
|
||||
"version": "1.0.30001746",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz",
|
||||
"integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -1725,9 +1836,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.230",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.230.tgz",
|
||||
"integrity": "sha512-A6A6Fd3+gMdaed9wX83CvHYJb4UuapPD5X5SLq72VZJzxHSY0/LUweGXRWmQlh2ln7KV7iw7jnwXK7dlPoOnHQ==",
|
||||
"version": "1.5.228",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz",
|
||||
"integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
@@ -1921,19 +2032,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.37.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
|
||||
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
|
||||
"version": "9.36.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
|
||||
"integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.21.0",
|
||||
"@eslint/config-helpers": "^0.4.0",
|
||||
"@eslint/core": "^0.16.0",
|
||||
"@eslint/config-helpers": "^0.3.1",
|
||||
"@eslint/core": "^0.15.2",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.37.0",
|
||||
"@eslint/plugin-kit": "^0.4.0",
|
||||
"@eslint/js": "9.36.0",
|
||||
"@eslint/plugin-kit": "^0.3.5",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@@ -2117,15 +2228,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react-hooks": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-6.1.1.tgz",
|
||||
"integrity": "sha512-St9EKZzOAQF704nt2oJvAKZHjhrpg25ClQoaAlHmPZuajFldVLqRDW4VBNAS01NzeiQF0m0qhG1ZA807K6aVaQ==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-6.1.0.tgz",
|
||||
"integrity": "sha512-72mucw/WLzEqGvL2vwE6fWR6geO6UbmDjz3eAb3pezxTpFzgbfyUeFKzmZKr9LhwUWMXfTVh1g0rKEJoyKNdoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/parser": "^7.24.4",
|
||||
"zod": "^3.22.4 || ^4.0.0",
|
||||
"zod-validation-error": "^3.0.3 || ^4.0.0"
|
||||
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||
"hermes-parser": "^0.25.1",
|
||||
"zod": "^3.22.4",
|
||||
"zod-validation-error": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -2974,6 +3087,21 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hermes-estree": {
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
|
||||
"integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hermes-parser": {
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
|
||||
"integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hermes-estree": "0.25.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -3783,9 +3911,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.23",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz",
|
||||
"integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==",
|
||||
"version": "2.0.21",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz",
|
||||
"integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
@@ -5060,16 +5188,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz",
|
||||
"integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz",
|
||||
"integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.46.0",
|
||||
"@typescript-eslint/parser": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0",
|
||||
"@typescript-eslint/utils": "8.46.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.45.0",
|
||||
"@typescript-eslint/parser": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0",
|
||||
"@typescript-eslint/utils": "8.45.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
||||
57
packages/prettier-config/package-lock.json
generated
57
packages/prettier-config/package-lock.json
generated
@@ -172,21 +172,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
|
||||
"integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
|
||||
"integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
|
||||
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
|
||||
"version": "0.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
|
||||
"integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
@@ -231,9 +228,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
|
||||
"integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
|
||||
"version": "9.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
|
||||
"integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -252,12 +249,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
|
||||
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
|
||||
"integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.16.0",
|
||||
"@eslint/core": "^0.15.2",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -388,13 +385,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz",
|
||||
"integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==",
|
||||
"version": "24.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz",
|
||||
"integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.14.0"
|
||||
"undici-types": "~7.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
@@ -692,19 +689,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.37.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
|
||||
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
|
||||
"version": "9.36.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
|
||||
"integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.21.0",
|
||||
"@eslint/config-helpers": "^0.4.0",
|
||||
"@eslint/core": "^0.16.0",
|
||||
"@eslint/config-helpers": "^0.3.1",
|
||||
"@eslint/core": "^0.15.2",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.37.0",
|
||||
"@eslint/plugin-kit": "^0.4.0",
|
||||
"@eslint/js": "9.36.0",
|
||||
"@eslint/plugin-kit": "^0.3.5",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@@ -1727,9 +1724,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
|
||||
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
|
||||
"version": "7.13.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz",
|
||||
"integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -7,12 +7,12 @@ requires-python = "==3.13.*"
|
||||
dependencies = [
|
||||
"argon2-cffi==25.1.0",
|
||||
"channels==4.3.1",
|
||||
"channels-postgres==1.1.2",
|
||||
"cryptography==45.0.5",
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
"django==5.2.7",
|
||||
"django-channels-postgres",
|
||||
"django==5.1.13",
|
||||
"django-countries==7.6.1",
|
||||
"django-cte==2.0.0",
|
||||
"django-dramatiq-postgres",
|
||||
@@ -24,7 +24,7 @@ dependencies = [
|
||||
"django-pgtrigger==4.15.2",
|
||||
"django-prometheus==2.4.1",
|
||||
"django-storages[s3]==1.14.6",
|
||||
"django-tenants==3.9.0",
|
||||
"django-tenants==3.8.0",
|
||||
"djangoql==0.18.1",
|
||||
"djangorestframework-guardian==0.4.0",
|
||||
"djangorestframework==3.16.0",
|
||||
@@ -79,7 +79,6 @@ dev = [
|
||||
"aws-cdk-lib==2.188.0",
|
||||
"bandit==1.8.3",
|
||||
"black==25.1.0",
|
||||
"bpython==0.25",
|
||||
"channels[daphne]==4.3.1",
|
||||
"codespell==2.4.1",
|
||||
"colorama==0.4.6",
|
||||
@@ -103,7 +102,6 @@ dev = [
|
||||
"requests-mock==1.12.1",
|
||||
"ruff==0.11.9",
|
||||
"selenium==4.32.0",
|
||||
"types-channels==4.3.0.20250822",
|
||||
"types-ldap3==2.9.13.20250622",
|
||||
]
|
||||
|
||||
@@ -121,14 +119,13 @@ no-binary-package = [
|
||||
|
||||
[tool.uv.sources]
|
||||
djangorestframework = { git = "https://github.com/goauthentik/django-rest-framework", rev = "896722bab969fabc74a08b827da59409cf9f1a4e" }
|
||||
django-channels-postgres = { workspace = true }
|
||||
django-dramatiq-postgres = { workspace = true }
|
||||
django-postgres-cache = { workspace = true }
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" }
|
||||
channels-postgres = { git = "https://github.com/rissson/channels_postgres", rev = "93ed24e3c5317d7ccf7e8b1ce913c0f365d1728f" }
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = [
|
||||
"packages/django-channels-postgres",
|
||||
"packages/django-dramatiq-postgres",
|
||||
"packages/django-postgres-cache",
|
||||
]
|
||||
@@ -208,11 +205,11 @@ plugins = ["mypy_django_plugin.main", "mypy_drf_plugin.main", "pydantic.mypy"]
|
||||
exclude = ['^gen-py-api/']
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["django_tenants.*", "dramatiq.*", "pglock.*"]
|
||||
module = ["dramatiq.*", "pglock.*"]
|
||||
follow_untyped_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["cron_converter.*", "msgpack.*"]
|
||||
module = ["cron_converter.*"]
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
|
||||
152
schema.yml
152
schema.yml
@@ -8138,6 +8138,11 @@ paths:
|
||||
type: string
|
||||
description: Querystring as received
|
||||
required: true
|
||||
- in: query
|
||||
name: xid
|
||||
schema:
|
||||
type: string
|
||||
description: Flow execution ID
|
||||
tags:
|
||||
- flows
|
||||
security:
|
||||
@@ -8178,6 +8183,12 @@ paths:
|
||||
type: string
|
||||
description: Querystring as received
|
||||
required: true
|
||||
- in: query
|
||||
name: xid
|
||||
schema:
|
||||
type: string
|
||||
description: Flow execution ID
|
||||
required: true
|
||||
tags:
|
||||
- flows
|
||||
requestBody:
|
||||
@@ -38920,11 +38931,8 @@ paths:
|
||||
- done
|
||||
- error
|
||||
- info
|
||||
- postprocess
|
||||
- preprocess
|
||||
- queued
|
||||
- rejected
|
||||
- running
|
||||
- warning
|
||||
explode: true
|
||||
style: form
|
||||
@@ -38959,11 +38967,8 @@ paths:
|
||||
enum:
|
||||
- consumed
|
||||
- done
|
||||
- postprocess
|
||||
- preprocess
|
||||
- queued
|
||||
- rejected
|
||||
- running
|
||||
description: |+
|
||||
Task status
|
||||
|
||||
@@ -39053,33 +39058,6 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/tasks/tasks/status/:
|
||||
get:
|
||||
operationId: tasks_tasks_status_retrieve
|
||||
description: Global status summary for all tasks
|
||||
tags:
|
||||
- tasks
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GlobalTaskStatus'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/tasks/workers:
|
||||
get:
|
||||
operationId: tasks_workers_list
|
||||
@@ -39607,6 +39585,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-access-denied
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -39640,7 +39620,6 @@ components:
|
||||
- name
|
||||
AppEnum:
|
||||
enum:
|
||||
- authentik.commands
|
||||
- authentik.tenants
|
||||
- authentik.tasks
|
||||
- authentik.admin
|
||||
@@ -39730,6 +39709,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-source-oauth-apple
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -40065,6 +40046,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-duo
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -40221,6 +40204,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-email
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -40475,6 +40460,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-sms
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -40635,6 +40622,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-static
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -40753,6 +40742,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-totp
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -40977,6 +40968,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-validate
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -41030,6 +41023,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-webauthn
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -41192,6 +41187,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-autosubmit
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -41486,6 +41483,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-captcha
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -41900,6 +41899,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-consent
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -42695,6 +42696,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-dummy
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -42897,6 +42900,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-email
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -43878,6 +43883,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-flow-error
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -44206,6 +44213,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-frame
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -44375,50 +44384,6 @@ components:
|
||||
- bind_continent_country
|
||||
- bind_continent_country_city
|
||||
type: string
|
||||
GlobalTaskStatus:
|
||||
type: object
|
||||
properties:
|
||||
queued:
|
||||
type: integer
|
||||
readOnly: true
|
||||
consumed:
|
||||
type: integer
|
||||
readOnly: true
|
||||
preprocess:
|
||||
type: integer
|
||||
readOnly: true
|
||||
running:
|
||||
type: integer
|
||||
readOnly: true
|
||||
postprocess:
|
||||
type: integer
|
||||
readOnly: true
|
||||
rejected:
|
||||
type: integer
|
||||
readOnly: true
|
||||
done:
|
||||
type: integer
|
||||
readOnly: true
|
||||
info:
|
||||
type: integer
|
||||
readOnly: true
|
||||
warning:
|
||||
type: integer
|
||||
readOnly: true
|
||||
error:
|
||||
type: integer
|
||||
readOnly: true
|
||||
required:
|
||||
- consumed
|
||||
- done
|
||||
- error
|
||||
- info
|
||||
- postprocess
|
||||
- preprocess
|
||||
- queued
|
||||
- rejected
|
||||
- running
|
||||
- warning
|
||||
GoogleWorkspaceProvider:
|
||||
type: object
|
||||
description: GoogleWorkspaceProvider Serializer
|
||||
@@ -45220,6 +45185,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-identification
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -46711,9 +46678,6 @@ components:
|
||||
enum:
|
||||
- queued
|
||||
- consumed
|
||||
- preprocess
|
||||
- running
|
||||
- postprocess
|
||||
- rejected
|
||||
- done
|
||||
- info
|
||||
@@ -47929,6 +47893,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-provider-oauth2-device-code
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -47957,6 +47923,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-provider-oauth2-device-code-finish
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -50752,6 +50720,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-password
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -54711,6 +54681,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-source-plex
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -55236,6 +55208,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-prompt
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -56449,6 +56423,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-redirect
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -58494,6 +58470,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-session-end
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -58664,6 +58642,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-shell
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -59032,9 +59012,6 @@ components:
|
||||
enum:
|
||||
- queued
|
||||
- consumed
|
||||
- preprocess
|
||||
- running
|
||||
- postprocess
|
||||
- rejected
|
||||
- done
|
||||
type: string
|
||||
@@ -59303,9 +59280,6 @@ components:
|
||||
enum:
|
||||
- queued
|
||||
- consumed
|
||||
- preprocess
|
||||
- running
|
||||
- postprocess
|
||||
- rejected
|
||||
- done
|
||||
- info
|
||||
@@ -59357,6 +59331,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-source-telegram
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@@ -60342,6 +60318,8 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-user-login
|
||||
xid:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
chrome:
|
||||
platform: linux/x86_64
|
||||
image: docker.io/selenium/standalone-chrome:141.0
|
||||
image: docker.io/selenium/standalone-chrome:140.0
|
||||
volumes:
|
||||
- /dev/shm:/dev/shm
|
||||
network_mode: host
|
||||
|
||||
183
uv.lock
generated
183
uv.lock
generated
@@ -1,11 +1,10 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
revision = 3
|
||||
requires-python = "==3.13.*"
|
||||
|
||||
[manifest]
|
||||
members = [
|
||||
"authentik",
|
||||
"django-channels-postgres",
|
||||
"django-dramatiq-postgres",
|
||||
"django-postgres-cache",
|
||||
]
|
||||
@@ -86,15 +85,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansicon"
|
||||
version = "1.89.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b6/e2/1c866404ddbd280efedff4a9f15abfe943cb83cde6e895022370f3a61f85/ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1", size = 67312, upload-time = "2019-04-29T20:23:57.314Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/75/f9/f1c10e223c7b56a38109a3f2eb4e7fe9a757ea3ed3a166754fb30f65e466/ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec", size = 63675, upload-time = "2019-04-29T20:23:53.83Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.11.0"
|
||||
@@ -175,12 +165,12 @@ source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "argon2-cffi" },
|
||||
{ name = "channels" },
|
||||
{ name = "channels-postgres" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "dacite" },
|
||||
{ name = "deepmerge" },
|
||||
{ name = "defusedxml" },
|
||||
{ name = "django" },
|
||||
{ name = "django-channels-postgres" },
|
||||
{ name = "django-countries" },
|
||||
{ name = "django-cte" },
|
||||
{ name = "django-dramatiq-postgres" },
|
||||
@@ -247,7 +237,6 @@ dev = [
|
||||
{ name = "aws-cdk-lib" },
|
||||
{ name = "bandit" },
|
||||
{ name = "black" },
|
||||
{ name = "bpython" },
|
||||
{ name = "channels", extra = ["daphne"] },
|
||||
{ name = "codespell" },
|
||||
{ name = "colorama" },
|
||||
@@ -271,7 +260,6 @@ dev = [
|
||||
{ name = "requests-mock" },
|
||||
{ name = "ruff" },
|
||||
{ name = "selenium" },
|
||||
{ name = "types-channels" },
|
||||
{ name = "types-ldap3" },
|
||||
]
|
||||
|
||||
@@ -279,12 +267,12 @@ dev = [
|
||||
requires-dist = [
|
||||
{ name = "argon2-cffi", specifier = "==25.1.0" },
|
||||
{ name = "channels", specifier = "==4.3.1" },
|
||||
{ name = "channels-postgres", git = "https://github.com/rissson/channels_postgres?rev=93ed24e3c5317d7ccf7e8b1ce913c0f365d1728f" },
|
||||
{ name = "cryptography", specifier = "==45.0.5" },
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
{ name = "django", specifier = "==5.2.7" },
|
||||
{ name = "django-channels-postgres", editable = "packages/django-channels-postgres" },
|
||||
{ name = "django", specifier = "==5.1.13" },
|
||||
{ name = "django-countries", specifier = "==7.6.1" },
|
||||
{ name = "django-cte", specifier = "==2.0.0" },
|
||||
{ name = "django-dramatiq-postgres", editable = "packages/django-dramatiq-postgres" },
|
||||
@@ -296,7 +284,7 @@ requires-dist = [
|
||||
{ name = "django-postgres-cache", editable = "packages/django-postgres-cache" },
|
||||
{ name = "django-prometheus", specifier = "==2.4.1" },
|
||||
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
|
||||
{ name = "django-tenants", specifier = "==3.9.0" },
|
||||
{ name = "django-tenants", specifier = "==3.8.0" },
|
||||
{ name = "djangoql", specifier = "==0.18.1" },
|
||||
{ name = "djangorestframework", git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
||||
{ name = "djangorestframework-guardian", specifier = "==0.4.0" },
|
||||
@@ -351,7 +339,6 @@ dev = [
|
||||
{ name = "aws-cdk-lib", specifier = "==2.188.0" },
|
||||
{ name = "bandit", specifier = "==1.8.3" },
|
||||
{ name = "black", specifier = "==25.1.0" },
|
||||
{ name = "bpython", specifier = "==0.25" },
|
||||
{ name = "channels", extras = ["daphne"], specifier = "==4.3.1" },
|
||||
{ name = "codespell", specifier = "==2.4.1" },
|
||||
{ name = "colorama", specifier = "==0.4.6" },
|
||||
@@ -375,7 +362,6 @@ dev = [
|
||||
{ name = "requests-mock", specifier = "==1.12.1" },
|
||||
{ name = "ruff", specifier = "==0.11.9" },
|
||||
{ name = "selenium", specifier = "==4.32.0" },
|
||||
{ name = "types-channels", specifier = "==4.3.0.20250822" },
|
||||
{ name = "types-ldap3", specifier = "==2.9.13.20250622" },
|
||||
]
|
||||
|
||||
@@ -581,19 +567,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blessed"
|
||||
version = "1.21.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jinxed", marker = "sys_platform == 'win32'" },
|
||||
{ name = "wcwidth" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0c/5e/3cada2f7514ee2a76bb8168c71f9b65d056840ebb711962e1ec08eeaa7b0/blessed-1.21.0.tar.gz", hash = "sha256:ece8bbc4758ab9176452f4e3a719d70088eb5739798cd5582c9e05f2a28337ec", size = 6660011, upload-time = "2025-04-26T21:56:58.199Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/8e/0a37e44878fd76fac9eff5355a1bf760701f53cb5c38cdcd59a8fd9ab2a2/blessed-1.21.0-py2.py3-none-any.whl", hash = "sha256:f831e847396f5a2eac6c106f4dfadedf46c4f804733574b15fe86d2ed45a9588", size = 84727, upload-time = "2025-04-26T16:58:29.919Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.40.43"
|
||||
@@ -622,23 +595,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/46/2eb4802e15e38befbea6cab7dafa1ab796722ab6f0833991c2a05e9f8ef0/botocore-1.40.43-py3-none-any.whl", hash = "sha256:1639f38999fc0cf42c92c5c83c5fbe189a4857a86f55b842be868e3283c6d3bb", size = 14057986, upload-time = "2025-10-01T19:38:13.714Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bpython"
|
||||
version = "0.25"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "curtsies" },
|
||||
{ name = "cwcwidth" },
|
||||
{ name = "greenlet" },
|
||||
{ name = "pygments" },
|
||||
{ name = "pyxdg" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/dd/cc02bf66f342a4673867fdf6c1f9fce90ec1e91e651b21bc4af4890101da/bpython-0.25.tar.gz", hash = "sha256:c246fc909ef6dcc26e9d8cb4615b0e6b1613f3543d12269b19ffd0782166c65b", size = 207610, upload-time = "2025-01-17T09:35:22.382Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/74/5470df025854d5e213793b62cbea032fd66919562662955789fcc5dc17d6/bpython-0.25-py3-none-any.whl", hash = "sha256:28fd86008ca5ef6100ead407c9743aa60c51293a18ba5b18fcacea7f5b7f2257", size = 176131, upload-time = "2025-01-17T09:35:19.444Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "5.5.2"
|
||||
@@ -728,6 +684,17 @@ daphne = [
|
||||
{ name = "daphne" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "channels-postgres"
|
||||
version = "1.1.4"
|
||||
source = { git = "https://github.com/rissson/channels_postgres?rev=93ed24e3c5317d7ccf7e8b1ce913c0f365d1728f#93ed24e3c5317d7ccf7e8b1ce913c0f365d1728f" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "channels" },
|
||||
{ name = "msgpack" },
|
||||
{ name = "psycopg", extra = ["pool"] },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.3"
|
||||
@@ -877,34 +844,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/b3/28ac139109d9005ad3f6b6f8976ffede6706a6478e21c889ce36c840918e/cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97", size = 3390016, upload-time = "2025-07-02T13:05:50.811Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curtsies"
|
||||
version = "0.4.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "blessed" },
|
||||
{ name = "cwcwidth" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d1/18/5741cb42624089a815520d5b65c39c3e59673a77fd1fab6ad65bdebf2f91/curtsies-0.4.3.tar.gz", hash = "sha256:102a0ffbf952124f1be222fd6989da4ec7cce04e49f613009e5f54ad37618825", size = 53401, upload-time = "2025-06-05T06:33:20.099Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/9b/b8ee3720d056309f4ab667bfc85995c4351f67b22e8c2008612b70350c3a/curtsies-0.4.3-py3-none-any.whl", hash = "sha256:65a1b4d6ff887bd9b0f0836cc6dc68c3a2c65c57f51a62f0ee5df408edee1a99", size = 35482, upload-time = "2025-06-05T06:33:19.122Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cwcwidth"
|
||||
version = "0.1.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/23/76/03fc9fb3441a13e9208bb6103ebb7200eba7647d040008b8303a1c03e152/cwcwidth-0.1.10.tar.gz", hash = "sha256:7468760f72c1f4107be1b2b2854bc000401ea36a69daed36fb966a1e19a7a124", size = 60265, upload-time = "2025-02-09T21:15:28.452Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/af/f7/8c4cfe0b08053eea4da585ad5e12fef7cd11a0c9e4603ac8644c2a0b04b5/cwcwidth-0.1.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2391073280d774ab5d9af1d3aaa26ec456956d04daa1134fb71c31cd72ba5bba", size = 22344, upload-time = "2025-02-09T21:15:10.136Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/48/176bbaf56520c5d6b72cbbe0d46821989eaa30df628daa5baecdd7f35458/cwcwidth-0.1.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bfbdc2943631ec770ee781b35b8876fa7e283ff2273f944e2a9ae1f3df4ecdf", size = 94907, upload-time = "2025-02-09T21:15:11.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/fc/4dfed13b316a67bf2419a63db53566e3e5e4d4fc5a94ef493d3334be3c1f/cwcwidth-0.1.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb0103c7db8d86e260e016ff89f8f00ef5eb75c481abc346bfaa756da9f976b4", size = 100046, upload-time = "2025-02-09T21:15:12.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/83/612eecdeddbb1329d0f4d416f643459c2c5ae7b753490e31d9dccfa6deed/cwcwidth-0.1.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3b7d38c552edf663bf13f32310f9fd6661070409807b1b5bf89917e2de804ab1", size = 96143, upload-time = "2025-02-09T21:15:14.136Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/98/87d10d88b5a6de3a4a3452802abed18b909510b9f118ad7f3a40ae48700a/cwcwidth-0.1.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1132be818498163a9208b9f4a28d759e08d3efeb885dfd10364434ccc7fa6a17", size = 99735, upload-time = "2025-02-09T21:15:15.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/de/e0c02f84b0418db5938eeb1269f53dee195615a856ed12f370ef79f6cd5b/cwcwidth-0.1.10-cp313-cp313-win32.whl", hash = "sha256:dcead1b7b60c99f8cda249feb8059e4da38587c34d0b5f3aa130f13c62c0ce35", size = 22922, upload-time = "2025-02-09T21:15:16.999Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/4a/1d272a69f14924e43f5d6d4a7935c7a892e25c6e5b9a2c4459472132ef0c/cwcwidth-0.1.10-cp313-cp313-win_amd64.whl", hash = "sha256:b6eafd16d3edfec9acfc3d7b8856313bc252e0eccd56fb088f51ceed14c1bbdd", size = 25211, upload-time = "2025-02-09T21:15:17.898Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dacite"
|
||||
version = "1.9.2"
|
||||
@@ -970,39 +909,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.2.7"
|
||||
version = "5.1.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/96/bd84e2bb997994de8bcda47ae4560991084e86536541d7214393880f01a8/django-5.2.7.tar.gz", hash = "sha256:e0f6f12e2551b1716a95a63a1366ca91bbcd7be059862c1b18f989b1da356cdd", size = 10865812, upload-time = "2025-10-01T14:22:12.081Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bb/57/ad9905d03a2ee39064ee7ba69f8e2790db4a7ffaef9c54f95e7a8f2cb0a1/django-5.1.13.tar.gz", hash = "sha256:543ff21679f15e80edfc01fe7ea35f8291b6d4ea589433882913626a7c1cf929", size = 10742756, upload-time = "2025-10-01T14:25:41.187Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/ef/81f3372b5dd35d8d354321155d1a38894b2b766f576d0abffac4d8ae78d9/django-5.2.7-py3-none-any.whl", hash = "sha256:59a13a6515f787dec9d97a0438cd2efac78c8aca1c80025244b0fe507fe0754b", size = 8307145, upload-time = "2025-10-01T14:22:49.476Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-channels-postgres"
|
||||
version = "0.1.0"
|
||||
source = { editable = "packages/django-channels-postgres" }
|
||||
dependencies = [
|
||||
{ name = "channels" },
|
||||
{ name = "django" },
|
||||
{ name = "django-pgtrigger" },
|
||||
{ name = "msgpack" },
|
||||
{ name = "psycopg", extra = ["pool"] },
|
||||
{ name = "structlog" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "channels", specifier = ">=4.3,<4.4" },
|
||||
{ name = "django", specifier = ">=4.2,<6.0" },
|
||||
{ name = "django-pgtrigger", specifier = ">=4,<5" },
|
||||
{ name = "msgpack", specifier = ">=1,<2" },
|
||||
{ name = "psycopg", extras = ["pool"], specifier = ">=3,<4" },
|
||||
{ name = "structlog", specifier = ">=25,<26" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/f2/4b39467b74de9bb698c95232011e97dc848f490baae8d78c2e58848c4562/django-5.1.13-py3-none-any.whl", hash = "sha256:06f257f79dc4c17f3f9e23b106a4c5ed1335abecbe731e83c598c941d14fbeed", size = 8277515, upload-time = "2025-10-01T14:25:28.65Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1037,7 +953,6 @@ source = { editable = "packages/django-dramatiq-postgres" }
|
||||
dependencies = [
|
||||
{ name = "cron-converter" },
|
||||
{ name = "django" },
|
||||
{ name = "django-pglock" },
|
||||
{ name = "django-pgtrigger" },
|
||||
{ name = "dramatiq", extra = ["watch"] },
|
||||
{ name = "structlog" },
|
||||
@@ -1048,7 +963,6 @@ dependencies = [
|
||||
requires-dist = [
|
||||
{ name = "cron-converter", specifier = ">=1,<2" },
|
||||
{ name = "django", specifier = ">=4.2,<6.0" },
|
||||
{ name = "django-pglock", specifier = ">=1.7,<2" },
|
||||
{ name = "django-pgtrigger", specifier = ">=4,<5" },
|
||||
{ name = "dramatiq", extras = ["watch"], specifier = ">=1.17,<1.18" },
|
||||
{ name = "structlog", specifier = ">=25,<26" },
|
||||
@@ -1204,15 +1118,12 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django-tenants"
|
||||
version = "3.9.0"
|
||||
version = "3.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6b/54/c2e0e2dacd1caa9729c054da5bbdee4244ccd0de912b70dc2932a46ef177/django_tenants-3.9.0.tar.gz", hash = "sha256:6bc67c26be57f412e47c476261a977ef0ef74ec3740d50fd1eb6b69cb072eb03", size = 154244, upload-time = "2025-09-05T10:08:31.973Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/57/918cfca627fcdc3441981dddc72a22be02e57abdb5391eb7339ea77a5ef4/django_tenants-3.9.0-py3-none-any.whl", hash = "sha256:14421088a4336444e2c4af54f21a6af2e57e53dcf95ba5d19b5fa17142cb460b", size = 215955, upload-time = "2025-09-06T21:46:05.939Z" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/7b/22e3bb79d48e5a4fdcacdcdc27bbc5c2523a2b7892b440bfe229f313d823/django_tenants-3.8.0.tar.gz", hash = "sha256:07d009d5d01be2d65c3f5ddbf323d58d1228838fc1a64fded15c8e5c6f41cf8f", size = 154307, upload-time = "2025-05-23T16:07:24.307Z" }
|
||||
|
||||
[[package]]
|
||||
name = "djangoql"
|
||||
@@ -1824,18 +1735,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinxed"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "ansicon", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/20/d0/59b2b80e7a52d255f9e0ad040d2e826342d05580c4b1d7d7747cfb8db731/jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf", size = 80981, upload-time = "2024-07-31T22:39:18.854Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/e3/0e0014d6ab159d48189e92044ace13b1e1fe9aa3024ba9f4e8cf172aa7c2/jinxed-1.3.0-py2.py3-none-any.whl", hash = "sha256:b993189f39dc2d7504d802152671535b06d380b26d78070559551cbf92df4fc5", size = 33085, upload-time = "2024-07-31T22:39:17.426Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jmespath"
|
||||
version = "1.0.1"
|
||||
@@ -2905,15 +2804,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152, upload-time = "2025-03-17T00:56:07.819Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyxdg"
|
||||
version = "0.28"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b0/25/7998cd2dec731acbd438fbf91bc619603fc5188de0a9a17699a781840452/pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4", size = 77776, upload-time = "2022-06-05T11:35:01Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab", size = 49520, upload-time = "2022-06-05T11:34:58.832Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
@@ -2931,6 +2821,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "6.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/e8b92798a5bd67d659d51a18170e91c16ac3b59738d91894651ee255ed49/redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010", size = 4647399, upload-time = "2025-08-07T08:10:11.441Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/02/89e2ed7e85db6c93dfa9e8f691c5087df4e3551ab39081a4d7c6d1f90e05/redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f", size = 279847, upload-time = "2025-08-07T08:10:09.84Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.36.2"
|
||||
@@ -3366,19 +3265,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/bb/d43e5c75054e53efce310e79d63df0ac3f25e34c926be5dffb7d283fb2a8/typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1", size = 17605, upload-time = "2021-12-10T21:09:37.844Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-channels"
|
||||
version = "4.3.0.20250822"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "django-stubs" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/3d/e0a164b7eaab18ab2302b7a3d90843824b729d9fe1d7bdbb3769dfc9d77b/types_channels-4.3.0.20250822.tar.gz", hash = "sha256:29a4928fdaed6d444b93b69d44fcdb5a8fe32fa72d6a41016c5d39fa7bd7f474", size = 15357, upload-time = "2025-08-22T03:04:26.444Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/52/4e3094e43d460feacb9051ec4c3498f8272f69d92b772647211478b25079/types_channels-4.3.0.20250822-py3-none-any.whl", hash = "sha256:d3fc0a1467c8cc901686826408c8a673822e07aa79cbe1a6d21946e7e55d9ddf", size = 21125, upload-time = "2025-08-22T03:04:25.539Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-ldap3"
|
||||
version = "2.9.13.20250622"
|
||||
@@ -3609,15 +3495,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webauthn"
|
||||
version = "2.6.0"
|
||||
|
||||
417
web/package-lock.json
generated
417
web/package-lock.json
generated
@@ -17,7 +17,7 @@
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
"@codemirror/lang-python": "^6.2.1",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.5.2",
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@floating-ui/dom": "^1.7.4",
|
||||
@@ -44,7 +44,7 @@
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@sentry/browser": "^10.17.0",
|
||||
"@spotlightjs/spotlight": "^4.1.3",
|
||||
"@spotlightjs/spotlight": "^4.1.2",
|
||||
"@storybook/addon-docs": "^9.1.10",
|
||||
"@storybook/addon-links": "^9.1.10",
|
||||
"@storybook/web-components": "^9.1.10",
|
||||
@@ -53,7 +53,7 @@
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.4",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "^24.7.0",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
@@ -72,7 +72,7 @@
|
||||
"dompurify": "^3.2.7",
|
||||
"esbuild": "^0.25.10",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-wc": "^3.0.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
@@ -108,7 +108,7 @@
|
||||
"turnstile-types": "^1.2.3",
|
||||
"type-fest": "^5.0.1",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.46.0",
|
||||
"typescript-eslint": "^8.45.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^7.1.9",
|
||||
"vitest": "^3.2.4",
|
||||
@@ -123,10 +123,10 @@
|
||||
"@esbuild/darwin-arm64": "^0.25.4",
|
||||
"@esbuild/linux-arm64": "^0.25.4",
|
||||
"@esbuild/linux-x64": "^0.25.4",
|
||||
"@rollup/rollup-darwin-arm64": "^4.52.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.52.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.52.4",
|
||||
"chromedriver": "^141.0.0",
|
||||
"@rollup/rollup-darwin-arm64": "^4.52.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.52.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.52.3",
|
||||
"chromedriver": "^140.0.4",
|
||||
"p-iteration": "^1.1.8"
|
||||
}
|
||||
},
|
||||
@@ -542,9 +542,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/legacy-modes": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.2.tgz",
|
||||
"integrity": "sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==",
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.1.tgz",
|
||||
"integrity": "sha512-DJYQQ00N1/KdESpZV7jg9hafof/iBNp9h7TYo1SLMk86TWl9uDsVdho2dzd81K+v4retmK6mdC7WpuOQDytQqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0"
|
||||
@@ -1151,21 +1151,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
|
||||
"integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
|
||||
"integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
|
||||
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
|
||||
"version": "0.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
|
||||
"integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
@@ -1232,9 +1229,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
|
||||
"integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
|
||||
"version": "9.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
|
||||
"integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1252,12 +1249,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
|
||||
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
|
||||
"integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.16.0",
|
||||
"@eslint/core": "^0.15.2",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3201,9 +3198,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "16.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.2.tgz",
|
||||
"integrity": "sha512-tCtHJ2BlhSoK4cCs25NMXfV7EALKr0jyasmqVCq3y9cBrKdmJhtsy1iTz36Xhk/O+pDJbzawxF4K6ZblqCnITQ==",
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
|
||||
"integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
@@ -3268,9 +3265,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz",
|
||||
"integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz",
|
||||
"integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -3281,9 +3278,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz",
|
||||
"integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3294,9 +3291,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz",
|
||||
"integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3307,9 +3304,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz",
|
||||
"integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz",
|
||||
"integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3320,9 +3317,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz",
|
||||
"integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3333,9 +3330,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz",
|
||||
"integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz",
|
||||
"integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3346,9 +3343,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz",
|
||||
"integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz",
|
||||
"integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -3359,9 +3356,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz",
|
||||
"integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz",
|
||||
"integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -3372,9 +3369,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3385,9 +3382,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz",
|
||||
"integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz",
|
||||
"integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3398,9 +3395,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -3411,9 +3408,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -3424,9 +3421,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -3437,9 +3434,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz",
|
||||
"integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz",
|
||||
"integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -3450,9 +3447,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -3463,9 +3460,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3476,9 +3473,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz",
|
||||
"integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz",
|
||||
"integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3489,9 +3486,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz",
|
||||
"integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3502,9 +3499,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz",
|
||||
"integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz",
|
||||
"integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3515,9 +3512,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz",
|
||||
"integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz",
|
||||
"integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -3528,9 +3525,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz",
|
||||
"integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3541,9 +3538,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz",
|
||||
"integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz",
|
||||
"integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3747,9 +3744,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@spotlightjs/overlay": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-4.2.0.tgz",
|
||||
"integrity": "sha512-4bhsZHUpbtGjs5WI5Zl4WEGZ4/zngqZtcxloCzE578b0bY6fsPw0lnRbNAebNgk2fCuzAsPzf/KGaYwXVLkzeg==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-4.1.0.tgz",
|
||||
"integrity": "sha512-/1xx4z/sYxMXNWVVaxXlf9R+p3eZjat6MFZ3vmT8BOu95/YpxHURO5JOPK6E0fm1QYbnrORpG0PvPTt+jutBzw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@spotlightjs/sidecar": {
|
||||
@@ -3778,13 +3775,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@spotlightjs/spotlight": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-4.1.3.tgz",
|
||||
"integrity": "sha512-oO9zCDlERHkKJOIoXzVabXvxW3S4IekJnJx++7z5TSo+eaOSWCGY7XQYd9WfdsD+MfTj4dglc6dgqNvExR6Csw==",
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-4.1.2.tgz",
|
||||
"integrity": "sha512-pwlqtXKGjXegjHrIPY+19W6F7RNhAEEPK75/qF+X1D1G/rPBMDLPIjyV7yngiLS6UetJdhNS46dhd0+KCWj9zg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@sentry/node": "^10",
|
||||
"@spotlightjs/overlay": "4.2.0",
|
||||
"@spotlightjs/overlay": "4.1.0",
|
||||
"@spotlightjs/sidecar": "2.1.2",
|
||||
"import-meta-resolve": "^4.1.0"
|
||||
},
|
||||
@@ -5403,18 +5400,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz",
|
||||
"integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==",
|
||||
"version": "24.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz",
|
||||
"integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.14.0"
|
||||
"undici-types": "~7.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node/node_modules/undici-types": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
|
||||
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
|
||||
"version": "7.13.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz",
|
||||
"integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
@@ -5518,16 +5515,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
|
||||
"integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz",
|
||||
"integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.46.0",
|
||||
"@typescript-eslint/type-utils": "8.46.0",
|
||||
"@typescript-eslint/utils": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0",
|
||||
"@typescript-eslint/scope-manager": "8.45.0",
|
||||
"@typescript-eslint/type-utils": "8.45.0",
|
||||
"@typescript-eslint/utils": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -5541,7 +5538,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.46.0",
|
||||
"@typescript-eslint/parser": "^8.45.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
@@ -5556,15 +5553,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz",
|
||||
"integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz",
|
||||
"integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.46.0",
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0",
|
||||
"@typescript-eslint/scope-manager": "8.45.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -5580,13 +5577,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz",
|
||||
"integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz",
|
||||
"integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.46.0",
|
||||
"@typescript-eslint/types": "^8.46.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.45.0",
|
||||
"@typescript-eslint/types": "^8.45.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -5601,13 +5598,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz",
|
||||
"integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz",
|
||||
"integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0"
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -5618,9 +5615,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz",
|
||||
"integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz",
|
||||
"integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -5634,14 +5631,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz",
|
||||
"integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz",
|
||||
"integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0",
|
||||
"@typescript-eslint/utils": "8.46.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0",
|
||||
"@typescript-eslint/utils": "8.45.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@@ -5658,9 +5655,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz",
|
||||
"integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz",
|
||||
"integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -5671,15 +5668,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz",
|
||||
"integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz",
|
||||
"integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.46.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.46.0",
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/visitor-keys": "8.46.0",
|
||||
"@typescript-eslint/project-service": "8.45.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.45.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/visitor-keys": "8.45.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -5699,15 +5696,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz",
|
||||
"integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz",
|
||||
"integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.46.0",
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0"
|
||||
"@typescript-eslint/scope-manager": "8.45.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -5722,12 +5719,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz",
|
||||
"integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz",
|
||||
"integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.46.0",
|
||||
"@typescript-eslint/types": "8.45.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -7371,9 +7368,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chromedriver": {
|
||||
"version": "141.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-141.0.0.tgz",
|
||||
"integrity": "sha512-w0U5jyWlLaRHV+dhaSikDz4x0qOwZcbles2HBu4oRdd+Eq7M43Uns4eoP/6dKu9Uc5ppcK9gA/E9GHROGXhgPg==",
|
||||
"version": "140.0.4",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-140.0.4.tgz",
|
||||
"integrity": "sha512-/NUoxYBNkJeoNj1B5ux3KxGShITlxJctkbApgVAa3ZC8EvCLKaBclwU3/IEj5MJHnBJzqOVDxs/eTyaF9k2fOg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
@@ -9121,19 +9118,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.37.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
|
||||
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
|
||||
"version": "9.36.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
|
||||
"integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.21.0",
|
||||
"@eslint/config-helpers": "^0.4.0",
|
||||
"@eslint/core": "^0.16.0",
|
||||
"@eslint/config-helpers": "^0.3.1",
|
||||
"@eslint/core": "^0.15.2",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.37.0",
|
||||
"@eslint/plugin-kit": "^0.4.0",
|
||||
"@eslint/js": "9.36.0",
|
||||
"@eslint/plugin-kit": "^0.3.5",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@@ -16072,9 +16069,9 @@
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
|
||||
"integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
|
||||
"version": "4.52.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz",
|
||||
"integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
@@ -16087,28 +16084,28 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.52.4",
|
||||
"@rollup/rollup-android-arm64": "4.52.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.52.4",
|
||||
"@rollup/rollup-darwin-x64": "4.52.4",
|
||||
"@rollup/rollup-freebsd-arm64": "4.52.4",
|
||||
"@rollup/rollup-freebsd-x64": "4.52.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.52.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.52.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.52.4",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.52.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.52.4",
|
||||
"@rollup/rollup-openharmony-arm64": "4.52.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.52.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.52.4",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.52.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.52.4",
|
||||
"@rollup/rollup-android-arm-eabi": "4.52.3",
|
||||
"@rollup/rollup-android-arm64": "4.52.3",
|
||||
"@rollup/rollup-darwin-arm64": "4.52.3",
|
||||
"@rollup/rollup-darwin-x64": "4.52.3",
|
||||
"@rollup/rollup-freebsd-arm64": "4.52.3",
|
||||
"@rollup/rollup-freebsd-x64": "4.52.3",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.52.3",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.52.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.52.3",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.52.3",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.52.3",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.52.3",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.52.3",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.52.3",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.52.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.52.3",
|
||||
"@rollup/rollup-linux-x64-musl": "4.52.3",
|
||||
"@rollup/rollup-openharmony-arm64": "4.52.3",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.52.3",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.52.3",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.52.3",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.52.3",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -17909,15 +17906,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz",
|
||||
"integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==",
|
||||
"version": "8.45.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz",
|
||||
"integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.46.0",
|
||||
"@typescript-eslint/parser": "8.46.0",
|
||||
"@typescript-eslint/typescript-estree": "8.46.0",
|
||||
"@typescript-eslint/utils": "8.46.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.45.0",
|
||||
"@typescript-eslint/parser": "8.45.0",
|
||||
"@typescript-eslint/typescript-estree": "8.45.0",
|
||||
"@typescript-eslint/utils": "8.45.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -19119,7 +19116,7 @@
|
||||
"dependencies": {
|
||||
"@goauthentik/prettier-config": "^3.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/node": "^24.7.0",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/semver": "^7.7.1",
|
||||
"prettier": "^3.6.2",
|
||||
"semver": "^7.7.2",
|
||||
@@ -19204,7 +19201,7 @@
|
||||
"@goauthentik/api": "^2025.10.0-rc1-1759234079",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.2",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-swc": "^0.4.0",
|
||||
"@swc/cli": "^0.7.8",
|
||||
"@swc/core": "^1.13.19",
|
||||
@@ -19214,7 +19211,7 @@
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup": "^4.52.4",
|
||||
"rollup": "^4.52.3",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
"@codemirror/lang-python": "^6.2.1",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.5.2",
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@floating-ui/dom": "^1.7.4",
|
||||
@@ -116,7 +116,7 @@
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@sentry/browser": "^10.17.0",
|
||||
"@spotlightjs/spotlight": "^4.1.3",
|
||||
"@spotlightjs/spotlight": "^4.1.2",
|
||||
"@storybook/addon-docs": "^9.1.10",
|
||||
"@storybook/addon-links": "^9.1.10",
|
||||
"@storybook/web-components": "^9.1.10",
|
||||
@@ -125,7 +125,7 @@
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.4",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "^24.7.0",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
@@ -144,7 +144,7 @@
|
||||
"dompurify": "^3.2.7",
|
||||
"esbuild": "^0.25.10",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-wc": "^3.0.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
@@ -180,7 +180,7 @@
|
||||
"turnstile-types": "^1.2.3",
|
||||
"type-fest": "^5.0.1",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.46.0",
|
||||
"typescript-eslint": "^8.45.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^7.1.9",
|
||||
"vitest": "^3.2.4",
|
||||
@@ -192,10 +192,10 @@
|
||||
"@esbuild/darwin-arm64": "^0.25.4",
|
||||
"@esbuild/linux-arm64": "^0.25.4",
|
||||
"@esbuild/linux-x64": "^0.25.4",
|
||||
"@rollup/rollup-darwin-arm64": "^4.52.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.52.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.52.4",
|
||||
"chromedriver": "^141.0.0",
|
||||
"@rollup/rollup-darwin-arm64": "^4.52.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.52.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.52.3",
|
||||
"chromedriver": "^140.0.4",
|
||||
"p-iteration": "^1.1.8"
|
||||
},
|
||||
"wireit": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"dependencies": {
|
||||
"@goauthentik/prettier-config": "^3.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/node": "^24.7.0",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/semver": "^7.7.1",
|
||||
"prettier": "^3.6.2",
|
||||
"semver": "^7.7.2",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@goauthentik/api": "^2025.10.0-rc1-1759234079",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.2",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-swc": "^0.4.0",
|
||||
"@swc/cli": "^0.7.8",
|
||||
"@swc/core": "^1.13.19",
|
||||
@@ -23,7 +23,7 @@
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup": "^4.52.4",
|
||||
"rollup": "^4.52.3",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
},
|
||||
|
||||
@@ -84,10 +84,6 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
PFDrawer,
|
||||
PFNav,
|
||||
css`
|
||||
.pf-c-page__main {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.pf-c-page__main,
|
||||
.pf-c-drawer__content,
|
||||
.pf-c-page__drawer {
|
||||
@@ -220,15 +216,17 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
<div class="pf-c-drawer__main">
|
||||
<div class="pf-c-drawer__content">
|
||||
<div class="pf-c-drawer__body">
|
||||
<ak-router-outlet
|
||||
role="presentation"
|
||||
class="pf-c-page__main"
|
||||
tabindex="-1"
|
||||
id="main-content"
|
||||
defaultUrl="/administration/overview"
|
||||
.routes=${ROUTES}
|
||||
>
|
||||
</ak-router-outlet>
|
||||
<div class="pf-c-page__main">
|
||||
<ak-router-outlet
|
||||
role="presentation"
|
||||
class="pf-c-page__main"
|
||||
tabindex="-1"
|
||||
id="main-content"
|
||||
defaultUrl="/administration/overview"
|
||||
.routes=${ROUTES}
|
||||
>
|
||||
</ak-router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ak-notification-drawer
|
||||
|
||||
@@ -4,7 +4,6 @@ import "#elements/buttons/SpinnerButton/index";
|
||||
import "#elements/events/LogViewer";
|
||||
import "#elements/tasks/ScheduleList";
|
||||
import "#elements/tasks/TaskList";
|
||||
import "#elements/tasks/TaskOverview";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
@@ -44,24 +43,6 @@ export class SystemTasksPage extends AKElement {
|
||||
render(): TemplateResult {
|
||||
return html` <main>
|
||||
<ak-tabs>
|
||||
<div
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
slot="page-tasks"
|
||||
id="page-tasks"
|
||||
aria-label="${msg("Tasks")}"
|
||||
>
|
||||
<ak-task-overview></ak-task-overview>
|
||||
<div
|
||||
class="pf-l-grid pf-m-gutter pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl"
|
||||
>
|
||||
<ak-task-list></ak-task-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
@@ -78,6 +59,22 @@ export class SystemTasksPage extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
slot="page-tasks"
|
||||
id="page-tasks"
|
||||
aria-label="${msg("Tasks")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl"
|
||||
>
|
||||
<ak-task-list></ak-task-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>
|
||||
</main>`;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
* @returns TemplateResult for status display
|
||||
*/
|
||||
private renderStatus(status: AdminStatus): SlottedTemplateResult {
|
||||
return html`<div class="status-container">
|
||||
return html` <div class="status-container">
|
||||
<h2 class="status-heading">
|
||||
<i class="${status.icon}" aria-hidden="true"></i>${this.renderValue()}
|
||||
</h2>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AdminStatus, AdminStatusCard } from "#admin/admin-overview/cards/AdminS
|
||||
import { AdminApi, Version } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, html, TemplateResult } from "lit";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-admin-status-version")
|
||||
@@ -13,28 +13,6 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
public override icon = "pf-icon pf-icon-bundle";
|
||||
public override label = msg("Version");
|
||||
|
||||
static styles = [
|
||||
...super.styles,
|
||||
// HACK: Fixes Lit Analyzer's outdated parser.
|
||||
(css as typeof css) /*css*/ `
|
||||
.pf-c-card {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.pf-c-card__title {
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--FontSize--sm);
|
||||
}
|
||||
}
|
||||
|
||||
.status-container {
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--icon--FontSize--md);
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
getPrimaryValue(): Promise<Version> {
|
||||
return new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
}
|
||||
|
||||
@@ -139,16 +139,6 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
}
|
||||
|
||||
renderSectionBefore(): TemplateResult {
|
||||
const {
|
||||
externalUsers = 0,
|
||||
internalUsers = 0,
|
||||
forecastedExternalUsers = 0,
|
||||
forecastedInternalUsers = 0,
|
||||
} = this.forecast || {};
|
||||
|
||||
const totalInternalUserEstimate = internalUsers + forecastedInternalUsers;
|
||||
const totalExternalUserEstimate = externalUsers + forecastedExternalUsers;
|
||||
|
||||
return html`
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||
<div
|
||||
@@ -156,40 +146,38 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
>
|
||||
${this.renderGetLicenseCard()}
|
||||
<ak-aggregate-card
|
||||
role="status"
|
||||
class="pf-l-grid__item"
|
||||
icon="pf-icon pf-icon-user"
|
||||
label=${msg("Forecast internal users")}
|
||||
subtext=${msg(
|
||||
str`Estimated user count one year from now based on ${internalUsers} current internal users and ${forecastedInternalUsers} forecasted internal users.`,
|
||||
str`Estimated user count one year from now based on ${this.forecast?.internalUsers} current internal users and ${this.forecast?.forecastedInternalUsers} forecasted internal users.`,
|
||||
)}
|
||||
><span aria-label=${msg("Approximately")}>≈</span
|
||||
>${totalInternalUserEstimate} </ak-aggregate-card
|
||||
>
|
||||
~ ${(this.forecast?.internalUsers || 0) +
|
||||
(this.forecast?.forecastedInternalUsers || 0)}
|
||||
</ak-aggregate-card>
|
||||
<ak-aggregate-card
|
||||
role="status"
|
||||
class="pf-l-grid__item"
|
||||
icon="pf-icon pf-icon-user"
|
||||
label=${msg("Forecast external users")}
|
||||
subtext=${msg(
|
||||
str`Estimated user count one year from now based on ${externalUsers} current external users and ${forecastedExternalUsers} forecasted external users.`,
|
||||
str`Estimated user count one year from now based on ${this.forecast?.externalUsers} current external users and ${this.forecast?.forecastedExternalUsers} forecasted external users.`,
|
||||
)}
|
||||
><span aria-label=${msg("Approximately")}>≈</span
|
||||
>${totalExternalUserEstimate} </ak-aggregate-card
|
||||
>
|
||||
~ ${(this.forecast?.externalUsers || 0) +
|
||||
(this.forecast?.forecastedExternalUsers || 0)}
|
||||
</ak-aggregate-card>
|
||||
<ak-aggregate-card
|
||||
role="status"
|
||||
class="pf-l-grid__item"
|
||||
icon="pf-icon pf-icon-user"
|
||||
label=${msg("Expiry")}
|
||||
subtext=${msg("Cumulative license expiry")}
|
||||
>${this.summary &&
|
||||
>
|
||||
${this.summary &&
|
||||
this.summary?.status !== LicenseSummaryStatusEnum.Unlicensed
|
||||
? Timestamp(this.summary.latestValid)
|
||||
: html`<span aria-label=${msg("No expiry")}
|
||||
>-</span
|
||||
>`}</ak-aggregate-card
|
||||
>
|
||||
: "-"}
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||
|
||||
@@ -19,7 +19,7 @@ import { DesignationToLabel } from "#admin/flows/utils";
|
||||
|
||||
import { Flow, FlowsApi, RbacPermissionsAssignedByUsersListModelEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
@@ -85,7 +85,7 @@ export class FlowViewPage extends AKElement {
|
||||
>
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<div
|
||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-3-col-on-xl pf-m-3-col-on-2xl"
|
||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-2-col-on-xl pf-m-2-col-on-2xl"
|
||||
>
|
||||
<div class="pf-c-card__title">${msg("Flow Info")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
@@ -155,9 +155,6 @@ export class FlowViewPage extends AKElement {
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<button
|
||||
aria-label=${msg(
|
||||
str`Execute "${this.flow.name}" normally`,
|
||||
)}
|
||||
class="pf-c-button pf-m-block pf-m-primary"
|
||||
@click=${() => {
|
||||
const finalURL = `${
|
||||
@@ -171,9 +168,6 @@ export class FlowViewPage extends AKElement {
|
||||
${msg("Normal")}
|
||||
</button>
|
||||
<button
|
||||
aria-label=${msg(
|
||||
str`Execute "${this.flow.name}" as current user`,
|
||||
)}
|
||||
class="pf-c-button pf-m-block pf-m-secondary"
|
||||
@click=${() => {
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
@@ -188,12 +182,9 @@ export class FlowViewPage extends AKElement {
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Current user")}
|
||||
${msg("with current user")}
|
||||
</button>
|
||||
<button
|
||||
aria-label=${msg(
|
||||
str`Execute "${this.flow.name}" with inspector`,
|
||||
)}
|
||||
class="pf-c-button pf-m-block pf-m-secondary"
|
||||
@click=${() => {
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
@@ -218,7 +209,7 @@ export class FlowViewPage extends AKElement {
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Use inspector")}
|
||||
${msg("with inspector")}
|
||||
</button>
|
||||
</div>
|
||||
</dd>
|
||||
@@ -242,7 +233,7 @@ export class FlowViewPage extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-9-col-on-xl pf-m-9-col-on-2xl"
|
||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-10-col-on-xl pf-m-10-col-on-2xl"
|
||||
>
|
||||
<div class="pf-c-card__title">${msg("Diagram")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
@@ -314,14 +305,11 @@ export class FlowViewPage extends AKElement {
|
||||
|
||||
updated(changed: PropertyValues<this>) {
|
||||
super.updated(changed);
|
||||
|
||||
if (changed.has("flow")) {
|
||||
setPageDetails({
|
||||
icon: "pf-icon pf-icon-process-automation",
|
||||
header: this.flow?.name,
|
||||
description: this.flow?.title,
|
||||
});
|
||||
}
|
||||
setPageDetails({
|
||||
icon: "pf-icon pf-icon-process-automation",
|
||||
header: this.flow.name,
|
||||
description: this.flow.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,6 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
.show-disabled-toggle-group {
|
||||
margin-inline-start: 0.5rem;
|
||||
}
|
||||
|
||||
[part="toolbar"] {
|
||||
gap: var(--pf-global--spacer--md);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -83,26 +79,27 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
this.fetch();
|
||||
};
|
||||
|
||||
return html`<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group show-disabled-toggle-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.userListFilter === "all"}
|
||||
@change=${toggleShowDisabledUsers}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group show-disabled-toggle-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.userListFilter === "all"}
|
||||
@change=${toggleShowDisabledUsers}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Show inactive users")}</span>
|
||||
</label>
|
||||
<span class="pf-c-switch__label">${msg("Show inactive users")}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: User): SlottedTemplateResult[] {
|
||||
@@ -125,8 +122,7 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-modal-box__body pf-m-light">${this.renderTable()}</div>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
<fieldset name="actions" class="pf-c-modal-box__footer">
|
||||
<ak-spinner-button
|
||||
.callAction=${() => {
|
||||
return this.confirm(this.selectedElements).then(() => {
|
||||
|
||||
@@ -209,8 +209,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
html`<ak-empty-state icon="pf-icon-module"
|
||||
><span>${msg("No Policies bound.")}</span>
|
||||
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
|
||||
<fieldset class="pf-c-form__group pf-m-action" slot="primary">
|
||||
<legend class="sr-only">${msg("Policy actions")}</legend>
|
||||
<div slot="primary">
|
||||
<ak-policy-wizard
|
||||
createText=${msg("Create and bind Policy")}
|
||||
showBindingPage
|
||||
@@ -230,7 +229,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
${msg("Bind existing policy/group/user")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</fieldset>
|
||||
</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,33 +128,34 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
}
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
return html`<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.hideManaged}
|
||||
@change=${() => {
|
||||
this.hideManaged = !this.hideManaged;
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
updateURLParams({
|
||||
hideManaged: this.hideManaged,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.hideManaged}
|
||||
@change=${() => {
|
||||
this.hideManaged = !this.hideManaged;
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
updateURLParams({
|
||||
hideManaged: this.hideManaged,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Hide managed mappings")}</span>
|
||||
</label>
|
||||
<span class="pf-c-switch__label">${msg("Hide managed mappings")}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
|
||||
renderExampleLDAP(): TemplateResult {
|
||||
return html`
|
||||
<button
|
||||
type="button"
|
||||
class="pf-c-button pf-m-secondary"
|
||||
role="button"
|
||||
@click=${() => {
|
||||
@@ -106,7 +105,6 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
|
||||
${msg("Active Directory User")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="pf-c-button pf-m-secondary"
|
||||
role="button"
|
||||
@click=${() => {
|
||||
|
||||
@@ -54,9 +54,10 @@ export function renderForm({ provider = {}, errors = {}, brand }: LDAPProviderFo
|
||||
name="name"
|
||||
placeholder=${msg("Provider name...")}
|
||||
value=${ifDefined(provider.name)}
|
||||
label=${msg("Name")}
|
||||
label=${msg("Provider Name")}
|
||||
.errorMessages=${errors.name}
|
||||
required
|
||||
help=${msg("Method's display Name.")}
|
||||
></ak-text-input>
|
||||
<ak-radio-input
|
||||
label=${msg("Bind mode")}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ConnectionToken, RacApi, RACProvider } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
@@ -77,7 +77,6 @@ export class ConnectionTokenListPage extends Table<ConnectionToken> {
|
||||
return item.providerObj.name ?? null;
|
||||
}
|
||||
|
||||
@state()
|
||||
protected get columns(): TableColumn[] {
|
||||
if (this.provider) {
|
||||
return [
|
||||
|
||||
@@ -110,6 +110,7 @@ export function renderForm({ provider = {}, errors = {}, update }: SCIMProviderF
|
||||
label=${msg("Name")}
|
||||
.errorMessages=${errors.name}
|
||||
required
|
||||
help=${msg("Method's display Name.")}
|
||||
></ak-text-input>
|
||||
<ak-form-group open label="${msg("Protocol settings")}">
|
||||
<div class="pf-c-form">
|
||||
|
||||
@@ -34,12 +34,16 @@ export class ObjectPermissionPage extends AKElement {
|
||||
|
||||
render() {
|
||||
return html` <ak-tabs pageIdentifier="permissionPage" ?vertical=${!this.embedded}>
|
||||
${this.model === RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreUser
|
||||
? this.renderCoreUser()
|
||||
: nothing}
|
||||
${this.model === RbacPermissionsAssignedByUsersListModelEnum.AuthentikRbacRole
|
||||
? this.renderRbacRole()
|
||||
: nothing}
|
||||
${
|
||||
this.model === RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreUser
|
||||
? this.renderCoreUser()
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
this.model === RbacPermissionsAssignedByUsersListModelEnum.AuthentikRbacRole
|
||||
? this.renderRbacRole()
|
||||
: nothing
|
||||
}
|
||||
<div
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
@@ -88,7 +92,8 @@ export class ObjectPermissionPage extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ak-tabs>`;
|
||||
</ak-tabs>
|
||||
</main>`;
|
||||
}
|
||||
|
||||
renderCoreUser() {
|
||||
@@ -99,20 +104,21 @@ export class ObjectPermissionPage extends AKElement {
|
||||
slot="page-assigned-global-permissions"
|
||||
id="page-assigned-global-permissions"
|
||||
aria-label="${msg("Assigned global permissions")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this user which affect all object instances of a given type.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-user-assigned-global-permissions-table
|
||||
userId=${this.objectPk as number}
|
||||
>
|
||||
</ak-user-assigned-global-permissions-table>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this user which affect all object instances of a given type.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-user-assigned-global-permissions-table
|
||||
userId=${this.objectPk as number}
|
||||
>
|
||||
</ak-user-assigned-global-permissions-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,20 +128,21 @@ export class ObjectPermissionPage extends AKElement {
|
||||
slot="page-assigned-object-permissions"
|
||||
id="page-assigned-object-permissions"
|
||||
aria-label="${msg("Assigned object permissions")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this user affecting specific object instances.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-user-assigned-object-permissions-table
|
||||
userId=${this.objectPk as number}
|
||||
>
|
||||
</ak-user-assigned-object-permissions-table>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this user affecting specific object instances.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-user-assigned-object-permissions-table
|
||||
userId=${this.objectPk as number}
|
||||
>
|
||||
</ak-user-assigned-object-permissions-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,20 +157,21 @@ export class ObjectPermissionPage extends AKElement {
|
||||
slot="page-assigned-global-permissions"
|
||||
id="page-assigned-global-permissions"
|
||||
aria-label="${msg("Assigned global permissions")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this role which affect all object instances of a given type.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-role-assigned-global-permissions-table
|
||||
roleUuid=${this.objectPk as string}
|
||||
>
|
||||
</ak-role-assigned-global-permissions-table>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this role which affect all object instances of a given type.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-role-assigned-global-permissions-table
|
||||
roleUuid=${this.objectPk as string}
|
||||
>
|
||||
</ak-role-assigned-global-permissions-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -173,20 +181,21 @@ export class ObjectPermissionPage extends AKElement {
|
||||
slot="page-assigned-object-permissions"
|
||||
id="page-assigned-object-permissions"
|
||||
aria-label="${msg("Assigned object permissions")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this user affecting specific object instances.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-role-assigned-object-permissions-table
|
||||
roleUuid=${this.objectPk as string}
|
||||
>
|
||||
</ak-role-assigned-object-permissions-table>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${msg(
|
||||
"Permissions assigned to this user affecting specific object instances.",
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-role-assigned-object-permissions-table
|
||||
roleUuid=${this.objectPk as string}
|
||||
>
|
||||
</ak-role-assigned-object-permissions-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,11 +51,9 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
|
||||
return value.codename !== `add_${this.model?.split(".")[1]}`;
|
||||
});
|
||||
this.modelPermissions = modelPermissions;
|
||||
this.requestUpdate("columns");
|
||||
return perms;
|
||||
}
|
||||
|
||||
@state()
|
||||
protected get columns(): TableColumn[] {
|
||||
const permissions = this.modelPermissions?.results ?? [];
|
||||
|
||||
|
||||
@@ -51,13 +51,9 @@ export class UserAssignedObjectPermissionTable extends Table<UserAssignedObjectP
|
||||
return value.codename !== `add_${this.model?.split(".")[1]}`;
|
||||
});
|
||||
this.modelPermissions = modelPermissions;
|
||||
|
||||
this.requestUpdate("columns");
|
||||
|
||||
return perms;
|
||||
}
|
||||
|
||||
@state()
|
||||
protected get columns(): TableColumn[] {
|
||||
const permissions = this.modelPermissions?.results ?? [];
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
}
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
return html`<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
return html` <div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import {
|
||||
Config,
|
||||
ConfigFromJSON,
|
||||
CurrentBrand,
|
||||
CurrentBrandFromJSON,
|
||||
FlowLayoutEnum,
|
||||
} from "@goauthentik/api";
|
||||
import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api";
|
||||
|
||||
export interface GlobalAuthentik {
|
||||
_converted?: boolean;
|
||||
locale?: string;
|
||||
flow?: {
|
||||
layout: FlowLayoutEnum;
|
||||
layout: string;
|
||||
};
|
||||
config: Config;
|
||||
brand: CurrentBrand;
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import {
|
||||
Device,
|
||||
DeviceChallenge,
|
||||
DeviceClassesEnum,
|
||||
EventActions,
|
||||
IntentEnum,
|
||||
SeverityEnum,
|
||||
UserTypeEnum,
|
||||
} from "@goauthentik/api";
|
||||
import { Device, EventActions, IntentEnum, SeverityEnum, UserTypeEnum } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
/* Various tables in the API for which we need to supply labels */
|
||||
|
||||
@@ -75,10 +67,7 @@ export function severityToLevel(severity?: SeverityEnum | null): string {
|
||||
return "pf-m-info";
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Add verbose_name field to now vendored OTP devices
|
||||
* @todo We seem to have these constants in the `ModelEnum` object in lowercase.
|
||||
*/
|
||||
// TODO: Add verbose_name field to now vendored OTP devices
|
||||
export const deviceTypeToLabel = new Map<string, string>([
|
||||
["authentik_stages_authenticator_static.StaticDevice", msg("Static tokens")],
|
||||
["authentik_stages_authenticator_totp.TOTPDevice", msg("TOTP Device")],
|
||||
@@ -87,26 +76,6 @@ export const deviceTypeToLabel = new Map<string, string>([
|
||||
export const deviceTypeName = (device: Device) =>
|
||||
deviceTypeToLabel.get(device.type) ?? device?.verboseName ?? "";
|
||||
|
||||
export function formatDeviceChallengeMessage(deviceChallenge?: DeviceChallenge | null): string {
|
||||
switch (deviceChallenge?.deviceClass) {
|
||||
case DeviceClassesEnum.Email: {
|
||||
const { email } = deviceChallenge.challenge;
|
||||
|
||||
return email
|
||||
? msg(str`A code has been sent to your address: ${email}`)
|
||||
: msg("A code has been sent to your email address.");
|
||||
}
|
||||
case DeviceClassesEnum.Sms:
|
||||
return msg("A one-time use code has been sent to you via SMS text message.");
|
||||
case DeviceClassesEnum.Totp:
|
||||
return msg("Open your authenticator app to retrieve a one-time use code.");
|
||||
case DeviceClassesEnum.Static:
|
||||
return msg("Enter a one-time recovery code for this user.");
|
||||
}
|
||||
|
||||
return msg("Enter the code from your authenticator device.");
|
||||
}
|
||||
|
||||
const _userTypeToLabel = new Map<UserTypeEnum | undefined, string>([
|
||||
[UserTypeEnum.Internal, msg("Internal")],
|
||||
[UserTypeEnum.External, msg("External")],
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
--ak-dark-background-light-ish: #212427;
|
||||
--ak-dark-background-lighter: #2b2e33;
|
||||
|
||||
--ak-flow-background-color-contrast: var(--pf-global--Color--100);
|
||||
--ak-flow-footer-color: var(--pf-global--Color--light-100);
|
||||
|
||||
/* PatternFly likes to override global variables for some reason */
|
||||
--ak-global--Color--100: var(--pf-global--Color--100);
|
||||
|
||||
@@ -42,96 +39,6 @@
|
||||
--pf-global--disabled-color--300: color-mix(in srgb, GrayText 100%, CanvasText 100%);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Scrollbars */
|
||||
|
||||
/**
|
||||
* Scrollbar colors derived from default user agent styles.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Scrollbar colors may be ignored by the browser depending on a few factors:
|
||||
*
|
||||
* - A preference for increased contrast
|
||||
* - A preference for scrollbar visibility
|
||||
* - The pointer precision available
|
||||
* - Operating system differences
|
||||
*/
|
||||
:root {
|
||||
--ak-scrollbar-background-color: hsl(0 0% 98%);
|
||||
--ak-scrollbar-thumb-background-color: hsl(0 0% 76%);
|
||||
}
|
||||
|
||||
/* Applicable to browsers with a WebKit lineage (Chrome, Edge, Safari) */
|
||||
::-webkit-scrollbar {
|
||||
background: var(--ak-scrollbar-background-color);
|
||||
|
||||
/* Emulate thin scrollbar sizing. */
|
||||
@media (pointer: fine) {
|
||||
max-height: 12px;
|
||||
max-width: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Necessary to avoid background color mismatch. */
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--ak-scrollbar-background-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--ak-scrollbar-thumb-background-color);
|
||||
/* Applies consistent shape across browsers and platforms */
|
||||
border: 3px solid transparent;
|
||||
border-radius: 8px;
|
||||
background-clip: content-box;
|
||||
|
||||
/* Emulate thin scrollbar sizing. */
|
||||
@media (pointer: fine) {
|
||||
border: 3px solid transparent;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides arrow buttons on the scrollbar.
|
||||
* Not applicable to Edge when scrollbars are set to always visible.
|
||||
*/
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
@supports (scrollbar-color: auto) {
|
||||
:root {
|
||||
/**
|
||||
* Applies consistent colors across browsers and platforms.
|
||||
* Not applicable when thin scrollbars are preferred.
|
||||
*/
|
||||
scrollbar-color: var(--ak-scrollbar-thumb-background-color)
|
||||
var(--ak-scrollbar-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
@supports (scrollbar-width: thin) {
|
||||
.pf-c-page__main,
|
||||
.pf-c-nav__list,
|
||||
.pf-c-card__body {
|
||||
scrollbar-width: thin;
|
||||
|
||||
@media (pointer: coarse) {
|
||||
/**
|
||||
* Avoids issues on touch devices where thin scrollbars
|
||||
* are difficult to interact with.
|
||||
*/
|
||||
scrollbar-width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
.pf-c-form__group {
|
||||
column-gap: var(--pf-global--spacer--md);
|
||||
}
|
||||
@@ -156,6 +63,29 @@
|
||||
color: var(--pf-c-form__label-required--Color);
|
||||
}
|
||||
|
||||
@supports selector(::-webkit-scrollbar) {
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--ak-accent);
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@supports not selector(::-webkit-scrollbar) {
|
||||
:root {
|
||||
scrollbar-color: var(--ak-accent) transparent;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
--pf-c-nav__link--PaddingTop: 0.5rem;
|
||||
--pf-c-nav__link--PaddingRight: 0.5rem;
|
||||
@@ -231,10 +161,8 @@ html > form > input {
|
||||
/* #region Tabs */
|
||||
|
||||
.pf-c-tabs {
|
||||
--pf-c-tabs--inset: var(--pf-global--spacer--lg);
|
||||
--pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg);
|
||||
|
||||
margin-block-start: var(--pf-global--spacer--xs);
|
||||
background-color: transparent;
|
||||
|
||||
&.pf-m-box.pf-m-vertical {
|
||||
@@ -314,38 +242,27 @@ ak-tabs[vertical] {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1199px) {
|
||||
.pf-c-login__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-login-container {
|
||||
max-width: 35rem;
|
||||
width: 100%;
|
||||
height: calc(100vh - var(--pf-global--spacer--lg) - var(--pf-global--spacer--lg));
|
||||
width: 35rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pf-c-login__header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pf-c-login__footer {
|
||||
color: var(--ak-flow-footer-color);
|
||||
flex: 0 0 auto;
|
||||
flex-grow: 2;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--ak-flow-footer-color: var(--ak-flow-background-color-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login__footer ul.pf-c-list.pf-m-inline {
|
||||
justify-content: center;
|
||||
padding: 2rem 0;
|
||||
@@ -386,95 +303,12 @@ ak-tabs[vertical] {
|
||||
|
||||
/* #region Fields */
|
||||
|
||||
fieldset {
|
||||
--ak-fieldset-border-width: thin;
|
||||
--ak-fieldset-border-color: var(--pf-global--BackgroundColor--light-100);
|
||||
--ak-legend-margin-inline-base: var(--pf-global--spacer--sm);
|
||||
--ak-legend-padding-inline-base: var(--pf-global--spacer--sm);
|
||||
|
||||
border-color: var(--ak-fieldset-border-color);
|
||||
border-width: var(--ak-fieldset-border-width);
|
||||
|
||||
padding: var(--ak-legend-padding-inline-base) !important;
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
--ak-fieldset-border-color: var(--pf-global--BorderColor--200);
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
--ak-fieldset-border-color: transparent;
|
||||
}
|
||||
|
||||
& > legend {
|
||||
line-height: 1;
|
||||
padding: var(--ak-legend-padding-inline-base) !important;
|
||||
margin-inline-start: var(
|
||||
--ak-legend-margin-inline-start,
|
||||
var(--ak-legend-margin-inline-base)
|
||||
) !important;
|
||||
margin-inline-end: var(
|
||||
--ak-legend-margin-inline-end,
|
||||
var(--ak-legend-margin-inline-base)
|
||||
) !important;
|
||||
}
|
||||
|
||||
&:has(legend.sr-only) {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
&.pf-c-card {
|
||||
--ak-legend-margin-inline-start: calc(
|
||||
var(--pf-c-card--child--PaddingLeft) - var(--ak-legend-padding-inline-base)
|
||||
);
|
||||
--ak-legend-margin-inline-end: calc(
|
||||
var(--pf-c-card--child--PaddingRight) - var(--ak-legend-padding-inline-base)
|
||||
);
|
||||
--pf-c-card--BoxShadow: none;
|
||||
--pf-c-card--BackgroundColor: transparent;
|
||||
}
|
||||
|
||||
&.pf-c-card,
|
||||
&.pf-c-form__group {
|
||||
border-radius: var(--pf-global--BorderRadius--sm);
|
||||
}
|
||||
|
||||
&.pf-c-form__group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.pf-m-action {
|
||||
gap: var(--pf-global--spacer--md) var(--pf-global--spacer--sm);
|
||||
margin-block-start: 0;
|
||||
margin-block-end: calc(var(--pf-c-form__group--m-action--MarginTop) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-c-modal-box__footer {
|
||||
--ak-legend-padding-inline-base: var(--pf-global--spacer--md);
|
||||
padding-block: calc(var(--ak-legend-padding-inline-base) / 2);
|
||||
border-inline: none;
|
||||
border-block-end: none;
|
||||
|
||||
--pf-c-modal-box__footer--c-button--sm--MarginRight: var(
|
||||
--pf-c-modal-box__footer--c-button--MarginRight
|
||||
);
|
||||
|
||||
& > ak-spinner-button:not(:last-child) {
|
||||
margin-right: var(--pf-c-modal-box__footer--c-button--MarginRight);
|
||||
}
|
||||
}
|
||||
fieldset[name="actions"] {
|
||||
border: none;
|
||||
display: flex;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Notifications */
|
||||
|
||||
.pf-c-notification-drawer {
|
||||
--pf-c-notification-drawer--BackgroundColor: var(--pf-global--BackgroundColor--150);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Switch */
|
||||
.pf-c-switch {
|
||||
--pf-c-switch__input--focus__toggle--OutlineWidth: 0;
|
||||
@@ -553,16 +387,11 @@ fieldset {
|
||||
}
|
||||
|
||||
@media (min-height: 60rem) {
|
||||
.pf-c-login[data-layout="stacked"] .pf-c-login__main {
|
||||
.pf-c-login.stacked .pf-c-login__main {
|
||||
margin-top: 13rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-login[data-layout="sidebar_left"],
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
--ak-flow-footer-color: var(--ak-flow-background-color-contrast);
|
||||
}
|
||||
|
||||
.pf-c-data-list {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
@@ -23,18 +23,6 @@ body {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* #region Scrollbars */
|
||||
|
||||
:root {
|
||||
/**
|
||||
* Scrollbar colors derived from default user agent styles.
|
||||
*/
|
||||
--ak-scrollbar-background-color: hsl(0 0% 18%);
|
||||
--ak-scrollbar-thumb-background-color: hsl(0 0% 42%);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
.pf-c-radio {
|
||||
--pf-c-radio__label--Color: var(--ak-dark-foreground);
|
||||
}
|
||||
@@ -91,22 +79,6 @@ body {
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Fields */
|
||||
|
||||
fieldset {
|
||||
--ak-fieldset-border-color: var(--pf-global--BackgroundColor--dark-transparent-200);
|
||||
|
||||
@media (prefers-contrast: more) {
|
||||
--ak-fieldset-border-color: var(--pf-global--BorderColor--300);
|
||||
}
|
||||
|
||||
@media (prefers-contrast: less) {
|
||||
--ak-fieldset-border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
.pf-c-toolbar {
|
||||
--pf-c-toolbar--BackgroundColor: var(--ak-dark-background-light);
|
||||
}
|
||||
@@ -205,17 +177,15 @@ select.pf-c-form-control {
|
||||
}
|
||||
|
||||
.pf-c-form-control {
|
||||
/* !important is required to ensure priority when in compatibility mode. */
|
||||
|
||||
--pf-c-form-control--BorderTopColor: transparent !important;
|
||||
--pf-c-form-control--BorderRightColor: transparent !important;
|
||||
--pf-c-form-control--BorderLeftColor: transparent !important;
|
||||
--pf-global--BackgroundColor--100: var(--ak-dark-background-light) !important;
|
||||
--pf-c-form-control--BackgroundColor: var(--ak-dark-background-light) !important;
|
||||
--pf-c-form-control--Color: var(--ak-dark-foreground) !important;
|
||||
--pf-c-form-control--BackgroundColor: var(--ak-dark-background-light);
|
||||
--pf-c-form-control--Color: var(--ak-dark-foreground);
|
||||
|
||||
--pf-c-form-control--readonly--BackgroundColor: var(--ak-dark-background-light) !important;
|
||||
--pf-c-form-control--disabled--BackgroundColor: var(--ak-dark-background-light) !important;
|
||||
--pf-c-form-control--readonly--BackgroundColor: var(--ak-dark-background-light);
|
||||
--pf-c-form-control--disabled--BackgroundColor: var(--ak-dark-background-light);
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
@@ -394,13 +364,11 @@ select.pf-c-form-control {
|
||||
}
|
||||
|
||||
.pf-c-notification-drawer {
|
||||
--pf-c-notification-drawer--BackgroundColor: var(--ak-dark-background) !important;
|
||||
--pf-c-notification-drawer__header--BackgroundColor: var(
|
||||
--ak-dark-background-lighter
|
||||
) !important;
|
||||
--pf-c-notification-drawer--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
|
||||
.pf-c-notification-drawer__header {
|
||||
background-color: var(--ak-dark-background-lighter);
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,13 +66,8 @@ export class AppIcon extends AKElement implements IAppIcon {
|
||||
padding: var(--icon-border);
|
||||
max-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
line-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
filter: drop-shadow(hsl(0deg 0% 85%) 5px 5px 5px);
|
||||
filter: drop-shadow(5px 5px 5px rgba(128, 128, 128, 0.25));
|
||||
}
|
||||
|
||||
:host([theme="dark"]) .icon {
|
||||
filter: drop-shadow(hsl(0deg 0% 11%) 5px 5px 4px);
|
||||
}
|
||||
|
||||
div {
|
||||
height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
}
|
||||
@@ -82,14 +77,14 @@ export class AppIcon extends AKElement implements IAppIcon {
|
||||
render(): TemplateResult {
|
||||
// prettier-ignore
|
||||
return match([this.name, this.icon])
|
||||
.with([P.nullish, P.nullish],
|
||||
() => html`<div><i aria-hidden="true" class="icon fas fa-question-circle"></i></div>`)
|
||||
.with([undefined, undefined],
|
||||
() => html`<div><i class="icon fas fa-question-circle" aria-hidden="true"></i></div>`)
|
||||
.with([P._, P.string.startsWith("fa://")],
|
||||
([_name, icon]) => html`<div><i aria-hidden="true" class="icon fas ${icon.replaceAll("fa://", "")}"></i></div>`)
|
||||
([_name, icon]) => html`<div><i class="icon fas ${icon.replaceAll("fa://", "")}"></i></div>`)
|
||||
.with([P._, P.string],
|
||||
([_name, icon]) => html`<img aria-hidden="true" class="icon pf-c-avatar" src="${icon}" alt="${msg("Application Icon")}" />`)
|
||||
.with([P.string, P.nullish],
|
||||
([name]) => html`<span aria-hidden="true" class="icon">${name.charAt(0).toUpperCase()}</span>`)
|
||||
([_name, icon]) => html`<img class="icon pf-c-avatar" src="${icon}" alt="${msg("Application Icon")}" />`)
|
||||
.with([P.string, undefined],
|
||||
([name]) => html`<span class="icon">${name.charAt(0).toUpperCase()}</span>`)
|
||||
.exhaustive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,24 +19,20 @@ export interface IExpand {
|
||||
@customElement("ak-expand")
|
||||
export class Expand extends AKElement implements IExpand {
|
||||
@property({ type: Boolean })
|
||||
public expanded = false;
|
||||
expanded = false;
|
||||
|
||||
@property({ type: String, attribute: "text-open" })
|
||||
public textOpen = msg("Show less");
|
||||
textOpen = msg("Show less");
|
||||
|
||||
@property({ type: String, attribute: "text-closed" })
|
||||
public textClosed = msg("Show more");
|
||||
textClosed = msg("Show more");
|
||||
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFExpandableSection,
|
||||
css`
|
||||
.pf-c-expandable-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.pf-c-expandable-section__toggle {
|
||||
user-select: none;
|
||||
.pf-c-expandable-section.pf-m-display-lg {
|
||||
background-color: var(--pf-global--BackgroundColor--100);
|
||||
}
|
||||
`,
|
||||
];
|
||||
@@ -50,8 +46,7 @@ export class Expand extends AKElement implements IExpand {
|
||||
<button
|
||||
type="button"
|
||||
class="pf-c-expandable-section__toggle"
|
||||
aria-expanded=${this.expanded ? "true" : "false"}
|
||||
aria-controls="expandable-content"
|
||||
aria-expanded="${this.expanded}"
|
||||
@click=${() => {
|
||||
this.expanded = !this.expanded;
|
||||
}}
|
||||
@@ -63,11 +58,7 @@ export class Expand extends AKElement implements IExpand {
|
||||
>${this.expanded ? this.textOpen : this.textClosed}</span
|
||||
>
|
||||
</button>
|
||||
<div
|
||||
id="expandable-content"
|
||||
class="pf-c-expandable-section__content"
|
||||
?hidden=${!this.expanded}
|
||||
>
|
||||
<div class="pf-c-expandable-section__content" ?hidden=${!this.expanded}>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@@ -64,6 +64,10 @@ export class AggregateCard extends AKElement implements IAggregateCard {
|
||||
PFCard,
|
||||
PFFlex,
|
||||
css`
|
||||
.pf-c-card {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.pf-c-card.pf-c-card-aggregate {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -75,6 +79,10 @@ export class AggregateCard extends AKElement implements IAggregateCard {
|
||||
align-items: center;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
flex: 1 1 auto;
|
||||
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--FontSize--sm);
|
||||
}
|
||||
}
|
||||
|
||||
.subtext {
|
||||
@@ -86,11 +94,14 @@ export class AggregateCard extends AKElement implements IAggregateCard {
|
||||
padding-left: calc(var(--pf-c-card--child--PaddingLeft) / 2);
|
||||
padding-right: calc(var(--pf-c-card--child--PaddingRight) / 2);
|
||||
}
|
||||
|
||||
.status-container {
|
||||
font-size: var(--pf-global--icon--FontSize--lg);
|
||||
text-align: center;
|
||||
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--icon--FontSize--md);
|
||||
}
|
||||
|
||||
.status-heading {
|
||||
display: flex;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
@@ -108,20 +119,10 @@ export class AggregateCard extends AKElement implements IAggregateCard {
|
||||
.pf-c-card__footer {
|
||||
min-height: 1ex;
|
||||
}
|
||||
|
||||
:host([role="status"]) {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
renderInner(): SlottedTemplateResult {
|
||||
if (this.role === "status") {
|
||||
return html`<div class="status-container">
|
||||
<slot class="status-heading"></slot>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`<slot></slot>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* @file Intersection Observer Decorator for LitElement
|
||||
*/
|
||||
|
||||
import { LitElement } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
|
||||
/**
|
||||
* Type for the decorator
|
||||
*/
|
||||
export type IntersectionDecorator = <T extends LitElement>(target: T, propertyKey: keyof T) => void;
|
||||
|
||||
/**
|
||||
* A decorator that applies an IntersectionObserver to the element.
|
||||
* This is useful for lazy-loading elements that are not visible on the screen.
|
||||
*
|
||||
* @param init Configuration options for the IntersectionObserver
|
||||
*
|
||||
* ```ts
|
||||
* class MyElement extends LitElement {
|
||||
* \@intersectionObserver()
|
||||
* protected visible!: boolean;
|
||||
*
|
||||
* \@intersectionObserver({ threshold: 0.5, rootMargin: '50px' })
|
||||
* protected halfVisible!: boolean;
|
||||
*
|
||||
* render() {
|
||||
* if (!this.visible) return nothing;
|
||||
*
|
||||
* return html`
|
||||
* <div>
|
||||
* Content is visible!
|
||||
* ${this.halfVisible ? html`<p>More than 50% visible</p>` : ''}
|
||||
* </div>
|
||||
* `;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function intersectionObserver(init: IntersectionObserverInit = {}): IntersectionDecorator {
|
||||
return <T extends LitElement, K extends keyof T>(target: T, key: K) => {
|
||||
//#region Prepare observer
|
||||
|
||||
property({ attribute: false, useDefault: false })(target, key);
|
||||
|
||||
const observerCallback: IntersectionObserverCallback = (entries) => {
|
||||
for (const entry of entries) {
|
||||
const currentTarget = entry.target as T;
|
||||
const cachedIntersecting = currentTarget[key];
|
||||
|
||||
if (cachedIntersecting !== entry.isIntersecting) {
|
||||
Object.assign(currentTarget, {
|
||||
[key]: entry.isIntersecting,
|
||||
});
|
||||
|
||||
currentTarget.requestUpdate(key, cachedIntersecting);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
const observer = new IntersectionObserver(observerCallback, {
|
||||
root: null,
|
||||
rootMargin: "0px",
|
||||
threshold: 0,
|
||||
...init,
|
||||
});
|
||||
|
||||
const { connectedCallback, disconnectedCallback } = target;
|
||||
|
||||
target.connectedCallback = function (this: T) {
|
||||
connectedCallback?.call(this);
|
||||
|
||||
if (this.hasUpdated) {
|
||||
observer.observe(this);
|
||||
} else {
|
||||
this.updateComplete.then(() => {
|
||||
observer.observe(this);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
target.disconnectedCallback = function (this: LitElement) {
|
||||
disconnectedCallback?.call(this);
|
||||
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
};
|
||||
//#endregion
|
||||
};
|
||||
}
|
||||
@@ -58,7 +58,6 @@ export class DeleteObjectsTable<T extends object> extends Table<T> {
|
||||
return name || null;
|
||||
}
|
||||
|
||||
@state()
|
||||
protected get columns(): TableColumn[] {
|
||||
return this.metadata(this.objects[0]).map((element) => [element.key]);
|
||||
}
|
||||
|
||||
@@ -92,14 +92,6 @@ export class ModalForm extends ModalButton {
|
||||
}
|
||||
};
|
||||
|
||||
#refreshListener = (e: Event): void => {
|
||||
// if the modal should stay open after successful submit, prevent EVENT_REFRESH from bubbling
|
||||
// to the parent components (which would cause table refreshes that destroy the modal)
|
||||
if (!this.closeAfterSuccessfulSubmit) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
#scrollListener = () => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("scroll", {
|
||||
@@ -108,16 +100,6 @@ export class ModalForm extends ModalButton {
|
||||
);
|
||||
};
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.addEventListener(EVENT_REFRESH, this.#refreshListener);
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener(EVENT_REFRESH, this.#refreshListener);
|
||||
}
|
||||
|
||||
protected renderModalInner(): TemplateResult {
|
||||
return html`${this.loading
|
||||
? html`<ak-loading-overlay topmost></ak-loading-overlay>`
|
||||
@@ -133,7 +115,7 @@ export class ModalForm extends ModalButton {
|
||||
<section class="pf-c-modal-box__body" @scroll=${this.#scrollListener}>
|
||||
<slot name="form"></slot>
|
||||
</section>
|
||||
<fieldset class="pf-c-modal-box__footer">
|
||||
<fieldset name="actions" class="pf-c-modal-box__footer">
|
||||
<legend class="sr-only">${msg("Form actions")}</legend>
|
||||
${this.showSubmitButton
|
||||
? html`<button
|
||||
|
||||
@@ -22,8 +22,6 @@ import { ifPresent } from "#elements/utils/attributes";
|
||||
|
||||
import { Pagination } from "@goauthentik/api";
|
||||
|
||||
import { kebabCase } from "change-case";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
@@ -84,12 +82,6 @@ export abstract class Table<T extends object>
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
[part="table-container"] {
|
||||
@media (max-width: 1199px) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-table {
|
||||
.presentational {
|
||||
--pf-c-table--cell--MinWidth: 0;
|
||||
@@ -106,10 +98,10 @@ export abstract class Table<T extends object>
|
||||
td:has(ak-action-button),
|
||||
td:has(ak-forms-modal),
|
||||
td:has(ak-rbac-object-permission-modal) {
|
||||
& > .pf-c-button,
|
||||
& > button[slot="trigger"]:has(i),
|
||||
& > *::part(spinner-button),
|
||||
& > *::part(button) {
|
||||
.pf-c-button,
|
||||
button[slot="trigger"]:has(i),
|
||||
*::part(spinner-button),
|
||||
ak-rbac-object-permission-modal::part(button) {
|
||||
--pf-global--spacer--form-element: 0;
|
||||
|
||||
padding-inline: 0.5em !important;
|
||||
@@ -137,7 +129,6 @@ export abstract class Table<T extends object>
|
||||
|
||||
.pf-m-search-filter {
|
||||
flex: 1 1 auto;
|
||||
margin-inline: 0;
|
||||
}
|
||||
.pf-c-table thead .pf-c-table__check {
|
||||
min-width: 3rem;
|
||||
@@ -146,27 +137,16 @@ export abstract class Table<T extends object>
|
||||
margin-top: calc(var(--pf-c-table__check--input--MarginTop) + 1px);
|
||||
}
|
||||
|
||||
.pf-c-toolbar {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-inline: var(--pf-global--spacer--md);
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
||||
.pf-c-toolbar__content {
|
||||
flex: 1 1 auto;
|
||||
flex-flow: row wrap;
|
||||
margin: 0;
|
||||
justify-content: space-between;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
padding-inline: 0;
|
||||
|
||||
.pf-c-switch {
|
||||
--pf-c-switch--ColumnGap: var(--pf-c-toolbar__item--m-search-filter--spacer);
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-toolbar__group {
|
||||
flex-flow: row wrap;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
|
||||
.pf-c-card__title .pf-icon {
|
||||
@@ -174,10 +154,6 @@ export abstract class Table<T extends object>
|
||||
}
|
||||
}
|
||||
|
||||
[part="toolbar-primary"] {
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
.pf-c-table {
|
||||
--pf-c-table--m-striped__tr--BackgroundColor: var(
|
||||
--pf-global--BackgroundColor--dark-300
|
||||
@@ -242,24 +218,13 @@ export abstract class Table<T extends object>
|
||||
*/
|
||||
#columnCount = 0;
|
||||
|
||||
#columnIDs = new WeakMap<TableColumn, string>();
|
||||
|
||||
#synchronizeColumnProperties() {
|
||||
#synchronizeColumnCount() {
|
||||
let nextColumnCount = this.columns.length;
|
||||
|
||||
if (this.checkbox) nextColumnCount += 1;
|
||||
if (this.expandable) nextColumnCount += 1;
|
||||
|
||||
this.#columnCount = nextColumnCount;
|
||||
|
||||
for (const column of this.columns) {
|
||||
const [label] = column;
|
||||
|
||||
if (!label) continue;
|
||||
|
||||
const columnName = kebabCase(label);
|
||||
this.#columnIDs.set(column, columnName);
|
||||
}
|
||||
}
|
||||
|
||||
@state()
|
||||
@@ -412,7 +377,7 @@ export abstract class Table<T extends object>
|
||||
changedProperties.has("checkbox") ||
|
||||
changedProperties.has("expandable")
|
||||
) {
|
||||
this.#synchronizeColumnProperties();
|
||||
this.#synchronizeColumnCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -739,15 +704,18 @@ export abstract class Table<T extends object>
|
||||
${this.checkbox ? renderCheckbox() : nothing}
|
||||
${this.expandable ? renderExpansion() : nothing}
|
||||
${this.row(item).map((cell, columnIndex) => {
|
||||
const columnID = this.#columnIDs.get(this.columns[columnIndex]);
|
||||
const [columnLabel] = this.columns[columnIndex];
|
||||
|
||||
const columnHeaderID = `table-header-${columnIndex}`;
|
||||
const headers = groupHeaderID
|
||||
? `${groupHeaderID} ${columnID}`.trim()
|
||||
: columnID;
|
||||
? `${groupHeaderID} ${columnHeaderID}`
|
||||
: columnHeaderID;
|
||||
|
||||
return html`<td
|
||||
class=${ifPresent(!columnID, "presentational")}
|
||||
headers=${ifPresent(headers)}
|
||||
class=${classMap({
|
||||
presentational: !columnLabel,
|
||||
})}
|
||||
headers=${headers}
|
||||
>
|
||||
${cell}
|
||||
</td>`;
|
||||
@@ -795,25 +763,18 @@ export abstract class Table<T extends object>
|
||||
|
||||
if (this.renderToolbarAfter) {
|
||||
primaryToolbar.push(
|
||||
html`<div class="pf-c-toolbar__group" part="toolbar-after">
|
||||
${this.renderToolbarAfter()}
|
||||
</div>`,
|
||||
html`<div class="pf-c-toolbar__group">${this.renderToolbarAfter()}</div>`,
|
||||
);
|
||||
}
|
||||
|
||||
return html`<header
|
||||
class="pf-c-toolbar"
|
||||
role="toolbar"
|
||||
aria-label="${label}"
|
||||
part="toolbar"
|
||||
>
|
||||
return html`<header class="pf-c-toolbar" role="toolbar" aria-label="${label}">
|
||||
${primaryToolbar.length
|
||||
? html`<div class="pf-c-toolbar__content" part="toolbar-primary">
|
||||
? html`<div class="pf-c-toolbar__content" name="toolbar-primary">
|
||||
${primaryToolbar}
|
||||
</div>`
|
||||
: nothing}
|
||||
|
||||
<div class="pf-c-toolbar__content" part="toolbar-secondary">
|
||||
<div class="pf-c-toolbar__content" name="toolbar-secondary">
|
||||
<div class="pf-c-toolbar__group">
|
||||
${this.renderToolbar()} ${this.renderToolbarSelected()}
|
||||
</div>
|
||||
@@ -843,7 +804,6 @@ export abstract class Table<T extends object>
|
||||
|
||||
return html` <ak-table-search
|
||||
class="pf-c-toolbar__item pf-m-search-filter ${isQL ? "ql" : ""}"
|
||||
part="toolbar-search"
|
||||
.defaultValue=${this.search}
|
||||
label=${ifDefined(this.searchLabel)}
|
||||
placeholder=${ifDefined(this.searchPlaceholder)}
|
||||
@@ -982,19 +942,15 @@ export abstract class Table<T extends object>
|
||||
<tr class="pf-c-table__header-row">
|
||||
${this.checkbox ? this.renderAllOnThisPageCheckbox() : nothing}
|
||||
${this.expandable ? html`<td aria-hidden="true"></td>` : nothing}
|
||||
${this.columns.map((column, idx) => {
|
||||
const [label, orderBy, ariaLabel] = column;
|
||||
const columnID = this.#columnIDs.get(column) ?? `column-${idx}`;
|
||||
|
||||
return renderTableColumn({
|
||||
${this.columns.map(([label, orderBy, ariaLabel], idx) =>
|
||||
renderTableColumn({
|
||||
label,
|
||||
id: columnID,
|
||||
ariaLabel,
|
||||
orderBy,
|
||||
table: this,
|
||||
columnIndex: idx,
|
||||
});
|
||||
})}
|
||||
}),
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
${this.renderRows()}
|
||||
|
||||
@@ -31,12 +31,13 @@ function formatSortDirection(table: TableLike, orderBy?: string | null): SortDir
|
||||
|
||||
export type TableColumn = [label: string, orderBy?: string | null, ariaLabel?: string | null];
|
||||
|
||||
const formatColumnID = (columnIndex: number): string => `table-header-${columnIndex}`;
|
||||
|
||||
export interface TableColumnProps {
|
||||
label: string;
|
||||
ariaLabel?: string | null;
|
||||
orderBy?: string | null;
|
||||
table: TableLike;
|
||||
id?: string;
|
||||
columnIndex: number;
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ export function renderTableColumn({
|
||||
ariaLabel,
|
||||
orderBy,
|
||||
table,
|
||||
id,
|
||||
columnIndex,
|
||||
}: TableColumnProps): TemplateResult {
|
||||
const classes = {
|
||||
"presentational": !label,
|
||||
@@ -80,8 +81,9 @@ export function renderTableColumn({
|
||||
: html`${label}`;
|
||||
|
||||
return html`<th
|
||||
id=${ifPresent(id)}
|
||||
id=${formatColumnID(columnIndex)}
|
||||
aria-label=${ifPresent(resolvedLabel)}
|
||||
data-column-id=${ifPresent(orderBy)}
|
||||
scope="col"
|
||||
aria-sort=${direction}
|
||||
class="${classMap(classes)}"
|
||||
|
||||
@@ -20,7 +20,6 @@ export abstract class TablePage<T extends object> extends Table<T> {
|
||||
PFSidebar,
|
||||
css`
|
||||
.pf-c-sidebar__panel {
|
||||
--pf-c-sidebar__panel--Position: static;
|
||||
flex: 0 1 25%;
|
||||
}
|
||||
.pf-c-sidebar__content {
|
||||
|
||||
@@ -83,28 +83,29 @@ export class ScheduleList extends Table<Schedule> {
|
||||
if (this.relObjId !== undefined) {
|
||||
return nothing;
|
||||
}
|
||||
return html`<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.showOnlyStandalone}
|
||||
@change=${this.#toggleShowOnlyStandalone}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"> </i>
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.showOnlyStandalone}
|
||||
@change=${this.#toggleShowOnlyStandalone}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"> </i>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Show only standalone schedules")}
|
||||
</span>
|
||||
</label>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Show only standalone schedules")}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: Schedule): SlottedTemplateResult[] {
|
||||
|
||||
@@ -66,9 +66,6 @@ export class TaskList extends Table<Task> {
|
||||
? [
|
||||
TasksTasksListAggregatedStatusEnum.Queued,
|
||||
TasksTasksListAggregatedStatusEnum.Consumed,
|
||||
TasksTasksListAggregatedStatusEnum.Preprocess,
|
||||
TasksTasksListAggregatedStatusEnum.Running,
|
||||
TasksTasksListAggregatedStatusEnum.Postprocess,
|
||||
TasksTasksListAggregatedStatusEnum.Rejected,
|
||||
TasksTasksListAggregatedStatusEnum.Warning,
|
||||
TasksTasksListAggregatedStatusEnum.Error,
|
||||
@@ -109,44 +106,47 @@ export class TaskList extends Table<Task> {
|
||||
];
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
return html`<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
${this.relObjId === undefined
|
||||
? html` <label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.showOnlyStandalone}
|
||||
@change=${this.#toggleShowOnlyStandalone}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"> </i>
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group">
|
||||
${this.relObjId === undefined
|
||||
? html` <label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.showOnlyStandalone}
|
||||
@change=${this.#toggleShowOnlyStandalone}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"> </i>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Show only standalone tasks")}
|
||||
</span>
|
||||
</label>`
|
||||
: nothing}
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.excludeSuccessful}
|
||||
@change=${this.#toggleExcludeSuccessful}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"> </i>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Show only standalone tasks")}
|
||||
</span>
|
||||
</label>`
|
||||
: nothing}
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.excludeSuccessful}
|
||||
@change=${this.#toggleExcludeSuccessful}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"> </i>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label"> ${msg("Exclude successful tasks")} </span>
|
||||
</label>
|
||||
<span class="pf-c-switch__label">
|
||||
${msg("Exclude successful tasks")}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: Task): SlottedTemplateResult[] {
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import "#elements/cards/AggregateCard";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
|
||||
import { GlobalTaskStatus, TasksApi } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-task-overview")
|
||||
export class TaskOverview extends AKElement {
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFGrid,
|
||||
PFCard,
|
||||
PFPage,
|
||||
css`
|
||||
.pf-m-no-padding-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
status?: GlobalTaskStatus;
|
||||
|
||||
async firstUpdated() {
|
||||
this.status = await new TasksApi(DEFAULT_CONFIG).tasksTasksStatusRetrieve();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.status) return nothing;
|
||||
return html`<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||
<div
|
||||
class="pf-l-grid pf-m-gutter pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-3-col-on-lg pf-m-all-3-col-on-xl"
|
||||
>
|
||||
<ak-aggregate-card
|
||||
class="pf-l-grid__item"
|
||||
icon="pf-icon pf-icon-in-progress"
|
||||
label=${msg("Running tasks")}
|
||||
>
|
||||
${this.status.running + this.status.preprocess + this.status.postprocess}
|
||||
</ak-aggregate-card>
|
||||
<ak-aggregate-card
|
||||
class="pf-l-grid__item"
|
||||
icon="pf-icon pf-icon-pending"
|
||||
label=${msg("Queued tasks")}
|
||||
>
|
||||
${this.status.queued + this.status.consumed}
|
||||
</ak-aggregate-card>
|
||||
<ak-aggregate-card
|
||||
class="pf-l-grid__item"
|
||||
icon="fa fa-check-circle"
|
||||
label=${msg("Successful tasks")}
|
||||
>
|
||||
${this.status.done + this.status.info + this.status.warning}
|
||||
</ak-aggregate-card>
|
||||
<ak-aggregate-card
|
||||
class="pf-l-grid__item"
|
||||
icon="fa fa-exclamation-triangle"
|
||||
label=${msg("Error tasks")}
|
||||
>
|
||||
${this.status.error + this.status.rejected}
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
</section>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-task-overview": TaskOverview;
|
||||
}
|
||||
}
|
||||
@@ -34,19 +34,7 @@ export class TaskStatus extends AKElement {
|
||||
case TasksTasksListAggregatedStatusEnum.Consumed:
|
||||
case TaskAggregatedStatusEnum.Consumed:
|
||||
case LastTaskStatusEnum.Consumed:
|
||||
return html`<ak-label color=${PFColor.Blue}>${msg("Consumed")}</ak-label>`;
|
||||
case TasksTasksListAggregatedStatusEnum.Preprocess:
|
||||
case TaskAggregatedStatusEnum.Preprocess:
|
||||
case LastTaskStatusEnum.Preprocess:
|
||||
return html`<ak-label color=${PFColor.Blue}>${msg("Pre-processing")}</ak-label>`;
|
||||
case TasksTasksListAggregatedStatusEnum.Running:
|
||||
case TaskAggregatedStatusEnum.Running:
|
||||
case LastTaskStatusEnum.Running:
|
||||
return html`<ak-label color=${PFColor.Blue}>${msg("Running")}</ak-label>`;
|
||||
case TasksTasksListAggregatedStatusEnum.Postprocess:
|
||||
case TaskAggregatedStatusEnum.Postprocess:
|
||||
case LastTaskStatusEnum.Postprocess:
|
||||
return html`<ak-label color=${PFColor.Blue}>${msg("Post-processing")}</ak-label>`;
|
||||
case TasksTasksListAggregatedStatusEnum.Done:
|
||||
case TaskAggregatedStatusEnum.Done:
|
||||
case LastTaskStatusEnum.Done:
|
||||
|
||||
@@ -48,38 +48,19 @@ export class FocusTarget<T extends HTMLElement = HTMLElement> {
|
||||
public focus = (options?: FocusOptions): void => {
|
||||
const { target } = this;
|
||||
|
||||
if (!target) {
|
||||
console.debug("FocusTarget: Skipping focus, no target", target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.activeElement === target) {
|
||||
console.debug("FocusTarget: Target is already focused", target);
|
||||
return;
|
||||
}
|
||||
if (!target) return;
|
||||
if (document.activeElement === target) return;
|
||||
|
||||
// Despite our type definitions, this method isn't available in all browsers,
|
||||
// so we fallback to assuming the element is visible.
|
||||
const visible = target.checkVisibility?.() ?? true;
|
||||
|
||||
if (!visible) {
|
||||
console.debug("FocusTarget: Skipping focus, target is not visible", target);
|
||||
return;
|
||||
}
|
||||
if (!visible) return;
|
||||
|
||||
if (typeof target.focus !== "function") {
|
||||
console.debug("FocusTarget: Skipping focus, target has no focus method", target);
|
||||
return;
|
||||
}
|
||||
|
||||
target.focus(options);
|
||||
target.focus?.(options);
|
||||
};
|
||||
|
||||
public toRef() {
|
||||
return ref(this.reference);
|
||||
}
|
||||
|
||||
public toEventListener(options?: FocusOptions) {
|
||||
return () => this.focus(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,15 +48,6 @@ import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
const FlowLayoutClasses = {
|
||||
[FlowLayoutEnum.ContentLeft]: "pf-c-login__container",
|
||||
[FlowLayoutEnum.ContentRight]: "pf-c-login__container content-right",
|
||||
[FlowLayoutEnum.SidebarLeft]: "ak-login-container",
|
||||
[FlowLayoutEnum.SidebarRight]: "ak-login-container",
|
||||
[FlowLayoutEnum.Stacked]: "ak-login-container",
|
||||
[FlowLayoutEnum.UnknownDefaultOpenApi]: "ak-login-container",
|
||||
} as const satisfies Record<FlowLayoutEnum, string>;
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor
|
||||
extends WithCapabilitiesConfig(WithBrandConfig(Interface))
|
||||
@@ -82,12 +73,7 @@ export class FlowExecutor
|
||||
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--sm-2x: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--lg: var(--ak-flow-background);
|
||||
|
||||
@media (max-width: 768px) {
|
||||
background: var(--pf-c-login__main--BackgroundColor) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ak-hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -99,7 +85,7 @@ export class FlowExecutor
|
||||
}
|
||||
/* layouts */
|
||||
@media (min-height: 60rem) {
|
||||
.pf-c-login[data-layout="stacked"] .pf-c-login__main {
|
||||
.pf-c-login.stacked .pf-c-login__main {
|
||||
margin-top: 13rem;
|
||||
}
|
||||
}
|
||||
@@ -109,34 +95,33 @@ export class FlowExecutor
|
||||
"footer main"
|
||||
". main";
|
||||
}
|
||||
.pf-c-login[data-layout="sidebar_left"] {
|
||||
.pf-c-login.sidebar_left {
|
||||
justify-content: flex-start;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.pf-c-login[data-layout="sidebar_left"] .ak-login-container,
|
||||
.pf-c-login[data-layout="sidebar_right"] .ak-login-container {
|
||||
height: 100%;
|
||||
min-height: 100dvh;
|
||||
.pf-c-login.sidebar_left .ak-login-container,
|
||||
.pf-c-login.sidebar_right .ak-login-container {
|
||||
height: 100vh;
|
||||
background-color: var(--pf-c-login__main--BackgroundColor);
|
||||
padding-inline: var(--pf-global--spacer--lg);
|
||||
padding-block-end: var(--pf-global--spacer--xs);
|
||||
padding-left: var(--pf-global--spacer--lg);
|
||||
padding-right: var(--pf-global--spacer--lg);
|
||||
}
|
||||
.pf-c-login[data-layout="sidebar_left"] .pf-c-list,
|
||||
.pf-c-login[data-layout="sidebar_right"] .pf-c-list {
|
||||
.pf-c-login.sidebar_left .pf-c-list,
|
||||
.pf-c-login.sidebar_right .pf-c-list {
|
||||
color: #000;
|
||||
}
|
||||
.pf-c-login[data-layout="sidebar_right"] {
|
||||
.pf-c-login.sidebar_right {
|
||||
justify-content: flex-end;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_left"] .ak-login-container,
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_right"] .ak-login-container {
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_left .ak-login-container,
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_right .ak-login-container {
|
||||
background-color: var(--ak-dark-background);
|
||||
}
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_left"] .pf-c-list,
|
||||
:host([theme="dark"]) .pf-c-login[data-layout="sidebar_right"] .pf-c-list {
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_left .pf-c-list,
|
||||
:host([theme="dark"]) .pf-c-login.sidebar_right .pf-c-list {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
.pf-c-brand {
|
||||
@@ -367,10 +352,26 @@ export class FlowExecutor
|
||||
|
||||
//#region Render
|
||||
|
||||
get layout(): FlowLayoutEnum {
|
||||
return (
|
||||
this.challenge?.flowInfo?.layout || globalAK()?.flow?.layout || FlowLayoutEnum.Stacked
|
||||
);
|
||||
getLayout(): string {
|
||||
const prefilledFlow = globalAK()?.flow?.layout || FlowLayoutEnum.Stacked;
|
||||
if (this.challenge) {
|
||||
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
||||
}
|
||||
return prefilledFlow;
|
||||
}
|
||||
|
||||
getLayoutClass(): string {
|
||||
const layout = this.getLayout();
|
||||
|
||||
switch (layout) {
|
||||
case FlowLayoutEnum.ContentLeft:
|
||||
return "pf-c-login__container";
|
||||
case FlowLayoutEnum.ContentRight:
|
||||
return "pf-c-login__container content-right";
|
||||
case FlowLayoutEnum.Stacked:
|
||||
default:
|
||||
return "ak-login-container";
|
||||
}
|
||||
}
|
||||
|
||||
async renderChallenge(): Promise<TemplateResult> {
|
||||
@@ -547,7 +548,6 @@ export class FlowExecutor
|
||||
return import("#flow/FlowInspector").then(
|
||||
() =>
|
||||
html`<ak-flow-inspector
|
||||
id="flow-inspector"
|
||||
class="pf-c-drawer__panel pf-m-width-33"
|
||||
.flowSlug=${this.flowSlug}
|
||||
></ak-flow-inspector>`,
|
||||
@@ -555,25 +555,16 @@ export class FlowExecutor
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
const { layout } = this;
|
||||
|
||||
return html`<ak-locale-context>
|
||||
<div class="pf-c-background-image" part="background-image"></div>
|
||||
<div class="pf-c-page__drawer" part="page-drawer">
|
||||
<div
|
||||
class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}"
|
||||
part="drawer"
|
||||
>
|
||||
<div class="pf-c-drawer__main" part="drawer-main">
|
||||
<div class="pf-c-drawer__content" part="drawer-content">
|
||||
<div class="pf-c-drawer__body" part="drawer-body">
|
||||
<div class="pf-c-login" data-layout=${layout} part="flow">
|
||||
<div class=${FlowLayoutClasses[layout]} part="flow-container">
|
||||
<main
|
||||
class="pf-c-login__main"
|
||||
aria-label=${msg("Authentication form")}
|
||||
part="flow-main"
|
||||
>
|
||||
return html` <ak-locale-context>
|
||||
<div class="pf-c-background-image"></div>
|
||||
<div class="pf-c-page__drawer">
|
||||
<div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
|
||||
<div class="pf-c-drawer__main">
|
||||
<div class="pf-c-drawer__content">
|
||||
<div class="pf-c-drawer__body">
|
||||
<div class="pf-c-login ${this.getLayout()}">
|
||||
<div class="${this.getLayoutClass()}">
|
||||
<div class="pf-c-login__main">
|
||||
${this.loading && this.challenge
|
||||
? html`<ak-loading-overlay></ak-loading-overlay>`
|
||||
: nothing}
|
||||
@@ -583,15 +574,11 @@ export class FlowExecutor
|
||||
<img
|
||||
src="${themeImage(this.brandingLogo)}"
|
||||
alt="${msg("authentik Logo")}"
|
||||
role="presentation"
|
||||
/>
|
||||
</div>
|
||||
${until(this.renderChallenge())}
|
||||
</main>
|
||||
</div>
|
||||
<ak-brand-links
|
||||
part="brand-links"
|
||||
role="contentinfo"
|
||||
aria-label=${msg("Site footer")}
|
||||
class="pf-c-login__footer"
|
||||
.links=${this.brandingFooterLinks}
|
||||
></ak-brand-links>
|
||||
@@ -601,12 +588,7 @@ export class FlowExecutor
|
||||
</div>
|
||||
${this.inspectorAvailable && !this.inspectorOpen
|
||||
? html`<button
|
||||
aria-label=${this.inspectorOpen
|
||||
? msg("Close flow inspector")
|
||||
: msg("Open flow inspector")}
|
||||
aria-expanded=${this.inspectorOpen ? "true" : "false"}
|
||||
class="inspector-toggle pf-c-button pf-m-primary"
|
||||
aria-controls="flow-inspector"
|
||||
@click=${() => {
|
||||
this.inspectorOpen = true;
|
||||
}}
|
||||
|
||||
@@ -21,10 +21,6 @@ import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper
|
||||
import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
function stringify(obj: unknown): string {
|
||||
return JSON.stringify(obj, null, 4);
|
||||
}
|
||||
|
||||
@customElement("ak-flow-inspector")
|
||||
export class FlowInspector extends AKElement {
|
||||
@property()
|
||||
@@ -46,28 +42,12 @@ export class FlowInspector extends AKElement {
|
||||
PFProgressStepper,
|
||||
css`
|
||||
.pf-c-drawer__body {
|
||||
height: 100dvh;
|
||||
min-height: 100vh;
|
||||
max-height: 100vh;
|
||||
}
|
||||
:host {
|
||||
background-color: var(--pf-c-notification-drawer--BackgroundColor) !important;
|
||||
}
|
||||
|
||||
.pf-c-notification-drawer__body {
|
||||
/* compatibility-mode-fix */
|
||||
& {
|
||||
padding-inline: var(--pf-global--spacer--md);
|
||||
padding-block: var(--pf-global--spacer--xs);
|
||||
}
|
||||
|
||||
.pf-l-stack__item:last-child {
|
||||
padding-block-end: var(--pf-global--spacer--md);
|
||||
}
|
||||
}
|
||||
|
||||
code.break {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
pre {
|
||||
word-break: break-all;
|
||||
overflow-x: hidden;
|
||||
@@ -111,8 +91,8 @@ export class FlowInspector extends AKElement {
|
||||
return stage;
|
||||
}
|
||||
|
||||
protected renderHeader() {
|
||||
return html`<div class="pf-c-notification-drawer__header">
|
||||
renderHeader() {
|
||||
return html` <div class="pf-c-notification-drawer__header">
|
||||
<div class="text">
|
||||
<h1 class="pf-c-notification-drawer__header-title">${msg("Flow inspector")}</h1>
|
||||
</div>
|
||||
@@ -129,7 +109,7 @@ export class FlowInspector extends AKElement {
|
||||
}}
|
||||
class="pf-c-button pf-m-plain"
|
||||
type="button"
|
||||
aria-label=${msg("Close flow inspector")}
|
||||
aria-label=${msg("Close")}
|
||||
>
|
||||
<i class="fas fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
@@ -138,11 +118,8 @@ export class FlowInspector extends AKElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
protected renderAccessDenied(): TemplateResult {
|
||||
return html`<aside
|
||||
aria-label=${msg("Flow inspector")}
|
||||
class="pf-c-drawer__body pf-m-no-padding"
|
||||
>
|
||||
renderAccessDenied(): TemplateResult {
|
||||
return html`<div class="pf-c-drawer__body pf-m-no-padding">
|
||||
<div class="pf-c-notification-drawer">
|
||||
${this.renderHeader()}
|
||||
<div class="pf-c-notification-drawer__body">
|
||||
@@ -155,171 +132,209 @@ export class FlowInspector extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>`;
|
||||
</div>`;
|
||||
}
|
||||
|
||||
protected renderNextStage({ currentPlan, isCompleted }: FlowInspection): TemplateResult {
|
||||
return html`<fieldset class="pf-c-card">
|
||||
<legend class="pf-c-card__title">${msg("Next stage")}</legend>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Stage name")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${currentPlan?.nextPlannedStage?.stageObj?.name || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Stage kind")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${currentPlan?.nextPlannedStage?.stageObj?.verboseName || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Stage object")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
${isCompleted
|
||||
? html`<div class="pf-c-description-list__text">
|
||||
${msg("This flow is completed.")}
|
||||
</div>`
|
||||
: html`<ak-expand>
|
||||
<pre class="pf-c-description-list__text">
|
||||
${stringify(this.getStage(currentPlan?.nextPlannedStage?.stageObj))}</pre
|
||||
>
|
||||
</ak-expand>`}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</fieldset>`;
|
||||
}
|
||||
|
||||
protected renderPlanHistory({
|
||||
plans,
|
||||
isCompleted,
|
||||
currentPlan,
|
||||
}: FlowInspection): TemplateResult {
|
||||
return html`<fieldset class="pf-c-card">
|
||||
<legend class="pf-c-card__title">${msg("Plan history")}</legend>
|
||||
<div class="pf-c-card__body">
|
||||
<ol class="pf-c-progress-stepper pf-m-vertical">
|
||||
${plans.map((plan) => {
|
||||
return html`<li class="pf-c-progress-stepper__step pf-m-success">
|
||||
<div class="pf-c-progress-stepper__step-connector">
|
||||
<span class="pf-c-progress-stepper__step-icon">
|
||||
<i class="fas fa-check-circle" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div class="pf-c-progress-stepper__step-title">
|
||||
${plan.currentStage.stageObj?.name}
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-description">
|
||||
${plan.currentStage.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li> `;
|
||||
})}
|
||||
${currentPlan?.currentStage && !isCompleted
|
||||
? html`<li class="pf-c-progress-stepper__step pf-m-current pf-m-info">
|
||||
<div class="pf-c-progress-stepper__step-connector">
|
||||
<span class="pf-c-progress-stepper__step-icon">
|
||||
<i
|
||||
class="pficon pf-icon-resources-full"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div class="pf-c-progress-stepper__step-title">
|
||||
${currentPlan?.currentStage?.stageObj?.name}
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-description">
|
||||
${currentPlan?.currentStage?.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
: nothing}
|
||||
${currentPlan?.nextPlannedStage && !isCompleted
|
||||
? html`<li class="pf-c-progress-stepper__step pf-m-pending">
|
||||
<div class="pf-c-progress-stepper__step-connector">
|
||||
<span class="pf-c-progress-stepper__step-icon"></span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div class="pf-c-progress-stepper__step-title">
|
||||
${currentPlan.nextPlannedStage.stageObj?.name}
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-description">
|
||||
${currentPlan?.nextPlannedStage?.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
: nothing}
|
||||
</ol>
|
||||
</div>
|
||||
</fieldset>`;
|
||||
}
|
||||
|
||||
protected renderCurrentPlan({ currentPlan }: FlowInspection): TemplateResult {
|
||||
return html`<fieldset class="pf-c-card">
|
||||
<legend class="pf-c-card__title">${msg("Current plan context")}</legend>
|
||||
<pre class="pf-c-card__body"><code>${stringify(currentPlan?.planContext)}</code></pre>
|
||||
</fieldset>`;
|
||||
}
|
||||
|
||||
protected renderSession({ currentPlan }: FlowInspection): TemplateResult {
|
||||
return html`<fieldset class="pf-c-card">
|
||||
<legend class="pf-c-card__title">${msg("Session ID")}</legend>
|
||||
<div class="pf-c-card__body">
|
||||
<code class="break"> ${currentPlan?.sessionId} </code>
|
||||
</div>
|
||||
</fieldset>`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
render(): TemplateResult {
|
||||
if (this.error) {
|
||||
return this.renderAccessDenied();
|
||||
}
|
||||
if (!this.state) {
|
||||
this.advanceHandler();
|
||||
return html`<aside
|
||||
aria-label=${msg("Flow inspector loading")}
|
||||
class="pf-c-drawer__body pf-m-no-padding"
|
||||
>
|
||||
return html`<div class="pf-c-drawer__body pf-m-no-padding">
|
||||
<div class="pf-c-notification-drawer">
|
||||
${this.renderHeader()}
|
||||
<div class="pf-c-notification-drawer__body"></div>
|
||||
<ak-empty-state loading> </ak-empty-state>
|
||||
</div>
|
||||
</aside>`;
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`<aside
|
||||
aria-label=${msg("Flow inspector")}
|
||||
class="pf-c-drawer__body pf-m-no-padding"
|
||||
>
|
||||
return html`<div class="pf-c-drawer__body pf-m-no-padding">
|
||||
<div class="pf-c-notification-drawer">
|
||||
${this.renderHeader()}
|
||||
<div class="pf-c-notification-drawer__body">
|
||||
<div class="pf-l-stack pf-m-gutter">
|
||||
<div class="pf-l-stack__item">${this.renderNextStage(this.state)}</div>
|
||||
<div class="pf-l-stack__item">${this.renderPlanHistory(this.state)}</div>
|
||||
<div class="pf-l-stack__item">${this.renderCurrentPlan(this.state)}</div>
|
||||
<div class="pf-l-stack__item">${this.renderSession(this.state)}</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${msg("Next stage")}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Stage name")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.state.currentPlan?.nextPlannedStage
|
||||
?.stageObj?.name || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Stage kind")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.state.currentPlan?.nextPlannedStage
|
||||
?.stageObj?.verboseName || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Stage object")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
${this.state.isCompleted
|
||||
? html` <div
|
||||
class="pf-c-description-list__text"
|
||||
>
|
||||
${msg("This flow is completed.")}
|
||||
</div>`
|
||||
: html`<ak-expand>
|
||||
<pre class="pf-c-description-list__text">
|
||||
${JSON.stringify(this.getStage(this.state.currentPlan?.nextPlannedStage?.stageObj), null, 4)}</pre
|
||||
>
|
||||
</ak-expand>`}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${msg("Plan history")}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ol class="pf-c-progress-stepper pf-m-vertical">
|
||||
${this.state.plans.map((plan) => {
|
||||
return html`<li
|
||||
class="pf-c-progress-stepper__step pf-m-success"
|
||||
>
|
||||
<div class="pf-c-progress-stepper__step-connector">
|
||||
<span class="pf-c-progress-stepper__step-icon">
|
||||
<i
|
||||
class="fas fa-check-circle"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div class="pf-c-progress-stepper__step-title">
|
||||
${plan.currentStage.stageObj?.name}
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-description"
|
||||
>
|
||||
${plan.currentStage.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li> `;
|
||||
})}
|
||||
${this.state.currentPlan?.currentStage &&
|
||||
!this.state.isCompleted
|
||||
? html` <li
|
||||
class="pf-c-progress-stepper__step pf-m-current pf-m-info"
|
||||
>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-connector"
|
||||
>
|
||||
<span
|
||||
class="pf-c-progress-stepper__step-icon"
|
||||
>
|
||||
<i
|
||||
class="pficon pf-icon-resources-full"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-title"
|
||||
>
|
||||
${this.state.currentPlan?.currentStage
|
||||
?.stageObj?.name}
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-description"
|
||||
>
|
||||
${this.state.currentPlan?.currentStage
|
||||
?.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
: nothing}
|
||||
${this.state.currentPlan?.nextPlannedStage &&
|
||||
!this.state.isCompleted
|
||||
? html`<li
|
||||
class="pf-c-progress-stepper__step pf-m-pending"
|
||||
>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-connector"
|
||||
>
|
||||
<span
|
||||
class="pf-c-progress-stepper__step-icon"
|
||||
></span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-title"
|
||||
>
|
||||
${this.state.currentPlan.nextPlannedStage
|
||||
.stageObj?.name}
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-description"
|
||||
>
|
||||
${this.state.currentPlan?.nextPlannedStage
|
||||
?.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
: nothing}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">
|
||||
${msg("Current plan context")}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<pre>
|
||||
${JSON.stringify(this.state.currentPlan?.planContext, null, 4)}</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${msg("Session ID")}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<code class="break">${this.state.currentPlan?.sessionId}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>`;
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user