Merge branch 'main' into celery-2-dramatiq
This commit is contained in:
		| @ -138,6 +138,7 @@ class EventViewSet(ModelViewSet): | |||||||
|         from authentik.enterprise.search.fields import ChoiceSearchField, JSONSearchField |         from authentik.enterprise.search.fields import ChoiceSearchField, JSONSearchField | ||||||
|  |  | ||||||
|         return [ |         return [ | ||||||
|  |             StrField(Event, "event_uuid"), | ||||||
|             StrField(Event, "app", suggest_options=True), |             StrField(Event, "app", suggest_options=True), | ||||||
|             StrField(Event, "client_ip"), |             StrField(Event, "client_ip"), | ||||||
|             JSONSearchField(Event, "user"), |             JSONSearchField(Event, "user"), | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ from authentik.events.models import NotificationRule | |||||||
| class NotificationRuleSerializer(ModelSerializer): | class NotificationRuleSerializer(ModelSerializer): | ||||||
|     """NotificationRule Serializer""" |     """NotificationRule Serializer""" | ||||||
|  |  | ||||||
|     group_obj = GroupSerializer(read_only=True, source="group") |     destination_group_obj = GroupSerializer(read_only=True, source="destination_group") | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = NotificationRule |         model = NotificationRule | ||||||
| @ -20,8 +20,9 @@ class NotificationRuleSerializer(ModelSerializer): | |||||||
|             "name", |             "name", | ||||||
|             "transports", |             "transports", | ||||||
|             "severity", |             "severity", | ||||||
|             "group", |             "destination_group", | ||||||
|             "group_obj", |             "destination_group_obj", | ||||||
|  |             "destination_event_user", | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -30,6 +31,6 @@ class NotificationRuleViewSet(UsedByMixin, ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = NotificationRule.objects.all() |     queryset = NotificationRule.objects.all() | ||||||
|     serializer_class = NotificationRuleSerializer |     serializer_class = NotificationRuleSerializer | ||||||
|     filterset_fields = ["name", "severity", "group__name"] |     filterset_fields = ["name", "severity", "destination_group__name"] | ||||||
|     ordering = ["name"] |     ordering = ["name"] | ||||||
|     search_fields = ["name", "group__name"] |     search_fields = ["name", "destination_group__name"] | ||||||
|  | |||||||
| @ -0,0 +1,26 @@ | |||||||
|  | # Generated by Django 5.1.11 on 2025-06-16 23:21 | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("authentik_events", "0009_remove_notificationtransport_webhook_mapping_and_more"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.RenameField( | ||||||
|  |             model_name="notificationrule", | ||||||
|  |             old_name="group", | ||||||
|  |             new_name="destination_group", | ||||||
|  |         ), | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name="notificationrule", | ||||||
|  |             name="destination_event_user", | ||||||
|  |             field=models.BooleanField( | ||||||
|  |                 default=False, | ||||||
|  |                 help_text="When enabled, notification will be sent to user the user that triggered the event.When destination_group is configured, notification is sent to both.", | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -1,10 +1,12 @@ | |||||||
| """authentik events models""" | """authentik events models""" | ||||||
|  |  | ||||||
|  | from collections.abc import Generator | ||||||
| from datetime import timedelta | from datetime import timedelta | ||||||
| from difflib import get_close_matches | from difflib import get_close_matches | ||||||
| from functools import lru_cache | from functools import lru_cache | ||||||
| from inspect import currentframe | from inspect import currentframe | ||||||
| from smtplib import SMTPException | from smtplib import SMTPException | ||||||
|  | from typing import Any | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
|  |  | ||||||
| from django.apps import apps | from django.apps import apps | ||||||
| @ -549,7 +551,7 @@ class NotificationRule(SerializerModel, PolicyBindingModel): | |||||||
|         default=NotificationSeverity.NOTICE, |         default=NotificationSeverity.NOTICE, | ||||||
|         help_text=_("Controls which severity level the created notifications will have."), |         help_text=_("Controls which severity level the created notifications will have."), | ||||||
|     ) |     ) | ||||||
|     group = models.ForeignKey( |     destination_group = models.ForeignKey( | ||||||
|         Group, |         Group, | ||||||
|         help_text=_( |         help_text=_( | ||||||
|             "Define which group of users this notification should be sent and shown to. " |             "Define which group of users this notification should be sent and shown to. " | ||||||
| @ -559,6 +561,19 @@ class NotificationRule(SerializerModel, PolicyBindingModel): | |||||||
|         blank=True, |         blank=True, | ||||||
|         on_delete=models.SET_NULL, |         on_delete=models.SET_NULL, | ||||||
|     ) |     ) | ||||||
|  |     destination_event_user = models.BooleanField( | ||||||
|  |         default=False, | ||||||
|  |         help_text=_( | ||||||
|  |             "When enabled, notification will be sent to user the user that triggered the event." | ||||||
|  |             "When destination_group is configured, notification is sent to both." | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     def destination_users(self, event: Event) -> Generator[User, Any]: | ||||||
|  |         if self.destination_event_user and event.user.get("pk"): | ||||||
|  |             yield User(pk=event.user.get("pk")) | ||||||
|  |         if self.destination_group: | ||||||
|  |             yield from self.destination_group.users.all() | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def serializer(self) -> type[Serializer]: |     def serializer(self) -> type[Serializer]: | ||||||
|  | |||||||
| @ -60,14 +60,10 @@ def event_trigger_handler(event_uuid: UUID, trigger_name: str): | |||||||
|     if not result.passing: |     if not result.passing: | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     if not trigger.group: |  | ||||||
|         LOGGER.debug("e(trigger): trigger has no group", trigger=trigger) |  | ||||||
|         return |  | ||||||
|  |  | ||||||
|     LOGGER.debug("e(trigger): event trigger matched", trigger=trigger) |     LOGGER.debug("e(trigger): event trigger matched", trigger=trigger) | ||||||
|     # Create the notification objects |     # Create the notification objects | ||||||
|     for transport in trigger.transports.all(): |     for transport in trigger.transports.all(): | ||||||
|         for user in trigger.group.users.all(): |         for user in trigger.destination_users(event): | ||||||
|             LOGGER.debug("created notification") |             LOGGER.debug("created notification") | ||||||
|             notification_transport.send_with_options( |             notification_transport.send_with_options( | ||||||
|                 args=( |                 args=( | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ from django.urls import reverse | |||||||
| from rest_framework.test import APITestCase | from rest_framework.test import APITestCase | ||||||
|  |  | ||||||
| from authentik.core.models import Group, User | from authentik.core.models import Group, User | ||||||
|  | from authentik.core.tests.utils import create_test_user | ||||||
| from authentik.events.models import ( | from authentik.events.models import ( | ||||||
|     Event, |     Event, | ||||||
|     EventAction, |     EventAction, | ||||||
| @ -34,7 +35,7 @@ class TestEventsNotifications(APITestCase): | |||||||
|     def test_trigger_empty(self): |     def test_trigger_empty(self): | ||||||
|         """Test trigger without any policies attached""" |         """Test trigger without any policies attached""" | ||||||
|         transport = NotificationTransport.objects.create(name=generate_id()) |         transport = NotificationTransport.objects.create(name=generate_id()) | ||||||
|         trigger = NotificationRule.objects.create(name=generate_id(), group=self.group) |         trigger = NotificationRule.objects.create(name=generate_id(), destination_group=self.group) | ||||||
|         trigger.transports.add(transport) |         trigger.transports.add(transport) | ||||||
|         trigger.save() |         trigger.save() | ||||||
|  |  | ||||||
| @ -46,7 +47,7 @@ class TestEventsNotifications(APITestCase): | |||||||
|     def test_trigger_single(self): |     def test_trigger_single(self): | ||||||
|         """Test simple transport triggering""" |         """Test simple transport triggering""" | ||||||
|         transport = NotificationTransport.objects.create(name=generate_id()) |         transport = NotificationTransport.objects.create(name=generate_id()) | ||||||
|         trigger = NotificationRule.objects.create(name=generate_id(), group=self.group) |         trigger = NotificationRule.objects.create(name=generate_id(), destination_group=self.group) | ||||||
|         trigger.transports.add(transport) |         trigger.transports.add(transport) | ||||||
|         trigger.save() |         trigger.save() | ||||||
|         matcher = EventMatcherPolicy.objects.create( |         matcher = EventMatcherPolicy.objects.create( | ||||||
| @ -59,6 +60,25 @@ class TestEventsNotifications(APITestCase): | |||||||
|             Event.new(EventAction.CUSTOM_PREFIX).save() |             Event.new(EventAction.CUSTOM_PREFIX).save() | ||||||
|         self.assertEqual(execute_mock.call_count, 1) |         self.assertEqual(execute_mock.call_count, 1) | ||||||
|  |  | ||||||
|  |     def test_trigger_event_user(self): | ||||||
|  |         """Test trigger with event user""" | ||||||
|  |         user = create_test_user() | ||||||
|  |         transport = NotificationTransport.objects.create(name=generate_id()) | ||||||
|  |         trigger = NotificationRule.objects.create(name=generate_id(), destination_event_user=True) | ||||||
|  |         trigger.transports.add(transport) | ||||||
|  |         trigger.save() | ||||||
|  |         matcher = EventMatcherPolicy.objects.create( | ||||||
|  |             name="matcher", action=EventAction.CUSTOM_PREFIX | ||||||
|  |         ) | ||||||
|  |         PolicyBinding.objects.create(target=trigger, policy=matcher, order=0) | ||||||
|  |  | ||||||
|  |         execute_mock = MagicMock() | ||||||
|  |         with patch("authentik.events.models.NotificationTransport.send", execute_mock): | ||||||
|  |             Event.new(EventAction.CUSTOM_PREFIX).set_user(user).save() | ||||||
|  |         self.assertEqual(execute_mock.call_count, 1) | ||||||
|  |         notification: Notification = execute_mock.call_args[0][0] | ||||||
|  |         self.assertEqual(notification.user, user) | ||||||
|  |  | ||||||
|     def test_trigger_no_group(self): |     def test_trigger_no_group(self): | ||||||
|         """Test trigger without group""" |         """Test trigger without group""" | ||||||
|         trigger = NotificationRule.objects.create(name=generate_id()) |         trigger = NotificationRule.objects.create(name=generate_id()) | ||||||
| @ -76,7 +96,7 @@ class TestEventsNotifications(APITestCase): | |||||||
|         """Test Policy error which would cause recursion""" |         """Test Policy error which would cause recursion""" | ||||||
|         transport = NotificationTransport.objects.create(name=generate_id()) |         transport = NotificationTransport.objects.create(name=generate_id()) | ||||||
|         NotificationRule.objects.filter(name__startswith="default").delete() |         NotificationRule.objects.filter(name__startswith="default").delete() | ||||||
|         trigger = NotificationRule.objects.create(name=generate_id(), group=self.group) |         trigger = NotificationRule.objects.create(name=generate_id(), destination_group=self.group) | ||||||
|         trigger.transports.add(transport) |         trigger.transports.add(transport) | ||||||
|         trigger.save() |         trigger.save() | ||||||
|         matcher = EventMatcherPolicy.objects.create( |         matcher = EventMatcherPolicy.objects.create( | ||||||
| @ -99,7 +119,7 @@ class TestEventsNotifications(APITestCase): | |||||||
|  |  | ||||||
|         transport = NotificationTransport.objects.create(name=generate_id(), send_once=True) |         transport = NotificationTransport.objects.create(name=generate_id(), send_once=True) | ||||||
|         NotificationRule.objects.filter(name__startswith="default").delete() |         NotificationRule.objects.filter(name__startswith="default").delete() | ||||||
|         trigger = NotificationRule.objects.create(name=generate_id(), group=self.group) |         trigger = NotificationRule.objects.create(name=generate_id(), destination_group=self.group) | ||||||
|         trigger.transports.add(transport) |         trigger.transports.add(transport) | ||||||
|         trigger.save() |         trigger.save() | ||||||
|         matcher = EventMatcherPolicy.objects.create( |         matcher = EventMatcherPolicy.objects.create( | ||||||
| @ -123,7 +143,7 @@ class TestEventsNotifications(APITestCase): | |||||||
|             name=generate_id(), webhook_mapping_body=mapping, mode=TransportMode.LOCAL |             name=generate_id(), webhook_mapping_body=mapping, mode=TransportMode.LOCAL | ||||||
|         ) |         ) | ||||||
|         NotificationRule.objects.filter(name__startswith="default").delete() |         NotificationRule.objects.filter(name__startswith="default").delete() | ||||||
|         trigger = NotificationRule.objects.create(name=generate_id(), group=self.group) |         trigger = NotificationRule.objects.create(name=generate_id(), destination_group=self.group) | ||||||
|         trigger.transports.add(transport) |         trigger.transports.add(transport) | ||||||
|         matcher = EventMatcherPolicy.objects.create( |         matcher = EventMatcherPolicy.objects.create( | ||||||
|             name="matcher", action=EventAction.CUSTOM_PREFIX |             name="matcher", action=EventAction.CUSTOM_PREFIX | ||||||
|  | |||||||
| @ -6671,11 +6671,16 @@ | |||||||
|                     "title": "Severity", |                     "title": "Severity", | ||||||
|                     "description": "Controls which severity level the created notifications will have." |                     "description": "Controls which severity level the created notifications will have." | ||||||
|                 }, |                 }, | ||||||
|                 "group": { |                 "destination_group": { | ||||||
|                     "type": "string", |                     "type": "string", | ||||||
|                     "format": "uuid", |                     "format": "uuid", | ||||||
|                     "title": "Group", |                     "title": "Destination group", | ||||||
|                     "description": "Define which group of users this notification should be sent and shown to. If left empty, Notification won't ben sent." |                     "description": "Define which group of users this notification should be sent and shown to. If left empty, Notification won't ben sent." | ||||||
|  |                 }, | ||||||
|  |                 "destination_event_user": { | ||||||
|  |                     "type": "boolean", | ||||||
|  |                     "title": "Destination event user", | ||||||
|  |                     "description": "When enabled, notification will be sent to user the user that triggered the event.When destination_group is configured, notification is sent to both." | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             "required": [] |             "required": [] | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								schema.yml
									
									
									
									
									
								
							| @ -7758,7 +7758,7 @@ paths: | |||||||
|       description: NotificationRule Viewset |       description: NotificationRule Viewset | ||||||
|       parameters: |       parameters: | ||||||
|       - in: query |       - in: query | ||||||
|         name: group__name |         name: destination_group__name | ||||||
|         schema: |         schema: | ||||||
|           type: string |           type: string | ||||||
|       - in: query |       - in: query | ||||||
| @ -49002,18 +49002,23 @@ components: | |||||||
|           - $ref: '#/components/schemas/SeverityEnum' |           - $ref: '#/components/schemas/SeverityEnum' | ||||||
|           description: Controls which severity level the created notifications will |           description: Controls which severity level the created notifications will | ||||||
|             have. |             have. | ||||||
|         group: |         destination_group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           nullable: true |           nullable: true | ||||||
|           description: Define which group of users this notification should be sent |           description: Define which group of users this notification should be sent | ||||||
|             and shown to. If left empty, Notification won't ben sent. |             and shown to. If left empty, Notification won't ben sent. | ||||||
|         group_obj: |         destination_group_obj: | ||||||
|           allOf: |           allOf: | ||||||
|           - $ref: '#/components/schemas/Group' |           - $ref: '#/components/schemas/Group' | ||||||
|           readOnly: true |           readOnly: true | ||||||
|  |         destination_event_user: | ||||||
|  |           type: boolean | ||||||
|  |           description: When enabled, notification will be sent to user the user that | ||||||
|  |             triggered the event.When destination_group is configured, notification | ||||||
|  |             is sent to both. | ||||||
|       required: |       required: | ||||||
|       - group_obj |       - destination_group_obj | ||||||
|       - name |       - name | ||||||
|       - pk |       - pk | ||||||
|     NotificationRuleRequest: |     NotificationRuleRequest: | ||||||
| @ -49036,12 +49041,17 @@ components: | |||||||
|           - $ref: '#/components/schemas/SeverityEnum' |           - $ref: '#/components/schemas/SeverityEnum' | ||||||
|           description: Controls which severity level the created notifications will |           description: Controls which severity level the created notifications will | ||||||
|             have. |             have. | ||||||
|         group: |         destination_group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           nullable: true |           nullable: true | ||||||
|           description: Define which group of users this notification should be sent |           description: Define which group of users this notification should be sent | ||||||
|             and shown to. If left empty, Notification won't ben sent. |             and shown to. If left empty, Notification won't ben sent. | ||||||
|  |         destination_event_user: | ||||||
|  |           type: boolean | ||||||
|  |           description: When enabled, notification will be sent to user the user that | ||||||
|  |             triggered the event.When destination_group is configured, notification | ||||||
|  |             is sent to both. | ||||||
|       required: |       required: | ||||||
|       - name |       - name | ||||||
|     NotificationTransport: |     NotificationTransport: | ||||||
| @ -54126,12 +54136,17 @@ components: | |||||||
|           - $ref: '#/components/schemas/SeverityEnum' |           - $ref: '#/components/schemas/SeverityEnum' | ||||||
|           description: Controls which severity level the created notifications will |           description: Controls which severity level the created notifications will | ||||||
|             have. |             have. | ||||||
|         group: |         destination_group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           nullable: true |           nullable: true | ||||||
|           description: Define which group of users this notification should be sent |           description: Define which group of users this notification should be sent | ||||||
|             and shown to. If left empty, Notification won't ben sent. |             and shown to. If left empty, Notification won't ben sent. | ||||||
|  |         destination_event_user: | ||||||
|  |           type: boolean | ||||||
|  |           description: When enabled, notification will be sent to user the user that | ||||||
|  |             triggered the event.When destination_group is configured, notification | ||||||
|  |             is sent to both. | ||||||
|     PatchedNotificationTransportRequest: |     PatchedNotificationTransportRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: NotificationTransport Serializer |       description: NotificationTransport Serializer | ||||||
|  | |||||||
							
								
								
									
										270
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										270
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -22,7 +22,7 @@ | |||||||
|                 "@floating-ui/dom": "^1.6.11", |                 "@floating-ui/dom": "^1.6.11", | ||||||
|                 "@formatjs/intl-listformat": "^7.7.11", |                 "@formatjs/intl-listformat": "^7.7.11", | ||||||
|                 "@fortawesome/fontawesome-free": "^6.7.2", |                 "@fortawesome/fontawesome-free": "^6.7.2", | ||||||
|                 "@goauthentik/api": "^2025.6.2-1750242193", |                 "@goauthentik/api": "^2025.6.2-1750246811", | ||||||
|                 "@lit/context": "^1.1.2", |                 "@lit/context": "^1.1.2", | ||||||
|                 "@lit/localize": "^0.12.2", |                 "@lit/localize": "^0.12.2", | ||||||
|                 "@lit/reactive-element": "^2.0.4", |                 "@lit/reactive-element": "^2.0.4", | ||||||
| @ -30,6 +30,8 @@ | |||||||
|                 "@mdx-js/mdx": "^3.1.0", |                 "@mdx-js/mdx": "^3.1.0", | ||||||
|                 "@mrmarble/djangoql-completion": "^0.8.3", |                 "@mrmarble/djangoql-completion": "^0.8.3", | ||||||
|                 "@open-wc/lit-helpers": "^0.7.0", |                 "@open-wc/lit-helpers": "^0.7.0", | ||||||
|  |                 "@openlayers-elements/core": "^0.4.0", | ||||||
|  |                 "@openlayers-elements/maps": "^0.4.0", | ||||||
|                 "@patternfly/elements": "^4.1.0", |                 "@patternfly/elements": "^4.1.0", | ||||||
|                 "@patternfly/patternfly": "^4.224.2", |                 "@patternfly/patternfly": "^4.224.2", | ||||||
|                 "@sentry/browser": "^9.30.0", |                 "@sentry/browser": "^9.30.0", | ||||||
| @ -1729,9 +1731,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@goauthentik/api": { |         "node_modules/@goauthentik/api": { | ||||||
|             "version": "2025.6.2-1750242193", |             "version": "2025.6.2-1750246811", | ||||||
|             "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.6.2-1750242193.tgz", |             "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.6.2-1750246811.tgz", | ||||||
|             "integrity": "sha512-yAi3LbxGP1pyu/lV+iiJp2FrIhOa9/nMErdiy4KC2uh63AS74iqNpYb4/IIbkCeT8fbSSNKhAbA6gXE9zFZ6Yg==" |             "integrity": "sha512-ENHEi3kGAodf5tKQb5kziUrT1EcJw3z8tp2mU7LWqNlXr4eoAI15BjDfH5DW56l4jy3xKqTd+R2Ntnj4hiVhHw==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/@goauthentik/core": { |         "node_modules/@goauthentik/core": { | ||||||
|             "resolved": "packages/core", |             "resolved": "packages/core", | ||||||
| @ -2589,6 +2591,48 @@ | |||||||
|                 "@lit/reactive-element": "^1.0.0 || ^2.0.0" |                 "@lit/reactive-element": "^1.0.0 || ^2.0.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/@mapbox/jsonlint-lines-primitives": { | ||||||
|  |             "version": "2.0.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", | ||||||
|  |             "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">= 0.6" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/@mapbox/mapbox-gl-style-spec": { | ||||||
|  |             "version": "13.28.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz", | ||||||
|  |             "integrity": "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==", | ||||||
|  |             "license": "ISC", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "@mapbox/jsonlint-lines-primitives": "~2.0.2", | ||||||
|  |                 "@mapbox/point-geometry": "^0.1.0", | ||||||
|  |                 "@mapbox/unitbezier": "^0.0.0", | ||||||
|  |                 "csscolorparser": "~1.0.2", | ||||||
|  |                 "json-stringify-pretty-compact": "^2.0.0", | ||||||
|  |                 "minimist": "^1.2.6", | ||||||
|  |                 "rw": "^1.3.3", | ||||||
|  |                 "sort-object": "^0.3.2" | ||||||
|  |             }, | ||||||
|  |             "bin": { | ||||||
|  |                 "gl-style-composite": "bin/gl-style-composite.js", | ||||||
|  |                 "gl-style-format": "bin/gl-style-format.js", | ||||||
|  |                 "gl-style-migrate": "bin/gl-style-migrate.js", | ||||||
|  |                 "gl-style-validate": "bin/gl-style-validate.js" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/@mapbox/point-geometry": { | ||||||
|  |             "version": "0.1.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", | ||||||
|  |             "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", | ||||||
|  |             "license": "ISC" | ||||||
|  |         }, | ||||||
|  |         "node_modules/@mapbox/unitbezier": { | ||||||
|  |             "version": "0.0.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", | ||||||
|  |             "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", | ||||||
|  |             "license": "BSD-2-Clause" | ||||||
|  |         }, | ||||||
|         "node_modules/@mdx-js/mdx": { |         "node_modules/@mdx-js/mdx": { | ||||||
|             "version": "3.1.0", |             "version": "3.1.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", | ||||||
| @ -3071,6 +3115,27 @@ | |||||||
|                 "lit": "^2.0.0 || ^3.0.0" |                 "lit": "^2.0.0 || ^3.0.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/@openlayers-elements/core": { | ||||||
|  |             "version": "0.4.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@openlayers-elements/core/-/core-0.4.0.tgz", | ||||||
|  |             "integrity": "sha512-msY2QGYCYf5Zph16j08KszgqtHmMORCK7B5afpe5iM8c3FFSfjijUffiw93MGeowoN4Yo5jfkxuI2plpyidR0A==", | ||||||
|  |             "license": "MIT", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "lit": "^3.1.4", | ||||||
|  |                 "ol": "^7.5.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/@openlayers-elements/maps": { | ||||||
|  |             "version": "0.4.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@openlayers-elements/maps/-/maps-0.4.0.tgz", | ||||||
|  |             "integrity": "sha512-uxGW3Lt1BVA8eC0HykXLZA4a3EfCU44FdGaudC4Xu0s+XYPOEPxCGLDCsWSuy67NvEUTFb+odu6mRDLofxdquA==", | ||||||
|  |             "license": "MIT", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "@openlayers-elements/core": "^0.4.0", | ||||||
|  |                 "lit": "^3.1.4", | ||||||
|  |                 "ol": "^7.5.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/@opentelemetry/api": { |         "node_modules/@opentelemetry/api": { | ||||||
|             "version": "1.9.0", |             "version": "1.9.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", |             "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", | ||||||
| @ -3971,6 +4036,12 @@ | |||||||
|                 "lit": "^3.2.1" |                 "lit": "^3.2.1" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/@petamoriken/float16": { | ||||||
|  |             "version": "3.9.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", | ||||||
|  |             "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", | ||||||
|  |             "license": "MIT" | ||||||
|  |         }, | ||||||
|         "node_modules/@pkgjs/parseargs": { |         "node_modules/@pkgjs/parseargs": { | ||||||
|             "version": "0.11.0", |             "version": "0.11.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", |             "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", | ||||||
| @ -12178,6 +12249,12 @@ | |||||||
|             "dev": true, |             "dev": true, | ||||||
|             "license": "MIT" |             "license": "MIT" | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/csscolorparser": { | ||||||
|  |             "version": "1.0.3", | ||||||
|  |             "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", | ||||||
|  |             "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", | ||||||
|  |             "license": "MIT" | ||||||
|  |         }, | ||||||
|         "node_modules/csstype": { |         "node_modules/csstype": { | ||||||
|             "version": "3.1.3", |             "version": "3.1.3", | ||||||
|             "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", |             "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", | ||||||
| @ -13381,6 +13458,12 @@ | |||||||
|                 "node": ">= 0.4" |                 "node": ">= 0.4" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/earcut": { | ||||||
|  |             "version": "2.2.4", | ||||||
|  |             "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", | ||||||
|  |             "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", | ||||||
|  |             "license": "ISC" | ||||||
|  |         }, | ||||||
|         "node_modules/eastasianwidth": { |         "node_modules/eastasianwidth": { | ||||||
|             "version": "0.2.0", |             "version": "0.2.0", | ||||||
|             "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", |             "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", | ||||||
| @ -15962,6 +16045,43 @@ | |||||||
|                 "node": ">=6.9.0" |                 "node": ">=6.9.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/geotiff": { | ||||||
|  |             "version": "2.1.3", | ||||||
|  |             "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.3.tgz", | ||||||
|  |             "integrity": "sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==", | ||||||
|  |             "license": "MIT", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "@petamoriken/float16": "^3.4.7", | ||||||
|  |                 "lerc": "^3.0.0", | ||||||
|  |                 "pako": "^2.0.4", | ||||||
|  |                 "parse-headers": "^2.0.2", | ||||||
|  |                 "quick-lru": "^6.1.1", | ||||||
|  |                 "web-worker": "^1.2.0", | ||||||
|  |                 "xml-utils": "^1.0.2", | ||||||
|  |                 "zstddec": "^0.1.0" | ||||||
|  |             }, | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">=10.19" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/geotiff/node_modules/pako": { | ||||||
|  |             "version": "2.1.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", | ||||||
|  |             "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", | ||||||
|  |             "license": "(MIT AND Zlib)" | ||||||
|  |         }, | ||||||
|  |         "node_modules/geotiff/node_modules/quick-lru": { | ||||||
|  |             "version": "6.1.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", | ||||||
|  |             "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", | ||||||
|  |             "license": "MIT", | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">=12" | ||||||
|  |             }, | ||||||
|  |             "funding": { | ||||||
|  |                 "url": "https://github.com/sponsors/sindresorhus" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/get-caller-file": { |         "node_modules/get-caller-file": { | ||||||
|             "version": "2.0.5", |             "version": "2.0.5", | ||||||
|             "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", |             "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", | ||||||
| @ -18528,6 +18648,12 @@ | |||||||
|             "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", |             "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", | ||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/json-stringify-pretty-compact": { | ||||||
|  |             "version": "2.0.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", | ||||||
|  |             "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==", | ||||||
|  |             "license": "MIT" | ||||||
|  |         }, | ||||||
|         "node_modules/json5": { |         "node_modules/json5": { | ||||||
|             "version": "2.2.3", |             "version": "2.2.3", | ||||||
|             "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", |             "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", | ||||||
| @ -18967,6 +19093,12 @@ | |||||||
|                 "safe-buffer": "~5.1.0" |                 "safe-buffer": "~5.1.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/lerc": { | ||||||
|  |             "version": "3.0.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", | ||||||
|  |             "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", | ||||||
|  |             "license": "Apache-2.0" | ||||||
|  |         }, | ||||||
|         "node_modules/leven": { |         "node_modules/leven": { | ||||||
|             "version": "3.1.0", |             "version": "3.1.0", | ||||||
|             "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", |             "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", | ||||||
| @ -19496,6 +19628,12 @@ | |||||||
|             "dev": true, |             "dev": true, | ||||||
|             "license": "MIT" |             "license": "MIT" | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/mapbox-to-css-font": { | ||||||
|  |             "version": "2.4.5", | ||||||
|  |             "resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-2.4.5.tgz", | ||||||
|  |             "integrity": "sha512-VJ6nB8emkO9VODI0Fk+TQ/0zKBTqmf/Pkt8Xv0kHstoc0iXRajA00DAid4Kc3K5xeFIOoiZrVxijEzj0GLVO2w==", | ||||||
|  |             "license": "BSD-2-Clause" | ||||||
|  |         }, | ||||||
|         "node_modules/markdown-extensions": { |         "node_modules/markdown-extensions": { | ||||||
|             "version": "2.0.0", |             "version": "2.0.0", | ||||||
|             "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", |             "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", | ||||||
| @ -20924,7 +21062,6 @@ | |||||||
|             "version": "1.2.8", |             "version": "1.2.8", | ||||||
|             "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", |             "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", | ||||||
|             "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", |             "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", | ||||||
|             "dev": true, |  | ||||||
|             "funding": { |             "funding": { | ||||||
|                 "url": "https://github.com/sponsors/ljharb" |                 "url": "https://github.com/sponsors/ljharb" | ||||||
|             } |             } | ||||||
| @ -22048,6 +22185,34 @@ | |||||||
|             "dev": true, |             "dev": true, | ||||||
|             "optional": true |             "optional": true | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/ol": { | ||||||
|  |             "version": "7.5.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/ol/-/ol-7.5.2.tgz", | ||||||
|  |             "integrity": "sha512-HJbb3CxXrksM6ct367LsP3N+uh+iBBMdP3DeGGipdV9YAYTP0vTJzqGnoqQ6C2IW4qf8krw9yuyQbc9fjOIaOQ==", | ||||||
|  |             "license": "BSD-2-Clause", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "earcut": "^2.2.3", | ||||||
|  |                 "geotiff": "^2.0.7", | ||||||
|  |                 "ol-mapbox-style": "^10.1.0", | ||||||
|  |                 "pbf": "3.2.1", | ||||||
|  |                 "rbush": "^3.0.1" | ||||||
|  |             }, | ||||||
|  |             "funding": { | ||||||
|  |                 "type": "opencollective", | ||||||
|  |                 "url": "https://opencollective.com/openlayers" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/ol-mapbox-style": { | ||||||
|  |             "version": "10.7.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-10.7.0.tgz", | ||||||
|  |             "integrity": "sha512-S/UdYBuOjrotcR95Iq9AejGYbifKeZE85D9VtH11ryJLQPTZXZSW1J5bIXcr4AlAH6tyjPPHTK34AdkwB32Myw==", | ||||||
|  |             "license": "BSD-2-Clause", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "@mapbox/mapbox-gl-style-spec": "^13.23.1", | ||||||
|  |                 "mapbox-to-css-font": "^2.4.1", | ||||||
|  |                 "ol": "^7.3.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/on-finished": { |         "node_modules/on-finished": { | ||||||
|             "version": "2.4.1", |             "version": "2.4.1", | ||||||
|             "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", |             "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", | ||||||
| @ -22378,6 +22543,12 @@ | |||||||
|             "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", |             "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", | ||||||
|             "license": "MIT" |             "license": "MIT" | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/parse-headers": { | ||||||
|  |             "version": "2.0.6", | ||||||
|  |             "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", | ||||||
|  |             "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", | ||||||
|  |             "license": "MIT" | ||||||
|  |         }, | ||||||
|         "node_modules/parse-ms": { |         "node_modules/parse-ms": { | ||||||
|             "version": "4.0.0", |             "version": "4.0.0", | ||||||
|             "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", |             "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", | ||||||
| @ -22528,6 +22699,19 @@ | |||||||
|                 "node": ">= 14.16" |                 "node": ">= 14.16" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/pbf": { | ||||||
|  |             "version": "3.2.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", | ||||||
|  |             "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", | ||||||
|  |             "license": "BSD-3-Clause", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "ieee754": "^1.1.12", | ||||||
|  |                 "resolve-protobuf-schema": "^2.1.0" | ||||||
|  |             }, | ||||||
|  |             "bin": { | ||||||
|  |                 "pbf": "bin/pbf" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/peek-readable": { |         "node_modules/peek-readable": { | ||||||
|             "version": "5.4.2", |             "version": "5.4.2", | ||||||
|             "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz", |             "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz", | ||||||
| @ -22989,6 +23173,12 @@ | |||||||
|                 "url": "https://github.com/sponsors/wooorm" |                 "url": "https://github.com/sponsors/wooorm" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/protocol-buffers-schema": { | ||||||
|  |             "version": "3.6.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", | ||||||
|  |             "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", | ||||||
|  |             "license": "MIT" | ||||||
|  |         }, | ||||||
|         "node_modules/proxy-agent": { |         "node_modules/proxy-agent": { | ||||||
|             "version": "6.5.0", |             "version": "6.5.0", | ||||||
|             "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", |             "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", | ||||||
| @ -23204,6 +23394,12 @@ | |||||||
|                 "url": "https://github.com/sponsors/sindresorhus" |                 "url": "https://github.com/sponsors/sindresorhus" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/quickselect": { | ||||||
|  |             "version": "2.0.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", | ||||||
|  |             "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", | ||||||
|  |             "license": "ISC" | ||||||
|  |         }, | ||||||
|         "node_modules/ramda": { |         "node_modules/ramda": { | ||||||
|             "version": "0.30.1", |             "version": "0.30.1", | ||||||
|             "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", |             "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", | ||||||
| @ -23325,6 +23521,15 @@ | |||||||
|                 "node": ">= 0.8" |                 "node": ">= 0.8" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/rbush": { | ||||||
|  |             "version": "3.0.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", | ||||||
|  |             "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", | ||||||
|  |             "license": "MIT", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "quickselect": "^2.0.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/rc9": { |         "node_modules/rc9": { | ||||||
|             "version": "2.1.2", |             "version": "2.1.2", | ||||||
|             "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", |             "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", | ||||||
| @ -24242,6 +24447,15 @@ | |||||||
|                 "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" |                 "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/resolve-protobuf-schema": { | ||||||
|  |             "version": "2.1.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", | ||||||
|  |             "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", | ||||||
|  |             "license": "MIT", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "protocol-buffers-schema": "^3.3.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/resolve.exports": { |         "node_modules/resolve.exports": { | ||||||
|             "version": "2.0.3", |             "version": "2.0.3", | ||||||
|             "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", |             "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", | ||||||
| @ -25083,6 +25297,22 @@ | |||||||
|                 "node": ">= 14" |                 "node": ">= 14" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/sort-asc": { | ||||||
|  |             "version": "0.1.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", | ||||||
|  |             "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==", | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">=0.10.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "node_modules/sort-desc": { | ||||||
|  |             "version": "0.1.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", | ||||||
|  |             "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==", | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">=0.10.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/sort-keys": { |         "node_modules/sort-keys": { | ||||||
|             "version": "1.1.2", |             "version": "1.1.2", | ||||||
|             "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", |             "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", | ||||||
| @ -25119,6 +25349,18 @@ | |||||||
|                 "node": ">=0.10.0" |                 "node": ">=0.10.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/sort-object": { | ||||||
|  |             "version": "0.3.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", | ||||||
|  |             "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", | ||||||
|  |             "dependencies": { | ||||||
|  |                 "sort-asc": "^0.1.0", | ||||||
|  |                 "sort-desc": "^0.1.1" | ||||||
|  |             }, | ||||||
|  |             "engines": { | ||||||
|  |                 "node": ">=0.10.0" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "node_modules/sort-object-keys": { |         "node_modules/sort-object-keys": { | ||||||
|             "version": "1.1.3", |             "version": "1.1.3", | ||||||
|             "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", |             "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", | ||||||
| @ -28351,6 +28593,12 @@ | |||||||
|             "license": "MIT", |             "license": "MIT", | ||||||
|             "optional": true |             "optional": true | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/web-worker": { | ||||||
|  |             "version": "1.5.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", | ||||||
|  |             "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", | ||||||
|  |             "license": "Apache-2.0" | ||||||
|  |         }, | ||||||
|         "node_modules/webauthn-polyfills": { |         "node_modules/webauthn-polyfills": { | ||||||
|             "version": "0.1.7", |             "version": "0.1.7", | ||||||
|             "resolved": "https://registry.npmjs.org/webauthn-polyfills/-/webauthn-polyfills-0.1.7.tgz", |             "resolved": "https://registry.npmjs.org/webauthn-polyfills/-/webauthn-polyfills-0.1.7.tgz", | ||||||
| @ -28925,6 +29173,12 @@ | |||||||
|                 "repeat-string": "^1.5.2" |                 "repeat-string": "^1.5.2" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/xml-utils": { | ||||||
|  |             "version": "1.10.2", | ||||||
|  |             "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz", | ||||||
|  |             "integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==", | ||||||
|  |             "license": "CC0-1.0" | ||||||
|  |         }, | ||||||
|         "node_modules/xtend": { |         "node_modules/xtend": { | ||||||
|             "version": "4.0.2", |             "version": "4.0.2", | ||||||
|             "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", |             "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", | ||||||
| @ -29182,6 +29436,12 @@ | |||||||
|                 "zod": "^3.18.0" |                 "zod": "^3.18.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/zstddec": { | ||||||
|  |             "version": "0.1.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", | ||||||
|  |             "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==", | ||||||
|  |             "license": "MIT AND BSD-3-Clause" | ||||||
|  |         }, | ||||||
|         "node_modules/zwitch": { |         "node_modules/zwitch": { | ||||||
|             "version": "2.0.4", |             "version": "2.0.4", | ||||||
|             "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", |             "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", | ||||||
|  | |||||||
| @ -93,7 +93,7 @@ | |||||||
|         "@floating-ui/dom": "^1.6.11", |         "@floating-ui/dom": "^1.6.11", | ||||||
|         "@formatjs/intl-listformat": "^7.7.11", |         "@formatjs/intl-listformat": "^7.7.11", | ||||||
|         "@fortawesome/fontawesome-free": "^6.7.2", |         "@fortawesome/fontawesome-free": "^6.7.2", | ||||||
|         "@goauthentik/api": "^2025.6.2-1750242193", |         "@goauthentik/api": "^2025.6.2-1750246811", | ||||||
|         "@lit/context": "^1.1.2", |         "@lit/context": "^1.1.2", | ||||||
|         "@lit/localize": "^0.12.2", |         "@lit/localize": "^0.12.2", | ||||||
|         "@lit/reactive-element": "^2.0.4", |         "@lit/reactive-element": "^2.0.4", | ||||||
| @ -101,6 +101,8 @@ | |||||||
|         "@mdx-js/mdx": "^3.1.0", |         "@mdx-js/mdx": "^3.1.0", | ||||||
|         "@mrmarble/djangoql-completion": "^0.8.3", |         "@mrmarble/djangoql-completion": "^0.8.3", | ||||||
|         "@open-wc/lit-helpers": "^0.7.0", |         "@open-wc/lit-helpers": "^0.7.0", | ||||||
|  |         "@openlayers-elements/core": "^0.4.0", | ||||||
|  |         "@openlayers-elements/maps": "^0.4.0", | ||||||
|         "@patternfly/elements": "^4.1.0", |         "@patternfly/elements": "^4.1.0", | ||||||
|         "@patternfly/patternfly": "^4.224.2", |         "@patternfly/patternfly": "^4.224.2", | ||||||
|         "@sentry/browser": "^9.30.0", |         "@sentry/browser": "^9.30.0", | ||||||
|  | |||||||
| @ -1,3 +1,7 @@ | |||||||
|  | import "#elements/Tabs"; | ||||||
|  | import { WithLicenseSummary } from "#elements/mixins/license"; | ||||||
|  | import { updateURLParams } from "#elements/router/RouteMatch"; | ||||||
|  | import "@goauthentik/admin/events/EventMap"; | ||||||
| import "@goauthentik/admin/events/EventVolumeChart"; | import "@goauthentik/admin/events/EventVolumeChart"; | ||||||
| import { EventGeo, EventUser } from "@goauthentik/admin/events/utils"; | import { EventGeo, EventUser } from "@goauthentik/admin/events/utils"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| @ -15,10 +19,12 @@ import { msg } from "@lit/localize"; | |||||||
| import { CSSResult, TemplateResult, css, html } from "lit"; | import { CSSResult, TemplateResult, css, html } from "lit"; | ||||||
| import { customElement, property } from "lit/decorators.js"; | import { customElement, property } from "lit/decorators.js"; | ||||||
|  |  | ||||||
| import { Event, EventsApi } from "@goauthentik/api"; | import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; | ||||||
|  |  | ||||||
|  | import { Event, EventsApi, LicenseSummaryStatusEnum } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| @customElement("ak-event-list") | @customElement("ak-event-list") | ||||||
| export class EventListPage extends TablePage<Event> { | export class EventListPage extends WithLicenseSummary(TablePage<Event>) { | ||||||
|     expandable = true; |     expandable = true; | ||||||
|     supportsQL = true; |     supportsQL = true; | ||||||
|  |  | ||||||
| @ -39,11 +45,15 @@ export class EventListPage extends TablePage<Event> { | |||||||
|     order = "-created"; |     order = "-created"; | ||||||
|  |  | ||||||
|     static get styles(): CSSResult[] { |     static get styles(): CSSResult[] { | ||||||
|         return super.styles.concat(css` |         // @ts-expect-error | ||||||
|  |         return super.styles.concat( | ||||||
|  |             PFGrid, | ||||||
|  |             css` | ||||||
|                 .pf-m-no-padding-bottom { |                 .pf-m-no-padding-bottom { | ||||||
|                     padding-bottom: 0; |                     padding-bottom: 0; | ||||||
|                 } |                 } | ||||||
|         `); |             `, | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async apiEndpoint(): Promise<PaginatedResponse<Event>> { |     async apiEndpoint(): Promise<PaginatedResponse<Event>> { | ||||||
| @ -62,16 +72,39 @@ export class EventListPage extends TablePage<Event> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderSectionBefore(): TemplateResult { |     renderSectionBefore(): TemplateResult { | ||||||
|         return html` |         if (this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed) { | ||||||
|             <div class="pf-c-page__main-section pf-m-no-padding-bottom"> |             return html`<div | ||||||
|  |                 class="pf-l-grid pf-m-gutter pf-c-page__main-section pf-m-no-padding-bottom" | ||||||
|  |             > | ||||||
|                 <ak-events-volume-chart |                 <ak-events-volume-chart | ||||||
|  |                     class="pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl " | ||||||
|                     .query=${{ |                     .query=${{ | ||||||
|                         page: this.page, |                         page: this.page, | ||||||
|                         search: this.search, |                         search: this.search, | ||||||
|                     }} |                     }} | ||||||
|  |                     with-map | ||||||
|                 ></ak-events-volume-chart> |                 ></ak-events-volume-chart> | ||||||
|             </div> |                 <ak-events-map | ||||||
|         `; |                     class="pf-l-grid__item pf-m-12-col pf-m-8-col-on-xl pf-m-8-col-on-2xl " | ||||||
|  |                     .events=${this.data} | ||||||
|  |                     @select-event=${(ev: CustomEvent<{ eventId: string }>) => { | ||||||
|  |                         this.search = `event_uuid = "${ev.detail.eventId}"`; | ||||||
|  |                         this.page = 1; | ||||||
|  |                         updateURLParams({ | ||||||
|  |                             search: this.search, | ||||||
|  |                             tablePage: this.page, | ||||||
|  |                         }); | ||||||
|  |                         this.fetch(); | ||||||
|  |                     }} | ||||||
|  |                 ></ak-events-map> | ||||||
|  |             </div>`; | ||||||
|  |         } | ||||||
|  |         return html`<ak-events-volume-chart | ||||||
|  |             .query=${{ | ||||||
|  |                 page: this.page, | ||||||
|  |                 search: this.search, | ||||||
|  |             }} | ||||||
|  |         ></ak-events-volume-chart>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     row(item: EventWithContext): SlottedTemplateResult[] { |     row(item: EventWithContext): SlottedTemplateResult[] { | ||||||
|  | |||||||
							
								
								
									
										139
									
								
								web/src/admin/events/EventMap.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								web/src/admin/events/EventMap.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | |||||||
|  | import { EventWithContext } from "#common/events"; | ||||||
|  | import { globalAK } from "#common/global"; | ||||||
|  | import { PaginatedResponse } from "#elements/table/Table"; | ||||||
|  | import { AKElement } from "@goauthentik/elements/Base"; | ||||||
|  | import "@openlayers-elements/core/ol-layer-vector"; | ||||||
|  | import type OlLayerVector from "@openlayers-elements/core/ol-layer-vector"; | ||||||
|  | import "@openlayers-elements/core/ol-map"; | ||||||
|  | import type OlMap from "@openlayers-elements/core/ol-map"; | ||||||
|  | import "@openlayers-elements/maps/ol-layer-openstreetmap"; | ||||||
|  | import "@openlayers-elements/maps/ol-select"; | ||||||
|  | import Feature from "ol/Feature"; | ||||||
|  | import { Point } from "ol/geom"; | ||||||
|  | import { fromLonLat } from "ol/proj"; | ||||||
|  | import Icon from "ol/style/Icon"; | ||||||
|  | import Style from "ol/style/Style"; | ||||||
|  |  | ||||||
|  | import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit"; | ||||||
|  | import { customElement, property, query } from "lit/decorators.js"; | ||||||
|  |  | ||||||
|  | import PFCard from "@patternfly/patternfly/components/Card/card.css"; | ||||||
|  | import PFBase from "@patternfly/patternfly/patternfly-base.css"; | ||||||
|  |  | ||||||
|  | import { Event } from "@goauthentik/api"; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @event {select-event} - Fired when an event is selected on the map. ID of the event is contained | ||||||
|  |  *      in the `Event.detail` field. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | @customElement("ak-events-map") | ||||||
|  | export class EventMap extends AKElement { | ||||||
|  |     @property({ attribute: false }) | ||||||
|  |     events?: PaginatedResponse<Event>; | ||||||
|  |  | ||||||
|  |     @query("ol-layer-vector") | ||||||
|  |     vectorLayer?: OlLayerVector; | ||||||
|  |  | ||||||
|  |     @query("ol-map") | ||||||
|  |     map?: OlMap; | ||||||
|  |  | ||||||
|  |     @property({ type: Number }) | ||||||
|  |     zoomPaddingPx = 100; | ||||||
|  |  | ||||||
|  |     static get styles(): CSSResult[] { | ||||||
|  |         return [ | ||||||
|  |             PFBase, | ||||||
|  |             PFCard, | ||||||
|  |             css` | ||||||
|  |                 .pf-c-card, | ||||||
|  |                 ol-map { | ||||||
|  |                     height: 24rem; | ||||||
|  |                 } | ||||||
|  |                 :host([theme="dark"]) ol-map { | ||||||
|  |                     filter: invert(100%) hue-rotate(180deg); | ||||||
|  |                 } | ||||||
|  |             `, | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     updated(_changedProperties: PropertyValues<this>): void { | ||||||
|  |         if (!_changedProperties.has("events")) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (!this.vectorLayer?.source || !this.map?.map) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // Remove all existing points | ||||||
|  |         this.vectorLayer.source.clear(); | ||||||
|  |         // Re-add them | ||||||
|  |         this.events?.results | ||||||
|  |             .filter((event) => { | ||||||
|  |                 if (!Object.hasOwn(event.context, "geo")) { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 const geo = (event as EventWithContext).context.geo; | ||||||
|  |                 if (!geo?.lat || !geo.long) { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 return true; | ||||||
|  |             }) | ||||||
|  |             .forEach((event) => { | ||||||
|  |                 const geo = (event as EventWithContext).context.geo!; | ||||||
|  |                 const point = new Point(fromLonLat([geo.long!, geo.lat!])); | ||||||
|  |                 const feature = new Feature({ | ||||||
|  |                     geometry: point, | ||||||
|  |                 }); | ||||||
|  |                 feature.setStyle( | ||||||
|  |                     new Style({ | ||||||
|  |                         image: new Icon({ | ||||||
|  |                             anchor: [0.5, 1], | ||||||
|  |                             offset: [0, 0], | ||||||
|  |                             opacity: 1, | ||||||
|  |                             scale: 1, | ||||||
|  |                             rotateWithView: false, | ||||||
|  |                             rotation: 0, | ||||||
|  |                             src: `${globalAK().api.base}static/dist/assets/images/map_pin.svg`, | ||||||
|  |                         }), | ||||||
|  |                     }), | ||||||
|  |                 ); | ||||||
|  |                 feature.setId(event.pk); | ||||||
|  |                 this.vectorLayer?.source?.addFeature(feature); | ||||||
|  |             }); | ||||||
|  |         // Zoom to show points better | ||||||
|  |         this.map.map.getView().fit(this.vectorLayer.source.getExtent(), { | ||||||
|  |             padding: [ | ||||||
|  |                 this.zoomPaddingPx, | ||||||
|  |                 this.zoomPaddingPx, | ||||||
|  |                 this.zoomPaddingPx, | ||||||
|  |                 this.zoomPaddingPx, | ||||||
|  |             ], | ||||||
|  |             duration: 500, | ||||||
|  |             maxZoom: 4.5, | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     render(): TemplateResult { | ||||||
|  |         return html`<div class="pf-c-card"> | ||||||
|  |             <ol-map> | ||||||
|  |                 <ol-select | ||||||
|  |                     @feature-selected=${(ev: CustomEvent<{ feature: Feature }>) => { | ||||||
|  |                         const eventId = ev.detail.feature.getId(); | ||||||
|  |                         this.dispatchEvent( | ||||||
|  |                             new CustomEvent("select-event", { | ||||||
|  |                                 composed: true, | ||||||
|  |                                 bubbles: true, | ||||||
|  |                                 detail: { | ||||||
|  |                                     eventId: eventId, | ||||||
|  |                                 }, | ||||||
|  |                             }), | ||||||
|  |                         ); | ||||||
|  |                     }} | ||||||
|  |                 ></ol-select> | ||||||
|  |                 <ol-layer-openstreetmap></ol-layer-openstreetmap> | ||||||
|  |                 <ol-layer-vector></ol-layer-vector> | ||||||
|  |             </ol-map> | ||||||
|  |         </div>`; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -11,11 +11,14 @@ import { EventVolume, EventsApi, EventsEventsListRequest } from "@goauthentik/ap | |||||||
|  |  | ||||||
| @customElement("ak-events-volume-chart") | @customElement("ak-events-volume-chart") | ||||||
| export class EventVolumeChart extends EventChart { | export class EventVolumeChart extends EventChart { | ||||||
|  |     @property({ attribute: "with-map", type: Boolean }) | ||||||
|  |     withMap = false; | ||||||
|  |  | ||||||
|     _query?: EventsEventsListRequest; |     _query?: EventsEventsListRequest; | ||||||
|  |  | ||||||
|     @property({ attribute: false }) |     @property({ attribute: false }) | ||||||
|     set query(value: EventsEventsListRequest | undefined) { |     set query(value: EventsEventsListRequest | undefined) { | ||||||
|         if (JSON.stringify(this._query) === JSON.stringify(value)) return; |         if (JSON.stringify(value) !== JSON.stringify(this._query)) return; | ||||||
|         this._query = value; |         this._query = value; | ||||||
|         this.refreshHandler(); |         this.refreshHandler(); | ||||||
|     } |     } | ||||||
| @ -24,6 +27,9 @@ export class EventVolumeChart extends EventChart { | |||||||
|         return super.styles.concat( |         return super.styles.concat( | ||||||
|             PFCard, |             PFCard, | ||||||
|             css` |             css` | ||||||
|  |                 :host([with-map]) .pf-c-card { | ||||||
|  |                     height: 24rem; | ||||||
|  |                 } | ||||||
|                 .pf-c-card { |                 .pf-c-card { | ||||||
|                     height: 20rem; |                     height: 20rem; | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -66,7 +66,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> { | |||||||
|                     required |                     required | ||||||
|                 /> |                 /> | ||||||
|             </ak-form-element-horizontal> |             </ak-form-element-horizontal> | ||||||
|             <ak-form-element-horizontal label=${msg("Group")} name="group"> |             <ak-form-element-horizontal label=${msg("Group")} name="destinationGroup"> | ||||||
|                 <ak-search-select |                 <ak-search-select | ||||||
|                     .fetchObjects=${async (query?: string): Promise<Group[]> => { |                     .fetchObjects=${async (query?: string): Promise<Group[]> => { | ||||||
|                         const args: CoreGroupsListRequest = { |                         const args: CoreGroupsListRequest = { | ||||||
| @ -86,14 +86,44 @@ export class RuleForm extends ModelForm<NotificationRule, string> { | |||||||
|                         return group?.pk; |                         return group?.pk; | ||||||
|                     }} |                     }} | ||||||
|                     .selected=${(group: Group): boolean => { |                     .selected=${(group: Group): boolean => { | ||||||
|                         return group.pk === this.instance?.group; |                         return group.pk === this.instance?.destinationGroup; | ||||||
|                     }} |                     }} | ||||||
|                     blankable |                     blankable | ||||||
|                 > |                 > | ||||||
|                 </ak-search-select> |                 </ak-search-select> | ||||||
|  |                 <p class="pf-c-form__helper-text"> | ||||||
|  |                     ${msg("Select the group of users which the alerts are sent to. ")} | ||||||
|  |                 </p> | ||||||
|                 <p class="pf-c-form__helper-text"> |                 <p class="pf-c-form__helper-text"> | ||||||
|                     ${msg( |                     ${msg( | ||||||
|                         "Select the group of users which the alerts are sent to. If no group is selected the rule is disabled.", |                         "If no group is selected and 'Send notification to event user' is disabled the rule is disabled. ", | ||||||
|  |                     )} | ||||||
|  |                 </p> | ||||||
|  |             </ak-form-element-horizontal> | ||||||
|  |             <ak-form-element-horizontal name="destinationEventUser"> | ||||||
|  |                 <label class="pf-c-switch"> | ||||||
|  |                     <input | ||||||
|  |                         class="pf-c-switch__input" | ||||||
|  |                         type="checkbox" | ||||||
|  |                         ?checked=${this.instance?.destinationEventUser ?? false} | ||||||
|  |                     /> | ||||||
|  |                     <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 class="pf-c-switch__label" | ||||||
|  |                         >${msg("Send notification to event user")}</span | ||||||
|  |                     > | ||||||
|  |                 </label> | ||||||
|  |                 <p class="pf-c-form__helper-text"> | ||||||
|  |                     ${msg( | ||||||
|  |                         "When enabled, notification will be sent to the user that triggered the event in addition to any users in the group above. The event user will always be the first user, to send a notification only to the event user enabled 'Send once' in the notification transport.", | ||||||
|  |                     )} | ||||||
|  |                 </p> | ||||||
|  |                 <p class="pf-c-form__helper-text"> | ||||||
|  |                     ${msg( | ||||||
|  |                         "If no group is selected and 'Send notification to event user' is disabled the rule is disabled. ", | ||||||
|                     )} |                     )} | ||||||
|                 </p> |                 </p> | ||||||
|             </ak-form-element-horizontal> |             </ak-form-element-horizontal> | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ import "@goauthentik/admin/policies/BoundPoliciesList"; | |||||||
| import "@goauthentik/admin/rbac/ObjectPermissionModal"; | import "@goauthentik/admin/rbac/ObjectPermissionModal"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { severityToLabel } from "@goauthentik/common/labels"; | import { severityToLabel } from "@goauthentik/common/labels"; | ||||||
|  | import "@goauthentik/components/ak-status-label"; | ||||||
| import "@goauthentik/elements/buttons/SpinnerButton"; | import "@goauthentik/elements/buttons/SpinnerButton"; | ||||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| import "@goauthentik/elements/forms/ModalForm"; | import "@goauthentik/elements/forms/ModalForm"; | ||||||
| @ -51,6 +52,7 @@ export class RuleListPage extends TablePage<NotificationRule> { | |||||||
|  |  | ||||||
|     columns(): TableColumn[] { |     columns(): TableColumn[] { | ||||||
|         return [ |         return [ | ||||||
|  |             new TableColumn(msg("Enabled")), | ||||||
|             new TableColumn(msg("Name"), "name"), |             new TableColumn(msg("Name"), "name"), | ||||||
|             new TableColumn(msg("Severity"), "severity"), |             new TableColumn(msg("Severity"), "severity"), | ||||||
|             new TableColumn(msg("Sent to group"), "group"), |             new TableColumn(msg("Sent to group"), "group"), | ||||||
| @ -81,12 +83,16 @@ export class RuleListPage extends TablePage<NotificationRule> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     row(item: NotificationRule): TemplateResult[] { |     row(item: NotificationRule): TemplateResult[] { | ||||||
|  |         const enabled = !!item.destinationGroupObj || item.destinationEventUser; | ||||||
|         return [ |         return [ | ||||||
|  |             html`<ak-status-label type="warning" ?good=${enabled}></ak-status-label>`, | ||||||
|             html`${item.name}`, |             html`${item.name}`, | ||||||
|             html`${severityToLabel(item.severity)}`, |             html`${severityToLabel(item.severity)}`, | ||||||
|             html`${item.groupObj |             html`${item.destinationGroupObj | ||||||
|                 ? html`<a href="#/identity/groups/${item.groupObj.pk}">${item.groupObj.name}</a>` |                 ? html`<a href="#/identity/groups/${item.destinationGroupObj.pk}" | ||||||
|                 : msg("None (rule disabled)")}`, |                       >${item.destinationGroupObj.name}</a | ||||||
|  |                   >` | ||||||
|  |                 : msg("-")}`, | ||||||
|             html`<ak-forms-modal> |             html`<ak-forms-modal> | ||||||
|                     <span slot="submit"> ${msg("Update")} </span> |                     <span slot="submit"> ${msg("Update")} </span> | ||||||
|                     <span slot="header"> ${msg("Update Notification Rule")} </span> |                     <span slot="header"> ${msg("Update Notification Rule")} </span> | ||||||
|  | |||||||
							
								
								
									
										77
									
								
								web/src/assets/images/map_pin.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								web/src/assets/images/map_pin.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||||
|  | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||||
|  |  | ||||||
|  | <svg | ||||||
|  |    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||||
|  |    xmlns:cc="http://creativecommons.org/ns#" | ||||||
|  |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||||
|  |    xmlns:svg="http://www.w3.org/2000/svg" | ||||||
|  |    xmlns="http://www.w3.org/2000/svg" | ||||||
|  |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||||
|  |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||||
|  |    width="5.6444445mm" | ||||||
|  |    height="9.847393mm" | ||||||
|  |    viewBox="0 0 20 34.892337" | ||||||
|  |    id="svg3455" | ||||||
|  |    version="1.1" | ||||||
|  |    inkscape:version="0.91 r13725" | ||||||
|  |    sodipodi:docname="Map Pin.svg"> | ||||||
|  |   <defs | ||||||
|  |      id="defs3457" /> | ||||||
|  |   <sodipodi:namedview | ||||||
|  |      id="base" | ||||||
|  |      pagecolor="#ffffff" | ||||||
|  |      bordercolor="#666666" | ||||||
|  |      borderopacity="1.0" | ||||||
|  |      inkscape:pageopacity="0.0" | ||||||
|  |      inkscape:pageshadow="2" | ||||||
|  |      inkscape:zoom="12.181359" | ||||||
|  |      inkscape:cx="8.4346812" | ||||||
|  |      inkscape:cy="14.715224" | ||||||
|  |      inkscape:document-units="px" | ||||||
|  |      inkscape:current-layer="layer1" | ||||||
|  |      showgrid="false" | ||||||
|  |      inkscape:window-width="1024" | ||||||
|  |      inkscape:window-height="705" | ||||||
|  |      inkscape:window-x="-4" | ||||||
|  |      inkscape:window-y="-4" | ||||||
|  |      inkscape:window-maximized="1" | ||||||
|  |      fit-margin-top="0" | ||||||
|  |      fit-margin-left="0" | ||||||
|  |      fit-margin-right="0" | ||||||
|  |      fit-margin-bottom="0" /> | ||||||
|  |   <metadata | ||||||
|  |      id="metadata3460"> | ||||||
|  |     <rdf:RDF> | ||||||
|  |       <cc:Work | ||||||
|  |          rdf:about=""> | ||||||
|  |         <dc:format>image/svg+xml</dc:format> | ||||||
|  |         <dc:type | ||||||
|  |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||||
|  |         <dc:title></dc:title> | ||||||
|  |       </cc:Work> | ||||||
|  |     </rdf:RDF> | ||||||
|  |   </metadata> | ||||||
|  |   <g | ||||||
|  |      inkscape:label="Layer 1" | ||||||
|  |      inkscape:groupmode="layer" | ||||||
|  |      id="layer1" | ||||||
|  |      transform="translate(-814.59595,-274.38623)"> | ||||||
|  |     <g | ||||||
|  |        id="g3477" | ||||||
|  |        transform="matrix(1.1855854,0,0,1.1855854,-151.17715,-57.3976)"> | ||||||
|  |       <path | ||||||
|  |          sodipodi:nodetypes="sscccccsscs" | ||||||
|  |          inkscape:connector-curvature="0" | ||||||
|  |          id="path4337-3" | ||||||
|  |          d="m 817.11249,282.97118 c -1.25816,1.34277 -2.04623,3.29881 -2.01563,5.13867 0.0639,3.84476 1.79693,5.3002 4.56836,10.59179 0.99832,2.32851 2.04027,4.79237 3.03125,8.87305 0.13772,0.60193 0.27203,1.16104 0.33416,1.20948 0.0621,0.0485 0.19644,-0.51262 0.33416,-1.11455 0.99098,-4.08068 2.03293,-6.54258 3.03125,-8.87109 2.77143,-5.29159 4.50444,-6.74704 4.56836,-10.5918 0.0306,-1.83986 -0.75942,-3.79785 -2.01758,-5.14062 -1.43724,-1.53389 -3.60504,-2.66908 -5.91619,-2.71655 -2.31115,-0.0475 -4.4809,1.08773 -5.91814,2.62162 z" | ||||||
|  |          style="display:inline;opacity:1;fill:#fd4b2d;fill-opacity:1;" /> | ||||||
|  |       <circle | ||||||
|  |          r="3.0355" | ||||||
|  |          cy="288.25278" | ||||||
|  |          cx="823.03064" | ||||||
|  |          id="path3049" | ||||||
|  |          style="display:inline;opacity:1;fill:#590000;fill-opacity:1;stroke-width:0" /> | ||||||
|  |     </g> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 2.7 KiB | 
| @ -12,6 +12,8 @@ export interface EventGeo { | |||||||
|     city?: string; |     city?: string; | ||||||
|     country?: string; |     country?: string; | ||||||
|     continent?: string; |     continent?: string; | ||||||
|  |     lat?: number; | ||||||
|  |     long?: number; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface EventModel { | export interface EventModel { | ||||||
|  | |||||||
| @ -76,7 +76,6 @@ export class ObjectChangelog extends Table<Event> { | |||||||
|             html`<div>${formatElapsedTime(item.created)}</div> |             html`<div>${formatElapsedTime(item.created)}</div> | ||||||
|                 <small>${item.created.toLocaleString()}</small>`, |                 <small>${item.created.toLocaleString()}</small>`, | ||||||
|             html`<div>${item.clientIp || msg("-")}</div> |             html`<div>${item.clientIp || msg("-")}</div> | ||||||
|  |  | ||||||
|                 <small>${EventGeo(item)}</small>`, |                 <small>${EventGeo(item)}</small>`, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Marc 'risson' Schmitt
					Marc 'risson' Schmitt