ethyca-fides 2.67.2b0__py2.py3-none-any.whl → 2.67.2b2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ethyca-fides might be problematic. Click here for more details.
- {ethyca_fides-2.67.2b0.dist-info → ethyca_fides-2.67.2b2.dist-info}/METADATA +1 -1
- {ethyca_fides-2.67.2b0.dist-info → ethyca_fides-2.67.2b2.dist-info}/RECORD +95 -95
- fides/_version.py +3 -3
- fides/api/graph/traversal.py +1 -1
- fides/api/models/detection_discovery/core.py +33 -20
- fides/api/task/manual/manual_task_utils.py +89 -150
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-e0a755c69081fffa.js → [id]-330475705adbd36f.js} +1 -1
- fides/ui-build/static/admin/_next/static/{fnhwfkI-rGZMUPYfQ5ACP → gqCWjlCr8J6bJ_5t4lTGZ}/_buildManifest.js +1 -1
- fides/ui-build/static/admin/add-systems/manual.html +1 -1
- fides/ui-build/static/admin/add-systems/multiple.html +1 -1
- fides/ui-build/static/admin/add-systems.html +1 -1
- fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
- fides/ui-build/static/admin/consent/configure.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
- fides/ui-build/static/admin/consent/properties.html +1 -1
- fides/ui-build/static/admin/consent/reporting.html +1 -1
- fides/ui-build/static/admin/consent.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
- fides/ui-build/static/admin/data-catalog.html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
- fides/ui-build/static/admin/data-discovery/activity.html +1 -1
- fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/detection.html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
- fides/ui-build/static/admin/datamap.html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
- fides/ui-build/static/admin/dataset/new.html +1 -1
- fides/ui-build/static/admin/dataset.html +1 -1
- fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
- fides/ui-build/static/admin/datastore-connection/new.html +1 -1
- fides/ui-build/static/admin/datastore-connection.html +1 -1
- fides/ui-build/static/admin/index.html +1 -1
- fides/ui-build/static/admin/integrations/[id].html +1 -1
- fides/ui-build/static/admin/integrations.html +1 -1
- fides/ui-build/static/admin/lib/fides-preview.js +1 -1
- fides/ui-build/static/admin/login/[provider].html +1 -1
- fides/ui-build/static/admin/login.html +1 -1
- fides/ui-build/static/admin/messaging/[id].html +1 -1
- fides/ui-build/static/admin/messaging/add-template.html +1 -1
- fides/ui-build/static/admin/messaging.html +1 -1
- fides/ui-build/static/admin/poc/ant-components.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
- fides/ui-build/static/admin/poc/forms.html +1 -1
- fides/ui-build/static/admin/poc/table-migration.html +1 -1
- fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
- fides/ui-build/static/admin/privacy-requests.html +1 -1
- fides/ui-build/static/admin/properties/[id].html +1 -1
- fides/ui-build/static/admin/properties/add-property.html +1 -1
- fides/ui-build/static/admin/properties.html +1 -1
- fides/ui-build/static/admin/reporting/datamap.html +1 -1
- fides/ui-build/static/admin/settings/about/alpha.html +1 -1
- fides/ui-build/static/admin/settings/about.html +1 -1
- fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
- fides/ui-build/static/admin/settings/consent.html +1 -1
- fides/ui-build/static/admin/settings/custom-fields.html +1 -1
- fides/ui-build/static/admin/settings/domain-records.html +1 -1
- fides/ui-build/static/admin/settings/domains.html +1 -1
- fides/ui-build/static/admin/settings/email-templates.html +1 -1
- fides/ui-build/static/admin/settings/locations.html +1 -1
- fides/ui-build/static/admin/settings/organization.html +1 -1
- fides/ui-build/static/admin/settings/regulations.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id].html +1 -1
- fides/ui-build/static/admin/systems.html +1 -1
- fides/ui-build/static/admin/taxonomy.html +1 -1
- fides/ui-build/static/admin/user-management/new.html +1 -1
- fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
- fides/ui-build/static/admin/user-management.html +1 -1
- {ethyca_fides-2.67.2b0.dist-info → ethyca_fides-2.67.2b2.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.67.2b0.dist-info → ethyca_fides-2.67.2b2.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.67.2b0.dist-info → ethyca_fides-2.67.2b2.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.67.2b0.dist-info → ethyca_fides-2.67.2b2.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{fnhwfkI-rGZMUPYfQ5ACP → gqCWjlCr8J6bJ_5t4lTGZ}/_ssgManifest.js +0 -0
|
@@ -1,27 +1,35 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
1
3
|
from loguru import logger
|
|
2
4
|
from sqlalchemy.orm import Session
|
|
3
5
|
|
|
4
6
|
from fides.api.graph.config import (
|
|
5
7
|
Collection,
|
|
6
8
|
CollectionAddress,
|
|
7
|
-
|
|
9
|
+
FieldAddress,
|
|
8
10
|
GraphDataset,
|
|
9
11
|
ScalarField,
|
|
10
12
|
)
|
|
11
|
-
from fides.api.graph.graph import Node
|
|
12
|
-
from fides.api.graph.traversal import TraversalNode
|
|
13
13
|
from fides.api.models.connectionconfig import ConnectionConfig
|
|
14
14
|
|
|
15
15
|
# Import application models
|
|
16
|
-
from fides.api.models.manual_task import
|
|
16
|
+
from fides.api.models.manual_task import (
|
|
17
|
+
ManualTask,
|
|
18
|
+
ManualTaskConditionalDependencyType,
|
|
19
|
+
ManualTaskConfigurationType,
|
|
20
|
+
)
|
|
17
21
|
from fides.api.task.manual.manual_task_address import ManualTaskAddress
|
|
18
22
|
|
|
23
|
+
PRIVACY_REQUEST_CONFIG_TYPES = {
|
|
24
|
+
ManualTaskConfigurationType.access_privacy_request,
|
|
25
|
+
ManualTaskConfigurationType.erasure_privacy_request,
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
|
|
20
29
|
def get_connection_configs_with_manual_tasks(db: Session) -> list[ConnectionConfig]:
|
|
21
30
|
"""
|
|
22
31
|
Get all connection configs that have manual tasks.
|
|
23
32
|
"""
|
|
24
|
-
logger.info("Querying for connection configs with manual tasks")
|
|
25
33
|
connection_configs = (
|
|
26
34
|
db.query(ConnectionConfig)
|
|
27
35
|
.join(ManualTask, ConnectionConfig.id == ManualTask.parent_entity_id)
|
|
@@ -29,9 +37,6 @@ def get_connection_configs_with_manual_tasks(db: Session) -> list[ConnectionConf
|
|
|
29
37
|
.filter(ConnectionConfig.disabled.is_(False))
|
|
30
38
|
.all()
|
|
31
39
|
)
|
|
32
|
-
logger.info(
|
|
33
|
-
f"Found {len(connection_configs)} connection configs with manual tasks: {[cc.key for cc in connection_configs]}"
|
|
34
|
-
)
|
|
35
40
|
return connection_configs
|
|
36
41
|
|
|
37
42
|
|
|
@@ -45,20 +50,12 @@ def get_manual_task_addresses(db: Session) -> list[CollectionAddress]:
|
|
|
45
50
|
"""
|
|
46
51
|
# Get all connection configs that have manual tasks (excluding disabled ones)
|
|
47
52
|
connection_configs_with_manual_tasks = get_connection_configs_with_manual_tasks(db)
|
|
48
|
-
logger.debug(
|
|
49
|
-
f"Found {len(connection_configs_with_manual_tasks)} connection configs with manual tasks"
|
|
50
|
-
)
|
|
51
53
|
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
logger.info(
|
|
59
|
-
f"Created {len(manual_task_addresses)} manual task addresses: {manual_task_addresses}"
|
|
60
|
-
)
|
|
61
|
-
return manual_task_addresses
|
|
54
|
+
# Return addresses for all connections that have manual tasks
|
|
55
|
+
return [
|
|
56
|
+
ManualTaskAddress.create(config.key)
|
|
57
|
+
for config in connection_configs_with_manual_tasks
|
|
58
|
+
]
|
|
62
59
|
|
|
63
60
|
|
|
64
61
|
def get_manual_task_for_connection_config(
|
|
@@ -67,11 +64,7 @@ def get_manual_task_for_connection_config(
|
|
|
67
64
|
"""Get the ManualTask for a specific connection config,
|
|
68
65
|
the manual task/connection config relationship is 1:1.
|
|
69
66
|
"""
|
|
70
|
-
|
|
71
|
-
f"Looking for manual task for connection config: {connection_config_key}"
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
manual_task = (
|
|
67
|
+
return (
|
|
75
68
|
db.query(ManualTask)
|
|
76
69
|
.join(ConnectionConfig, ManualTask.parent_entity_id == ConnectionConfig.id)
|
|
77
70
|
.filter(
|
|
@@ -81,32 +74,19 @@ def get_manual_task_for_connection_config(
|
|
|
81
74
|
.one_or_none()
|
|
82
75
|
)
|
|
83
76
|
|
|
84
|
-
if manual_task:
|
|
85
|
-
logger.info(
|
|
86
|
-
f"Found manual task {manual_task.id} for connection {connection_config_key}"
|
|
87
|
-
)
|
|
88
|
-
else:
|
|
89
|
-
logger.warning(
|
|
90
|
-
f"No manual task found for connection config: {connection_config_key}"
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
return manual_task
|
|
94
|
-
|
|
95
77
|
|
|
96
|
-
def
|
|
97
|
-
db: Session, address: CollectionAddress
|
|
98
|
-
) -> "TraversalNode":
|
|
78
|
+
def create_data_category_scalar_fields(manual_task: ManualTask) -> list[ScalarField]:
|
|
99
79
|
"""
|
|
100
|
-
Create
|
|
80
|
+
Create scalar fields for each field in the given manual task configs.
|
|
101
81
|
"""
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
for config in
|
|
82
|
+
fields = []
|
|
83
|
+
# Get current privacy request configs for this manual task
|
|
84
|
+
current_configs = [
|
|
85
|
+
config
|
|
86
|
+
for config in manual_task.configs
|
|
87
|
+
if config.is_current and config.config_type in PRIVACY_REQUEST_CONFIG_TYPES
|
|
88
|
+
]
|
|
89
|
+
for config in current_configs:
|
|
110
90
|
for field in config.field_definitions:
|
|
111
91
|
# Create a scalar field for each manual task field
|
|
112
92
|
# Extract data categories from field metadata if available
|
|
@@ -119,137 +99,96 @@ def create_manual_data_traversal_node(
|
|
|
119
99
|
# Manual task fields don't have complex relationships
|
|
120
100
|
)
|
|
121
101
|
fields.append(scalar_field)
|
|
102
|
+
return fields
|
|
122
103
|
|
|
123
|
-
# Create a synthetic Collection
|
|
124
|
-
collection = Collection(
|
|
125
|
-
name=ManualTaskAddress.MANUAL_DATA_COLLECTION,
|
|
126
|
-
fields=fields,
|
|
127
|
-
# Manual tasks don't have complex dependencies
|
|
128
|
-
after=set(),
|
|
129
|
-
)
|
|
130
104
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
105
|
+
def create_conditional_dependency_scalar_fields(
|
|
106
|
+
field_addresses: set[str],
|
|
107
|
+
) -> list[ScalarField]:
|
|
108
|
+
fields: list[ScalarField] = []
|
|
109
|
+
for field_address in field_addresses:
|
|
110
|
+
# Use the full field address as the field name to preserve collection context
|
|
111
|
+
# This allows the manual task to receive data from specific collections
|
|
112
|
+
# e.g., "user.name" or "customer.profile.email" instead of just "name" or "email"
|
|
113
|
+
logger.info(
|
|
114
|
+
f"Creating conditional dependency scalar field for field address: {field_address}"
|
|
115
|
+
)
|
|
116
|
+
field_address_obj = FieldAddress.from_string(field_address)
|
|
117
|
+
|
|
118
|
+
scalar_field = ScalarField(
|
|
119
|
+
name=field_address_obj.value,
|
|
120
|
+
# Conditional dependency fields don't have predefined data categories
|
|
121
|
+
data_categories=[],
|
|
122
|
+
references=[(field_address_obj, "from")],
|
|
123
|
+
)
|
|
124
|
+
fields.append(scalar_field)
|
|
125
|
+
|
|
126
|
+
return fields
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def create_collection_for_connection_key(
|
|
130
|
+
db: Session, connection_key: str
|
|
131
|
+
) -> Optional[Collection]:
|
|
132
|
+
# Get the manual task for this connection config
|
|
133
|
+
manual_task = get_manual_task_for_connection_config(db, connection_key)
|
|
134
|
+
|
|
135
|
+
if not manual_task:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
# Get conditional dependency field addresses - raw field data
|
|
139
|
+
conditional_field_addresses: set[str] = {
|
|
140
|
+
dependency.field_address
|
|
141
|
+
for dependency in manual_task.conditional_dependencies
|
|
142
|
+
if dependency.condition_type == ManualTaskConditionalDependencyType.leaf
|
|
143
|
+
and dependency.field_address is not None
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Create scalar fields for data category fields and conditional dependency field addresses
|
|
147
|
+
fields: list[ScalarField] = []
|
|
148
|
+
fields.extend(create_data_category_scalar_fields(manual_task))
|
|
149
|
+
fields.extend(
|
|
150
|
+
create_conditional_dependency_scalar_fields(conditional_field_addresses)
|
|
137
151
|
)
|
|
138
152
|
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
# Only create collection if there are fields
|
|
154
|
+
if not fields:
|
|
155
|
+
return None
|
|
141
156
|
|
|
142
|
-
return
|
|
157
|
+
return Collection(name=ManualTaskAddress.MANUAL_DATA_COLLECTION, fields=fields)
|
|
143
158
|
|
|
144
159
|
|
|
145
|
-
def create_manual_task_artificial_graphs(
|
|
146
|
-
db: Session,
|
|
147
|
-
) -> list:
|
|
160
|
+
def create_manual_task_artificial_graphs(db: Session) -> list[GraphDataset]:
|
|
148
161
|
"""
|
|
149
162
|
Create artificial GraphDataset objects for manual tasks that can be included
|
|
150
163
|
in the main dataset graph during the dataset configuration phase.
|
|
151
164
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
Manual task collections are designed as root nodes that execute immediately when
|
|
156
|
-
the privacy request starts, in parallel with identity processing. They don't depend
|
|
157
|
-
on identity data since they provide manually-entered data rather than consuming it.
|
|
165
|
+
Each manual task gets its own collection with its own dependencies based on
|
|
166
|
+
its specific conditional dependencies. This allows individual manual tasks
|
|
167
|
+
to receive only the data they need from regular tasks.
|
|
158
168
|
|
|
159
169
|
Args:
|
|
160
170
|
db: Database session
|
|
161
|
-
policy: The policy being executed (optional, for filtering manual task configs)
|
|
162
171
|
|
|
163
172
|
Returns:
|
|
164
|
-
List of GraphDataset objects representing manual tasks as
|
|
173
|
+
List of GraphDataset objects representing manual tasks as individual collections
|
|
165
174
|
"""
|
|
166
|
-
|
|
167
|
-
logger.debug("Creating manual task artificial graphs")
|
|
168
175
|
manual_task_graphs = []
|
|
169
176
|
manual_addresses = get_manual_task_addresses(db)
|
|
170
|
-
logger.debug(
|
|
171
|
-
f"Found {len(manual_addresses)} manual task addresses: {manual_addresses}"
|
|
172
|
-
)
|
|
173
177
|
|
|
174
178
|
for address in manual_addresses:
|
|
175
179
|
connection_key = address.dataset
|
|
176
|
-
logger.debug(
|
|
177
|
-
f"Processing manual task address: {address} for connection: {connection_key}"
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# Get manual tasks for this connection to determine fields
|
|
181
|
-
manual_task = get_manual_task_for_connection_config(db, connection_key)
|
|
182
180
|
|
|
183
|
-
#
|
|
184
|
-
|
|
181
|
+
# Get the collection for this connection config using the reusable function
|
|
182
|
+
collection = create_collection_for_connection_key(db, connection_key)
|
|
185
183
|
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
if manual_task:
|
|
189
|
-
logger.debug(
|
|
190
|
-
f"Processing manual task {manual_task.id} with {len(manual_task.configs)} configs"
|
|
191
|
-
)
|
|
192
|
-
current_configs = [
|
|
193
|
-
config
|
|
194
|
-
for config in manual_task.configs
|
|
195
|
-
if config.is_current
|
|
196
|
-
and config.config_type
|
|
197
|
-
in [
|
|
198
|
-
ManualTaskConfigurationType.access_privacy_request,
|
|
199
|
-
ManualTaskConfigurationType.erasure_privacy_request,
|
|
200
|
-
]
|
|
201
|
-
]
|
|
202
|
-
logger.debug(
|
|
203
|
-
f"Found {len(current_configs)} current configs for manual task {manual_task.id}"
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
for config in current_configs:
|
|
207
|
-
logger.debug(
|
|
208
|
-
f"Processing config {config.id} with {len(config.field_definitions)} fields"
|
|
209
|
-
)
|
|
210
|
-
for field in config.field_definitions:
|
|
211
|
-
# Create a scalar field for each manual task field
|
|
212
|
-
field_metadata = field.field_metadata or {}
|
|
213
|
-
data_categories = field_metadata.get("data_categories", [])
|
|
214
|
-
|
|
215
|
-
scalar_field = ScalarField(
|
|
216
|
-
name=field.field_key,
|
|
217
|
-
data_categories=data_categories,
|
|
218
|
-
)
|
|
219
|
-
fields.append(scalar_field)
|
|
220
|
-
else:
|
|
221
|
-
logger.warning(
|
|
222
|
-
f"No manual task found for connection {connection_key}, skipping"
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
if fields: # Only create graph if there are fields
|
|
226
|
-
logger.debug(
|
|
227
|
-
f"Creating graph for connection {connection_key} with {len(fields)} fields"
|
|
228
|
-
)
|
|
229
|
-
# Create a synthetic Collection
|
|
230
|
-
collection = Collection(
|
|
231
|
-
name=ManualTaskAddress.MANUAL_DATA_COLLECTION,
|
|
232
|
-
fields=fields,
|
|
233
|
-
# Manual tasks have no dependencies - they're root nodes
|
|
234
|
-
after=set(),
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
# Create a synthetic GraphDataset
|
|
184
|
+
if collection: # Only create graph if there are collections
|
|
185
|
+
# Create a synthetic GraphDataset with all manual task collections
|
|
238
186
|
graph_dataset = GraphDataset(
|
|
239
187
|
name=connection_key,
|
|
240
188
|
collections=[collection],
|
|
241
189
|
connection_key=connection_key,
|
|
242
|
-
after=set(),
|
|
243
190
|
)
|
|
244
191
|
|
|
245
192
|
manual_task_graphs.append(graph_dataset)
|
|
246
|
-
logger.debug(
|
|
247
|
-
f"Successfully created manual task graph for connection {connection_key}"
|
|
248
|
-
)
|
|
249
|
-
else:
|
|
250
|
-
logger.warning(
|
|
251
|
-
f"No fields found for connection {connection_key}, skipping graph creation"
|
|
252
|
-
)
|
|
253
193
|
|
|
254
|
-
logger.debug(f"Created {len(manual_task_graphs)} manual task graphs")
|
|
255
194
|
return manual_task_graphs
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/d9924caa849931b3.css" as="style"/><link rel="stylesheet" href="/_next/static/css/d9924caa849931b3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-90e8ec1fc5c6455b.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-750d6bd16c971bb9.js" defer=""></script><script src="/_next/static/chunks/pages/404-2d803dab6a00f353.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/d9924caa849931b3.css" as="style"/><link rel="stylesheet" href="/_next/static/css/d9924caa849931b3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-90e8ec1fc5c6455b.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-750d6bd16c971bb9.js" defer=""></script><script src="/_next/static/chunks/pages/404-2d803dab6a00f353.js" defer=""></script><script src="/_next/static/gqCWjlCr8J6bJ_5t4lTGZ/_buildManifest.js" defer=""></script><script src="/_next/static/gqCWjlCr8J6bJ_5t4lTGZ/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/404","query":{},"buildId":"gqCWjlCr8J6bJ_5t4lTGZ","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|