Compare commits
95 commits
main
...
patch-audi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c59114188c | ||
|
|
6457e383bb | ||
|
|
c22898bce2 | ||
|
|
9807e066a7 | ||
|
|
4d8947c805 | ||
|
|
3a39ca8f4e | ||
|
|
86f0baa30a | ||
|
|
b2cb874a2a | ||
|
|
fdc281a406 | ||
|
|
7f5c342a64 | ||
|
|
e31f8d0ab2 | ||
|
|
9ce67efb26 | ||
|
|
56ba1770f7 | ||
|
|
8a99bcf7c8 | ||
|
|
a5c236d648 | ||
|
|
16656d5618 | ||
|
|
ef482d35a8 | ||
|
|
ca4c568883 | ||
|
|
8cecd5f4d5 | ||
|
|
e729a6adac | ||
|
|
da083dd277 | ||
|
|
5f144636e6 | ||
|
|
97a6cf9a52 | ||
|
|
0cec551fa0 | ||
|
|
0c66686e15 | ||
|
|
e84bdb5eef | ||
|
|
d9fe2b5bb8 | ||
|
|
9940316ce3 | ||
|
|
9eeaeaf4eb | ||
|
|
77e6f9aa03 | ||
|
|
90d13c69ab | ||
|
|
396aefaf90 | ||
|
|
201b74350c | ||
|
|
03c3dc4d6f | ||
|
|
e2556ea76e | ||
|
|
b353c818c3 | ||
|
|
6802ddcd93 | ||
|
|
63f8b176f6 | ||
|
|
ca5c3fa836 | ||
|
|
ffedeb4910 | ||
|
|
1c7ce95a3c | ||
|
|
a56b94ba92 | ||
|
|
cbafc09bf7 | ||
|
|
9e89c661c5 | ||
|
|
96e3db1450 | ||
|
|
4401cf930f | ||
|
|
8c5a5e165e | ||
|
|
ccc407137c | ||
|
|
b7bd14a3aa | ||
|
|
49dd17ebe6 | ||
|
|
57b3044800 | ||
|
|
72325c6991 | ||
|
|
a7adf4ad54 | ||
|
|
54abbe58a2 | ||
|
|
b43a35e458 | ||
|
|
28a9b53d05 | ||
|
|
111e9c6474 | ||
|
|
f8d8cc5150 | ||
|
|
aa25b0e882 | ||
|
|
b53b14ae5f | ||
|
|
e3f6a8fadc | ||
|
|
e95611c1a6 | ||
|
|
46aebe9a8c | ||
|
|
e9a9d2ff2a | ||
|
|
1f7d587796 | ||
|
|
7c20b608c5 | ||
|
|
88a82acb8b | ||
|
|
36c9b5e234 | ||
|
|
4cfdee28c3 | ||
|
|
62ef3ca2dd | ||
|
|
9d0f3b34ae | ||
|
|
93c605a28e | ||
|
|
872263624d | ||
|
|
1dee5853a5 | ||
|
|
5cf584e8e1 | ||
|
|
cfc1f807ec | ||
|
|
de19f0397d | ||
|
|
a11c683baf | ||
|
|
f5949cc08e | ||
|
|
d7cb49d490 | ||
|
|
6e5d92ed79 | ||
|
|
018bf9c0be | ||
|
|
c56a2c2d25 | ||
|
|
5d786bde56 | ||
|
|
c7e6f31941 | ||
|
|
ad0ef00842 | ||
|
|
2ffec26d02 | ||
|
|
b63962b51a | ||
|
|
abe440f729 | ||
|
|
71e7552899 | ||
|
|
7906384fe7 | ||
|
|
bdd564f49c | ||
|
|
b89248067a | ||
|
|
9ed207abb1 | ||
|
|
cbe2d62def |
88 changed files with 2206 additions and 1311 deletions
6
.github/workflows/patch-build.yaml
vendored
6
.github/workflows/patch-build.yaml
vendored
|
|
@ -83,8 +83,12 @@ jobs:
|
|||
[ -d $MSAAS_REPO_FOLDER ] || {
|
||||
git clone -b dev --recursive https://x-access-token:$MSAAS_REPO_CLONE_TOKEN@$MSAAS_REPO_URL $MSAAS_REPO_FOLDER
|
||||
cd $MSAAS_REPO_FOLDER
|
||||
cd openreplay && git fetch origin && git checkout main # This have to be changed to specific tag
|
||||
git log -1
|
||||
cd $MSAAS_REPO_FOLDER
|
||||
bash git-init.sh
|
||||
git checkout
|
||||
git --git-dir=./openreplay/.git status
|
||||
}
|
||||
}
|
||||
function build_managed() {
|
||||
|
|
@ -97,7 +101,7 @@ jobs:
|
|||
else
|
||||
cd $MSAAS_REPO_FOLDER/openreplay/$service
|
||||
fi
|
||||
IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=arm64 DOCKER_REPO=$DOCKER_REPO_ARM PUSH_IMAGE=0 bash build.sh >> /tmp/arm.txt
|
||||
IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=arm64 DOCKER_REPO=$DOCKER_REPO_ARM PUSH_IMAGE=0 bash -x build.sh >> /tmp/arm.txt
|
||||
}
|
||||
# Checking for backend images
|
||||
ls backend/cmd >> /tmp/backend.txt
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ from chalicelib.core.collaboration_slack import Slack
|
|||
from chalicelib.utils import pg_client, helper, email_helper, smtp
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get(id):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
|
|
|
|||
|
|
@ -26,17 +26,23 @@ class MSTeams(BaseCollaboration):
|
|||
|
||||
@classmethod
|
||||
def say_hello(cls, url):
|
||||
r = requests.post(
|
||||
url=url,
|
||||
json={
|
||||
"@type": "MessageCard",
|
||||
"@context": "https://schema.org/extensions",
|
||||
"summary": "Welcome to OpenReplay",
|
||||
"title": "Welcome to OpenReplay"
|
||||
})
|
||||
if r.status_code != 200:
|
||||
logger.warning("MSTeams integration failed")
|
||||
logger.warning(r.text)
|
||||
try:
|
||||
r = requests.post(
|
||||
url=url,
|
||||
json={
|
||||
"@type": "MessageCard",
|
||||
"@context": "https://schema.org/extensions",
|
||||
"summary": "Welcome to OpenReplay",
|
||||
"title": "Welcome to OpenReplay"
|
||||
},
|
||||
timeout=3)
|
||||
if r.status_code != 200:
|
||||
logger.warning("MSTeams integration failed")
|
||||
logger.warning(r.text)
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.warning("!!! MSTeams integration failed")
|
||||
logger.exception(e)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +1,34 @@
|
|||
import logging
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import sessions_mobs, sessions, events
|
||||
from chalicelib.core import sessions_mobs, sessions
|
||||
from chalicelib.utils import pg_client, helper
|
||||
|
||||
# from chalicelib.utils import sql_helper as sh
|
||||
from chalicelib.utils import sql_helper as sh
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_by_url(project_id, data: schemas.GetHeatMapPayloadSchema):
|
||||
if data.url is None or data.url == "":
|
||||
return []
|
||||
args = {"startDate": data.startTimestamp, "endDate": data.endTimestamp,
|
||||
"project_id": project_id, "url": data.url}
|
||||
constraints = ["sessions.project_id = %(project_id)s",
|
||||
"(url = %(url)s OR path= %(url)s)",
|
||||
"clicks.timestamp >= %(startDate)s",
|
||||
"clicks.timestamp <= %(endDate)s",
|
||||
"start_ts >= %(startDate)s",
|
||||
"start_ts <= %(endDate)s",
|
||||
"duration IS NOT NULL",
|
||||
"normalized_x IS NOT NULL"]
|
||||
if data.operator == schemas.SearchEventOperator.IS:
|
||||
constraints.append("path= %(url)s")
|
||||
else:
|
||||
constraints.append("path ILIKE %(url)s")
|
||||
args["url"] = helper.values_for_operator(data.url, data.operator)
|
||||
|
||||
query_from = "events.clicks INNER JOIN sessions USING (session_id)"
|
||||
has_click_rage_filter = False
|
||||
# TODO: is this used ?
|
||||
# has_click_rage_filter = False
|
||||
# if len(data.filters) > 0:
|
||||
# for i, f in enumerate(data.filters):
|
||||
# if f.type == schemas.FilterType.issue and len(f.value) > 0:
|
||||
|
|
@ -49,15 +55,15 @@ def get_by_url(project_id, data: schemas.GetHeatMapPayloadSchema):
|
|||
# f.value, value_key=f_k))
|
||||
# constraints.append(sh.multi_conditions(f"mis.type = %({f_k})s",
|
||||
# f.value, value_key=f_k))
|
||||
|
||||
if data.click_rage and not has_click_rage_filter:
|
||||
constraints.append("""(issues.session_id IS NULL
|
||||
OR (issues.timestamp >= %(startDate)s
|
||||
AND issues.timestamp <= %(endDate)s
|
||||
AND mis.project_id = %(project_id)s
|
||||
AND mis.type='click_rage'))""")
|
||||
query_from += """LEFT JOIN events_common.issues USING (timestamp, session_id)
|
||||
LEFT JOIN issues AS mis USING (issue_id)"""
|
||||
# TODO: change this once click-rage is fixed
|
||||
# if data.click_rage and not has_click_rage_filter:
|
||||
# constraints.append("""(issues.session_id IS NULL
|
||||
# OR (issues.timestamp >= %(startDate)s
|
||||
# AND issues.timestamp <= %(endDate)s
|
||||
# AND mis.project_id = %(project_id)s
|
||||
# AND mis.type='click_rage'))""")
|
||||
# query_from += """LEFT JOIN events_common.issues USING (timestamp, session_id)
|
||||
# LEFT JOIN issues AS mis USING (issue_id)"""
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(f"""SELECT normalized_x, normalized_y
|
||||
FROM {query_from}
|
||||
|
|
@ -83,8 +89,13 @@ def get_by_url(project_id, data: schemas.GetHeatMapPayloadSchema):
|
|||
def get_x_y_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatMapPayloadSchema):
|
||||
args = {"session_id": session_id, "url": data.url}
|
||||
constraints = ["session_id = %(session_id)s",
|
||||
"(url = %(url)s OR path= %(url)s)",
|
||||
"normalized_x IS NOT NULL"]
|
||||
if data.operator == schemas.SearchEventOperator.IS:
|
||||
constraints.append("path= %(url)s")
|
||||
else:
|
||||
constraints.append("path ILIKE %(url)s")
|
||||
args["url"] = helper.values_for_operator(data.url, data.operator)
|
||||
|
||||
query_from = "events.clicks"
|
||||
|
||||
with pg_client.PostgresClient() as cur:
|
||||
|
|
@ -110,8 +121,13 @@ def get_x_y_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatM
|
|||
|
||||
def get_selectors_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatMapPayloadSchema):
|
||||
args = {"session_id": session_id, "url": data.url}
|
||||
constraints = ["session_id = %(session_id)s",
|
||||
"(url = %(url)s OR path= %(url)s)"]
|
||||
constraints = ["session_id = %(session_id)s"]
|
||||
if data.operator == schemas.SearchEventOperator.IS:
|
||||
constraints.append("path= %(url)s")
|
||||
else:
|
||||
constraints.append("path ILIKE %(url)s")
|
||||
args["url"] = helper.values_for_operator(data.url, data.operator)
|
||||
|
||||
query_from = "events.clicks"
|
||||
|
||||
with pg_client.PostgresClient() as cur:
|
||||
|
|
@ -143,29 +159,93 @@ s.start_ts,
|
|||
s.duration"""
|
||||
|
||||
|
||||
def __get_1_url(location_condition: schemas.SessionSearchEventSchema2 | None, session_id: str, project_id: int,
|
||||
start_time: int,
|
||||
end_time: int) -> str | None:
|
||||
full_args = {
|
||||
"sessionId": session_id,
|
||||
"projectId": project_id,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
}
|
||||
sub_condition = ["session_id = %(sessionId)s"]
|
||||
if location_condition and len(location_condition.value) > 0:
|
||||
f_k = "LOC"
|
||||
op = sh.get_sql_operator(location_condition.operator)
|
||||
full_args = {**full_args, **sh.multi_values(location_condition.value, value_key=f_k)}
|
||||
sub_condition.append(
|
||||
sh.multi_conditions(f'path {op} %({f_k})s', location_condition.value, is_not=False,
|
||||
value_key=f_k))
|
||||
with pg_client.PostgresClient() as cur:
|
||||
main_query = cur.mogrify(f"""WITH paths AS (SELECT DISTINCT path
|
||||
FROM events.clicks
|
||||
WHERE {" AND ".join(sub_condition)})
|
||||
SELECT path, COUNT(1) AS count
|
||||
FROM events.clicks
|
||||
INNER JOIN public.sessions USING (session_id)
|
||||
INNER JOIN paths USING (path)
|
||||
WHERE sessions.project_id = %(projectId)s
|
||||
AND clicks.timestamp >= %(start_time)s
|
||||
AND clicks.timestamp <= %(end_time)s
|
||||
AND start_ts >= %(start_time)s
|
||||
AND start_ts <= %(end_time)s
|
||||
AND duration IS NOT NULL
|
||||
GROUP BY path
|
||||
ORDER BY count DESC
|
||||
LIMIT 1;""", full_args)
|
||||
logger.debug("--------------------")
|
||||
logger.debug(main_query)
|
||||
logger.debug("--------------------")
|
||||
try:
|
||||
cur.execute(main_query)
|
||||
except Exception as err:
|
||||
logger.warning("--------- CLICK MAP BEST URL SEARCH QUERY EXCEPTION -----------")
|
||||
logger.warning(main_query.decode('UTF-8'))
|
||||
logger.warning("--------- PAYLOAD -----------")
|
||||
logger.warning(full_args)
|
||||
logger.warning("--------------------")
|
||||
raise err
|
||||
|
||||
url = cur.fetchone()
|
||||
if url is None:
|
||||
return None
|
||||
return url["path"]
|
||||
|
||||
|
||||
def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_id,
|
||||
include_mobs: bool = True, exclude_sessions: list[str] = [],
|
||||
_depth: int = 3):
|
||||
no_platform = True
|
||||
no_location = True
|
||||
location_condition = None
|
||||
no_click = True
|
||||
for f in data.filters:
|
||||
if f.type == schemas.FilterType.PLATFORM:
|
||||
no_platform = False
|
||||
break
|
||||
for f in data.events:
|
||||
if f.type == schemas.EventType.LOCATION:
|
||||
no_location = False
|
||||
if len(f.value) == 0:
|
||||
f.operator = schemas.SearchEventOperator.IS_ANY
|
||||
location_condition = f.model_copy()
|
||||
elif f.type == schemas.EventType.CLICK:
|
||||
no_click = False
|
||||
if len(f.value) == 0:
|
||||
f.operator = schemas.SearchEventOperator.IS_ANY
|
||||
if location_condition and not no_click:
|
||||
break
|
||||
|
||||
if no_platform:
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(type=schemas.FilterType.PLATFORM,
|
||||
value=[schemas.PlatformType.DESKTOP],
|
||||
operator=schemas.SearchEventOperator.IS))
|
||||
if no_location:
|
||||
if not location_condition:
|
||||
data.events.append(schemas.SessionSearchEventSchema2(type=schemas.EventType.LOCATION,
|
||||
value=[],
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
if no_click:
|
||||
data.events.append(schemas.SessionSearchEventSchema2(type=schemas.EventType.CLICK,
|
||||
value=[],
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(type=schemas.FilterType.EVENTS_COUNT,
|
||||
value=[0],
|
||||
|
|
@ -183,7 +263,8 @@ def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_i
|
|||
main_query = cur.mogrify(f"""SELECT *
|
||||
FROM (SELECT {SESSION_PROJECTION_COLS}
|
||||
{query_part}
|
||||
ORDER BY {data.sort} {data.order.value}
|
||||
--ignoring the sort made the query faster (from 6s to 100ms)
|
||||
--ORDER BY {data.sort} {data.order.value}
|
||||
LIMIT 20) AS raw
|
||||
ORDER BY random()
|
||||
LIMIT 1;""", full_args)
|
||||
|
|
@ -202,6 +283,13 @@ def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_i
|
|||
|
||||
session = cur.fetchone()
|
||||
if session:
|
||||
if not location_condition or location_condition.operator == schemas.SearchEventOperator.IS_ANY:
|
||||
session["path"] = __get_1_url(project_id=project_id, session_id=session["session_id"],
|
||||
location_condition=location_condition,
|
||||
start_time=data.startTimestamp, end_time=data.endTimestamp)
|
||||
else:
|
||||
session["path"] = location_condition.value[0]
|
||||
|
||||
if include_mobs:
|
||||
session['domURL'] = sessions_mobs.get_urls(session_id=session["session_id"], project_id=project_id)
|
||||
session['mobsUrl'] = sessions_mobs.get_urls_depercated(session_id=session["session_id"])
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class JIRAIntegration(integration_base.BaseIntegration):
|
|||
except Exception as e:
|
||||
self._issue_handler = None
|
||||
self.integration["valid"] = False
|
||||
return {"errors": ["Something went wrong, please check your JIRA credentials."]}
|
||||
return self._issue_handler
|
||||
|
||||
# TODO: remove this once jira-oauth is done
|
||||
|
|
|
|||
|
|
@ -336,10 +336,13 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de
|
|||
if v not in extra_conditions[e.operator].value:
|
||||
extra_conditions[e.operator].value.append(v)
|
||||
extra_conditions = list(extra_conditions.values())
|
||||
|
||||
elif metric_of == schemas.MetricOfTable.ISSUES and len(metric_value) > 0:
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(value=metric_value, type=schemas.FilterType.ISSUE,
|
||||
operator=schemas.SearchEventOperator.IS))
|
||||
elif metric_of == schemas.MetricOfTable.REFERRER:
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(value=metric_value, type=schemas.FilterType.REFERRER,
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
|
||||
full_args, query_part = search_query_parts(data=data, error_status=None, errors_only=False,
|
||||
favorite_only=False, issue=None, project_id=project_id,
|
||||
user_id=None, extra_event=extra_event, extra_conditions=extra_conditions)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from decouple import config
|
|||
from . import smtp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=config("LOGLEVEL", default=logging.info))
|
||||
logging.basicConfig(level=config("LOGLEVEL", default=logging.INFO))
|
||||
|
||||
if smtp.has_smtp():
|
||||
logger.info("valid SMTP configuration found")
|
||||
|
|
|
|||
|
|
@ -394,8 +394,11 @@ def get_all_issue_tracking_projects(context: schemas.CurrentContext = Depends(OR
|
|||
user_id=context.user_id)
|
||||
if error is not None:
|
||||
return error
|
||||
data = integration.issue_handler.get_projects()
|
||||
if "errors" in data:
|
||||
data = integration.issue_handler
|
||||
if isinstance(data, dict) and "errors" in data:
|
||||
return data
|
||||
data = data.get_projects()
|
||||
if isinstance(data, dict) and "errors" in data:
|
||||
return data
|
||||
return {"data": data}
|
||||
|
||||
|
|
@ -406,8 +409,11 @@ def get_integration_metadata(integrationProjectId: int, context: schemas.Current
|
|||
user_id=context.user_id)
|
||||
if error is not None:
|
||||
return error
|
||||
data = integration.issue_handler.get_metas(integrationProjectId)
|
||||
if "errors" in data.keys():
|
||||
data = integration
|
||||
if isinstance(data, dict) and "errors" in data:
|
||||
return data
|
||||
data = data.issue_handler.get_metas(integrationProjectId)
|
||||
if isinstance(data, dict) and "errors" in data:
|
||||
return data
|
||||
return {"data": data}
|
||||
|
||||
|
|
|
|||
|
|
@ -777,7 +777,8 @@ class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
|||
for f in values.get("filters", []):
|
||||
vals = []
|
||||
for v in f.get("value", []):
|
||||
if v is not None:
|
||||
if v is not None and (f.get("type", "") != FilterType.DURATION.value
|
||||
or str(v).isnumeric()):
|
||||
vals.append(v)
|
||||
f["value"] = vals
|
||||
return values
|
||||
|
|
@ -1594,9 +1595,11 @@ class HeatMapFilterSchema(BaseModel):
|
|||
|
||||
|
||||
class GetHeatMapPayloadSchema(_TimedSchema):
|
||||
url: str = Field(...)
|
||||
url: Optional[str] = Field(default=None)
|
||||
filters: List[HeatMapFilterSchema] = Field(default=[])
|
||||
click_rage: bool = Field(default=False)
|
||||
operator: Literal[SearchEventOperator.IS, SearchEventOperator.STARTS_WITH,
|
||||
SearchEventOperator.CONTAINS, SearchEventOperator.ENDS_WITH] = Field(default=SearchEventOperator.STARTS_WITH)
|
||||
|
||||
|
||||
class GetClickMapPayloadSchema(GetHeatMapPayloadSchema):
|
||||
|
|
|
|||
46
assist/package-lock.json
generated
46
assist/package-lock.json
generated
|
|
@ -10,10 +10,10 @@
|
|||
"license": "Elastic License 2.0 (ELv2)",
|
||||
"dependencies": {
|
||||
"@maxmind/geoip2-node": "^4.2.0",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"prom-client": "^15.0.0",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io": "^4.8.0",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
|
|
@ -72,9 +72,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
|
||||
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||
"version": "22.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz",
|
||||
"integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
|
|
@ -241,9 +241,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -338,16 +338,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.5.5",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
|
||||
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
|
||||
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.4.1",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
|
|
@ -366,9 +366,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/cookie": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -427,16 +427,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
|
|
@ -1141,15 +1141,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
|
||||
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
|
||||
"integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io": "~6.5.2",
|
||||
"engine.io": "~6.6.0",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
"homepage": "https://github.com/openreplay/openreplay#readme",
|
||||
"dependencies": {
|
||||
"@maxmind/geoip2-node": "^4.2.0",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"prom-client": "^15.0.0",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io": "^4.8.0",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,9 +302,14 @@ func (e *Router) pushMessagesHandlerWeb(w http.ResponseWriter, r *http.Request)
|
|||
if sessionData != nil {
|
||||
r = r.WithContext(context.WithValue(r.Context(), "sessionID", fmt.Sprintf("%d", sessionData.ID)))
|
||||
}
|
||||
tokenJustExpired := false
|
||||
if err != nil {
|
||||
e.ResponseWithError(r.Context(), w, http.StatusUnauthorized, err, startTime, r.URL.Path, bodySize)
|
||||
return
|
||||
if errors.Is(err, token.JUST_EXPIRED) {
|
||||
tokenJustExpired = true
|
||||
} else {
|
||||
e.ResponseWithError(r.Context(), w, http.StatusUnauthorized, err, startTime, r.URL.Path, bodySize)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Add sessionID and projectID to context
|
||||
|
|
@ -314,13 +319,21 @@ func (e *Router) pushMessagesHandlerWeb(w http.ResponseWriter, r *http.Request)
|
|||
|
||||
// Check request body
|
||||
if r.Body == nil {
|
||||
e.ResponseWithError(r.Context(), w, http.StatusBadRequest, errors.New("request body is empty"), startTime, r.URL.Path, bodySize)
|
||||
errCode := http.StatusBadRequest
|
||||
if tokenJustExpired {
|
||||
errCode = http.StatusUnauthorized
|
||||
}
|
||||
e.ResponseWithError(r.Context(), w, errCode, errors.New("request body is empty"), startTime, r.URL.Path, bodySize)
|
||||
return
|
||||
}
|
||||
|
||||
bodyBytes, err := e.readBody(w, r, e.getBeaconSize(sessionData.ID))
|
||||
if err != nil {
|
||||
e.ResponseWithError(r.Context(), w, http.StatusRequestEntityTooLarge, err, startTime, r.URL.Path, bodySize)
|
||||
errCode := http.StatusRequestEntityTooLarge
|
||||
if tokenJustExpired {
|
||||
errCode = http.StatusUnauthorized
|
||||
}
|
||||
e.ResponseWithError(r.Context(), w, errCode, err, startTime, r.URL.Path, bodySize)
|
||||
return
|
||||
}
|
||||
bodySize = len(bodyBytes)
|
||||
|
|
@ -329,10 +342,18 @@ func (e *Router) pushMessagesHandlerWeb(w http.ResponseWriter, r *http.Request)
|
|||
err = e.services.Producer.Produce(e.cfg.TopicRawWeb, sessionData.ID, bodyBytes)
|
||||
if err != nil {
|
||||
e.log.Error(r.Context(), "can't send messages batch to queue: %s", err)
|
||||
e.ResponseWithError(r.Context(), w, http.StatusInternalServerError, errors.New("can't save message, try again"), startTime, r.URL.Path, bodySize)
|
||||
errCode := http.StatusInternalServerError
|
||||
if tokenJustExpired {
|
||||
errCode = http.StatusUnauthorized
|
||||
}
|
||||
e.ResponseWithError(r.Context(), w, errCode, errors.New("can't save message, try again"), startTime, r.URL.Path, bodySize)
|
||||
return
|
||||
}
|
||||
|
||||
if tokenJustExpired {
|
||||
e.ResponseWithError(r.Context(), w, http.StatusUnauthorized, errors.New("token expired"), startTime, r.URL.Path, bodySize)
|
||||
return
|
||||
}
|
||||
e.ResponseOK(r.Context(), w, startTime, r.URL.Path, bodySize)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const BEARER_SCHEMA = "Bearer "
|
|||
func (tokenizer *Tokenizer) ParseFromHTTPRequest(r *http.Request) (*TokenData, error) {
|
||||
header := r.Header.Get("Authorization")
|
||||
if !strings.HasPrefix(header, BEARER_SCHEMA) {
|
||||
return nil, errors.New("Missing token")
|
||||
return nil, errors.New("missing token")
|
||||
}
|
||||
token := header[len(BEARER_SCHEMA):]
|
||||
return tokenizer.Parse(token)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ import (
|
|||
"github.com/btcsuite/btcutil/base58"
|
||||
)
|
||||
|
||||
var EXPIRED = errors.New("token expired")
|
||||
var (
|
||||
EXPIRED = errors.New("token expired")
|
||||
JUST_EXPIRED = errors.New("token just expired")
|
||||
)
|
||||
|
||||
type Tokenizer struct {
|
||||
secret []byte
|
||||
|
|
@ -64,8 +67,13 @@ func (tokenizer *Tokenizer) Parse(token string) (*TokenData, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &TokenData{id, delay, expTime}
|
||||
if expTime <= time.Now().UnixMilli() {
|
||||
return &TokenData{id, delay, expTime}, EXPIRED
|
||||
// If token is expired less than 30 seconds ago, we still consider it semi-valid
|
||||
if expTime+30000 > time.Now().UnixMilli() {
|
||||
return res, JUST_EXPIRED
|
||||
}
|
||||
return res, EXPIRED
|
||||
}
|
||||
return &TokenData{id, delay, expTime}, nil
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
|||
1
ee/api/.gitignore
vendored
1
ee/api/.gitignore
vendored
|
|
@ -273,7 +273,6 @@ Pipfile.lock
|
|||
/chalicelib/core/usability_testing/
|
||||
/NOTES.md
|
||||
/chalicelib/core/db_request_handler.py
|
||||
/routers/subs/spot.py
|
||||
/chalicelib/utils/or_cache/
|
||||
/routers/subs/health.py
|
||||
/chalicelib/core/spot.py
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import chalicelib.utils.exp_ch_helper
|
||||
import schemas
|
||||
from chalicelib.core import countries, events, metadata
|
||||
from chalicelib.utils import ch_client
|
||||
|
|
@ -325,12 +326,13 @@ def get_top_values(project_id, event_type, event_key=None):
|
|||
FROM raw;"""
|
||||
else:
|
||||
colname = TYPE_TO_COLUMN.get(event_type)
|
||||
event_type = exp_ch_helper.get_event_type(event_type)
|
||||
query = f"""WITH raw AS (SELECT DISTINCT {colname} AS c_value,
|
||||
COUNT(1) OVER (PARTITION BY c_value) AS row_count,
|
||||
COUNT(1) OVER () AS total_count
|
||||
FROM experimental.events
|
||||
WHERE project_id = %(project_id)s
|
||||
AND event_type = '{event_type.upper()}'
|
||||
AND event_type = '{event_type}'
|
||||
AND isNotNull(c_value)
|
||||
AND notEmpty(c_value)
|
||||
ORDER BY row_count DESC
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ from decouple import config
|
|||
|
||||
import schemas
|
||||
from chalicelib.core import sessions_mobs, events
|
||||
|
||||
# from chalicelib.utils import sql_helper as sh
|
||||
from chalicelib.utils import sql_helper as sh
|
||||
|
||||
if config("EXP_SESSIONS_SEARCH", cast=bool, default=False):
|
||||
from chalicelib.core import sessions_exp as sessions
|
||||
|
|
@ -18,17 +17,24 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
def get_by_url(project_id, data: schemas.GetHeatMapPayloadSchema):
|
||||
if data.url is None or data.url == "":
|
||||
return []
|
||||
args = {"startDate": data.startTimestamp, "endDate": data.endTimestamp,
|
||||
"project_id": project_id, "url": data.url}
|
||||
constraints = ["main_events.project_id = toUInt16(%(project_id)s)",
|
||||
"(main_events.url_hostpath = %(url)s OR main_events.url_path = %(url)s)",
|
||||
"main_events.datetime >= toDateTime(%(startDate)s/1000)",
|
||||
"main_events.datetime <= toDateTime(%(endDate)s/1000)",
|
||||
"main_events.event_type='CLICK'",
|
||||
"isNotNull(main_events.normalized_x)"]
|
||||
if data.operator == schemas.SearchEventOperator.IS:
|
||||
constraints.append("url_path= %(url)s")
|
||||
else:
|
||||
constraints.append("url_path ILIKE %(url)s")
|
||||
args["url"] = helper.values_for_operator(data.url, data.operator)
|
||||
|
||||
query_from = f"{exp_ch_helper.get_main_events_table(data.startTimestamp)} AS main_events"
|
||||
has_click_rage_filter = False
|
||||
# TODO: is this used ?
|
||||
# has_click_rage_filter = False
|
||||
# if len(data.filters) > 0:
|
||||
# for i, f in enumerate(data.filters):
|
||||
# if f.type == schemas.FilterType.issue and len(f.value) > 0:
|
||||
|
|
@ -55,18 +61,18 @@ def get_by_url(project_id, data: schemas.GetHeatMapPayloadSchema):
|
|||
# f.value, value_key=f_k))
|
||||
# constraints.append(sh.multi_conditions(f"mis.type = %({f_k})s",
|
||||
# f.value, value_key=f_k))
|
||||
|
||||
if data.click_rage and not has_click_rage_filter:
|
||||
constraints.append("""(issues_t.session_id IS NULL
|
||||
OR (issues_t.datetime >= toDateTime(%(startDate)s/1000)
|
||||
AND issues_t.datetime <= toDateTime(%(endDate)s/1000)
|
||||
AND issues_t.project_id = toUInt16(%(project_id)s)
|
||||
AND issues_t.event_type = 'ISSUE'
|
||||
AND issues_t.project_id = toUInt16(%(project_id)s)
|
||||
AND mis.project_id = toUInt16(%(project_id)s)
|
||||
AND mis.type='click_rage'))""")
|
||||
query_from += """ LEFT JOIN experimental.events AS issues_t ON (main_events.session_id=issues_t.session_id)
|
||||
LEFT JOIN experimental.issues AS mis ON (issues_t.issue_id=mis.issue_id)"""
|
||||
# TODO: change this once click-rage is fixed
|
||||
# if data.click_rage and not has_click_rage_filter:
|
||||
# constraints.append("""(issues_t.session_id IS NULL
|
||||
# OR (issues_t.datetime >= toDateTime(%(startDate)s/1000)
|
||||
# AND issues_t.datetime <= toDateTime(%(endDate)s/1000)
|
||||
# AND issues_t.project_id = toUInt16(%(project_id)s)
|
||||
# AND issues_t.event_type = 'ISSUE'
|
||||
# AND issues_t.project_id = toUInt16(%(project_id)s)
|
||||
# AND mis.project_id = toUInt16(%(project_id)s)
|
||||
# AND mis.type='click_rage'))""")
|
||||
# query_from += """ LEFT JOIN experimental.events AS issues_t ON (main_events.session_id=issues_t.session_id)
|
||||
# LEFT JOIN experimental.issues AS mis ON (issues_t.issue_id=mis.issue_id)"""
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
query = cur.format(f"""SELECT main_events.normalized_x AS normalized_x,
|
||||
main_events.normalized_y AS normalized_y
|
||||
|
|
@ -93,16 +99,21 @@ def get_x_y_by_url_and_session_id(project_id, session_id, data: schemas.GetHeatM
|
|||
args = {"project_id": project_id, "session_id": session_id, "url": data.url}
|
||||
constraints = ["main_events.project_id = toUInt16(%(project_id)s)",
|
||||
"main_events.session_id = %(session_id)s",
|
||||
"(main_events.url_hostpath = %(url)s OR main_events.url_path = %(url)s)",
|
||||
"main_events.event_type='CLICK'",
|
||||
"isNotNull(main_events.normalized_x)"]
|
||||
if data.operator == schemas.SearchEventOperator.IS:
|
||||
constraints.append("main_events.url_path = %(url)s")
|
||||
else:
|
||||
constraints.append("main_events.url_path ILIKE %(url)s")
|
||||
args["url"] = helper.values_for_operator(data.url, data.operator)
|
||||
|
||||
query_from = f"{exp_ch_helper.get_main_events_table(0)} AS main_events"
|
||||
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
query = cur.format(f"""SELECT main_events.normalized_x AS normalized_x,
|
||||
main_events.normalized_y AS normalized_y
|
||||
FROM {query_from}
|
||||
WHERE {" AND ".join(constraints)};""", args)
|
||||
FROM {query_from}
|
||||
WHERE {" AND ".join(constraints)};""", args)
|
||||
logger.debug("---------")
|
||||
logger.debug(query)
|
||||
logger.debug("---------")
|
||||
|
|
@ -123,17 +134,22 @@ def get_selectors_by_url_and_session_id(project_id, session_id, data: schemas.Ge
|
|||
args = {"project_id": project_id, "session_id": session_id, "url": data.url}
|
||||
constraints = ["main_events.project_id = toUInt16(%(project_id)s)",
|
||||
"main_events.session_id = %(session_id)s",
|
||||
"(main_events.url_hostpath = %(url)s OR main_events.url_path = %(url)s)",
|
||||
"main_events.event_type='CLICK'"]
|
||||
if data.operator == schemas.SearchEventOperator.IS:
|
||||
constraints.append("main_events.url_path = %(url)s")
|
||||
else:
|
||||
constraints.append("main_events.url_path ILIKE %(url)s")
|
||||
args["url"] = helper.values_for_operator(data.url, data.operator)
|
||||
|
||||
query_from = f"{exp_ch_helper.get_main_events_table(0)} AS main_events"
|
||||
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
query = cur.format(f"""SELECT main_events.selector AS selector,
|
||||
COUNT(1) AS count
|
||||
FROM {query_from}
|
||||
WHERE {" AND ".join(constraints)}
|
||||
GROUP BY 1
|
||||
ORDER BY count DESC;""", args)
|
||||
COUNT(1) AS count
|
||||
FROM {query_from}
|
||||
WHERE {" AND ".join(constraints)}
|
||||
GROUP BY 1
|
||||
ORDER BY count DESC;""", args)
|
||||
logger.debug("---------")
|
||||
logger.debug(query)
|
||||
logger.debug("---------")
|
||||
|
|
@ -158,29 +174,93 @@ if not config("EXP_SESSIONS_SEARCH", cast=bool, default=False):
|
|||
s.duration"""
|
||||
|
||||
|
||||
def __get_1_url(location_condition: schemas.SessionSearchEventSchema2 | None, session_id: str, project_id: int,
|
||||
start_time: int,
|
||||
end_time: int) -> str | None:
|
||||
full_args = {
|
||||
"sessionId": session_id,
|
||||
"projectId": project_id,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
}
|
||||
sub_condition = ["session_id = %(sessionId)s"]
|
||||
if location_condition and len(location_condition.value) > 0:
|
||||
f_k = "LOC"
|
||||
op = sh.get_sql_operator(location_condition.operator)
|
||||
full_args = {**full_args, **sh.multi_values(location_condition.value, value_key=f_k)}
|
||||
sub_condition.append(
|
||||
sh.multi_conditions(f'path {op} %({f_k})s', location_condition.value, is_not=False,
|
||||
value_key=f_k))
|
||||
with pg_client.PostgresClient() as cur:
|
||||
main_query = cur.mogrify(f"""WITH paths AS (SELECT DISTINCT path
|
||||
FROM events.clicks
|
||||
WHERE {" AND ".join(sub_condition)})
|
||||
SELECT path, COUNT(1) AS count
|
||||
FROM events.clicks
|
||||
INNER JOIN public.sessions USING (session_id)
|
||||
INNER JOIN paths USING (path)
|
||||
WHERE sessions.project_id = %(projectId)s
|
||||
AND clicks.timestamp >= %(start_time)s
|
||||
AND clicks.timestamp <= %(end_time)s
|
||||
AND start_ts >= %(start_time)s
|
||||
AND start_ts <= %(end_time)s
|
||||
AND duration IS NOT NULL
|
||||
GROUP BY path
|
||||
ORDER BY count DESC
|
||||
LIMIT 1;""", full_args)
|
||||
logger.debug("--------------------")
|
||||
logger.debug(main_query)
|
||||
logger.debug("--------------------")
|
||||
try:
|
||||
cur.execute(main_query)
|
||||
except Exception as err:
|
||||
logger.warning("--------- CLICK MAP BEST URL SEARCH QUERY EXCEPTION -----------")
|
||||
logger.warning(main_query.decode('UTF-8'))
|
||||
logger.warning("--------- PAYLOAD -----------")
|
||||
logger.warning(full_args)
|
||||
logger.warning("--------------------")
|
||||
raise err
|
||||
|
||||
url = cur.fetchone()
|
||||
if url is None:
|
||||
return None
|
||||
return url["path"]
|
||||
|
||||
|
||||
def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_id,
|
||||
include_mobs: bool = True, exclude_sessions: list[str] = [],
|
||||
_depth: int = 3):
|
||||
no_platform = True
|
||||
no_location = True
|
||||
location_condition = None
|
||||
no_click = True
|
||||
for f in data.filters:
|
||||
if f.type == schemas.FilterType.PLATFORM:
|
||||
no_platform = False
|
||||
break
|
||||
for f in data.events:
|
||||
if f.type == schemas.EventType.LOCATION:
|
||||
no_location = False
|
||||
if len(f.value) == 0:
|
||||
f.operator = schemas.SearchEventOperator.IS_ANY
|
||||
location_condition = f.model_copy()
|
||||
elif f.type == schemas.EventType.CLICK:
|
||||
no_click = False
|
||||
if len(f.value) == 0:
|
||||
f.operator = schemas.SearchEventOperator.IS_ANY
|
||||
if location_condition and not no_click:
|
||||
break
|
||||
|
||||
if no_platform:
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(type=schemas.FilterType.PLATFORM,
|
||||
value=[schemas.PlatformType.DESKTOP],
|
||||
operator=schemas.SearchEventOperator.IS))
|
||||
if no_location:
|
||||
if not location_condition:
|
||||
data.events.append(schemas.SessionSearchEventSchema2(type=schemas.EventType.LOCATION,
|
||||
value=[],
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
if no_click:
|
||||
data.events.append(schemas.SessionSearchEventSchema2(type=schemas.EventType.CLICK,
|
||||
value=[],
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(type=schemas.FilterType.EVENTS_COUNT,
|
||||
value=[0],
|
||||
|
|
@ -198,7 +278,8 @@ if not config("EXP_SESSIONS_SEARCH", cast=bool, default=False):
|
|||
main_query = cur.mogrify(f"""SELECT *
|
||||
FROM (SELECT {SESSION_PROJECTION_COLS}
|
||||
{query_part}
|
||||
ORDER BY {data.sort} {data.order.value}
|
||||
--ignoring the sort made the query faster (from 6s to 100ms)
|
||||
--ORDER BY {data.sort} {data.order.value}
|
||||
LIMIT 20) AS raw
|
||||
ORDER BY random()
|
||||
LIMIT 1;""", full_args)
|
||||
|
|
@ -217,6 +298,13 @@ if not config("EXP_SESSIONS_SEARCH", cast=bool, default=False):
|
|||
|
||||
session = cur.fetchone()
|
||||
if session:
|
||||
if not location_condition or location_condition.operator == schemas.SearchEventOperator.IS_ANY:
|
||||
session["path"] = __get_1_url(project_id=project_id, session_id=session["session_id"],
|
||||
location_condition=location_condition,
|
||||
start_time=data.startTimestamp, end_time=data.endTimestamp)
|
||||
else:
|
||||
session["path"] = location_condition.value[0]
|
||||
|
||||
if include_mobs:
|
||||
session['domURL'] = sessions_mobs.get_urls(session_id=session["session_id"], project_id=project_id)
|
||||
session['mobsUrl'] = sessions_mobs.get_urls_depercated(session_id=session["session_id"])
|
||||
|
|
@ -288,29 +376,89 @@ else:
|
|||
s.duration AS duration"""
|
||||
|
||||
|
||||
def __get_1_url(location_condition: schemas.SessionSearchEventSchema2 | None, session_id: str, project_id: int,
|
||||
start_time: int,
|
||||
end_time: int) -> str | None:
|
||||
full_args = {
|
||||
"sessionId": session_id,
|
||||
"projectId": project_id,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
}
|
||||
sub_condition = ["session_id = %(sessionId)s", "event_type = 'CLICK'", "project_id = %(projectId)s"]
|
||||
if location_condition and len(location_condition.value) > 0:
|
||||
f_k = "LOC"
|
||||
op = sh.get_sql_operator(location_condition.operator)
|
||||
full_args = {**full_args, **sh.multi_values(location_condition.value, value_key=f_k)}
|
||||
sub_condition.append(
|
||||
sh.multi_conditions(f'url_path {op} %({f_k})s', location_condition.value, is_not=False,
|
||||
value_key=f_k))
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
main_query = cur.format(f"""WITH paths AS (SELECT DISTINCT url_path
|
||||
FROM experimental.events
|
||||
WHERE {" AND ".join(sub_condition)})
|
||||
SELECT url_path, COUNT(1) AS count
|
||||
FROM experimental.events
|
||||
INNER JOIN paths USING (url_path)
|
||||
WHERE event_type = 'CLICK'
|
||||
AND project_id = %(projectId)s
|
||||
AND datetime >= toDateTime(%(start_time)s / 1000)
|
||||
AND datetime <= toDateTime(%(end_time)s / 1000)
|
||||
GROUP BY url_path
|
||||
ORDER BY count DESC
|
||||
LIMIT 1;""", full_args)
|
||||
logger.debug("--------------------")
|
||||
logger.debug(main_query)
|
||||
logger.debug("--------------------")
|
||||
try:
|
||||
url = cur.execute(main_query)
|
||||
except Exception as err:
|
||||
logger.warning("--------- CLICK MAP BEST URL SEARCH QUERY EXCEPTION CH-----------")
|
||||
logger.warning(main_query)
|
||||
logger.warning("--------- PAYLOAD -----------")
|
||||
logger.warning(full_args)
|
||||
logger.warning("--------------------")
|
||||
raise err
|
||||
|
||||
if url is None or len(url) == 0:
|
||||
return None
|
||||
return url[0]["path"]
|
||||
|
||||
|
||||
def search_short_session(data: schemas.HeatMapSessionsSearch, project_id, user_id,
|
||||
include_mobs: bool = True, exclude_sessions: list[str] = [],
|
||||
_depth: int = 3):
|
||||
no_platform = True
|
||||
no_location = True
|
||||
location_condition = None
|
||||
no_click = True
|
||||
for f in data.filters:
|
||||
if f.type == schemas.FilterType.PLATFORM:
|
||||
no_platform = False
|
||||
break
|
||||
for f in data.events:
|
||||
if f.type == schemas.EventType.LOCATION:
|
||||
no_location = False
|
||||
if len(f.value) == 0:
|
||||
f.operator = schemas.SearchEventOperator.IS_ANY
|
||||
location_condition = f.model_copy()
|
||||
elif f.type == schemas.EventType.CLICK:
|
||||
no_click = False
|
||||
if len(f.value) == 0:
|
||||
f.operator = schemas.SearchEventOperator.IS_ANY
|
||||
if location_condition and not no_click:
|
||||
break
|
||||
|
||||
if no_platform:
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(type=schemas.FilterType.PLATFORM,
|
||||
value=[schemas.PlatformType.DESKTOP],
|
||||
operator=schemas.SearchEventOperator.IS))
|
||||
if no_location:
|
||||
if not location_condition:
|
||||
data.events.append(schemas.SessionSearchEventSchema2(type=schemas.EventType.LOCATION,
|
||||
value=[],
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
if no_click:
|
||||
data.events.append(schemas.SessionSearchEventSchema2(type=schemas.EventType.CLICK,
|
||||
value=[],
|
||||
operator=schemas.SearchEventOperator.IS_ANY))
|
||||
|
||||
data.filters.append(schemas.SessionSearchFilterSchema(type=schemas.FilterType.EVENTS_COUNT,
|
||||
value=[0],
|
||||
|
|
@ -328,7 +476,7 @@ else:
|
|||
main_query = cur.format(f"""SELECT *
|
||||
FROM (SELECT {SESSION_PROJECTION_COLS}
|
||||
{query_part}
|
||||
ORDER BY {data.sort} {data.order.value}
|
||||
-- ORDER BY {data.sort} {data.order.value}
|
||||
LIMIT 20) AS raw
|
||||
ORDER BY rand()
|
||||
LIMIT 1;""", full_args)
|
||||
|
|
@ -347,6 +495,13 @@ else:
|
|||
|
||||
if len(session) > 0:
|
||||
session = session[0]
|
||||
if not location_condition or location_condition.operator == schemas.SearchEventOperator.IS_ANY:
|
||||
session["path"] = __get_1_url(project_id=project_id, session_id=session["session_id"],
|
||||
location_condition=location_condition,
|
||||
start_time=data.startTimestamp, end_time=data.endTimestamp)
|
||||
else:
|
||||
session["path"] = location_condition.value[0]
|
||||
|
||||
if include_mobs:
|
||||
session['domURL'] = sessions_mobs.get_urls(session_id=session["session_id"], project_id=project_id)
|
||||
session['mobsUrl'] = sessions_mobs.get_urls_depercated(session_id=session["session_id"])
|
||||
|
|
@ -369,16 +524,16 @@ else:
|
|||
def get_selected_session(project_id, session_id):
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
main_query = cur.format(f"""SELECT {SESSION_PROJECTION_COLS}
|
||||
FROM experimental.sessions AS s
|
||||
WHERE session_id=%(session_id)s;""", {"session_id": session_id})
|
||||
FROM experimental.sessions AS s
|
||||
WHERE session_id=%(session_id)s;""", {"session_id": session_id})
|
||||
logger.debug("--------------------")
|
||||
logger.debug(main_query)
|
||||
logger.debug("--------------------")
|
||||
try:
|
||||
session = cur.execute(main_query)
|
||||
except Exception as err:
|
||||
logger.warning("--------- CLICK MAP GET SELECTED SESSION QUERY EXCEPTION -----------")
|
||||
logger.warning(main_query.decode('UTF-8'))
|
||||
logger.warning("--------- CLICK MAP GET SELECTED SESSION QUERY EXCEPTION CH-----------")
|
||||
logger.warning(main_query)
|
||||
raise err
|
||||
if len(session) > 0:
|
||||
session = session[0]
|
||||
|
|
@ -411,6 +566,6 @@ else:
|
|||
WHERE session_id = %(session_id)s
|
||||
AND event_type='LOCATION'
|
||||
AND project_id= %(project_id)s
|
||||
ORDER BY datetime,message_id;""", {"session_id": session_id,"project_id": project_id})
|
||||
ORDER BY datetime,message_id;""", {"session_id": session_id, "project_id": project_id})
|
||||
rows = helper.list_to_camel_case(rows)
|
||||
return rows
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ def search2_table(data: schemas.SessionsSearchPayloadSchema, project_id: int, de
|
|||
elif metric_of == schemas.MetricOfTable.REFERRER:
|
||||
main_col = "referrer"
|
||||
extra_col = ", referrer"
|
||||
extra_where = "WHERE isNotNull(referrer)"
|
||||
elif metric_of == schemas.MetricOfTable.FETCH:
|
||||
main_col = "url_path"
|
||||
extra_col = ", s.url_path"
|
||||
|
|
@ -554,39 +555,6 @@ def __is_valid_event(is_any: bool, event: schemas.SessionSearchEventSchema2):
|
|||
event.filters is None or len(event.filters) == 0))
|
||||
|
||||
|
||||
def __get_event_type(event_type: Union[schemas.EventType, schemas.PerformanceEventType], platform="web"):
|
||||
defs = {
|
||||
schemas.EventType.CLICK: "CLICK",
|
||||
schemas.EventType.INPUT: "INPUT",
|
||||
schemas.EventType.LOCATION: "LOCATION",
|
||||
schemas.PerformanceEventType.LOCATION_DOM_COMPLETE: "LOCATION",
|
||||
schemas.PerformanceEventType.LOCATION_LARGEST_CONTENTFUL_PAINT_TIME: "LOCATION",
|
||||
schemas.PerformanceEventType.LOCATION_TTFB: "LOCATION",
|
||||
schemas.EventType.CUSTOM: "CUSTOM",
|
||||
schemas.EventType.REQUEST: "REQUEST",
|
||||
schemas.EventType.REQUEST_DETAILS: "REQUEST",
|
||||
schemas.PerformanceEventType.FETCH_FAILED: "REQUEST",
|
||||
schemas.EventType.STATE_ACTION: "STATEACTION",
|
||||
schemas.EventType.ERROR: "ERROR",
|
||||
schemas.PerformanceEventType.LOCATION_AVG_CPU_LOAD: 'PERFORMANCE',
|
||||
schemas.PerformanceEventType.LOCATION_AVG_MEMORY_USAGE: 'PERFORMANCE'
|
||||
}
|
||||
defs_mobile = {
|
||||
schemas.EventType.CLICK_MOBILE: "TAP",
|
||||
schemas.EventType.INPUT_MOBILE: "INPUT",
|
||||
schemas.EventType.CUSTOM_MOBILE: "CUSTOM",
|
||||
schemas.EventType.REQUEST_MOBILE: "REQUEST",
|
||||
schemas.EventType.ERROR_MOBILE: "CRASH",
|
||||
schemas.EventType.VIEW_MOBILE: "VIEW",
|
||||
schemas.EventType.SWIPE_MOBILE: "SWIPE"
|
||||
}
|
||||
if platform != "web" and event_type in defs_mobile:
|
||||
return defs_mobile.get(event_type)
|
||||
if event_type not in defs:
|
||||
raise Exception(f"unsupported EventType:{event_type}")
|
||||
return defs.get(event_type)
|
||||
|
||||
|
||||
# this function generates the query and return the generated-query with the dict of query arguments
|
||||
def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_status, errors_only, favorite_only, issue,
|
||||
project_id, user_id, platform="web", extra_event=None, extra_deduplication=[],
|
||||
|
|
@ -925,7 +893,8 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
if platform == "web":
|
||||
_column = events.EventType.CLICK.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(
|
||||
f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if schemas.ClickEventExtraOperator.has_value(event.operator):
|
||||
|
|
@ -937,7 +906,8 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{
|
||||
"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -945,14 +915,16 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
_column = events.EventType.CLICK_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(
|
||||
f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{
|
||||
"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -963,14 +935,16 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
if platform == "web":
|
||||
_column = events.EventType.INPUT.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(
|
||||
f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{
|
||||
"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -982,14 +956,16 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
full_args = {**full_args, **_multiple_values(event.source, value_key=f"custom{i}")}
|
||||
else:
|
||||
_column = events.EventType.INPUT_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(
|
||||
f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{
|
||||
"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -1000,14 +976,16 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
if platform == "web":
|
||||
_column = 'url_path'
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(
|
||||
f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{
|
||||
"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1015,14 +993,16 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
_column = events.EventType.VIEW_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(
|
||||
f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{
|
||||
"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1031,14 +1011,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type == events.EventType.CUSTOM.ui_type:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
_column = events.EventType.CUSTOM.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -1047,14 +1027,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type == events.EventType.REQUEST.ui_type:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
_column = 'url_path'
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -1072,14 +1052,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type == events.EventType.STATEACTION.ui_type:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
_column = events.EventType.STATEACTION.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1089,7 +1069,7 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type == events.EventType.ERROR.ui_type:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main"
|
||||
events_extra_join = f"SELECT * FROM {MAIN_EVENTS_TABLE} AS main1 WHERE main1.project_id=%(project_id)s"
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
event.source = tuple(event.source)
|
||||
events_conditions[-1]["condition"] = []
|
||||
|
|
@ -1109,14 +1089,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
# ----- Mobile
|
||||
elif event_type == events.EventType.CLICK_MOBILE.ui_type:
|
||||
_column = events.EventType.CLICK_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -1124,14 +1104,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
elif event_type == events.EventType.INPUT_MOBILE.ui_type:
|
||||
_column = events.EventType.INPUT_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -1139,14 +1119,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
elif event_type == events.EventType.VIEW_MOBILE.ui_type:
|
||||
_column = events.EventType.VIEW_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1154,14 +1134,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
elif event_type == events.EventType.CUSTOM_MOBILE.ui_type:
|
||||
_column = events.EventType.CUSTOM_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1170,14 +1150,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type == events.EventType.REQUEST_MOBILE.ui_type:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
_column = 'url_path'
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s", event.value,
|
||||
|
|
@ -1185,14 +1165,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
elif event_type == events.EventType.CRASH_MOBILE.ui_type:
|
||||
_column = events.EventType.CRASH_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1200,14 +1180,14 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
events_conditions[-1]["condition"] = event_where[-1]
|
||||
elif event_type == events.EventType.SWIPE_MOBILE.ui_type and platform != "web":
|
||||
_column = events.EventType.SWIPE_MOBILE.column
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
if not is_any:
|
||||
if is_not:
|
||||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1217,7 +1197,7 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type == schemas.PerformanceEventType.FETCH_FAILED:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
_column = 'url_path'
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
events_conditions[-1]["condition"] = []
|
||||
if not is_any:
|
||||
|
|
@ -1225,7 +1205,7 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
event_where.append(_multiple_conditions(f"sub.{_column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
events_conditions_not.append(
|
||||
{"type": f"sub.event_type='{__get_event_type(event_type, platform=platform)}'"})
|
||||
{"type": f"sub.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'"})
|
||||
events_conditions_not[-1]["condition"] = event_where[-1]
|
||||
else:
|
||||
event_where.append(_multiple_conditions(f"main.{_column} {op} %({e_k})s",
|
||||
|
|
@ -1256,7 +1236,7 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
schemas.PerformanceEventType.LOCATION_LARGEST_CONTENTFUL_PAINT_TIME,
|
||||
schemas.PerformanceEventType.LOCATION_TTFB]:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
events_conditions[-1]["condition"] = []
|
||||
col = performance_event.get_col(event_type)
|
||||
|
|
@ -1279,7 +1259,7 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
elif event_type in [schemas.PerformanceEventType.LOCATION_AVG_CPU_LOAD,
|
||||
schemas.PerformanceEventType.LOCATION_AVG_MEMORY_USAGE]:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
events_conditions[-1]["condition"] = []
|
||||
col = performance_event.get_col(event_type)
|
||||
|
|
@ -1302,9 +1282,9 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
# elif event_type == schemas.PerformanceEventType.time_between_events:
|
||||
# event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
# # event_from = event_from % f"{getattr(events.event_type, event.value[0].type).table} AS main INNER JOIN {getattr(events.event_type, event.value[1].type).table} AS main2 USING(session_id) "
|
||||
# event_where.append(f"main.event_type='{__get_event_type(event.value[0].type, platform=platform)}'")
|
||||
# event_where.append(f"main.event_type='{__exp_ch_helper.get_event_type(event.value[0].type, platform=platform)}'")
|
||||
# events_conditions.append({"type": event_where[-1]})
|
||||
# event_where.append(f"main.event_type='{__get_event_type(event.value[0].type, platform=platform)}'")
|
||||
# event_where.append(f"main.event_type='{__exp_ch_helper.get_event_type(event.value[0].type, platform=platform)}'")
|
||||
# events_conditions.append({"type": event_where[-1]})
|
||||
#
|
||||
# if not isinstance(event.value[0].value, list):
|
||||
|
|
@ -1352,7 +1332,7 @@ def search_query_parts_ch(data: schemas.SessionsSearchPayloadSchema, error_statu
|
|||
# TODO: no isNot for RequestDetails
|
||||
elif event_type == schemas.EventType.REQUEST_DETAILS:
|
||||
event_from = event_from % f"{MAIN_EVENTS_TABLE} AS main "
|
||||
event_where.append(f"main.event_type='{__get_event_type(event_type, platform=platform)}'")
|
||||
event_where.append(f"main.event_type='{exp_ch_helper.get_event_type(event_type, platform=platform)}'")
|
||||
events_conditions.append({"type": event_where[-1]})
|
||||
apply = False
|
||||
events_conditions[-1]["condition"] = []
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
from typing import Union
|
||||
|
||||
import schemas
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
from decouple import config
|
||||
import logging
|
||||
|
|
@ -51,3 +54,37 @@ def get_main_js_errors_sessions_table(timestamp=0):
|
|||
# return "experimental.js_errors_sessions_mv" # \
|
||||
# if config("EXP_7D_MV", cast=bool, default=True) \
|
||||
# and timestamp >= TimeUTC.now(delta_days=-7) else "experimental.events"
|
||||
|
||||
|
||||
def get_event_type(event_type: Union[schemas.EventType, schemas.PerformanceEventType], platform="web"):
|
||||
defs = {
|
||||
schemas.EventType.CLICK: "CLICK",
|
||||
schemas.EventType.INPUT: "INPUT",
|
||||
schemas.EventType.LOCATION: "LOCATION",
|
||||
schemas.PerformanceEventType.LOCATION_DOM_COMPLETE: "LOCATION",
|
||||
schemas.PerformanceEventType.LOCATION_LARGEST_CONTENTFUL_PAINT_TIME: "LOCATION",
|
||||
schemas.PerformanceEventType.LOCATION_TTFB: "LOCATION",
|
||||
schemas.EventType.CUSTOM: "CUSTOM",
|
||||
schemas.EventType.REQUEST: "REQUEST",
|
||||
schemas.EventType.REQUEST_DETAILS: "REQUEST",
|
||||
schemas.PerformanceEventType.FETCH_FAILED: "REQUEST",
|
||||
schemas.EventType.STATE_ACTION: "STATEACTION",
|
||||
schemas.EventType.ERROR: "ERROR",
|
||||
schemas.PerformanceEventType.LOCATION_AVG_CPU_LOAD: 'PERFORMANCE',
|
||||
schemas.PerformanceEventType.LOCATION_AVG_MEMORY_USAGE: 'PERFORMANCE',
|
||||
schemas.FetchFilterType.FETCH_URL: 'REQUEST'
|
||||
}
|
||||
defs_mobile = {
|
||||
schemas.EventType.CLICK_MOBILE: "TAP",
|
||||
schemas.EventType.INPUT_MOBILE: "INPUT",
|
||||
schemas.EventType.CUSTOM_MOBILE: "CUSTOM",
|
||||
schemas.EventType.REQUEST_MOBILE: "REQUEST",
|
||||
schemas.EventType.ERROR_MOBILE: "CRASH",
|
||||
schemas.EventType.VIEW_MOBILE: "VIEW",
|
||||
schemas.EventType.SWIPE_MOBILE: "SWIPE"
|
||||
}
|
||||
if platform != "web" and event_type in defs_mobile:
|
||||
return defs_mobile.get(event_type)
|
||||
if event_type not in defs:
|
||||
raise Exception(f"unsupported EventType:{event_type}")
|
||||
return defs.get(event_type)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ rm -rf ./orpy.py
|
|||
rm -rf ./chalicelib/core/usability_testing/
|
||||
rm -rf ./chalicelib/core/db_request_handler.py
|
||||
rm -rf ./chalicelib/core/db_request_handler.py
|
||||
rm -rf ./routers/subs/spot.py
|
||||
rm -rf ./chalicelib/utils/or_cache
|
||||
rm -rf ./routers/subs/health.py
|
||||
rm -rf ./chalicelib/core/spot.py
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ psycopg2-binary==2.9.9
|
|||
psycopg[pool,binary]==3.2.1
|
||||
elasticsearch==8.15.0
|
||||
jira==3.8.0
|
||||
cachetools==5.5.0
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ psycopg2-binary==2.9.9
|
|||
psycopg[pool,binary]==3.2.1
|
||||
elasticsearch==8.15.0
|
||||
jira==3.8.0
|
||||
cachetools==5.5.0
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -346,7 +346,8 @@ def get_error_trace(projectId: int, sessionId: int, errorId: str,
|
|||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}', tags=['errors'], dependencies=[OR_scope(Permissions.DEV_TOOLS)])
|
||||
@app.get('/{projectId}/errors/{errorId}', tags=['errors'],
|
||||
dependencies=[OR_scope(Permissions.DEV_TOOLS, ServicePermissions.DEV_TOOLS)])
|
||||
def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24,
|
||||
density30: int = 30, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
|
|
@ -357,7 +358,8 @@ def errors_get_details(projectId: int, errorId: str, background_tasks: Backgroun
|
|||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/sourcemaps', tags=['errors'], dependencies=[OR_scope(Permissions.DEV_TOOLS)])
|
||||
@app.get('/{projectId}/errors/{errorId}/sourcemaps', tags=['errors'],
|
||||
dependencies=[OR_scope(Permissions.DEV_TOOLS, ServicePermissions.DEV_TOOLS)])
|
||||
def errors_get_details_sourcemaps(projectId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
|
|
@ -523,7 +525,7 @@ def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteSchema
|
|||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/notes', tags=["sessions", "notes"],
|
||||
dependencies=[OR_scope(Permissions.SESSION_REPLAY)])
|
||||
dependencies=[OR_scope(Permissions.SESSION_REPLAY, ServicePermissions.READ_NOTES)])
|
||||
def get_session_notes(projectId: int, sessionId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_notes.get_session_notes(tenant_id=context.tenant_id, project_id=projectId,
|
||||
session_id=sessionId, user_id=context.user_id)
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ async def __process_assertion(request: Request, tenant_key=None) -> Response | d
|
|||
tenant_key = user_data.get("tenantKey", [])
|
||||
else:
|
||||
logger.info("Using tenant key from ACS-URL")
|
||||
tenant_key = [tenant_key]
|
||||
|
||||
logger.debug(f"received nameId: {email} tenant_key: {tenant_key}")
|
||||
logger.debug(">user_data:")
|
||||
|
|
@ -90,26 +91,35 @@ async def __process_assertion(request: Request, tenant_key=None) -> Response | d
|
|||
return {"errors": ["invalid tenantKey, please copy the correct value from Preferences > Account"]}
|
||||
existing = users.get_by_email_only(email)
|
||||
role_names = user_data.get("role", [])
|
||||
if len(role_names) == 0:
|
||||
logger.info("No role specified, setting role to member")
|
||||
role_names = ["member"]
|
||||
role = None
|
||||
for r in role_names:
|
||||
if r.lower() == existing["roleName"].lower():
|
||||
role = {"roleId": existing["roleId"], "name": r}
|
||||
if len(role_names) == 0:
|
||||
if existing is None:
|
||||
logger.info("No role specified, setting role to member")
|
||||
role_names = ["member"]
|
||||
else:
|
||||
role = roles.get_role_by_name(tenant_id=t['tenantId'], name=r)
|
||||
role_names = [existing["roleName"]]
|
||||
role = {"name": existing["roleName"], "roleId": existing["roleId"]}
|
||||
if role is None:
|
||||
for r in role_names:
|
||||
if existing and r.lower() == existing["roleName"].lower():
|
||||
role = {"roleId": existing["roleId"], "name": r}
|
||||
else:
|
||||
role = roles.get_role_by_name(tenant_id=t['tenantId'], name=r)
|
||||
|
||||
if role is not None:
|
||||
break
|
||||
if role is not None:
|
||||
break
|
||||
|
||||
if role is None:
|
||||
return {"errors": [f"role '{role_names}' not found, please create it in OpenReplay first"]}
|
||||
logger.info(f"received roles:{role_names}; using:{role['name']}")
|
||||
admin_privileges = user_data.get("adminPrivileges", [])
|
||||
admin_privileges = not (len(admin_privileges) == 0
|
||||
or admin_privileges[0] is None
|
||||
or admin_privileges[0].lower() == "false")
|
||||
if len(admin_privileges) == 0:
|
||||
if existing is None:
|
||||
admin_privileges = not (len(admin_privileges) == 0
|
||||
or admin_privileges[0] is None
|
||||
or admin_privileges[0].lower() == "false")
|
||||
else:
|
||||
admin_privileges = existing["admin"]
|
||||
|
||||
internal_id = next(iter(user_data.get("internalId", [])), None)
|
||||
full_name = " ".join(user_data.get("firstName", []) + user_data.get("lastName", []))
|
||||
|
|
|
|||
32
ee/api/routers/subs/spot.py
Normal file
32
ee/api/routers/subs/spot.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
from fastapi import Depends
|
||||
from starlette.responses import JSONResponse, Response
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import spot, webhook
|
||||
from or_dependencies import OR_context
|
||||
from routers.base import get_routers
|
||||
|
||||
public_app, app, app_apikey = get_routers(prefix="/spot", tags=["spot"])
|
||||
|
||||
COOKIE_PATH = "/api/spot/refresh"
|
||||
|
||||
|
||||
@app.get('/logout')
|
||||
def logout_spot(response: Response, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
spot.logout(user_id=context.user_id)
|
||||
response.delete_cookie(key="spotRefreshToken", path=COOKIE_PATH)
|
||||
return {"data": "success"}
|
||||
|
||||
|
||||
@app.get('/refresh')
|
||||
def refresh_spot_login(response: JSONResponse, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
r = spot.refresh(user_id=context.user_id, tenant_id=context.tenant_id)
|
||||
content = {"jwt": r.get("jwt")}
|
||||
response.set_cookie(key="spotRefreshToken", value=r.get("refreshToken"), path=COOKIE_PATH,
|
||||
max_age=r.pop("refreshTokenMaxAge"), secure=True, httponly=True)
|
||||
return content
|
||||
|
||||
|
||||
@app.get('/integrations/slack/channels', tags=["integrations"])
|
||||
def get_slack_channels(context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.SLACK)}
|
||||
|
|
@ -25,6 +25,7 @@ class ServicePermissions(str, Enum):
|
|||
DEV_TOOLS = "SERVICE_DEV_TOOLS"
|
||||
ASSIST_LIVE = "SERVICE_ASSIST_LIVE"
|
||||
ASSIST_CALL = "SERVICE_ASSIST_CALL"
|
||||
READ_NOTES = "SERVICE_READ_NOTES"
|
||||
|
||||
|
||||
class CurrentContext(schemas.CurrentContext):
|
||||
|
|
|
|||
46
ee/assist/package-lock.json
generated
46
ee/assist/package-lock.json
generated
|
|
@ -11,11 +11,11 @@
|
|||
"dependencies": {
|
||||
"@maxmind/geoip2-node": "^4.2.0",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"prom-client": "^15.0.0",
|
||||
"redis": "^4.6.10",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io": "^4.8.0",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0",
|
||||
"winston": "^3.13.0"
|
||||
|
|
@ -144,9 +144,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
|
||||
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||
"version": "22.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz",
|
||||
"integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
|
|
@ -334,9 +334,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -439,16 +439,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.5.5",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
|
||||
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
|
||||
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.4.1",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
|
|
@ -467,9 +467,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/cookie": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -507,16 +507,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
|
|
@ -1271,15 +1271,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
|
||||
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
|
||||
"integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io": "~6.5.2",
|
||||
"engine.io": "~6.6.0",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@
|
|||
"dependencies": {
|
||||
"@maxmind/geoip2-node": "^4.2.0",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"prom-client": "^15.0.0",
|
||||
"redis": "^4.6.10",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io": "^4.8.0",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0",
|
||||
"winston": "^3.13.0"
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ var batches = map[string]string{
|
|||
"resources": "INSERT INTO experimental.resources (session_id, project_id, message_id, datetime, url, type, duration, ttfb, header_size, encoded_body_size, decoded_body_size, success, url_path) VALUES (?, ?, ?, ?, SUBSTR(?, 1, 8000), ?, ?, ?, ?, ?, ?, ?, SUBSTR(?, 1, 8000))",
|
||||
"autocompletes": "INSERT INTO experimental.autocomplete (project_id, type, value) VALUES (?, ?, SUBSTR(?, 1, 8000))",
|
||||
"pages": "INSERT INTO experimental.events (session_id, project_id, message_id, datetime, url, request_start, response_start, response_end, dom_content_loaded_event_start, dom_content_loaded_event_end, load_event_start, load_event_end, first_paint, first_contentful_paint_time, speed_index, visually_complete, time_to_interactive, url_path, event_type) VALUES (?, ?, ?, ?, SUBSTR(?, 1, 8000), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, SUBSTR(?, 1, 8000), ?)",
|
||||
"clicks": "INSERT INTO experimental.events (session_id, project_id, message_id, datetime, label, hesitation_time, event_type, selector, normalized_x, normalized_y) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
"clicks": "INSERT INTO experimental.events (session_id, project_id, message_id, datetime, label, hesitation_time, event_type, selector, normalized_x, normalized_y, url, url_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, SUBSTR(?, 1, 8000), SUBSTR(?, 1, 8000))",
|
||||
"inputs": "INSERT INTO experimental.events (session_id, project_id, message_id, datetime, label, event_type, duration, hesitation_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
"errors": "INSERT INTO experimental.events (session_id, project_id, message_id, datetime, source, name, message, error_id, event_type, error_tags_keys, error_tags_values) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
"performance": "INSERT INTO experimental.events (session_id, project_id, message_id, datetime, url, min_fps, avg_fps, max_fps, min_cpu, avg_cpu, max_cpu, min_total_js_heap_size, avg_total_js_heap_size, max_total_js_heap_size, min_used_js_heap_size, avg_used_js_heap_size, max_used_js_heap_size, event_type) VALUES (?, ?, ?, ?, SUBSTR(?, 1, 8000), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
|
|
@ -247,7 +247,7 @@ func (c *connectorImpl) InsertIssue(session *sessions.Session, msg *messages.Iss
|
|||
issueID := hashid.IssueID(session.ProjectID, msg)
|
||||
// Check issue type before insert to avoid panic from clickhouse lib
|
||||
switch msg.Type {
|
||||
case "click_rage", "dead_click", "excessive_scrolling", "bad_request", "missing_resource", "memory", "cpu", "slow_resource", "slow_page_load", "crash", "ml_cpu", "ml_memory", "ml_dead_click", "ml_click_rage", "ml_mouse_thrashing", "ml_excessive_scrolling", "ml_slow_resources", "custom", "js_exception", "mouse_thrashing":
|
||||
case "click_rage", "dead_click", "excessive_scrolling", "bad_request", "missing_resource", "memory", "cpu", "slow_resource", "slow_page_load", "crash", "ml_cpu", "ml_memory", "ml_dead_click", "ml_click_rage", "ml_mouse_thrashing", "ml_excessive_scrolling", "ml_slow_resources", "custom", "js_exception", "mouse_thrashing", "app_crash":
|
||||
default:
|
||||
return fmt.Errorf("unknown issueType: %s", msg.Type)
|
||||
}
|
||||
|
|
@ -423,6 +423,8 @@ func (c *connectorImpl) InsertWebClickEvent(session *sessions.Session, msg *mess
|
|||
msg.Selector,
|
||||
nX,
|
||||
nY,
|
||||
msg.Url,
|
||||
extractUrlPath(msg.Url),
|
||||
); err != nil {
|
||||
c.checkError("clicks", err)
|
||||
return fmt.Errorf("can't append to clicks batch: %s", err)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ SET metric_type='heatMap',
|
|||
WHERE metric_type = 'clickMap';
|
||||
|
||||
UPDATE public.roles
|
||||
SET permissions='{SERVICE_SESSION_REPLAY,SERVICE_DEV_TOOLS,SERVICE_ASSIST_LIVE,SERVICE_ASSIST_CALL}'
|
||||
SET permissions='{SERVICE_SESSION_REPLAY,SERVICE_DEV_TOOLS,SERVICE_ASSIST_LIVE,SERVICE_ASSIST_CALL,SERVICE_READ_NOTES}'
|
||||
WHERE service_role;
|
||||
|
||||
UPDATE public.users
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ WHERE NOT permissions @> '{SPOT_PUBLIC}'
|
|||
AND NOT service_role;
|
||||
-- AND name ILIKE 'owner';
|
||||
|
||||
UPDATE public.roles
|
||||
SET permissions='{SERVICE_SESSION_REPLAY,SERVICE_DEV_TOOLS,SERVICE_ASSIST_LIVE,SERVICE_ASSIST_CALL,SERVICE_READ_NOTES}'
|
||||
WHERE service_role;
|
||||
|
||||
ALTER TABLE IF EXISTS public.users
|
||||
ADD COLUMN IF NOT EXISTS spot_jwt_iat timestamp without time zone NULL DEFAULT NULL,
|
||||
ADD COLUMN IF NOT EXISTS spot_jwt_refresh_jti integer NULL DEFAULT NULL,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
\set or_version 'v1.19.0-ee'
|
||||
\set or_version 'v1.20.0-ee'
|
||||
SET client_min_messages TO NOTICE;
|
||||
\set ON_ERROR_STOP true
|
||||
SELECT EXISTS (SELECT 1
|
||||
|
|
|
|||
|
|
@ -23,4 +23,4 @@ MINIO_SECRET_KEY = ''
|
|||
|
||||
# APP and TRACKER VERSIONS
|
||||
VERSION = 1.20.0
|
||||
TRACKER_VERSION = '14.0.6'
|
||||
TRACKER_VERSION = '14.0.7'
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ interface Props {
|
|||
function PrivateRoutes(props: Props) {
|
||||
const { onboarding, sites, siteId } = props;
|
||||
const hasRecordings = sites.some(s => s.recorded);
|
||||
const redirectToSetup = props.scope === 0;
|
||||
const redirectToOnboarding =
|
||||
!onboarding && (localStorage.getItem(GLOBAL_HAS_NO_RECORDINGS) === 'true' || !hasRecordings) && props.scope > 0;
|
||||
const siteIdList: any = sites.map(({ id }) => id).toJS();
|
||||
|
|
@ -126,6 +127,13 @@ function PrivateRoutes(props: Props) {
|
|||
return (
|
||||
<Suspense fallback={<Loader loading={true} className="flex-1" />}>
|
||||
<Switch key="content">
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={SCOPE_SETUP}
|
||||
component={enhancedComponents.ScopeSetup}
|
||||
/>
|
||||
{redirectToSetup ? <Redirect to={SCOPE_SETUP} /> : null}
|
||||
<Route path={CLIENT_PATH} component={enhancedComponents.Client} />
|
||||
<Route
|
||||
path={withSiteId(ONBOARDING_PATH, siteIdList)}
|
||||
|
|
@ -143,12 +151,6 @@ function PrivateRoutes(props: Props) {
|
|||
path={SPOT_PATH}
|
||||
component={enhancedComponents.Spot}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={SCOPE_SETUP}
|
||||
component={enhancedComponents.ScopeSetup}
|
||||
/>
|
||||
{props.scope === 1 ? <Redirect to={SPOTS_LIST_PATH} /> : null}
|
||||
<Route
|
||||
path="/integrations/"
|
||||
|
|
|
|||
|
|
@ -10,21 +10,19 @@ import {
|
|||
GLOBAL_DESTINATION_PATH,
|
||||
IFRAME,
|
||||
JWT_PARAM,
|
||||
SPOT_ONBOARDING
|
||||
} from "App/constants/storageKeys";
|
||||
SPOT_ONBOARDING,
|
||||
} from 'App/constants/storageKeys';
|
||||
import Layout from 'App/layout/Layout';
|
||||
import { withStore } from "App/mstore";
|
||||
import { checkParam, handleSpotJWT, isTokenExpired } from "App/utils";
|
||||
import { withStore } from 'App/mstore';
|
||||
import { checkParam, handleSpotJWT, isTokenExpired } from 'App/utils';
|
||||
import { ModalProvider } from 'Components/Modal';
|
||||
import { ModalProvider as NewModalProvider } from 'Components/ModalContext';
|
||||
import { fetchListActive as fetchMetadata } from 'Duck/customField';
|
||||
import { setSessionPath } from 'Duck/sessions';
|
||||
import { fetchList as fetchSiteList } from 'Duck/site';
|
||||
import { init as initSite } from 'Duck/site';
|
||||
import { fetchUserInfo, getScope, setJwt, logout } from "Duck/user";
|
||||
import { fetchTenants } from 'Duck/user';
|
||||
import { fetchUserInfo, getScope, logout, setJwt } from 'Duck/user';
|
||||
import { Loader } from 'UI';
|
||||
import { spotsList } from "./routes";
|
||||
import * as routes from './routes';
|
||||
|
||||
interface RouterProps
|
||||
|
|
@ -36,7 +34,6 @@ interface RouterProps
|
|||
changePassword: boolean;
|
||||
isEnterprise: boolean;
|
||||
fetchUserInfo: () => any;
|
||||
fetchTenants: () => any;
|
||||
setSessionPath: (path: any) => any;
|
||||
fetchSiteList: (siteId?: number) => any;
|
||||
match: {
|
||||
|
|
@ -45,7 +42,7 @@ interface RouterProps
|
|||
};
|
||||
};
|
||||
mstore: any;
|
||||
setJwt: (params: { jwt: string, spotJwt: string | null }) => any;
|
||||
setJwt: (params: { jwt: string; spotJwt: string | null }) => any;
|
||||
fetchMetadata: (siteId: string) => void;
|
||||
initSite: (site: any) => void;
|
||||
scopeSetup: boolean;
|
||||
|
|
@ -68,15 +65,16 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
logout,
|
||||
} = props;
|
||||
|
||||
const params = new URLSearchParams(location.search)
|
||||
const params = new URLSearchParams(location.search);
|
||||
const spotCb = params.get('spotCallback');
|
||||
const spotReqSent = React.useRef(false)
|
||||
const spotReqSent = React.useRef(false);
|
||||
const [isSpotCb, setIsSpotCb] = React.useState(false);
|
||||
const [isSignup, setIsSignup] = React.useState(false);
|
||||
const [isIframe, setIsIframe] = React.useState(false);
|
||||
const [isJwt, setIsJwt] = React.useState(false);
|
||||
|
||||
const handleJwtFromUrl = () => {
|
||||
const params = new URLSearchParams(location.search)
|
||||
const params = new URLSearchParams(location.search);
|
||||
const urlJWT = params.get('jwt');
|
||||
const spotJwt = params.get('spotJwt');
|
||||
if (spotJwt) {
|
||||
|
|
@ -92,6 +90,7 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
return;
|
||||
} else {
|
||||
spotReqSent.current = true;
|
||||
setIsSpotCb(false);
|
||||
}
|
||||
handleSpotJWT(jwt);
|
||||
};
|
||||
|
|
@ -107,13 +106,17 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
|
||||
const handleUserLogin = async () => {
|
||||
if (isSpotCb) {
|
||||
localStorage.setItem(SPOT_ONBOARDING, 'true')
|
||||
localStorage.setItem(SPOT_ONBOARDING, 'true');
|
||||
}
|
||||
await fetchUserInfo();
|
||||
const siteIdFromPath = parseInt(location.pathname.split('/')[1]);
|
||||
await fetchSiteList(siteIdFromPath);
|
||||
props.mstore.initClient();
|
||||
|
||||
if (localSpotJwt && !isTokenExpired(localSpotJwt)) {
|
||||
handleSpotLogin(localSpotJwt);
|
||||
}
|
||||
|
||||
const destinationPath = localStorage.getItem(GLOBAL_DESTINATION_PATH);
|
||||
if (
|
||||
destinationPath &&
|
||||
|
|
@ -144,7 +147,10 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
if (spotCb) {
|
||||
setIsSpotCb(true);
|
||||
}
|
||||
}, [spotCb])
|
||||
if (location.pathname.includes('signup')) {
|
||||
setIsSignup(true);
|
||||
}
|
||||
}, [spotCb]);
|
||||
|
||||
useEffect(() => {
|
||||
handleDestinationPath();
|
||||
|
|
@ -159,22 +165,14 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
}, [isLoggedIn]);
|
||||
|
||||
useEffect(() => {
|
||||
if (scopeSetup) {
|
||||
history.push(routes.scopeSetup())
|
||||
}
|
||||
}, [scopeSetup])
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoggedIn && (location.pathname.includes('login') || isSpotCb)) {
|
||||
if (localSpotJwt) {
|
||||
if (!isTokenExpired(localSpotJwt)) {
|
||||
handleSpotLogin(localSpotJwt);
|
||||
} else {
|
||||
logout();
|
||||
}
|
||||
if (isLoggedIn && isSpotCb && !isSignup) {
|
||||
if (localSpotJwt && !isTokenExpired(localSpotJwt)) {
|
||||
handleSpotLogin(localSpotJwt);
|
||||
} else {
|
||||
logout();
|
||||
}
|
||||
}
|
||||
}, [isSpotCb, location, isLoggedIn, localSpotJwt])
|
||||
}, [isSpotCb, isLoggedIn, localSpotJwt, isSignup]);
|
||||
|
||||
useEffect(() => {
|
||||
if (siteId && siteId !== lastFetchedSiteIdRef.current) {
|
||||
|
|
@ -204,8 +202,7 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
location.pathname.includes('multiview') ||
|
||||
location.pathname.includes('/view-spot/') ||
|
||||
location.pathname.includes('/spots/') ||
|
||||
location.pathname.includes('/scope-setup')
|
||||
|
||||
location.pathname.includes('/scope-setup');
|
||||
|
||||
if (isIframe) {
|
||||
return (
|
||||
|
|
@ -238,8 +235,11 @@ const mapStateToProps = (state: Map<string, any>) => {
|
|||
'loading',
|
||||
]);
|
||||
const sitesLoading = state.getIn(['site', 'fetchListRequest', 'loading']);
|
||||
const scopeSetup = getScope(state) === 0
|
||||
const loading = Boolean(userInfoLoading) || Boolean(sitesLoading) || (!scopeSetup && !siteId);
|
||||
const scopeSetup = getScope(state) === 0;
|
||||
const loading =
|
||||
Boolean(userInfoLoading) ||
|
||||
Boolean(sitesLoading) ||
|
||||
(!scopeSetup && !siteId);
|
||||
return {
|
||||
siteId,
|
||||
changePassword,
|
||||
|
|
@ -262,7 +262,6 @@ const mapStateToProps = (state: Map<string, any>) => {
|
|||
|
||||
const mapDispatchToProps = {
|
||||
fetchUserInfo,
|
||||
fetchTenants,
|
||||
setSessionPath,
|
||||
fetchSiteList,
|
||||
setJwt,
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ function ClickMapCard({
|
|||
const onMarkerClick = (s: string, innerText: string) => {
|
||||
metricStore.changeClickMapSearch(s, innerText)
|
||||
}
|
||||
const mapUrl = metricStore.instance.series[0].filter.filters[0].value[0]
|
||||
|
||||
const sessionId = metricStore.instance.data.sessionId
|
||||
const url = metricStore.instance.data.path;
|
||||
const operator = metricStore.instance.series[0].filter.filters[0].operator
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => setCustomSession(null)
|
||||
|
|
@ -36,8 +38,16 @@ function ClickMapCard({
|
|||
const rangeValue = dashboardStore.drillDownPeriod.rangeValue
|
||||
const startDate = dashboardStore.drillDownPeriod.start
|
||||
const endDate = dashboardStore.drillDownPeriod.end
|
||||
fetchInsights({ ...insightsFilters, url: mapUrl || '/', startDate, endDate, rangeValue, clickRage: metricStore.clickMapFilter })
|
||||
}, [dashboardStore.drillDownPeriod.start, dashboardStore.drillDownPeriod.end, dashboardStore.drillDownPeriod.rangeValue, metricStore.clickMapFilter])
|
||||
fetchInsights({
|
||||
...insightsFilters,
|
||||
url,
|
||||
startDate,
|
||||
endDate,
|
||||
rangeValue,
|
||||
clickRage: metricStore.clickMapFilter,
|
||||
operator,
|
||||
})
|
||||
}, [dashboardStore.drillDownPeriod.start, operator, url, dashboardStore.drillDownPeriod.end, dashboardStore.drillDownPeriod.rangeValue, metricStore.clickMapFilter])
|
||||
|
||||
if (!metricStore.instance.data.domURL || insights.size === 0) {
|
||||
return (
|
||||
|
|
@ -59,7 +69,7 @@ function ClickMapCard({
|
|||
}
|
||||
|
||||
const jumpToEvent = metricStore.instance.data.events.find((evt: Record<string, any>) => {
|
||||
if (mapUrl) return evt.path.includes(mapUrl)
|
||||
if (url) return evt.path.includes(url)
|
||||
return evt
|
||||
}) || { timestamp: metricStore.instance.data.startTs }
|
||||
const ts = jumpToEvent.timestamp ?? metricStore.instance.data.startTs
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { ArrowRightOutlined } from '@ant-design/icons';
|
|||
import { Button, Card, Radio } from 'antd';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { upgradeScope, downgradeScope } from "App/duck/user";
|
||||
import { upgradeScope, downgradeScope, getScope } from 'App/duck/user';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import * as routes from 'App/routes'
|
||||
import { SPOT_ONBOARDING } from "../../constants/storageKeys";
|
||||
|
|
@ -15,8 +15,18 @@ const Scope = {
|
|||
function ScopeForm({
|
||||
upgradeScope,
|
||||
downgradeScope,
|
||||
scopeState,
|
||||
}: any) {
|
||||
const [scope, setScope] = React.useState(Scope.FULL);
|
||||
React.useEffect(() => {
|
||||
if (scopeState !== 0) {
|
||||
if (scopeState === 2) {
|
||||
history.replace(routes.onboarding())
|
||||
} else {
|
||||
history.replace(routes.spotsList())
|
||||
}
|
||||
}
|
||||
}, [scopeState])
|
||||
React.useEffect(() => {
|
||||
const isSpotSetup = localStorage.getItem(SPOT_ONBOARDING)
|
||||
if (isSpotSetup) {
|
||||
|
|
@ -36,50 +46,52 @@ function ScopeForm({
|
|||
};
|
||||
return (
|
||||
<div className={'flex items-center justify-center w-screen h-screen'}>
|
||||
<Card
|
||||
style={{ width: 540 }}
|
||||
title={'👋 Welcome to OpenReplay'}
|
||||
classNames={{
|
||||
header: 'text-2xl font-semibold text-center',
|
||||
body: 'flex flex-col gap-2',
|
||||
}}
|
||||
>
|
||||
<div className={'font-semibold'}>
|
||||
How will you primarily use OpenReplay?{' '}
|
||||
</div>
|
||||
<div className={'text-disabled-text'}>
|
||||
<div>
|
||||
You will have access to all OpenReplay features regardless of your
|
||||
choice.
|
||||
</div>
|
||||
<div>
|
||||
Your preference will simply help us tailor your onboarding experience.
|
||||
</div>
|
||||
</div>
|
||||
<Radio.Group
|
||||
value={scope}
|
||||
onChange={(e) => setScope(e.target.value)}
|
||||
className={'flex flex-col gap-2 mt-4 '}
|
||||
<Card
|
||||
style={{ width: 540 }}
|
||||
title={'👋 Welcome to OpenReplay'}
|
||||
classNames={{
|
||||
header: 'text-2xl font-semibold text-center',
|
||||
body: 'flex flex-col gap-2',
|
||||
}}
|
||||
>
|
||||
<Radio value={'full'}>
|
||||
Session Replay & Debugging, Customer Support and more
|
||||
</Radio>
|
||||
<Radio value={'spot'}>Report bugs via Spot</Radio>
|
||||
</Radio.Group>
|
||||
|
||||
<div className={'self-end'}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={() => onContinue()}
|
||||
icon={<ArrowRightOutlined />}
|
||||
iconPosition={'end'}
|
||||
<div className={'font-semibold'}>
|
||||
How will you primarily use OpenReplay?{' '}
|
||||
</div>
|
||||
<div className={'text-disabled-text'}>
|
||||
<div>
|
||||
You will have access to all OpenReplay features regardless of your
|
||||
choice.
|
||||
</div>
|
||||
<div>
|
||||
Your preference will simply help us tailor your onboarding experience.
|
||||
</div>
|
||||
</div>
|
||||
<Radio.Group
|
||||
value={scope}
|
||||
onChange={(e) => setScope(e.target.value)}
|
||||
className={'flex flex-col gap-2 mt-4 '}
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<Radio value={'full'}>
|
||||
Session Replay & Debugging, Customer Support and more
|
||||
</Radio>
|
||||
<Radio value={'spot'}>Report bugs via Spot</Radio>
|
||||
</Radio.Group>
|
||||
|
||||
<div className={'self-end'}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={() => onContinue()}
|
||||
icon={<ArrowRightOutlined />}
|
||||
iconPosition={'end'}
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, { upgradeScope, downgradeScope })(ScopeForm);
|
||||
export default connect((state) => ({
|
||||
scopeState: getScope(state),
|
||||
}), { upgradeScope, downgradeScope })(ScopeForm);
|
||||
|
|
|
|||
|
|
@ -83,9 +83,8 @@ function Player(props: IProps) {
|
|||
<div
|
||||
onMouseDown={handleResize}
|
||||
className={'w-full h-2 cursor-ns-resize absolute top-0 left-0 z-20'}
|
||||
>
|
||||
/>
|
||||
<ConsolePanel isLive />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{!fullView && !isMultiview ? <LiveControls jump={playerContext.player.jump} /> : null}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from '@ant-design/icons';
|
||||
import { Button, InputNumber, Popover } from 'antd';
|
||||
import { Slider } from 'antd';
|
||||
import cn from 'classnames';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
|
|
@ -24,17 +25,38 @@ function DropdownAudioPlayer({
|
|||
const [isMuted, setIsMuted] = useState(false);
|
||||
const lastPlayerTime = useRef(0);
|
||||
const audioRefs = useRef<Record<string, HTMLAudioElement | null>>({});
|
||||
|
||||
const fileLengths = useRef<Record<string, number>>({});
|
||||
const { time = 0, speed = 1, playing, sessionStart } = store?.get() ?? {};
|
||||
|
||||
const files = audioEvents.map((pa) => {
|
||||
const data = pa.payload;
|
||||
return {
|
||||
url: data.url,
|
||||
timestamp: data.timestamp,
|
||||
start: pa.timestamp - sessionStart,
|
||||
};
|
||||
});
|
||||
const files = React.useMemo(
|
||||
() =>
|
||||
audioEvents.map((pa) => {
|
||||
const data = pa.payload;
|
||||
const nativeTs = data.timestamp;
|
||||
const startTs = nativeTs
|
||||
? nativeTs > sessionStart
|
||||
? nativeTs - sessionStart
|
||||
: nativeTs
|
||||
: pa.timestamp - sessionStart;
|
||||
return {
|
||||
url: data.url,
|
||||
timestamp: data.timestamp,
|
||||
start: Math.max(0, startTs),
|
||||
};
|
||||
}),
|
||||
[audioEvents.length, sessionStart]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
Object.entries(audioRefs.current).forEach(([url, audio]) => {
|
||||
if (audio) {
|
||||
audio.loop = false;
|
||||
audio.addEventListener('loadedmetadata', () => {
|
||||
fileLengths.current[url] = audio.duration;
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [audioRefs.current]);
|
||||
|
||||
const toggleMute = () => {
|
||||
Object.values(audioRefs.current).forEach((audio) => {
|
||||
|
|
@ -89,10 +111,15 @@ function DropdownAudioPlayer({
|
|||
if (audio) {
|
||||
const file = files.find((f) => f.url === key);
|
||||
if (file) {
|
||||
audio.currentTime = Math.max(
|
||||
(timeMs + delta * 1000 - file.start) / 1000,
|
||||
0
|
||||
);
|
||||
const targetTime = (timeMs + delta * 1000 - file.start) / 1000;
|
||||
const fileLength = fileLengths.current[key];
|
||||
if (targetTime < 0 || (fileLength && targetTime > fileLength)) {
|
||||
audio.pause();
|
||||
audio.currentTime = 0;
|
||||
return;
|
||||
} else {
|
||||
audio.currentTime = targetTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -108,27 +135,39 @@ function DropdownAudioPlayer({
|
|||
|
||||
useEffect(() => {
|
||||
const deltaMs = delta * 1000;
|
||||
if (Math.abs(lastPlayerTime.current - time - deltaMs) >= 250) {
|
||||
const deltaTime = Math.abs(lastPlayerTime.current - time - deltaMs);
|
||||
if (deltaTime >= 250) {
|
||||
handleSeek(time);
|
||||
}
|
||||
Object.entries(audioRefs.current).forEach(([url, audio]) => {
|
||||
if (audio) {
|
||||
const file = files.find((f) => f.url === url);
|
||||
if (file && time >= file.start) {
|
||||
if (audio.paused && playing) {
|
||||
audio.play();
|
||||
const fileLength = fileLengths.current[url];
|
||||
if (file) {
|
||||
if (fileLength && fileLength * 1000 + file.start < time) {
|
||||
return;
|
||||
}
|
||||
if (time >= file.start) {
|
||||
if (audio.paused && playing) {
|
||||
audio.play();
|
||||
}
|
||||
} else {
|
||||
audio.pause();
|
||||
}
|
||||
} else {
|
||||
audio.pause();
|
||||
}
|
||||
if (audio.muted !== isMuted) {
|
||||
audio.muted = isMuted;
|
||||
}
|
||||
}
|
||||
});
|
||||
lastPlayerTime.current = time + deltaMs;
|
||||
}, [time, delta]);
|
||||
|
||||
useEffect(() => {
|
||||
Object.values(audioRefs.current).forEach((audio) => {
|
||||
if (audio) {
|
||||
audio.muted = isMuted;
|
||||
}
|
||||
});
|
||||
}, [isMuted]);
|
||||
|
||||
useEffect(() => {
|
||||
changePlaybackSpeed(speed);
|
||||
}, [speed]);
|
||||
|
|
@ -137,22 +176,30 @@ function DropdownAudioPlayer({
|
|||
Object.entries(audioRefs.current).forEach(([url, audio]) => {
|
||||
if (audio) {
|
||||
const file = files.find((f) => f.url === url);
|
||||
if (file && playing && time >= file.start) {
|
||||
audio.play();
|
||||
} else {
|
||||
audio.pause();
|
||||
const fileLength = fileLengths.current[url];
|
||||
if (file) {
|
||||
if (fileLength && fileLength * 1000 + file.start < time) {
|
||||
audio.pause();
|
||||
return;
|
||||
}
|
||||
if (playing && time >= file.start) {
|
||||
audio.play();
|
||||
} else {
|
||||
audio.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
setVolume(isMuted ? 0 : volume);
|
||||
}, [playing]);
|
||||
|
||||
const buttonIcon =
|
||||
'px-2 cursor-pointer border border-gray-light hover:border-main hover:text-main hover:z-10 h-fit';
|
||||
return (
|
||||
<div className={'relative'}>
|
||||
<div className={'flex items-center'} style={{ height: 24 }}>
|
||||
<Popover
|
||||
trigger={'click'}
|
||||
className={'h-full'}
|
||||
content={
|
||||
<div
|
||||
className={'flex flex-col gap-2 rounded'}
|
||||
|
|
@ -169,20 +216,14 @@ function DropdownAudioPlayer({
|
|||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
'px-2 h-full cursor-pointer border rounded-l border-gray-light hover:border-main hover:text-main hover:z-10'
|
||||
}
|
||||
>
|
||||
<div className={cn(buttonIcon, 'rounded-l')}>
|
||||
{isMuted ? <MutedOutlined /> : <SoundOutlined />}
|
||||
</div>
|
||||
</Popover>
|
||||
<div
|
||||
onClick={toggleVisible}
|
||||
style={{ marginLeft: -1 }}
|
||||
className={
|
||||
'px-2 h-full border rounded-r border-gray-light cursor-pointer hover:border-main hover:text-main hover:z-10'
|
||||
}
|
||||
className={cn(buttonIcon, 'rounded-r')}
|
||||
>
|
||||
<CaretDownOutlined />
|
||||
</div>
|
||||
|
|
@ -236,6 +277,7 @@ function DropdownAudioPlayer({
|
|||
<div style={{ display: 'none' }}>
|
||||
{files.map((file) => (
|
||||
<audio
|
||||
loop={false}
|
||||
key={file.url}
|
||||
ref={(el) => (audioRefs.current[file.url] = el)}
|
||||
controls
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function ConsoleRow(props: Props) {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={cn(stl.line, 'flex py-2 px-4 overflow-hidden group relative select-none', {
|
||||
className={cn(stl.line, 'flex py-2 px-4 overflow-hidden group relative', {
|
||||
info: !log.isYellow && !log.isRed,
|
||||
warn: log.isYellow,
|
||||
error: log.isRed,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const SpotsListHeader = observer(
|
|||
onDelete,
|
||||
selectedCount,
|
||||
onClearSelection,
|
||||
isEmpty,
|
||||
tenantHasSpots,
|
||||
onRefresh,
|
||||
}: {
|
||||
onDelete: () => void;
|
||||
|
|
@ -18,6 +18,7 @@ const SpotsListHeader = observer(
|
|||
onClearSelection: () => void;
|
||||
onRefresh: () => void;
|
||||
isEmpty?: boolean;
|
||||
tenantHasSpots: boolean;
|
||||
}) => {
|
||||
const { spotStore } = useStore();
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ const SpotsListHeader = observer(
|
|||
<ReloadButton buttonSize={'small'} onClick={onRefresh} iconSize={16} />
|
||||
</div>
|
||||
|
||||
{isEmpty ? null : (
|
||||
{tenantHasSpots ? (
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className={'ml-auto'}>
|
||||
{selectedCount > 0 && (
|
||||
|
|
@ -90,7 +91,7 @@ const SpotsListHeader = observer(
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ function SpotsList() {
|
|||
selectedCount={selectedSpots.length}
|
||||
onClearSelection={clearSelection}
|
||||
isEmpty={isEmpty}
|
||||
tenantHasSpots={spotStore.tenantHasSpots}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ function ConsolePanel({
|
|||
exceptionsList = [],
|
||||
logListNow = [],
|
||||
exceptionsListNow = [],
|
||||
} = tabStates[currentTab];
|
||||
} = tabStates[currentTab] ?? {};
|
||||
|
||||
const list = isLive
|
||||
? (useMemo(
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ function ConsoleRow(props: Props) {
|
|||
<div
|
||||
style={style}
|
||||
className={cn(
|
||||
'border-b flex items-start py-1 px-4 pe-8 overflow-hidden group relative select-none',
|
||||
'border-b flex items-start py-1 px-4 pe-8 overflow-hidden group relative',
|
||||
{
|
||||
info: !log.isYellow && !log.isRed,
|
||||
warn: log.isYellow,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const ALL = 'ALL';
|
|||
const TAB_KEYS = [ALL, ...typeList] as const;
|
||||
const TABS = TAB_KEYS.map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
type EventsList = Array<Timed & { name: string; source: string; key: string }>;
|
||||
type EventsList = Array<Timed & { name: string; source: string; key: string; payload?: string[] }>;
|
||||
|
||||
const WebStackEventPanelComp = observer(
|
||||
({
|
||||
|
|
@ -95,7 +95,7 @@ export const MobileStackEventPanel = connect((state: Record<string, any>) => ({
|
|||
zoomEndTs: state.getIn(['components', 'player']).timelineZoom.endTs,
|
||||
}))(MobileStackEventPanelComp);
|
||||
|
||||
function EventsPanel({
|
||||
const EventsPanel = observer(({
|
||||
list,
|
||||
listNow,
|
||||
jump,
|
||||
|
|
@ -109,7 +109,7 @@ function EventsPanel({
|
|||
zoomEnabled: boolean;
|
||||
zoomStartTs: number;
|
||||
zoomEndTs: number;
|
||||
}) {
|
||||
}) => {
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
} = useStore();
|
||||
|
|
@ -126,13 +126,19 @@ function EventsPanel({
|
|||
zoomEnabled ? zoomStartTs <= time && time <= zoomEndTs : true
|
||||
);
|
||||
|
||||
let filteredList = useRegExListFilterMemo(inZoomRangeList, (it) => it.name, filter);
|
||||
let filteredList = useRegExListFilterMemo(inZoomRangeList, (it) => {
|
||||
const searchBy = [it.name]
|
||||
if (it.payload) {
|
||||
const payload = Array.isArray(it.payload) ? it.payload.join(',') : JSON.stringify(it.payload);
|
||||
searchBy.push(payload);
|
||||
}
|
||||
return searchBy
|
||||
}, filter);
|
||||
filteredList = useTabListFilterMemo(filteredList, (it) => it.source, ALL, activeTab);
|
||||
|
||||
const onTabClick = (activeTab: (typeof TAB_KEYS)[number]) =>
|
||||
devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) =>
|
||||
devTools.update(INDEX_KEY, { filter: value });
|
||||
const onFilterChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => devTools.update(INDEX_KEY, { filter: value });
|
||||
const tabs = useMemo(
|
||||
() => TABS.filter(({ key }) => key === ALL || inZoomRangeList.some(({ source }) => key === source)),
|
||||
[inZoomRangeList.length]
|
||||
|
|
@ -229,4 +235,4 @@ function EventsPanel({
|
|||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import { makeAutoObservable } from 'mobx';
|
||||
|
||||
|
||||
|
||||
import { spotService } from 'App/services';
|
||||
import { UpdateSpotRequest } from 'App/services/spotService';
|
||||
|
||||
|
||||
|
||||
import { Spot } from './types/spot';
|
||||
|
||||
|
||||
export default class SpotStore {
|
||||
isLoading: boolean = false;
|
||||
spots: Spot[] = [];
|
||||
|
|
@ -18,6 +23,7 @@ export default class SpotStore {
|
|||
pubKey: { value: string; expiration: number } | null = null;
|
||||
readonly order = 'desc';
|
||||
accessError = false;
|
||||
tenantHasSpots = false;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
|
|
@ -81,13 +87,18 @@ export default class SpotStore {
|
|||
limit: this.limit,
|
||||
} as const;
|
||||
|
||||
const response = await this.withLoader(() =>
|
||||
const { spots, tenantHasSpots, total } = await this.withLoader(() =>
|
||||
spotService.fetchSpots(filters)
|
||||
);
|
||||
this.setSpots(response.spots.map((spot: any) => new Spot(spot)));
|
||||
this.setTotal(response.total);
|
||||
this.setSpots(spots.map((spot: any) => new Spot(spot)));
|
||||
this.setTotal(total);
|
||||
this.setTenantHasSpots(tenantHasSpots);
|
||||
};
|
||||
|
||||
setTenantHasSpots(hasSpots: boolean) {
|
||||
this.tenantHasSpots = hasSpots;
|
||||
}
|
||||
|
||||
async fetchSpotById(id: string) {
|
||||
try {
|
||||
const response = await this.withLoader(() =>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
position: absolute;
|
||||
width: 36px;
|
||||
height: 60px;
|
||||
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg enable-background='new 0 0 28 28' version='1.1' viewBox='0 0 28 28' xml:space='preserve' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon points='8.2 20.9 8.2 4.9 19.8 16.5 13 16.5 12.6 16.6' fill='%23fff'/%3E%3Cpolygon points='17.3 21.6 13.7 23.1 9 12 12.7 10.5' fill='%23fff'/%3E%3Crect transform='matrix(.9221 -.3871 .3871 .9221 -5.7605 6.5909)' x='12.5' y='13.6' width='2' height='8'/%3E%3Cpolygon points='9.2 7.3 9.2 18.5 12.2 15.6 12.6 15.5 17.4 15.5'/%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3Csvg enable-background='new 0 0 20 20' version='1.1' viewBox='8.5 5.2 28 28' xml:space='preserve' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon points='8.2 20.9 8.2 4.9 19.8 16.5 13 16.5 12.6 16.6' fill='%23fff'/%3E%3Cpolygon points='17.3 21.6 13.7 23.1 9 12 12.7 10.5' fill='%23fff'/%3E%3Crect transform='matrix(.9221 -.3871 .3871 .9221 -5.7605 6.5909)' x='12.5' y='13.6' width='2' height='8'/%3E%3Cpolygon points='9.2 7.3 9.2 18.5 12.2 15.6 12.6 15.5 17.4 15.5'/%3E%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
transition: top .15s ease-out, left .15s ease-out;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import logger from 'App/logger';
|
||||
import { resolveURL } from "../../messages/rewriter/urlResolve";
|
||||
|
||||
import type Screen from '../../Screen/Screen';
|
||||
import type { Message, SetNodeScroll } from '../../messages';
|
||||
|
|
@ -32,6 +31,8 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
private readonly vTexts: Map<number, VText> = new Map() // map vs object here?
|
||||
private readonly vElements: Map<number, VElement> = new Map()
|
||||
private readonly olVRoots: Map<number, OnloadVRoot> = new Map()
|
||||
/** required to keep track of iframes, frameId : vnodeId */
|
||||
private readonly iframeRoots: Record<number, number> = {}
|
||||
/** Constructed StyleSheets https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
|
||||
* as well as <style> tag owned StyleSheets
|
||||
*/
|
||||
|
|
@ -219,6 +220,10 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
if (['STYLE', 'style', 'LINK'].includes(msg.tag)) {
|
||||
vElem.prioritized = true
|
||||
}
|
||||
if (this.vElements.has(msg.id)) {
|
||||
logger.error("CreateElementNode: Node already exists", msg)
|
||||
return
|
||||
}
|
||||
this.vElements.set(msg.id, vElem)
|
||||
this.insertNode(msg)
|
||||
this.removeBodyScroll(msg.id, vElem)
|
||||
|
|
@ -316,6 +321,10 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
case MType.CreateIFrameDocument: {
|
||||
const vElem = this.vElements.get(msg.frameID)
|
||||
if (!vElem) { logger.error("CreateIFrameDocument: Node not found", msg); return }
|
||||
if (this.iframeRoots[msg.frameID] && !this.olVRoots.has(msg.id)) {
|
||||
this.olVRoots.delete(this.iframeRoots[msg.frameID])
|
||||
}
|
||||
this.iframeRoots[msg.frameID] = msg.id
|
||||
const vRoot = OnloadVRoot.fromVElement(vElem)
|
||||
vRoot.catch(e => logger.warn(e, msg))
|
||||
this.olVRoots.set(msg.id, vRoot)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ interface AddCommentRequest {
|
|||
interface GetSpotsResponse {
|
||||
spots: SpotInfo[];
|
||||
total: number;
|
||||
tenantHasSpots: boolean;
|
||||
}
|
||||
|
||||
interface GetSpotsRequest {
|
||||
|
|
|
|||
|
|
@ -504,7 +504,6 @@ export function truncateStringToFit(string: string, screenWidth: number, charWid
|
|||
|
||||
let sendingRequest = false;
|
||||
export const handleSpotJWT = (jwt: string) => {
|
||||
console.log(jwt, sendingRequest)
|
||||
let tries = 0;
|
||||
if (!jwt || sendingRequest) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
"@floating-ui/react-dom-interactions": "^0.10.3",
|
||||
"@medv/finder": "^3.1.0",
|
||||
"@reduxjs/toolkit": "^2.2.2",
|
||||
"@sentry/browser": "^5.21.1",
|
||||
"@sentry/browser": "^8.34.0",
|
||||
"@svg-maps/world": "^1.0.1",
|
||||
"@svgr/webpack": "^6.2.1",
|
||||
"@wojtekmaj/react-daterange-picker": "^6.0.0",
|
||||
|
|
@ -142,7 +142,7 @@
|
|||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"minio": "^7.1.3",
|
||||
"node-gyp": "^9.0.0",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss": "^8.4.39",
|
||||
"postcss-import": "^14.1.0",
|
||||
"postcss-loader": "^7.0.0",
|
||||
"postcss-mixins": "^9.0.2",
|
||||
|
|
@ -159,7 +159,7 @@
|
|||
"ts-jest": "^29.0.5",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^4.6.4",
|
||||
"webpack": "^5.92.1",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3188,67 +3188,90 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/browser@npm:^5.21.1":
|
||||
version: 5.30.0
|
||||
resolution: "@sentry/browser@npm:5.30.0"
|
||||
"@sentry-internal/browser-utils@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry-internal/browser-utils@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry/core": 5.30.0
|
||||
"@sentry/types": 5.30.0
|
||||
"@sentry/utils": 5.30.0
|
||||
tslib: ^1.9.3
|
||||
checksum: 6793e1b49a8cdb1f025115bcc591bf67c97b6515f62a33ffcbb7b1ab66e459ebc471797d02e471be1ebf14092b56eb25ed914f043962388cc224bc961e334a17
|
||||
"@sentry/core": 8.34.0
|
||||
"@sentry/types": 8.34.0
|
||||
"@sentry/utils": 8.34.0
|
||||
checksum: fb764f52a989307bb6369a2ae24bb83ef9880c108d2cc2aba94106c846dae743ba379291c84539306d26c3f35f16b6cd2341fa32e85cb942057f2905a33e82bf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/core@npm:5.30.0":
|
||||
version: 5.30.0
|
||||
resolution: "@sentry/core@npm:5.30.0"
|
||||
"@sentry-internal/feedback@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry-internal/feedback@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry/hub": 5.30.0
|
||||
"@sentry/minimal": 5.30.0
|
||||
"@sentry/types": 5.30.0
|
||||
"@sentry/utils": 5.30.0
|
||||
tslib: ^1.9.3
|
||||
checksum: 6407b9c2a6a56f90c198f5714b3257df24d89d1b4ca6726bd44760d0adabc25798b69fef2c88ccea461c7e79e3c78861aaebfd51fd3cb892aee656c3f7e11801
|
||||
"@sentry/core": 8.34.0
|
||||
"@sentry/types": 8.34.0
|
||||
"@sentry/utils": 8.34.0
|
||||
checksum: 7137a6b589cb56b541df52abd75a73280d3f8fd09f1983f298e29647c5dd941d5fc404d32599d4c1fe2bdcb7693d0e7886f2c08c10ad1eb7c8e17cad650e4cb3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/hub@npm:5.30.0":
|
||||
version: 5.30.0
|
||||
resolution: "@sentry/hub@npm:5.30.0"
|
||||
"@sentry-internal/replay-canvas@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry-internal/replay-canvas@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry/types": 5.30.0
|
||||
"@sentry/utils": 5.30.0
|
||||
tslib: ^1.9.3
|
||||
checksum: 386c91d06aa44be0465fc11330d748a113e464d41cd562a9e1d222a682cbcb14e697a3e640953e7a0239997ad8a02b223a0df3d9e1d8816cb823fd3613be3e2f
|
||||
"@sentry-internal/replay": 8.34.0
|
||||
"@sentry/core": 8.34.0
|
||||
"@sentry/types": 8.34.0
|
||||
"@sentry/utils": 8.34.0
|
||||
checksum: 55c53be37e0c06706e099a96d1485636b8d3f11b72078c279fda6e7992205d217d27dae9e609db2c0466db0755bd038087e76cfe746eaff9ce39bbfd1f1571a5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/minimal@npm:5.30.0":
|
||||
version: 5.30.0
|
||||
resolution: "@sentry/minimal@npm:5.30.0"
|
||||
"@sentry-internal/replay@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry-internal/replay@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry/hub": 5.30.0
|
||||
"@sentry/types": 5.30.0
|
||||
tslib: ^1.9.3
|
||||
checksum: 34ec05503de46d01f98c94701475d5d89cc044892c86ccce30e01f62f28344eb23b718e7cf573815e46f30a4ac9da3129bed9b3d20c822938acfb40cbe72437b
|
||||
"@sentry-internal/browser-utils": 8.34.0
|
||||
"@sentry/core": 8.34.0
|
||||
"@sentry/types": 8.34.0
|
||||
"@sentry/utils": 8.34.0
|
||||
checksum: 8a4b6f1f169584ddd62c372760168ea2d63ca0d6ebd6433e45d760fcbb2610418a2bf6546bbda49ecd619deddf39b4ac268b87a15adbb56efc0b86edf4c40dd9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/types@npm:5.30.0":
|
||||
version: 5.30.0
|
||||
resolution: "@sentry/types@npm:5.30.0"
|
||||
checksum: 99c6e55c0a82c8ca95be2e9dbb35f581b29e4ff7af74b23bc62b690de4e35febfa15868184a2303480ef86babd4fea5273cf3b5ddf4a27685b841a72f13a0c88
|
||||
"@sentry/browser@npm:^8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry/browser@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils": 8.34.0
|
||||
"@sentry-internal/feedback": 8.34.0
|
||||
"@sentry-internal/replay": 8.34.0
|
||||
"@sentry-internal/replay-canvas": 8.34.0
|
||||
"@sentry/core": 8.34.0
|
||||
"@sentry/types": 8.34.0
|
||||
"@sentry/utils": 8.34.0
|
||||
checksum: 8a08033fce2908018cc3fc81cf1110a93a338c0d370628a2e9aaa9f43703041824462474037e59b2b166141835b7e94b437325bd6a46bb8371e37b659b216d10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/utils@npm:5.30.0":
|
||||
version: 5.30.0
|
||||
resolution: "@sentry/utils@npm:5.30.0"
|
||||
"@sentry/core@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry/core@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry/types": 5.30.0
|
||||
tslib: ^1.9.3
|
||||
checksum: ca8eebfea7ac7db6d16f6c0b8a66ac62587df12a79ce9d0d8393f4d69880bb8d40d438f9810f7fb107a9880fe0d68bbf797b89cbafd113e89a0829eb06b205f8
|
||||
"@sentry/types": 8.34.0
|
||||
"@sentry/utils": 8.34.0
|
||||
checksum: 0ab7e11bd382cb47ade38f3c9615e6fb876bad43eba4b376a51e44b1c57e00efe2e74f3cc0790a8da6c0be16093086bc65d89cf5387453f93ae96e10a41a0d60
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/types@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry/types@npm:8.34.0"
|
||||
checksum: d35bf72129f621af2f7916b0805c6948d210791757bee690fc6b68f2412bbe80c8ec704a0f8eb8ee45eb78deeadbd3c69830469b62fba4827506ea30c235f4e8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sentry/utils@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@sentry/utils@npm:8.34.0"
|
||||
dependencies:
|
||||
"@sentry/types": 8.34.0
|
||||
checksum: 60612dba8320c736f9559ba2fb4efe2927fd9d4a1f29bff36f116ad30c9ce210f6677013052a69cb7e16c5d28f1d8d7465d9278e72f7384b84e924cf3ed2790c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -7667,9 +7690,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"body-parser@npm:1.20.2":
|
||||
version: 1.20.2
|
||||
resolution: "body-parser@npm:1.20.2"
|
||||
"body-parser@npm:1.20.3":
|
||||
version: 1.20.3
|
||||
resolution: "body-parser@npm:1.20.3"
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: ~1.0.5
|
||||
|
|
@ -7679,11 +7702,11 @@ __metadata:
|
|||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
on-finished: 2.4.1
|
||||
qs: 6.11.0
|
||||
qs: 6.13.0
|
||||
raw-body: 2.5.2
|
||||
type-is: ~1.6.18
|
||||
unpipe: 1.0.0
|
||||
checksum: 06f1438fff388a2e2354c96aa3ea8147b79bfcb1262dfcc2aae68ec13723d01d5781680657b74e9f83c808266d5baf52804032fbde2b7382b89bd8cdb273ace9
|
||||
checksum: 0a9a93b7518f222885498dcecaad528cf010dd109b071bf471c93def4bfe30958b83e03496eb9c1ad4896db543d999bb62be1a3087294162a88cfa1b42c16310
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -9099,10 +9122,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie@npm:0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "cookie@npm:0.6.0"
|
||||
checksum: f2318b31af7a31b4ddb4a678d024514df5e705f9be5909a192d7f116cfb6d45cbacf96a473fa733faa95050e7cff26e7832bb3ef94751592f1387b71c8956686
|
||||
"cookie@npm:0.7.1":
|
||||
version: 0.7.1
|
||||
resolution: "cookie@npm:0.7.1"
|
||||
checksum: 5de60c67a410e7c8dc8a46a4b72eb0fe925871d057c9a5d2c0e8145c4270a4f81076de83410c4d397179744b478e33cd80ccbcc457abf40a9409ad27dcd21dde
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -10507,9 +10530,9 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"dompurify@npm:^2.2.0":
|
||||
version: 2.5.0
|
||||
resolution: "dompurify@npm:2.5.0"
|
||||
checksum: 637dcf3430f3fedf66b58f84fd59ea9b3615a19a6db5efe444c635b2473a77a345b31d7328b56dbc80f692791915ffd6049d69041ff013e33692fdb8b0d84e48
|
||||
version: 2.5.7
|
||||
resolution: "dompurify@npm:2.5.7"
|
||||
checksum: 23c4f737182fcf3e731e458c3930ef4d2916191e4180e1e345f153124dfa7ec117d2810af1754e8854c581131fc75dac914a8391183d1511852ef32b4055f711
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -10639,8 +10662,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"elliptic@npm:^6.5.3, elliptic@npm:^6.5.5":
|
||||
version: 6.5.5
|
||||
resolution: "elliptic@npm:6.5.5"
|
||||
version: 6.5.7
|
||||
resolution: "elliptic@npm:6.5.7"
|
||||
dependencies:
|
||||
bn.js: ^4.11.9
|
||||
brorand: ^1.1.0
|
||||
|
|
@ -10649,7 +10672,7 @@ __metadata:
|
|||
inherits: ^2.0.4
|
||||
minimalistic-assert: ^1.0.1
|
||||
minimalistic-crypto-utils: ^1.0.1
|
||||
checksum: 3e591e93783a1b66f234ebf5bd3a8a9a8e063a75073a35a671e03e3b25253b6e33ac121f7efe9b8808890fffb17b40596cc19d01e6e8d1fa13b9a56ff65597c8
|
||||
checksum: 799959b6c54ea3564e8961f35abdf8c77e37617f3051614b05ab1fb6a04ddb65bd1caa75ed1bae375b15dda312a0f79fed26ebe76ecf05c5a7af244152a601b8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -10695,6 +10718,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"encodeurl@npm:~2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "encodeurl@npm:2.0.0"
|
||||
checksum: 5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"encoding@npm:^0.1.11, encoding@npm:^0.1.13":
|
||||
version: 0.1.13
|
||||
resolution: "encoding@npm:0.1.13"
|
||||
|
|
@ -10765,13 +10795,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"enhanced-resolve@npm:^5.17.0":
|
||||
version: 5.17.0
|
||||
resolution: "enhanced-resolve@npm:5.17.0"
|
||||
"enhanced-resolve@npm:^5.17.1":
|
||||
version: 5.17.1
|
||||
resolution: "enhanced-resolve@npm:5.17.1"
|
||||
dependencies:
|
||||
graceful-fs: ^4.2.4
|
||||
tapable: ^2.2.0
|
||||
checksum: 90065e58e4fd08e77ba47f827eaa17d60c335e01e4859f6e644bb3b8d0e32b203d33894aee92adfa5121fa262f912b48bdf0d0475e98b4a0a1132eea1169ad37
|
||||
checksum: 81a0515675eca17efdba2cf5bad87abc91a528fc1191aad50e275e74f045b41506167d420099022da7181c8d787170ea41e4a11a0b10b7a16f6237daecb15370
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -11464,41 +11494,41 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"express@npm:^4.17.1, express@npm:^4.17.3":
|
||||
version: 4.19.2
|
||||
resolution: "express@npm:4.19.2"
|
||||
version: 4.21.1
|
||||
resolution: "express@npm:4.21.1"
|
||||
dependencies:
|
||||
accepts: ~1.3.8
|
||||
array-flatten: 1.1.1
|
||||
body-parser: 1.20.2
|
||||
body-parser: 1.20.3
|
||||
content-disposition: 0.5.4
|
||||
content-type: ~1.0.4
|
||||
cookie: 0.6.0
|
||||
cookie: 0.7.1
|
||||
cookie-signature: 1.0.6
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
encodeurl: ~1.0.2
|
||||
encodeurl: ~2.0.0
|
||||
escape-html: ~1.0.3
|
||||
etag: ~1.8.1
|
||||
finalhandler: 1.2.0
|
||||
finalhandler: 1.3.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
merge-descriptors: 1.0.1
|
||||
merge-descriptors: 1.0.3
|
||||
methods: ~1.1.2
|
||||
on-finished: 2.4.1
|
||||
parseurl: ~1.3.3
|
||||
path-to-regexp: 0.1.7
|
||||
path-to-regexp: 0.1.10
|
||||
proxy-addr: ~2.0.7
|
||||
qs: 6.11.0
|
||||
qs: 6.13.0
|
||||
range-parser: ~1.2.1
|
||||
safe-buffer: 5.2.1
|
||||
send: 0.18.0
|
||||
serve-static: 1.15.0
|
||||
send: 0.19.0
|
||||
serve-static: 1.16.2
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.1
|
||||
type-is: ~1.6.18
|
||||
utils-merge: 1.0.1
|
||||
vary: ~1.1.2
|
||||
checksum: e82e2662ea9971c1407aea9fc3c16d6b963e55e3830cd0ef5e00b533feda8b770af4e3be630488ef8a752d7c75c4fcefb15892868eeaafe7353cb9e3e269fdcb
|
||||
checksum: 0c287867e5f6129d3def1edd9b63103a53c40d4dc8628839d4b6827e35eb8f0de5a4656f9d85f4457eba584f9871ebb2ad26c750b36bd75d9bbb8bcebdc4892c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -11638,13 +11668,13 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"fast-xml-parser@npm:^4.2.2":
|
||||
version: 4.3.6
|
||||
resolution: "fast-xml-parser@npm:4.3.6"
|
||||
version: 4.5.0
|
||||
resolution: "fast-xml-parser@npm:4.5.0"
|
||||
dependencies:
|
||||
strnum: ^1.0.5
|
||||
bin:
|
||||
fxparser: src/cli/cli.js
|
||||
checksum: 9ebe2ac142c6978cae423c39c2a9b561edb76be584317d578768ed4a006a61fc0e83abf8c6fe31029139c4ad15ea1f2e7b6720ba9e6eda0e5266d7f2770fb079
|
||||
checksum: 71d206c9e137f5c26af88d27dde0108068a5d074401901d643c500c36e95dfd828333a98bda020846c41f5b9b364e2b0e9be5b19b0bdcab5cf31559c07b80a95
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -11794,18 +11824,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"finalhandler@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "finalhandler@npm:1.2.0"
|
||||
"finalhandler@npm:1.3.1":
|
||||
version: 1.3.1
|
||||
resolution: "finalhandler@npm:1.3.1"
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
encodeurl: ~1.0.2
|
||||
encodeurl: ~2.0.0
|
||||
escape-html: ~1.0.3
|
||||
on-finished: 2.4.1
|
||||
parseurl: ~1.3.3
|
||||
statuses: 2.0.1
|
||||
unpipe: ~1.0.0
|
||||
checksum: 64b7e5ff2ad1fcb14931cd012651631b721ce657da24aedb5650ddde9378bf8e95daa451da43398123f5de161a81e79ff5affe4f9f2a6d2df4a813d6d3e254b7
|
||||
checksum: d38035831865a49b5610206a3a9a9aae4e8523cbbcd01175d0480ffbf1278c47f11d89be3ca7f617ae6d94f29cf797546a4619cd84dd109009ef33f12f69019f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -16660,10 +16690,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge-descriptors@npm:1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "merge-descriptors@npm:1.0.1"
|
||||
checksum: b67d07bd44cfc45cebdec349bb6e1f7b077ee2fd5beb15d1f7af073849208cb6f144fe403e29a36571baf3f4e86469ac39acf13c318381e958e186b2766f54ec
|
||||
"merge-descriptors@npm:1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "merge-descriptors@npm:1.0.3"
|
||||
checksum: 866b7094afd9293b5ea5dcd82d71f80e51514bed33b4c4e9f516795dc366612a4cbb4dc94356e943a8a6914889a914530badff27f397191b9b75cda20b6bae93
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -18149,7 +18179,7 @@ __metadata:
|
|||
"@medv/finder": ^3.1.0
|
||||
"@openreplay/sourcemap-uploader": ^3.0.8
|
||||
"@reduxjs/toolkit": ^2.2.2
|
||||
"@sentry/browser": ^5.21.1
|
||||
"@sentry/browser": ^8.34.0
|
||||
"@storybook/addon-actions": ^6.5.12
|
||||
"@storybook/addon-docs": ^6.5.12
|
||||
"@storybook/addon-essentials": ^6.5.12
|
||||
|
|
@ -18217,7 +18247,7 @@ __metadata:
|
|||
mobx-react-lite: ^3.1.6
|
||||
node-gyp: ^9.0.0
|
||||
peerjs: 1.3.2
|
||||
postcss: ^8.4.14
|
||||
postcss: ^8.4.39
|
||||
postcss-import: ^14.1.0
|
||||
postcss-loader: ^7.0.0
|
||||
postcss-mixins: ^9.0.2
|
||||
|
|
@ -18260,7 +18290,7 @@ __metadata:
|
|||
ts-node: ^10.7.0
|
||||
typescript: ^4.6.4
|
||||
virtua: ^0.33.4
|
||||
webpack: ^5.92.1
|
||||
webpack: ^5.94.0
|
||||
webpack-cli: ^5.1.4
|
||||
webpack-dev-server: ^5.0.4
|
||||
languageName: unknown
|
||||
|
|
@ -18770,10 +18800,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-to-regexp@npm:0.1.7":
|
||||
version: 0.1.7
|
||||
resolution: "path-to-regexp@npm:0.1.7"
|
||||
checksum: 50a1ddb1af41a9e68bd67ca8e331a705899d16fb720a1ea3a41e310480948387daf603abb14d7b0826c58f10146d49050a1291ba6a82b78a382d1c02c0b8f905
|
||||
"path-to-regexp@npm:0.1.10":
|
||||
version: 0.1.10
|
||||
resolution: "path-to-regexp@npm:0.1.10"
|
||||
checksum: 34196775b9113ca6df88e94c8d83ba82c0e1a2063dd33bfe2803a980da8d49b91db8104f49d5191b44ea780d46b8670ce2b7f4a5e349b0c48c6779b653f1afe4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -18873,6 +18903,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"picocolors@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "picocolors@npm:1.1.0"
|
||||
checksum: 86946f6032148801ef09c051c6fb13b5cf942eaf147e30ea79edb91dd32d700934edebe782a1078ff859fb2b816792e97ef4dab03d7f0b804f6b01a0df35e023
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.0, picomatch@npm:^2.3.1":
|
||||
version: 2.3.1
|
||||
resolution: "picomatch@npm:2.3.1"
|
||||
|
|
@ -19594,7 +19631,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.2.15, postcss@npm:^8.4.14, postcss@npm:^8.4.23, postcss@npm:^8.4.33":
|
||||
"postcss@npm:^8.2.15, postcss@npm:^8.4.23, postcss@npm:^8.4.33":
|
||||
version: 8.4.38
|
||||
resolution: "postcss@npm:8.4.38"
|
||||
dependencies:
|
||||
|
|
@ -19605,6 +19642,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.4.39":
|
||||
version: 8.4.47
|
||||
resolution: "postcss@npm:8.4.47"
|
||||
dependencies:
|
||||
nanoid: ^3.3.7
|
||||
picocolors: ^1.1.0
|
||||
source-map-js: ^1.2.1
|
||||
checksum: 929f68b5081b7202709456532cee2a145c1843d391508c5a09de2517e8c4791638f71dd63b1898dba6712f8839d7a6da046c72a5e44c162e908f5911f57b5f44
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prelude-ls@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "prelude-ls@npm:1.2.1"
|
||||
|
|
@ -19961,12 +20009,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:6.11.0":
|
||||
version: 6.11.0
|
||||
resolution: "qs@npm:6.11.0"
|
||||
"qs@npm:6.13.0":
|
||||
version: 6.13.0
|
||||
resolution: "qs@npm:6.13.0"
|
||||
dependencies:
|
||||
side-channel: ^1.0.4
|
||||
checksum: 4e4875e4d7c7c31c233d07a448e7e4650f456178b9dd3766b7cfa13158fdb24ecb8c4f059fa91e820dc6ab9f2d243721d071c9c0378892dcdad86e9e9a27c68f
|
||||
side-channel: ^1.0.6
|
||||
checksum: 62372cdeec24dc83a9fb240b7533c0fdcf0c5f7e0b83343edd7310f0ab4c8205a5e7c56406531f2e47e1b4878a3821d652be4192c841de5b032ca83619d8f860
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -22349,9 +22397,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"send@npm:0.18.0":
|
||||
version: 0.18.0
|
||||
resolution: "send@npm:0.18.0"
|
||||
"send@npm:0.19.0":
|
||||
version: 0.19.0
|
||||
resolution: "send@npm:0.19.0"
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
|
|
@ -22366,7 +22414,7 @@ __metadata:
|
|||
on-finished: 2.4.1
|
||||
range-parser: ~1.2.1
|
||||
statuses: 2.0.1
|
||||
checksum: 0eb134d6a51fc13bbcb976a1f4214ea1e33f242fae046efc311e80aff66c7a43603e26a79d9d06670283a13000e51be6e0a2cb80ff0942eaf9f1cd30b7ae736a
|
||||
checksum: ea3f8a67a8f0be3d6bf9080f0baed6d2c51d11d4f7b4470de96a5029c598a7011c497511ccc28968b70ef05508675cebff27da9151dd2ceadd60be4e6cf845e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -22425,15 +22473,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"serve-static@npm:1.15.0":
|
||||
version: 1.15.0
|
||||
resolution: "serve-static@npm:1.15.0"
|
||||
"serve-static@npm:1.16.2":
|
||||
version: 1.16.2
|
||||
resolution: "serve-static@npm:1.16.2"
|
||||
dependencies:
|
||||
encodeurl: ~1.0.2
|
||||
encodeurl: ~2.0.0
|
||||
escape-html: ~1.0.3
|
||||
parseurl: ~1.3.3
|
||||
send: 0.18.0
|
||||
checksum: fa9f0e21a540a28f301258dfe1e57bb4f81cd460d28f0e973860477dd4acef946a1f41748b5bd41c73b621bea2029569c935faa38578fd34cd42a9b4947088ba
|
||||
send: 0.19.0
|
||||
checksum: 528fff6f5e12d0c5a391229ad893910709bc51b5705962b09404a1d813857578149b8815f35d3ee5752f44cd378d0f31669d4b1d7e2d11f41e08283d5134bd1f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -22828,6 +22876,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"source-map-js@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "source-map-js@npm:1.2.1"
|
||||
checksum: 7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"source-map-resolve@npm:^0.5.0":
|
||||
version: 0.5.3
|
||||
resolution: "source-map-resolve@npm:0.5.3"
|
||||
|
|
@ -25619,11 +25674,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webpack@npm:^5.92.1":
|
||||
version: 5.92.1
|
||||
resolution: "webpack@npm:5.92.1"
|
||||
"webpack@npm:^5.94.0":
|
||||
version: 5.95.0
|
||||
resolution: "webpack@npm:5.95.0"
|
||||
dependencies:
|
||||
"@types/eslint-scope": ^3.7.3
|
||||
"@types/estree": ^1.0.5
|
||||
"@webassemblyjs/ast": ^1.12.1
|
||||
"@webassemblyjs/wasm-edit": ^1.12.1
|
||||
|
|
@ -25632,7 +25686,7 @@ __metadata:
|
|||
acorn-import-attributes: ^1.9.5
|
||||
browserslist: ^4.21.10
|
||||
chrome-trace-event: ^1.0.2
|
||||
enhanced-resolve: ^5.17.0
|
||||
enhanced-resolve: ^5.17.1
|
||||
es-module-lexer: ^1.2.1
|
||||
eslint-scope: 5.1.1
|
||||
events: ^3.2.0
|
||||
|
|
@ -25652,7 +25706,7 @@ __metadata:
|
|||
optional: true
|
||||
bin:
|
||||
webpack: bin/webpack.js
|
||||
checksum: 43ca7c76b9c1005bd85f05303d048f918bac10276a209e3ef5e359353fbfef4e5fcee876265e6bc305bf5ef326576e02df63bc7e5af878fb7f06d7e1795b811a
|
||||
checksum: b9e6d0f8ebcbf0632494ac0b90fe4acb8f4a9b83f7ace4a67a15545a36fe58599c912ab58e625e1bf58ab3b0916c75fe99da6196d412ee0cab0b5065edd84238
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
|
|||
16
peers/package-lock.json
generated
16
peers/package-lock.json
generated
|
|
@ -9,7 +9,7 @@
|
|||
"version": "v1.12.0",
|
||||
"license": "Elastic License 2.0 (ELv2)",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"peer": "^v1.0.1",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
|
|
@ -313,9 +313,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -450,16 +450,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/openreplay/openreplay#readme",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"peer": "^v1.0.1",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ docker rmi alpine || true
|
|||
# Signing image
|
||||
# cosign sign --key awskms:///alias/openreplay-container-sign image_url:tag
|
||||
export SIGN_IMAGE=1
|
||||
export ARCH=${ARCH:-"amd64"}
|
||||
export PUSH_IMAGE=0
|
||||
export AWS_DEFAULT_REGION="eu-central-1"
|
||||
export SIGN_KEY="awskms:///alias/openreplay-container-sign"
|
||||
|
|
@ -21,17 +22,17 @@ echo $DOCKER_REPO
|
|||
} || {
|
||||
# docker login $DOCKER_REPO
|
||||
# tmux set-option remain-on-exit on
|
||||
tmux split-window "cd ../../backend && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../assist && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../backend && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../assist && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux select-layout tiled
|
||||
tmux split-window "cd ../../peers && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../frontend && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../peers && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../frontend && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux select-layout tiled
|
||||
tmux split-window "cd ../../sourcemapreader && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../api && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@ \
|
||||
&& DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build_alerts.sh $@ \
|
||||
&& DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build_crons.sh $@ \
|
||||
&& cd ../assist-stats && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../sourcemapreader && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux split-window "cd ../../api && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@ \
|
||||
&& DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build_alerts.sh $@ \
|
||||
&& DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build_crons.sh $@ \
|
||||
&& cd ../assist-stats && DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=$ARCH IMAGE_TAG=$IMAGE_TAG DOCKER_REPO=$DOCKER_REPO PUSH_IMAGE=0 bash build.sh $@; read"
|
||||
tmux select-layout tiled
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ function install_openreplay_actions() {
|
|||
sudo rm -rf $openreplay_code_dir
|
||||
fi
|
||||
sudo cp -rfb ./vars.yaml $openreplay_home_dir
|
||||
sudo cp -rf "$(cd ../.. && pwd)" $openreplay_code_dir
|
||||
sudo cp -rf "$(cd ../.. && pwd)" $openreplay_home_dir
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ function status() {
|
|||
return
|
||||
}
|
||||
|
||||
# Create OR version patch with gith sha
|
||||
function patch_version() {
|
||||
# Patching config version for console
|
||||
version=$(/var/lib/openreplay/yq '.fromVersion' vars.yaml)-$(sudo git rev-parse --short HEAD)
|
||||
|
|
@ -327,11 +328,11 @@ function cleanup() {
|
|||
fi
|
||||
|
||||
# Run pg cleanup
|
||||
pguser=$(awk '/postgresqlUser/{print $2}' <"${OR_DIR}/vars.yaml" | xargs)
|
||||
pgpassword=$(awk '/postgresqlPassword/{print $2}' <"${OR_DIR}/vars.yaml" | xargs)
|
||||
pghost=$(awk '/postgresqlHost/{print $2}' <"${OR_DIR}/vars.yaml" | xargs)
|
||||
pgport=$(awk '/postgresqlPort/{print $2}' <"${OR_DIR}/vars.yaml" | xargs)
|
||||
pgdatabase=$(awk '/postgresqlDatabase/{print $2}' <"${OR_DIR}/vars.yaml" | xargs)
|
||||
pguser=$(yq 'explode(.) | .global.postgresql.postgresqlUser' ${OR_DIR}/vars.yaml)
|
||||
pgpassword=$(yq 'explode(.) | .global.postgresql.postgresqlPassword' ${OR_DIR}/vars.yaml)
|
||||
pghost=$(yq 'explode(.) | .global.postgresql.postgresqlHost' ${OR_DIR}/vars.yaml)
|
||||
pgport=$(yq 'explode(.) | .global.postgresql.postgresqlPort' ${OR_DIR}/vars.yaml)
|
||||
pgdatabase=$(yq 'explode(.) | .global.postgresql.postgresqlDatabase' ${OR_DIR}/vars.yaml)
|
||||
cleanup_query="DELETE FROM public.sessions WHERE start_ts < extract(epoch from '${delete_from_date}'::date) * 1000;"
|
||||
[[ $EE ]] && cleanup_query="DELETE FROM public.sessions WHERE start_ts < extract(epoch from '${delete_from_date}'::date) * 1000 AND session_id NOT IN (SELECT session_id FROM user_favorite_sessions);"
|
||||
kubectl delete po -n "${APP_NS}" pg-cleanup &>/dev/null || true
|
||||
|
|
@ -344,13 +345,13 @@ function cleanup() {
|
|||
--env PGPORT="$pgport" \
|
||||
--image bitnami/postgresql -- psql -c "$cleanup_query"
|
||||
# Run minio cleanup
|
||||
MINIO_ACCESS_KEY=$(awk '/accessKey/{print $NF}' <"${OR_DIR}/vars.yaml" | tail -n1 | xargs)
|
||||
MINIO_SECRET_KEY=$(awk '/secretKey/{print $NF}' <"${OR_DIR}/vars.yaml" | tail -n1 | xargs)
|
||||
MINIO_HOST=$(awk '/endpoint/{print $NF}' <"${OR_DIR}/vars.yaml" | tail -n1 | xargs)
|
||||
MINIO_ACCESS_KEY=$(yq 'explode(.) | .global.s3.accessKey' ${OR_DIR}/vars.yaml)
|
||||
MINIO_SECRET_KEY=$(yq 'explode(.) | .global.s3.secretKey' ${OR_DIR}/vars.yaml)
|
||||
MINIO_HOST=$(yq 'explode(.) | .global.s3.endpoint' ${OR_DIR}/vars.yaml)
|
||||
kubectl delete po -n "${APP_NS}" minio-cleanup &>/dev/null || true
|
||||
kubectl run minio-cleanup -n "${APP_NS}" \
|
||||
--restart=Never \
|
||||
--env MINIO_HOST="$pghost" \
|
||||
--env MINIO_HOST="$MINIO_HOST" \
|
||||
--image bitnami/minio:2020.10.9-debian-10-r6 -- /bin/sh -c "
|
||||
mc alias set minio $MINIO_HOST $MINIO_ACCESS_KEY $MINIO_SECRET_KEY &&
|
||||
mc rm --recursive --dangerous --force --older-than ${delete_from_number_days}d minio/mobs
|
||||
|
|
@ -385,7 +386,7 @@ function upgrade() {
|
|||
|
||||
time_now=$(date +%m-%d-%Y-%I%M%S)
|
||||
# Creating backup dir of current installation
|
||||
[[ -d "$OR_DIR/openreplay" ]] && sudo mv "$OR_DIR/openreplay" "$OR_DIR/openreplay_${or_version//\"/}_${time_now}"
|
||||
[[ -d "$OR_DIR/openreplay" ]] && sudo cp -rf "$OR_DIR/openreplay" "$OR_DIR/openreplay_${or_version//\"/}_${time_now}"
|
||||
|
||||
clone_repo
|
||||
err_cd openreplay/scripts/helmcharts
|
||||
|
|
@ -406,7 +407,8 @@ function upgrade() {
|
|||
sudo mv ./openreplay-cli /bin/openreplay
|
||||
sudo chmod +x /bin/openreplay
|
||||
sudo mv ./vars.yaml "$OR_DIR"
|
||||
sudo cp -rf ../../../openreplay "$OR_DIR/"
|
||||
sudo rm -rf "$OR_DIR/openreplay" || true
|
||||
sudo cp -rf "${tmp_dir}/openreplay" "$OR_DIR/"
|
||||
log info "Configuration file is saved in /var/lib/openreplay/vars.yaml"
|
||||
log info "Run ${BWHITE}openreplay -h${GREEN} to see the cli information to manage OpenReplay."
|
||||
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ version: 0.1.1
|
|||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.20.1"
|
||||
AppVersion: "v1.20.6"
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ spec:
|
|||
# 4. Using AWS itself.
|
||||
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
|
||||
- name: ASSETS_ORIGIN
|
||||
value: "{{ include "openreplay.s3Endpoint" . }}/{{.Values.global.s3.assetsBucket}}"
|
||||
value: "{{ include "openreplay.assets_origin" . }}"
|
||||
{{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }}
|
||||
ports:
|
||||
{{- range $key, $val := .Values.service.ports }}
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ version: 0.1.7
|
|||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.20.0"
|
||||
AppVersion: "v1.20.19"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
apiVersion: v2
|
||||
name: db
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
|
|
@ -11,14 +10,12 @@ description: A Helm chart for Kubernetes
|
|||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.1
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.20.0"
|
||||
AppVersion: "v1.20.3"
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ version: 0.1.10
|
|||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.20.4"
|
||||
AppVersion: "v1.20.17"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
apiVersion: v2
|
||||
name: http
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
|
|
@ -11,14 +10,12 @@ description: A Helm chart for Kubernetes
|
|||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.1
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.20.0"
|
||||
AppVersion: "v1.20.1"
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ spec:
|
|||
# 4. Using AWS itself.
|
||||
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
|
||||
- name: ASSETS_ORIGIN
|
||||
value: "{{ include "openreplay.s3Endpoint" . }}/{{.Values.global.s3.assetsBucket}}"
|
||||
value: {{ include "openreplay.assets_origin" . }}
|
||||
{{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }}
|
||||
ports:
|
||||
{{- range $key, $val := .Values.service.ports }}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ spec:
|
|||
# 4. Using AWS itself.
|
||||
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
|
||||
- name: ASSETS_ORIGIN
|
||||
value: "{{ include "openreplay.s3Endpoint" . }}/{{.Values.global.s3.assetsBucket}}"
|
||||
value: {{ include "openreplay.assets_origin" . }}
|
||||
ports:
|
||||
{{- range $key, $val := .Values.service.ports }}
|
||||
- name: {{ $key }}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
apiVersion: v2
|
||||
name: spot
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
|
|
@ -11,14 +10,12 @@ description: A Helm chart for Kubernetes
|
|||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.1
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.20.0"
|
||||
AppVersion: "v1.20.1"
|
||||
|
|
|
|||
|
|
@ -5,9 +5,25 @@ cd $(dirname $0)
|
|||
|
||||
is_migrate=$1
|
||||
|
||||
# Check if the openreplay version is set.
|
||||
# This will take precedence over the .Values.fromVersion variable
|
||||
# Because its created by installation programatically.
|
||||
if [[ -n $OPENREPLAY_VERSION ]]; then
|
||||
is_migrate=true
|
||||
PREVIOUS_APP_VERSION=$OPENREPLAY_VERSION
|
||||
echo "$OPENREPLAY_VERSION set"
|
||||
fi
|
||||
|
||||
if [[ $FORCE_MIGRATION == "true" ]]; then
|
||||
is_migrate=true
|
||||
fi
|
||||
|
||||
# Passed from env
|
||||
# PREVIOUS_APP_VERSION
|
||||
# CHART_APP_VERSION
|
||||
# Converting alphaneumeric to number.
|
||||
PREVIOUS_APP_VERSION=$(echo $PREVIOUS_APP_VERSION | cut -d "v" -f2)
|
||||
CHART_APP_VERSION=$(echo $CHART_APP_VERSION | cut -d "v" -f2)
|
||||
|
||||
function migration() {
|
||||
ls -la /opt/openreplay/openreplay
|
||||
|
|
|
|||
|
|
@ -138,3 +138,11 @@ Create the volume mount config for redis TLS certificates
|
|||
subPath: {{ .tls.certCAFilename }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "openreplay.assets_origin"}}
|
||||
{{- if .Values.global.assetsOrigin }}
|
||||
{{- .Values.global.assetsOrigin }}
|
||||
{{- else }}
|
||||
{{- include "openreplay.s3Endpoint" . }}/{{.Values.global.s3.assetsBucket}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
33
scripts/helmcharts/openreplay/templates/configmap.yaml
Normal file
33
scripts/helmcharts/openreplay/templates/configmap.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: openreplay-version
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
annotations:
|
||||
"helm.sh/hook": post-install, post-upgrade
|
||||
"helm.sh/hook-weight": "-6" # Higher precidence, so the first the config map will get created.
|
||||
data:
|
||||
version: {{ .Chart.AppVersion }}
|
||||
---
|
||||
# If some jobs or crons are doing db operations, or using credentias,
|
||||
# it should fetch them from this secret.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: openreplay-secrets
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install, pre-upgrade
|
||||
"helm.sh/hook-weight": "-6" # Higher precidence, so the first the config map will get created.
|
||||
"helm.sh/hook-delete-policy": "before-hook-creation"
|
||||
data:
|
||||
PGHOST: "{{ .Values.global.postgresql.postgresqlHost | b64enc }}"
|
||||
PGPORT: "{{ .Values.global.postgresql.postgresqlPort | b64enc }}"
|
||||
PGDATABASE: "{{ .Values.global.postgresql.postgresqlDatabase | b64enc }}"
|
||||
PGUSER: "{{ .Values.global.postgresql.postgresqlUser | b64enc }}"
|
||||
PGPASSWORD: "{{ .Values.global.postgresql.postgresqlPassword | b64enc }}"
|
||||
CLICKHOUSE_USER: "{{ .Values.global.clickhouse.username | b64enc }}"
|
||||
CLICKHOUSE_PASSWORD: "{{ .Values.global.clickhouse.password | b64enc }}"
|
||||
MINIO_HOST: "{{ .Values.global.s3.endpoint | b64enc }}"
|
||||
MINIO_ACCESS_KEY: "{{ .Values.global.s3.accessKey | b64enc }}"
|
||||
MINIO_SECRET_KEY: "{{ .Values.global.s3.secretKey | b64enc }}"
|
||||
|
|
@ -2,9 +2,15 @@
|
|||
Don't have to trigger migration if there is no version change
|
||||
Don't have to trigger migration if skipMigration is set
|
||||
Have to trigger migration if forceMigration is set
|
||||
|
||||
versionChange is true when:
|
||||
Release.IsUpgrade is false.
|
||||
Or .Values.deployment.argo is set.
|
||||
Or Release.IsUpgrade is true and .Values.fromVersion is not equal to .Chart.AppVersion.
|
||||
*/}}
|
||||
{{- $versionChange := and (eq .Values.fromVersion .Chart.AppVersion) (.Release.IsUpgrade) }}
|
||||
{{- if or (not (or .Values.skipMigration $versionChange)) .Values.forceMigration }}
|
||||
|
||||
{{- $versionChange := (or (not .Release.IsUpgrade) .Values.deployment.argo (and .Release.IsUpgrade (not (eq .Values.fromVersion .Chart.AppVersion)))) }}
|
||||
{{- if or .Values.forceMigration (and (not .Values.skipMigration) $versionChange) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
|
|
@ -74,7 +80,23 @@ spec:
|
|||
- |
|
||||
set -x
|
||||
mkdir -p /opt/openreplay/openreplay && cd /opt/openreplay/openreplay
|
||||
git clone {{ .Values.global.dbMigrationUpstreamRepoURL | default "https://github.com/openreplay/openreplay" }} .
|
||||
|
||||
# Function to check if GitHub is available
|
||||
check_github() {
|
||||
for i in {1..10}; do
|
||||
if ping -c 1 github.com &> /dev/null || wget -q --spider https://github.com; then
|
||||
echo "GitHub is available."
|
||||
git clone {{ .Values.global.dbMigrationUpstreamRepoURL | default "https://github.com/openreplay/openreplay" }} .
|
||||
break
|
||||
else
|
||||
echo "GitHub is not available. Retrying in 3 seconds..."
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_github
|
||||
|
||||
ls /opt/openreplay/openreplay
|
||||
git checkout {{ default .Chart.AppVersion .Values.dbMigrationUpstreamBranch }} || exit 10
|
||||
git log -1
|
||||
|
|
@ -159,8 +181,8 @@ spec:
|
|||
- |
|
||||
pg_version=$(psql -c "SHOW server_version;" | xargs | grep -oP '\d+(?=\.)')
|
||||
if [[ $pg_version -le 14 ]]; then
|
||||
echo "[error] postgresql version is $pg_version which is < 16. Exiting."
|
||||
For upgrade steps, refer: https://docs.openreplay.com/en/deployment/openreplay-admin/#upgrade-postgresql
|
||||
echo "[error] postgresql version is $pg_version which is < 16. Exiting.
|
||||
For upgrade steps, refer: https://docs.openreplay.com/en/deployment/openreplay-admin/#upgrade-postgresql"
|
||||
exit 101
|
||||
fi
|
||||
volumeMounts:
|
||||
|
|
@ -171,6 +193,12 @@ spec:
|
|||
containers:
|
||||
- name: postgres
|
||||
env:
|
||||
- name: OPENREPLAY_VERSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: openreplay-version
|
||||
key: version
|
||||
optional: true
|
||||
- name: FORCE_MIGRATION
|
||||
value: "{{ .Values.forceMigration }}"
|
||||
- name: PREVIOUS_APP_VERSION
|
||||
|
|
@ -217,6 +245,12 @@ spec:
|
|||
- name: minio
|
||||
image: bitnami/minio:2023.11.20
|
||||
env:
|
||||
- name: OPENREPLAY_VERSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: openreplay-version
|
||||
key: version
|
||||
optional: true
|
||||
{{- range $key, $val := .Values.global.env }}
|
||||
- name: {{ $key }}
|
||||
value: '{{ $val }}'
|
||||
|
|
@ -340,6 +374,12 @@ spec:
|
|||
- name: clickhouse
|
||||
image: clickhouse/clickhouse-server:22.12-alpine
|
||||
env:
|
||||
- name: OPENREPLAY_VERSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: openreplay-version
|
||||
key: version
|
||||
optional: true
|
||||
{{- range $key, $val := .Values.global.env }}
|
||||
- name: {{ $key }}
|
||||
value: '{{ $val }}'
|
||||
|
|
@ -375,6 +415,12 @@ spec:
|
|||
- name: kafka
|
||||
image: bitnami/kafka:2.6.0-debian-10-r30
|
||||
env:
|
||||
- name: OPENREPLAY_VERSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: openreplay-version
|
||||
key: version
|
||||
optional: true
|
||||
{{- range $key, $val := .Values.global.env }}
|
||||
- name: {{ $key }}
|
||||
value: '{{ $val }}'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ migrationJob:
|
|||
migration:
|
||||
env: {}
|
||||
|
||||
deployment:
|
||||
argo: false
|
||||
forceMigration: false
|
||||
skipMigration: false
|
||||
|
||||
redis: &redis
|
||||
tls:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
\set or_version 'v1.19.0'
|
||||
\set or_version 'v1.20.0'
|
||||
SET client_min_messages TO NOTICE;
|
||||
\set ON_ERROR_STOP true
|
||||
SELECT EXISTS (SELECT 1
|
||||
|
|
|
|||
16
sourcemapreader/package-lock.json
generated
16
sourcemapreader/package-lock.json
generated
|
|
@ -11,7 +11,7 @@
|
|||
"dependencies": {
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"aws-sdk": "^2.1606.0",
|
||||
"express": "^4.19.2",
|
||||
"express": "^4.21.1",
|
||||
"source-map": "^0.7.4",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
|
|
@ -506,9 +506,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -618,16 +618,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"dependencies": {
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"aws-sdk": "^2.1606.0",
|
||||
"express": "^4.19.2",
|
||||
"express": "^4.21.1",
|
||||
"source-map": "^0.7.4",
|
||||
"winston": "^3.13.0"
|
||||
}
|
||||
|
|
|
|||
263
spot/package-lock.json
generated
263
spot/package-lock.json
generated
|
|
@ -1,21 +1,20 @@
|
|||
{
|
||||
"name": "wxt-starter",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wxt-starter",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.6",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@neodrag/solid": "^2.0.4",
|
||||
"@thedutchcoder/postcss-rem-to-px": "^0.0.2",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"install": "^0.13.0",
|
||||
"lucide-solid": "^0.408.0",
|
||||
"npm": "^10.8.1",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss": "^8.4.47",
|
||||
"prettier": "^3.3.2",
|
||||
"solid-js": "^1.8.17",
|
||||
"tailwindcss": "^3.4.4",
|
||||
|
|
@ -25,7 +24,7 @@
|
|||
"@wxt-dev/module-solid": "^1.1.2",
|
||||
"daisyui": "^4.12.10",
|
||||
"typescript": "^5.4.5",
|
||||
"wxt": "0.19.7"
|
||||
"wxt": "0.19.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@aklinker1/rollup-plugin-visualizer": {
|
||||
|
|
@ -1109,224 +1108,208 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz",
|
||||
"integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
||||
"integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz",
|
||||
"integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz",
|
||||
"integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz",
|
||||
"integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
|
||||
"integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz",
|
||||
"integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz",
|
||||
"integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz",
|
||||
"integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz",
|
||||
"integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz",
|
||||
"integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz",
|
||||
"integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz",
|
||||
"integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz",
|
||||
"integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz",
|
||||
"integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz",
|
||||
"integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz",
|
||||
"integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz",
|
||||
"integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
|
@ -1435,12 +1418,42 @@
|
|||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"node_modules/@types/chrome": {
|
||||
"version": "0.0.269",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.269.tgz",
|
||||
"integrity": "sha512-vF7x8YywnhXX2F06njQ/OE7a3Qeful43C5GUOsUksXWk89WoSFUU3iLeZW8lDpVO9atm8iZIEiLQTRC3H7NOXQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dependencies": {
|
||||
"@types/filesystem": "*",
|
||||
"@types/har-format": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/filesystem": {
|
||||
"version": "0.0.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
|
||||
"integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/filewriter": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/filewriter": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
|
||||
"integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/har-format": {
|
||||
"version": "1.2.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
|
||||
"integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/http-cache-semantics": {
|
||||
"version": "4.0.4",
|
||||
|
|
@ -5085,15 +5098,6 @@
|
|||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lucide-solid": {
|
||||
"version": "0.408.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-solid/-/lucide-solid-0.408.0.tgz",
|
||||
"integrity": "sha512-YJslzmGotW/s69Zygp1W+hIYnZSLihylufxZWXTARSh+ruILyRnXZlTxQiTiYhPLqyd0YWOanxou0HukRhjUng==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"solid-js": "^1.4.7"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.11",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
|
||||
|
|
@ -8793,10 +8797,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
|
||||
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
|
||||
"license": "ISC"
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
|
|
@ -8841,9 +8844,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.41",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
|
||||
"integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -8858,11 +8861,10 @@
|
|||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.1",
|
||||
"source-map-js": "^1.2.0"
|
||||
"picocolors": "^1.1.0",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
|
|
@ -9869,13 +9871,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz",
|
||||
"integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==",
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
|
||||
"integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
"@types/estree": "1.0.6"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
|
|
@ -9885,22 +9886,22 @@
|
|||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.21.0",
|
||||
"@rollup/rollup-android-arm64": "4.21.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.21.0",
|
||||
"@rollup/rollup-darwin-x64": "4.21.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.21.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.21.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.21.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.21.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.21.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.21.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.21.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.21.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.21.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.21.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.21.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.21.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.24.0",
|
||||
"@rollup/rollup-android-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-x64": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.24.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.24.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.24.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
|
|
@ -10189,10 +10190,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -10890,14 +10890,13 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz",
|
||||
"integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==",
|
||||
"version": "5.4.9",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
|
||||
"integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.41",
|
||||
"postcss": "^8.4.43",
|
||||
"rollup": "^4.20.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -11335,13 +11334,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/wxt": {
|
||||
"version": "0.19.7",
|
||||
"resolved": "https://registry.npmjs.org/wxt/-/wxt-0.19.7.tgz",
|
||||
"integrity": "sha512-nAoEYodA6Tgc93m0C4H64rUZe3WpR8aIL04L3BbmEnLfAfaLARKIELHBCOTr0m+6maMsAlppKVvX9O7n0Lg2/Q==",
|
||||
"version": "0.19.9",
|
||||
"resolved": "https://registry.npmjs.org/wxt/-/wxt-0.19.9.tgz",
|
||||
"integrity": "sha512-XUbF4JNyx2jTDpXwx2c/esaJcUD2Dr482C2GGenkGRMH2UnerzOIchGCtaa1hb2U8eAed7Akda0yRoMJU0uxUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aklinker1/rollup-plugin-visualizer": "5.12.0",
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@types/webextension-polyfill": "^0.10.7",
|
||||
"@webext-core/fake-browser": "^1.3.1",
|
||||
"@webext-core/isolated-element": "^1.1.2",
|
||||
|
|
@ -11355,7 +11354,7 @@
|
|||
"defu": "^6.1.4",
|
||||
"dequal": "^2.0.3",
|
||||
"esbuild": "^0.23.0",
|
||||
"execa": "^9.3.0",
|
||||
"execa": "^9.3.1",
|
||||
"fast-glob": "^3.3.2",
|
||||
"filesize": "^10.1.4",
|
||||
"fs-extra": "^11.2.0",
|
||||
|
|
@ -11374,11 +11373,12 @@
|
|||
"nypm": "^0.3.9",
|
||||
"ohash": "^1.1.3",
|
||||
"open": "^10.1.0",
|
||||
"ora": "^8.0.1",
|
||||
"ora": "^8.1.0",
|
||||
"picocolors": "^1.0.1",
|
||||
"prompts": "^2.4.2",
|
||||
"publish-browser-extension": "^2.1.3",
|
||||
"unimport": "^3.9.1",
|
||||
"scule": "^1.3.0",
|
||||
"unimport": "^3.11.1",
|
||||
"vite": "^5.3.5",
|
||||
"vite-node": "^2.0.4",
|
||||
"web-ext-run": "^0.2.1",
|
||||
|
|
@ -11388,9 +11388,6 @@
|
|||
"wxt": "bin/wxt.mjs",
|
||||
"wxt-publish-extension": "bin/wxt-publish-extension.cjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/chrome": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/chrome": {
|
||||
"optional": true
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "wxt-starter",
|
||||
"description": "manifest.json description",
|
||||
"private": true,
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
"autoprefixer": "^10.4.19",
|
||||
"install": "^0.13.0",
|
||||
"npm": "^10.8.1",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss": "^8.4.47",
|
||||
"prettier": "^3.3.2",
|
||||
"solid-js": "^1.8.17",
|
||||
"tailwindcss": "^3.4.4",
|
||||
|
|
|
|||
243
spot/yarn.lock
243
spot/yarn.lock
|
|
@ -742,85 +742,85 @@
|
|||
estree-walker "^2.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz#d941173f82f9b041c61b0dc1a2a91dcd06e4b31e"
|
||||
integrity sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==
|
||||
"@rollup/rollup-android-arm-eabi@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz#1661ff5ea9beb362795304cb916049aba7ac9c54"
|
||||
integrity sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz#7e7157c8543215245ceffc445134d9e843ba51c0"
|
||||
integrity sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==
|
||||
"@rollup/rollup-android-arm64@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz#2ffaa91f1b55a0082b8a722525741aadcbd3971e"
|
||||
integrity sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz"
|
||||
integrity sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==
|
||||
"@rollup/rollup-darwin-arm64@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz#627007221b24b8cc3063703eee0b9177edf49c1f"
|
||||
integrity sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz#34b7867613e5cc42d2b85ddc0424228cc33b43f0"
|
||||
integrity sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==
|
||||
"@rollup/rollup-darwin-x64@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz#0605506142b9e796c370d59c5984ae95b9758724"
|
||||
integrity sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz#422b19ff9ae02b05d3395183d1d43b38c7c8be0b"
|
||||
integrity sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz#62dfd196d4b10c0c2db833897164d2d319ee0cbb"
|
||||
integrity sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz#568aa29195ef6fc57ec6ed3f518923764406a8ee"
|
||||
integrity sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz#53ce72aeb982f1f34b58b380baafaf6a240fddb3"
|
||||
integrity sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz#22309c8bcba9a73114f69165c72bc94b2fbec085"
|
||||
integrity sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==
|
||||
"@rollup/rollup-linux-arm64-gnu@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz#1632990f62a75c74f43e4b14ab3597d7ed416496"
|
||||
integrity sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz#c93c388af6d33f082894b8a60839d7265b2b9bc5"
|
||||
integrity sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==
|
||||
"@rollup/rollup-linux-arm64-musl@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz#8c03a996efb41e257b414b2e0560b7a21f2d9065"
|
||||
integrity sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz#493c5e19e395cf3c6bd860c7139c8a903dea72b4"
|
||||
integrity sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz#5b98729628d5bcc8f7f37b58b04d6845f85c7b5d"
|
||||
integrity sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz#a2eab4346fbe5909165ce99adb935ba30c9fb444"
|
||||
integrity sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz#48e42e41f4cabf3573cfefcb448599c512e22983"
|
||||
integrity sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz#0bc49a79db4345d78d757bb1b05e73a1b42fa5c3"
|
||||
integrity sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==
|
||||
"@rollup/rollup-linux-s390x-gnu@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz#e0b4f9a966872cb7d3e21b9e412a4b7efd7f0b58"
|
||||
integrity sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz#4fd36a6a41f3406d8693321b13d4f9b7658dd4b9"
|
||||
integrity sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==
|
||||
"@rollup/rollup-linux-x64-gnu@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz#78144741993100f47bd3da72fce215e077ae036b"
|
||||
integrity sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz#10ebb13bd4469cbad1a5d9b073bd27ec8a886200"
|
||||
integrity sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==
|
||||
"@rollup/rollup-linux-x64-musl@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz#d9fe32971883cd1bd858336bd33a1c3ca6146127"
|
||||
integrity sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz#2fef1a90f1402258ef915ae5a94cc91a5a1d5bfc"
|
||||
integrity sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==
|
||||
"@rollup/rollup-win32-arm64-msvc@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz#71fa3ea369316db703a909c790743972e98afae5"
|
||||
integrity sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz#a18ad47a95c5f264defb60acdd8c27569f816fc1"
|
||||
integrity sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==
|
||||
"@rollup/rollup-win32-ia32-msvc@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz#653f5989a60658e17d7576a3996deb3902e342e2"
|
||||
integrity sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz#20c09cf44dcb082140cc7f439dd679fe4bba3375"
|
||||
integrity sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==
|
||||
"@rollup/rollup-win32-x64-msvc@4.24.0":
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz#0574d7e87b44ee8511d08cc7f914bcb802b70818"
|
||||
integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==
|
||||
|
||||
"@sec-ant/readable-stream@^0.4.1":
|
||||
version "0.4.1"
|
||||
|
|
@ -949,7 +949,12 @@
|
|||
"@types/filesystem" "*"
|
||||
"@types/har-format" "*"
|
||||
|
||||
"@types/estree@1.0.5", "@types/estree@^1.0.0":
|
||||
"@types/estree@1.0.6":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||
|
||||
"@types/estree@^1.0.0":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
|
||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||
|
|
@ -4040,10 +4045,10 @@ perfect-debounce@^1.0.0:
|
|||
resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz"
|
||||
integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==
|
||||
|
||||
picocolors@^1, picocolors@^1.0.0, picocolors@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz"
|
||||
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
|
||||
picocolors@^1, picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
|
||||
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
|
|
@ -4113,14 +4118,14 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
|
|||
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.4.23, postcss@^8.4.38, postcss@^8.4.41:
|
||||
version "8.4.41"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz"
|
||||
integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==
|
||||
postcss@^8.4.23, postcss@^8.4.43, postcss@^8.4.47:
|
||||
version "8.4.47"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
|
||||
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||
dependencies:
|
||||
nanoid "^3.3.7"
|
||||
picocolors "^1.0.1"
|
||||
source-map-js "^1.2.0"
|
||||
picocolors "^1.1.0"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
prettier@^3.3.2:
|
||||
version "3.3.3"
|
||||
|
|
@ -4408,28 +4413,28 @@ rimraf@~2.4.0:
|
|||
glob "^6.0.1"
|
||||
|
||||
rollup@^4.20.0:
|
||||
version "4.21.0"
|
||||
resolved "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz"
|
||||
integrity sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==
|
||||
version "4.24.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.0.tgz#c14a3576f20622ea6a5c9cad7caca5e6e9555d05"
|
||||
integrity sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==
|
||||
dependencies:
|
||||
"@types/estree" "1.0.5"
|
||||
"@types/estree" "1.0.6"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-android-arm-eabi" "4.21.0"
|
||||
"@rollup/rollup-android-arm64" "4.21.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.21.0"
|
||||
"@rollup/rollup-darwin-x64" "4.21.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.21.0"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.21.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.21.0"
|
||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.21.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.21.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.21.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.21.0"
|
||||
"@rollup/rollup-android-arm-eabi" "4.24.0"
|
||||
"@rollup/rollup-android-arm64" "4.24.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.24.0"
|
||||
"@rollup/rollup-darwin-x64" "4.24.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.24.0"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.24.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.24.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.24.0"
|
||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.24.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.24.0"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.24.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.24.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.24.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.24.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.24.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.24.0"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
run-applescript@^5.0.0:
|
||||
|
|
@ -4621,10 +4626,10 @@ solid-refresh@^0.6.3:
|
|||
"@babel/helper-module-imports" "^7.22.15"
|
||||
"@babel/types" "^7.23.6"
|
||||
|
||||
source-map-js@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
source-map-support@0.5.21:
|
||||
version "0.5.21"
|
||||
|
|
@ -4717,7 +4722,16 @@ stdin-discarder@^0.2.2:
|
|||
resolved "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz"
|
||||
integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
|
|
@ -4751,7 +4765,14 @@ string_decoder@^1.1.1, string_decoder@~1.1.1:
|
|||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
|
|
@ -5151,12 +5172,12 @@ vite-plugin-solid@^2.10.2:
|
|||
vitefu "^0.2.5"
|
||||
|
||||
vite@^5.0.0, vite@^5.3.5:
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz"
|
||||
integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
|
||||
version "5.4.9"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.9.tgz#215c80cbebfd09ccbb9ceb8c0621391c9abdc19c"
|
||||
integrity sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==
|
||||
dependencies:
|
||||
esbuild "^0.21.3"
|
||||
postcss "^8.4.41"
|
||||
postcss "^8.4.43"
|
||||
rollup "^4.20.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
|
@ -5275,8 +5296,16 @@ winreg@0.0.12:
|
|||
resolved "https://registry.npmjs.org/winreg/-/winreg-0.0.12.tgz"
|
||||
integrity sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
name wrap-ansi-cjs
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 567 KiB After Width: | Height: | Size: 519 KiB |
|
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@openreplay/tracker": "file:../tracker",
|
||||
"axios": "^0.26.0",
|
||||
"axios": "^1.7.7",
|
||||
"prettier": "^1.18.2",
|
||||
"replace-in-files-cli": "^1.0.0",
|
||||
"typescript": "^4.6.0-dev.20211126"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"axios": "^0.27.2",
|
||||
"axios": "^1.7.7",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.2",
|
||||
"react-dom": "^18.3.1",
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ export interface StartOptions {
|
|||
forceNew?: boolean
|
||||
sessionHash?: string
|
||||
assistOnly?: boolean
|
||||
/**
|
||||
* @deprecated We strongly advise to use .start().then instead.
|
||||
*
|
||||
* This method is kept for snippet compatibility only
|
||||
* */
|
||||
startCallback?: (result: StartPromiseReturn) => void
|
||||
}
|
||||
|
||||
interface OnStartInfo {
|
||||
|
|
@ -161,6 +167,12 @@ type AppOptions = {
|
|||
}
|
||||
|
||||
network?: NetworkOptions
|
||||
/**
|
||||
* use this flag if you're using Angular
|
||||
* basically goes around window.Zone api changes to mutation observer
|
||||
* and event listeners
|
||||
* */
|
||||
angularMode?: boolean
|
||||
} & WebworkerOptions &
|
||||
SessOptions
|
||||
|
||||
|
|
@ -185,12 +197,14 @@ const proto = {
|
|||
resp: 'never-gonna-let-you-down',
|
||||
// regenerating id (copied other tab)
|
||||
reg: 'never-gonna-run-around-and-desert-you',
|
||||
// tracker inside a child iframe
|
||||
iframeSignal: 'never-gonna-make-you-cry',
|
||||
// getting node id for child iframe
|
||||
iframeId: 'never-gonna-say-goodbye',
|
||||
// batch of messages from an iframe window
|
||||
iframeBatch: 'never-gonna-tell-a-lie-and-hurt-you',
|
||||
iframeSignal: 'tracker inside a child iframe',
|
||||
iframeId: 'getting node id for child iframe',
|
||||
iframeBatch: 'batch of messages from an iframe window',
|
||||
parentAlive: 'signal that parent is live',
|
||||
killIframe: 'stop tracker inside frame',
|
||||
startIframe: 'start tracker inside frame',
|
||||
// checking updates
|
||||
polling: 'hello-how-are-you-im-under-the-water-please-help-me',
|
||||
} as const
|
||||
|
||||
export default class App {
|
||||
|
|
@ -237,7 +251,6 @@ export default class App {
|
|||
private rootId: number | null = null
|
||||
private pageFrames: HTMLIFrameElement[] = []
|
||||
private frameOderNumber = 0
|
||||
private readonly initialHostName = location.hostname
|
||||
private features = {
|
||||
'feature-flags': true,
|
||||
'usability-test': true,
|
||||
|
|
@ -248,7 +261,7 @@ export default class App {
|
|||
sessionToken: string | undefined,
|
||||
options: Partial<Options>,
|
||||
private readonly signalError: (error: string, apis: string[]) => void,
|
||||
private readonly insideIframe: boolean,
|
||||
public readonly insideIframe: boolean,
|
||||
) {
|
||||
this.contextId = Math.random().toString(36).slice(2)
|
||||
this.projectKey = projectKey
|
||||
|
|
@ -305,6 +318,7 @@ export default class App {
|
|||
__save_canvas_locally: false,
|
||||
useAnimationFrame: false,
|
||||
},
|
||||
angularMode: false,
|
||||
}
|
||||
this.options = simpleMerge(defaultOptions, options)
|
||||
|
||||
|
|
@ -322,7 +336,7 @@ export default class App {
|
|||
this.localStorage = this.options.localStorage ?? window.localStorage
|
||||
this.sessionStorage = this.options.sessionStorage ?? window.sessionStorage
|
||||
this.sanitizer = new Sanitizer(this, options)
|
||||
this.nodes = new Nodes(this.options.node_id)
|
||||
this.nodes = new Nodes(this.options.node_id, Boolean(options.angularMode))
|
||||
this.observer = new Observer(this, options)
|
||||
this.ticker = new Ticker(this)
|
||||
this.ticker.attach(() => this.commit())
|
||||
|
|
@ -348,136 +362,31 @@ export default class App {
|
|||
this.session.applySessionHash(sessionToken)
|
||||
}
|
||||
|
||||
this.initWorker()
|
||||
|
||||
const thisTab = this.session.getTabId()
|
||||
|
||||
if (this.insideIframe) {
|
||||
/**
|
||||
* listen for messages from parent window, so we can signal that we're alive
|
||||
* */
|
||||
window.addEventListener('message', this.parentCrossDomainFrameListener)
|
||||
setInterval(() => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
line: proto.polling,
|
||||
context: this.contextId,
|
||||
},
|
||||
'*',
|
||||
)
|
||||
}, 250)
|
||||
} else {
|
||||
this.initWorker()
|
||||
}
|
||||
if (!this.insideIframe) {
|
||||
/**
|
||||
* if we get a signal from child iframes, we check for their node_id and send it back,
|
||||
* so they can act as if it was just a same-domain iframe
|
||||
* */
|
||||
let crossdomainFrameCount = 0
|
||||
const catchIframeMessage = (event: MessageEvent) => {
|
||||
const { data } = event
|
||||
if (data.line === proto.iframeSignal) {
|
||||
const childIframeDomain = data.domain
|
||||
const pageIframes = Array.from(document.querySelectorAll('iframe'))
|
||||
this.pageFrames = pageIframes
|
||||
const signalId = async () => {
|
||||
let tries = 0
|
||||
while (tries < 10) {
|
||||
const id = this.checkNodeId(pageIframes, childIframeDomain)
|
||||
if (id) {
|
||||
this.waitStarted()
|
||||
.then(() => {
|
||||
crossdomainFrameCount++
|
||||
const token = this.session.getSessionToken()
|
||||
const iframeData = {
|
||||
line: proto.iframeId,
|
||||
context: this.contextId,
|
||||
domain: childIframeDomain,
|
||||
id,
|
||||
token,
|
||||
frameOrderNumber: crossdomainFrameCount,
|
||||
}
|
||||
this.debug.log('iframe_data', iframeData)
|
||||
// @ts-ignore
|
||||
event.source?.postMessage(iframeData, '*')
|
||||
})
|
||||
.catch(console.error)
|
||||
tries = 10
|
||||
break
|
||||
}
|
||||
tries++
|
||||
await delay(100)
|
||||
}
|
||||
}
|
||||
void signalId()
|
||||
}
|
||||
/**
|
||||
* proxying messages from iframe to main body, so they can be in one batch (same indexes, etc)
|
||||
* plus we rewrite some of the messages to be relative to the main context/window
|
||||
* */
|
||||
if (data.line === proto.iframeBatch) {
|
||||
const msgBatch = data.messages
|
||||
const mappedMessages: Message[] = msgBatch.map((msg: Message) => {
|
||||
if (msg[0] === MType.MouseMove) {
|
||||
let fixedMessage = msg
|
||||
this.pageFrames.forEach((frame) => {
|
||||
if (frame.dataset.domain === event.data.domain) {
|
||||
const [type, x, y] = msg
|
||||
const { left, top } = frame.getBoundingClientRect()
|
||||
fixedMessage = [type, x + left, y + top]
|
||||
}
|
||||
})
|
||||
return fixedMessage
|
||||
}
|
||||
if (msg[0] === MType.MouseClick) {
|
||||
let fixedMessage = msg
|
||||
this.pageFrames.forEach((frame) => {
|
||||
if (frame.dataset.domain === event.data.domain) {
|
||||
const [type, id, hesitationTime, label, selector, normX, normY] = msg
|
||||
const { left, top, width, height } = frame.getBoundingClientRect()
|
||||
|
||||
const contentWidth = document.documentElement.scrollWidth
|
||||
const contentHeight = document.documentElement.scrollHeight
|
||||
// (normalizedX * frameWidth + frameLeftOffset)/docSize
|
||||
const fullX = (normX / 100) * width + left
|
||||
const fullY = (normY / 100) * height + top
|
||||
const fixedX = fullX / contentWidth
|
||||
const fixedY = fullY / contentHeight
|
||||
|
||||
fixedMessage = [
|
||||
type,
|
||||
id,
|
||||
hesitationTime,
|
||||
label,
|
||||
selector,
|
||||
Math.round(fixedX * 1e3) / 1e1,
|
||||
Math.round(fixedY * 1e3) / 1e1,
|
||||
]
|
||||
}
|
||||
})
|
||||
return fixedMessage
|
||||
}
|
||||
return msg
|
||||
})
|
||||
this.messages.push(...mappedMessages)
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', catchIframeMessage)
|
||||
this.attachStopCallback(() => {
|
||||
window.removeEventListener('message', catchIframeMessage)
|
||||
})
|
||||
} else {
|
||||
const catchParentMessage = (event: MessageEvent) => {
|
||||
const { data } = event
|
||||
if (data.line !== proto.iframeId) {
|
||||
return
|
||||
}
|
||||
this.rootId = data.id
|
||||
this.session.setSessionToken(data.token as string)
|
||||
this.frameOderNumber = data.frameOrderNumber
|
||||
this.debug.log('starting iframe tracking', data)
|
||||
this.allowAppStart()
|
||||
}
|
||||
window.addEventListener('message', catchParentMessage)
|
||||
this.attachStopCallback(() => {
|
||||
window.removeEventListener('message', catchParentMessage)
|
||||
})
|
||||
// communicating with parent window,
|
||||
// even if its crossdomain is possible via postMessage api
|
||||
const domain = this.initialHostName
|
||||
window.parent.postMessage(
|
||||
{
|
||||
line: proto.iframeSignal,
|
||||
source: thisTab,
|
||||
context: this.contextId,
|
||||
domain,
|
||||
},
|
||||
'*',
|
||||
)
|
||||
window.addEventListener('message', this.crossDomainIframeListener)
|
||||
}
|
||||
|
||||
if (this.bc !== null) {
|
||||
|
|
@ -488,7 +397,7 @@ export default class App {
|
|||
})
|
||||
this.startTimeout = setTimeout(() => {
|
||||
this.allowAppStart()
|
||||
}, 500)
|
||||
}, 250)
|
||||
this.bc.onmessage = (ev: MessageEvent<RickRoll>) => {
|
||||
if (ev.data.context === this.contextId) {
|
||||
return
|
||||
|
|
@ -519,8 +428,204 @@ export default class App {
|
|||
}
|
||||
}
|
||||
|
||||
/** used by child iframes for crossdomain only */
|
||||
/** used by child iframes for crossdomain only */
|
||||
parentActive = false
|
||||
checkStatus = () => {
|
||||
return this.parentActive
|
||||
}
|
||||
parentCrossDomainFrameListener = (event: MessageEvent) => {
|
||||
const { data } = event
|
||||
if (!data || event.source === window) return
|
||||
if (data.line === proto.startIframe) {
|
||||
if (this.active()) return
|
||||
try {
|
||||
this.allowAppStart()
|
||||
void this.start()
|
||||
} catch (e) {
|
||||
console.error('children frame restart failed:', e)
|
||||
}
|
||||
}
|
||||
if (data.line === proto.parentAlive) {
|
||||
this.parentActive = true
|
||||
}
|
||||
if (data.line === proto.iframeId) {
|
||||
this.parentActive = true
|
||||
this.rootId = data.id
|
||||
this.session.setSessionToken(data.token as string)
|
||||
this.frameOderNumber = data.frameOrderNumber
|
||||
this.debug.log('starting iframe tracking', data)
|
||||
this.allowAppStart()
|
||||
}
|
||||
if (data.line === proto.killIframe) {
|
||||
if (this.active()) {
|
||||
this.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* context ids for iframes,
|
||||
* order is not so important as long as its consistent
|
||||
* */
|
||||
trackedFrames: string[] = []
|
||||
crossDomainIframeListener = (event: MessageEvent) => {
|
||||
if (!this.active() || event.source === window) return
|
||||
const { data } = event
|
||||
if (!data) return
|
||||
if (data.line === proto.iframeSignal) {
|
||||
// @ts-ignore
|
||||
event.source?.postMessage({ ping: true, line: proto.parentAlive }, '*')
|
||||
const pageIframes = Array.from(document.querySelectorAll('iframe'))
|
||||
this.pageFrames = pageIframes
|
||||
const signalId = async () => {
|
||||
if (event.source === null) {
|
||||
return console.error('Couldnt connect to event.source for child iframe tracking')
|
||||
}
|
||||
const id = await this.checkNodeId(pageIframes, event.source)
|
||||
if (id && !this.trackedFrames.includes(data.context)) {
|
||||
try {
|
||||
this.trackedFrames.push(data.context)
|
||||
await this.waitStarted()
|
||||
const token = this.session.getSessionToken()
|
||||
const order = this.trackedFrames.findIndex((f) => f === data.context) + 1
|
||||
if (order === 0) {
|
||||
this.debug.error(
|
||||
'Couldnt get order number for iframe',
|
||||
data.context,
|
||||
this.trackedFrames,
|
||||
)
|
||||
}
|
||||
const iframeData = {
|
||||
line: proto.iframeId,
|
||||
id,
|
||||
token,
|
||||
// since indexes go from 0 we +1
|
||||
frameOrderNumber: order,
|
||||
}
|
||||
this.debug.log('Got child frame signal; nodeId', id, event.source, iframeData)
|
||||
// @ts-ignore
|
||||
event.source?.postMessage(iframeData, '*')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
} else {
|
||||
this.debug.log('Couldnt get node id for iframe', event.source, pageIframes)
|
||||
}
|
||||
}
|
||||
void signalId()
|
||||
}
|
||||
/**
|
||||
* proxying messages from iframe to main body, so they can be in one batch (same indexes, etc)
|
||||
* plus we rewrite some of the messages to be relative to the main context/window
|
||||
* */
|
||||
if (data.line === proto.iframeBatch) {
|
||||
const msgBatch = data.messages
|
||||
const mappedMessages: Message[] = msgBatch.map((msg: Message) => {
|
||||
if (msg[0] === MType.MouseMove) {
|
||||
let fixedMessage = msg
|
||||
this.pageFrames.forEach((frame) => {
|
||||
if (frame.contentWindow === event.source) {
|
||||
const [type, x, y] = msg
|
||||
const { left, top } = frame.getBoundingClientRect()
|
||||
fixedMessage = [type, x + left, y + top]
|
||||
}
|
||||
})
|
||||
return fixedMessage
|
||||
}
|
||||
if (msg[0] === MType.MouseClick) {
|
||||
let fixedMessage = msg
|
||||
this.pageFrames.forEach((frame) => {
|
||||
if (frame.contentWindow === event.source) {
|
||||
const [type, id, hesitationTime, label, selector, normX, normY] = msg
|
||||
const { left, top, width, height } = frame.getBoundingClientRect()
|
||||
|
||||
const contentWidth = document.documentElement.scrollWidth
|
||||
const contentHeight = document.documentElement.scrollHeight
|
||||
// (normalizedX * frameWidth + frameLeftOffset)/docSize
|
||||
const fullX = (normX / 100) * width + left
|
||||
const fullY = (normY / 100) * height + top
|
||||
const fixedX = fullX / contentWidth
|
||||
const fixedY = fullY / contentHeight
|
||||
|
||||
fixedMessage = [
|
||||
type,
|
||||
id,
|
||||
hesitationTime,
|
||||
label,
|
||||
selector,
|
||||
Math.round(fixedX * 1e3) / 1e1,
|
||||
Math.round(fixedY * 1e3) / 1e1,
|
||||
]
|
||||
}
|
||||
})
|
||||
return fixedMessage
|
||||
}
|
||||
return msg
|
||||
})
|
||||
this.messages.push(...mappedMessages)
|
||||
}
|
||||
if (data.line === proto.polling) {
|
||||
if (!this.pollingQueue.order.length) {
|
||||
return
|
||||
}
|
||||
const nextCommand = this.pollingQueue.order[0]
|
||||
if (this.pollingQueue[nextCommand].includes(data.context)) {
|
||||
this.pollingQueue[nextCommand] = this.pollingQueue[nextCommand].filter(
|
||||
(c: string) => c !== data.context,
|
||||
)
|
||||
// @ts-ignore
|
||||
event.source?.postMessage({ line: nextCommand }, '*')
|
||||
if (this.pollingQueue[nextCommand].length === 0) {
|
||||
this.pollingQueue.order.shift()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* { command : [remaining iframes] }
|
||||
* + order of commands
|
||||
**/
|
||||
pollingQueue: Record<string, any> = {
|
||||
order: [],
|
||||
}
|
||||
private readonly addCommand = (cmd: string) => {
|
||||
this.pollingQueue.order.push(cmd)
|
||||
this.pollingQueue[cmd] = [...this.trackedFrames]
|
||||
}
|
||||
|
||||
public bootChildrenFrames = async () => {
|
||||
await this.waitStarted()
|
||||
this.addCommand(proto.startIframe)
|
||||
}
|
||||
|
||||
public killChildrenFrames = () => {
|
||||
this.addCommand(proto.killIframe)
|
||||
}
|
||||
|
||||
signalIframeTracker = () => {
|
||||
const thisTab = this.session.getTabId()
|
||||
const signalToParent = (n: number) => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
line: proto.iframeSignal,
|
||||
source: thisTab,
|
||||
context: this.contextId,
|
||||
},
|
||||
this.options.crossdomain?.parentDomain ?? '*',
|
||||
)
|
||||
setTimeout(() => {
|
||||
if (!this.checkStatus() && n < 100) {
|
||||
void signalToParent(n + 1)
|
||||
}
|
||||
}, 250)
|
||||
}
|
||||
void signalToParent(1)
|
||||
}
|
||||
|
||||
startTimeout: ReturnType<typeof setTimeout> | null = null
|
||||
private allowAppStart() {
|
||||
public allowAppStart() {
|
||||
this.canStart = true
|
||||
if (this.startTimeout) {
|
||||
clearTimeout(this.startTimeout)
|
||||
|
|
@ -528,15 +633,38 @@ export default class App {
|
|||
}
|
||||
}
|
||||
|
||||
private checkNodeId(iframes: HTMLIFrameElement[], domain: string) {
|
||||
private async checkNodeId(
|
||||
iframes: HTMLIFrameElement[],
|
||||
source: MessageEventSource,
|
||||
): Promise<number | null> {
|
||||
for (const iframe of iframes) {
|
||||
if (iframe.dataset.domain === domain) {
|
||||
// @ts-ignore
|
||||
return iframe[this.options.node_id] as number | undefined
|
||||
if (iframe.contentWindow && iframe.contentWindow === source) {
|
||||
/**
|
||||
* Here we're trying to get node id from the iframe (which is kept in observer)
|
||||
* because of async nature of dom initialization, we give 100 retries with 100ms delay each
|
||||
* which equals to 10 seconds. This way we have a period where we give app some time to load
|
||||
* and tracker some time to parse the initial DOM tree even on slower devices
|
||||
* */
|
||||
let tries = 0
|
||||
while (tries < 100) {
|
||||
// @ts-ignore
|
||||
const potentialId = iframe[this.options.node_id]
|
||||
if (potentialId !== undefined) {
|
||||
tries = 100
|
||||
return potentialId
|
||||
} else {
|
||||
tries++
|
||||
await delay(100)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private initWorker() {
|
||||
try {
|
||||
this.worker = new Worker(
|
||||
|
|
@ -647,28 +775,28 @@ export default class App {
|
|||
this.messages.length = 0
|
||||
return
|
||||
}
|
||||
if (this.worker === undefined || !this.messages.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.insideIframe) {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
line: proto.iframeBatch,
|
||||
messages: this.messages,
|
||||
domain: this.initialHostName,
|
||||
},
|
||||
'*',
|
||||
this.options.crossdomain?.parentDomain ?? '*',
|
||||
)
|
||||
this.commitCallbacks.forEach((cb) => cb(this.messages))
|
||||
this.messages.length = 0
|
||||
return
|
||||
}
|
||||
|
||||
if (this.worker === undefined || !this.messages.length) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
requestIdleCb(() => {
|
||||
this.messages.unshift(TabData(this.session.getTabId()))
|
||||
this.messages.unshift(Timestamp(this.timestamp()))
|
||||
// why I need to add opt chaining?
|
||||
this.worker?.postMessage(this.messages)
|
||||
this.commitCallbacks.forEach((cb) => cb(this.messages))
|
||||
this.messages.length = 0
|
||||
|
|
@ -740,36 +868,39 @@ export default class App {
|
|||
this.commitCallbacks.push(cb)
|
||||
}
|
||||
|
||||
attachStartCallback(cb: StartCallback, useSafe = false): void {
|
||||
attachStartCallback = (cb: StartCallback, useSafe = false): void => {
|
||||
if (useSafe) {
|
||||
cb = this.safe(cb)
|
||||
}
|
||||
this.startCallbacks.push(cb)
|
||||
}
|
||||
|
||||
attachStopCallback(cb: () => any, useSafe = false): void {
|
||||
attachStopCallback = (cb: () => any, useSafe = false): void => {
|
||||
if (useSafe) {
|
||||
cb = this.safe(cb)
|
||||
}
|
||||
this.stopCallbacks.push(cb)
|
||||
}
|
||||
|
||||
// Use app.nodes.attachNodeListener for registered nodes instead
|
||||
attachEventListener(
|
||||
attachEventListener = (
|
||||
target: EventTarget,
|
||||
type: string,
|
||||
listener: EventListener,
|
||||
useSafe = true,
|
||||
useCapture = true,
|
||||
): void {
|
||||
): void => {
|
||||
if (useSafe) {
|
||||
listener = this.safe(listener)
|
||||
}
|
||||
|
||||
const createListener = () =>
|
||||
target ? createEventListener(target, type, listener, useCapture) : null
|
||||
target
|
||||
? createEventListener(target, type, listener, useCapture, this.options.angularMode)
|
||||
: null
|
||||
const deleteListener = () =>
|
||||
target ? deleteEventListener(target, type, listener, useCapture) : null
|
||||
target
|
||||
? deleteEventListener(target, type, listener, useCapture, this.options.angularMode)
|
||||
: null
|
||||
|
||||
this.attachStartCallback(createListener, useSafe)
|
||||
this.attachStopCallback(deleteListener, useSafe)
|
||||
|
|
@ -1157,7 +1288,7 @@ export default class App {
|
|||
if (isColdStart && this.coldInterval) {
|
||||
clearInterval(this.coldInterval)
|
||||
}
|
||||
if (!this.worker) {
|
||||
if (!this.worker && !this.insideIframe) {
|
||||
const reason = 'No worker found: perhaps, CSP is not set.'
|
||||
this.signalError(reason, [])
|
||||
return Promise.resolve(UnsuccessfulStart(reason))
|
||||
|
|
@ -1189,7 +1320,7 @@ export default class App {
|
|||
})
|
||||
|
||||
const timestamp = now()
|
||||
this.worker.postMessage({
|
||||
this.worker?.postMessage({
|
||||
type: 'start',
|
||||
pageNo: this.session.incPageNo(),
|
||||
ingestPoint: this.options.ingestPoint,
|
||||
|
|
@ -1237,7 +1368,7 @@ export default class App {
|
|||
const reason = error === CANCELED ? CANCELED : `Server error: ${r.status}. ${error}`
|
||||
return UnsuccessfulStart(reason)
|
||||
}
|
||||
if (!this.worker) {
|
||||
if (!this.worker && !this.insideIframe) {
|
||||
const reason = 'no worker found after start request (this should not happen in real world)'
|
||||
this.signalError(reason, [])
|
||||
return UnsuccessfulStart(reason)
|
||||
|
|
@ -1295,9 +1426,9 @@ export default class App {
|
|||
|
||||
if (socketOnly) {
|
||||
this.socketMode = true
|
||||
this.worker.postMessage('stop')
|
||||
this.worker?.postMessage('stop')
|
||||
} else {
|
||||
this.worker.postMessage({
|
||||
this.worker?.postMessage({
|
||||
type: 'auth',
|
||||
token,
|
||||
beaconSizeLimit,
|
||||
|
|
@ -1320,11 +1451,17 @@ export default class App {
|
|||
// TODO: start as early as possible (before receiving the token)
|
||||
/** after start */
|
||||
this.startCallbacks.forEach((cb) => cb(onStartInfo)) // MBTODO: callbacks after DOM "mounted" (observed)
|
||||
if (startOpts.startCallback) {
|
||||
startOpts.startCallback(SuccessfulStart(onStartInfo))
|
||||
}
|
||||
if (this.features['feature-flags']) {
|
||||
void this.featureFlags.reloadFlags()
|
||||
}
|
||||
await this.tagWatcher.fetchTags(this.options.ingestPoint, token)
|
||||
this.activityState = ActivityState.Active
|
||||
if (this.options.crossdomain?.enabled && !this.insideIframe) {
|
||||
void this.bootChildrenFrames()
|
||||
}
|
||||
|
||||
if (canvasEnabled && !this.options.canvas.disableCanvas) {
|
||||
this.canvasRecorder =
|
||||
|
|
@ -1336,7 +1473,6 @@ export default class App {
|
|||
fixedScaling: this.options.canvas.fixedCanvasScaling,
|
||||
useAnimationFrame: this.options.canvas.useAnimationFrame,
|
||||
})
|
||||
this.canvasRecorder.startTracking()
|
||||
}
|
||||
|
||||
/** --------------- COLD START BUFFER ------------------*/
|
||||
|
|
@ -1359,9 +1495,12 @@ export default class App {
|
|||
}
|
||||
this.ticker.start()
|
||||
}
|
||||
this.canvasRecorder?.startTracking()
|
||||
|
||||
if (this.features['usability-test']) {
|
||||
this.uxtManager = this.uxtManager ? this.uxtManager : new UserTestManager(this, uxtStorageKey)
|
||||
this.uxtManager = this.uxtManager
|
||||
? this.uxtManager
|
||||
: new UserTestManager(this, uxtStorageKey)
|
||||
let uxtId: number | undefined
|
||||
const savedUxtTag = this.localStorage.getItem(uxtStorageKey)
|
||||
if (savedUxtTag) {
|
||||
|
|
@ -1394,6 +1533,11 @@ export default class App {
|
|||
} catch (reason) {
|
||||
this.stop()
|
||||
this.session.reset()
|
||||
if (!reason) {
|
||||
console.error('Unknown error during start')
|
||||
this.signalError('Unknown error', [])
|
||||
return UnsuccessfulStart('Unknown error')
|
||||
}
|
||||
if (reason === CANCELED) {
|
||||
this.signalError(CANCELED, [])
|
||||
return UnsuccessfulStart(CANCELED)
|
||||
|
|
@ -1452,9 +1596,13 @@ export default class App {
|
|||
}
|
||||
|
||||
async waitStarted() {
|
||||
return this.waitStatus(ActivityState.Active)
|
||||
}
|
||||
|
||||
async waitStatus(status: ActivityState) {
|
||||
return new Promise((resolve) => {
|
||||
const check = () => {
|
||||
if (this.activityState === ActivityState.Active) {
|
||||
if (this.activityState === status) {
|
||||
resolve(true)
|
||||
} else {
|
||||
setTimeout(check, 25)
|
||||
|
|
@ -1478,6 +1626,10 @@ export default class App {
|
|||
return Promise.resolve(UnsuccessfulStart(reason))
|
||||
}
|
||||
|
||||
if (this.insideIframe) {
|
||||
this.signalIframeTracker()
|
||||
}
|
||||
|
||||
if (!document.hidden) {
|
||||
await this.waitStart()
|
||||
return this._start(...args)
|
||||
|
|
@ -1533,20 +1685,28 @@ export default class App {
|
|||
stop(stopWorker = true): void {
|
||||
if (this.activityState !== ActivityState.NotActive) {
|
||||
try {
|
||||
if (!this.insideIframe && this.options.crossdomain?.enabled) {
|
||||
this.killChildrenFrames()
|
||||
}
|
||||
this.attributeSender.clear()
|
||||
this.sanitizer.clear()
|
||||
this.observer.disconnect()
|
||||
this.nodes.clear()
|
||||
this.ticker.stop()
|
||||
this.stopCallbacks.forEach((cb) => cb())
|
||||
this.debug.log('OpenReplay tracking stopped.')
|
||||
this.tagWatcher.clear()
|
||||
if (this.worker && stopWorker) {
|
||||
this.worker.postMessage('stop')
|
||||
}
|
||||
this.canvasRecorder?.clear()
|
||||
this.messages.length = 0
|
||||
this.trackedFrames = []
|
||||
this.parentActive = false
|
||||
this.canStart = false
|
||||
this.pollingQueue = { order: [] }
|
||||
} finally {
|
||||
this.activityState = ActivityState.NotActive
|
||||
this.debug.log('OpenReplay tracking stopped.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@ export default class Nodes {
|
|||
private readonly elementListeners: Map<number, Array<ElementListener>> = new Map()
|
||||
private nextNodeId = 0
|
||||
|
||||
constructor(private readonly node_id: string) {}
|
||||
constructor(
|
||||
private readonly node_id: string,
|
||||
private readonly angularMode: boolean,
|
||||
) {}
|
||||
|
||||
syntheticMode(frameOrder: number) {
|
||||
const maxSafeNumber = 9007199254740900
|
||||
const maxSafeNumber = Number.MAX_SAFE_INTEGER
|
||||
const placeholderSize = 99999999
|
||||
const nextFrameId = placeholderSize * frameOrder
|
||||
// I highly doubt that this will ever happen,
|
||||
|
|
@ -25,7 +28,7 @@ export default class Nodes {
|
|||
}
|
||||
|
||||
// Attached once per Tracker instance
|
||||
attachNodeCallback(nodeCallback: NodeCallback): void {
|
||||
attachNodeCallback = (nodeCallback: NodeCallback): void => {
|
||||
this.nodeCallbacks.push(nodeCallback)
|
||||
}
|
||||
|
||||
|
|
@ -33,12 +36,12 @@ export default class Nodes {
|
|||
this.nodes.forEach((node) => cb(node))
|
||||
}
|
||||
|
||||
attachNodeListener(node: Node, type: string, listener: EventListener, useCapture = true): void {
|
||||
attachNodeListener = (node: Node, type: string, listener: EventListener, useCapture = true): void => {
|
||||
const id = this.getID(node)
|
||||
if (id === undefined) {
|
||||
return
|
||||
}
|
||||
createEventListener(node, type, listener, useCapture)
|
||||
createEventListener(node, type, listener, useCapture, this.angularMode)
|
||||
let listeners = this.elementListeners.get(id)
|
||||
if (listeners === undefined) {
|
||||
listeners = []
|
||||
|
|
@ -70,7 +73,7 @@ export default class Nodes {
|
|||
if (listeners !== undefined) {
|
||||
this.elementListeners.delete(id)
|
||||
listeners.forEach((listener) =>
|
||||
deleteEventListener(node, listener[0], listener[1], listener[2]),
|
||||
deleteEventListener(node, listener[0], listener[1], listener[2], this.angularMode),
|
||||
)
|
||||
}
|
||||
this.totalNodeAmount--
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ export default class IFrameObserver extends Observer {
|
|||
})
|
||||
}
|
||||
|
||||
syntheticObserve(selfId: number, doc: Document) {
|
||||
syntheticObserve(rootNodeId: number, doc: Document) {
|
||||
this.observeRoot(doc, (docID) => {
|
||||
if (docID === undefined) {
|
||||
this.app.debug.log('OpenReplay: Iframe document not bound')
|
||||
return
|
||||
}
|
||||
this.app.send(CreateIFrameDocument(selfId, docID))
|
||||
this.app.send(CreateIFrameDocument(rootNodeId, docID))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { createMutationObserver, ngSafeBrowserMethod } from '../../utils.js'
|
||||
import { createMutationObserver } from '../../utils.js'
|
||||
import {
|
||||
RemoveNodeAttribute,
|
||||
SetNodeAttributeURLBased,
|
||||
|
|
@ -105,6 +105,9 @@ export default abstract class Observer {
|
|||
if (name === null) {
|
||||
continue
|
||||
}
|
||||
if (target instanceof HTMLIFrameElement && name === 'src') {
|
||||
this.handleIframeSrcChange(target)
|
||||
}
|
||||
let attr = this.attributesMap.get(id)
|
||||
if (attr === undefined) {
|
||||
this.attributesMap.set(id, (attr = new Set()))
|
||||
|
|
@ -119,6 +122,7 @@ export default abstract class Observer {
|
|||
}
|
||||
this.commitNodes()
|
||||
}) as MutationCallback,
|
||||
this.app.options.angularMode,
|
||||
)
|
||||
}
|
||||
private clear(): void {
|
||||
|
|
@ -129,10 +133,49 @@ export default abstract class Observer {
|
|||
this.textSet.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds the removed nodes in case of iframe src change.
|
||||
*/
|
||||
private handleIframeSrcChange(iframe: HTMLIFrameElement): void {
|
||||
const oldContentDocument = iframe.contentDocument
|
||||
if (oldContentDocument) {
|
||||
const id = this.app.nodes.getID(oldContentDocument)
|
||||
if (id !== undefined) {
|
||||
const walker = document.createTreeWalker(
|
||||
oldContentDocument,
|
||||
NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT,
|
||||
{
|
||||
acceptNode: (node) =>
|
||||
isIgnored(node) || this.app.nodes.getID(node) === undefined
|
||||
? NodeFilter.FILTER_REJECT
|
||||
: NodeFilter.FILTER_ACCEPT,
|
||||
},
|
||||
// @ts-ignore
|
||||
false,
|
||||
)
|
||||
|
||||
let removed = 0
|
||||
const totalBeforeRemove = this.app.nodes.getNodeCount()
|
||||
|
||||
while (walker.nextNode()) {
|
||||
if (!iframe.contentDocument.contains(walker.currentNode)) {
|
||||
removed += 1
|
||||
this.app.nodes.unregisterNode(walker.currentNode)
|
||||
}
|
||||
}
|
||||
|
||||
const removedPercent = Math.floor((removed / totalBeforeRemove) * 100)
|
||||
if (removedPercent > 30) {
|
||||
this.app.send(UnbindNodes(removedPercent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sendNodeAttribute(id: number, node: Element, name: string, value: string | null): void {
|
||||
if (isSVGElement(node)) {
|
||||
if (name.substr(0, 6) === 'xlink:') {
|
||||
name = name.substr(6)
|
||||
if (name.substring(0, 6) === 'xlink:') {
|
||||
name = name.substring(6)
|
||||
}
|
||||
if (value === null) {
|
||||
this.app.send(RemoveNodeAttribute(id, name))
|
||||
|
|
@ -152,7 +195,7 @@ export default abstract class Observer {
|
|||
name === 'integrity' ||
|
||||
name === 'crossorigin' ||
|
||||
name === 'autocomplete' ||
|
||||
name.substr(0, 2) === 'on'
|
||||
name.substring(0, 2) === 'on'
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ export default class TopObserver extends Observer {
|
|||
)
|
||||
}
|
||||
|
||||
crossdomainObserve(selfId: number, frameOder: number) {
|
||||
crossdomainObserve(rootNodeId: number, frameOder: number) {
|
||||
const observer = this
|
||||
Element.prototype.attachShadow = function () {
|
||||
// eslint-disable-next-line
|
||||
|
|
@ -152,7 +152,7 @@ export default class TopObserver extends Observer {
|
|||
this.app.nodes.syntheticMode(frameOder)
|
||||
const iframeObserver = new IFrameObserver(this.app)
|
||||
this.iframeObservers.push(iframeObserver)
|
||||
iframeObserver.syntheticObserve(selfId, window.document)
|
||||
iframeObserver.syntheticObserve(rootNodeId, window.document)
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ export default function (app: App): void {
|
|||
}
|
||||
}
|
||||
}) as MutationCallback,
|
||||
app.options.angularMode,
|
||||
)
|
||||
|
||||
app.attachStopCallback(() => {
|
||||
|
|
|
|||
|
|
@ -132,9 +132,13 @@ export function ngSafeBrowserMethod(method: string): string {
|
|||
: method
|
||||
}
|
||||
|
||||
export function createMutationObserver(cb: MutationCallback) {
|
||||
const mObserver = ngSafeBrowserMethod('MutationObserver') as 'MutationObserver'
|
||||
return new window[mObserver](cb)
|
||||
export function createMutationObserver(cb: MutationCallback, angularMode?: boolean) {
|
||||
if (angularMode) {
|
||||
const mObserver = ngSafeBrowserMethod('MutationObserver') as 'MutationObserver'
|
||||
return new window[mObserver](cb)
|
||||
} else {
|
||||
return new MutationObserver(cb)
|
||||
}
|
||||
}
|
||||
|
||||
export function createEventListener(
|
||||
|
|
@ -142,15 +146,23 @@ export function createEventListener(
|
|||
event: string,
|
||||
cb: EventListenerOrEventListenerObject,
|
||||
capture?: boolean,
|
||||
angularMode?: boolean,
|
||||
) {
|
||||
const safeAddEventListener = ngSafeBrowserMethod('addEventListener') as 'addEventListener'
|
||||
let safeAddEventListener: 'addEventListener'
|
||||
if (angularMode) {
|
||||
safeAddEventListener = ngSafeBrowserMethod('addEventListener') as 'addEventListener'
|
||||
} else {
|
||||
safeAddEventListener = 'addEventListener'
|
||||
}
|
||||
try {
|
||||
target[safeAddEventListener](event, cb, capture)
|
||||
} catch (e) {
|
||||
const msg = e.message
|
||||
console.debug(
|
||||
console.error(
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
`Openreplay: ${msg}; if this error is caused by an IframeObserver, ignore it`,
|
||||
event,
|
||||
target,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -160,17 +172,23 @@ export function deleteEventListener(
|
|||
event: string,
|
||||
cb: EventListenerOrEventListenerObject,
|
||||
capture?: boolean,
|
||||
angularMode?: boolean,
|
||||
) {
|
||||
const safeRemoveEventListener = ngSafeBrowserMethod(
|
||||
'removeEventListener',
|
||||
) as 'removeEventListener'
|
||||
let safeRemoveEventListener: 'removeEventListener'
|
||||
if (angularMode) {
|
||||
safeRemoveEventListener = ngSafeBrowserMethod('removeEventListener') as 'removeEventListener'
|
||||
} else {
|
||||
safeRemoveEventListener = 'removeEventListener'
|
||||
}
|
||||
try {
|
||||
target[safeRemoveEventListener](event, cb, capture)
|
||||
} catch (e) {
|
||||
const msg = e.message
|
||||
console.debug(
|
||||
console.error(
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
`Openreplay: ${msg}; if this error is caused by an IframeObserver, ignore it`,
|
||||
event,
|
||||
target,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ describe('Nodes', () => {
|
|||
const mockCallback = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
nodes = new Nodes(nodeId)
|
||||
nodes = new Nodes(nodeId, false)
|
||||
mockCallback.mockClear()
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue