Merge branch 'dev' into player-refactoring-phase-1

This commit is contained in:
sylenien 2022-11-25 17:20:37 +01:00
commit ee2eeadf56
63 changed files with 621 additions and 282 deletions

View file

@ -161,16 +161,16 @@ jobs:
# Deploy command
helm template openreplay -n app openreplay -f vars.yaml -f /tmp/image_override.yaml --set ingress-nginx.enabled=false --set skipMigration=true | kubectl apply -f -
- name: Alert slack
if: ${{ failure() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ee
SLACK_TITLE: "Failed ${{ github.workflow }}"
SLACK_COLOR: ${{ job.status }} # or a specific color like 'good' or '#ff00ff'
SLACK_WEBHOOK: ${{ secrets.SLACK_WEB_HOOK }}
SLACK_USERNAME: "OR Bot"
SLACK_MESSAGE: 'Build failed :bomb:'
#- name: Alert slack
# if: ${{ failure() }}
# uses: rtCamp/action-slack-notify@v2
# env:
# SLACK_CHANNEL: ee
# SLACK_TITLE: "Failed ${{ github.workflow }}"
# SLACK_COLOR: ${{ job.status }} # or a specific color like 'good' or '#ff00ff'
# SLACK_WEBHOOK: ${{ secrets.SLACK_WEB_HOOK }}
# SLACK_USERNAME: "OR Bot"
# SLACK_MESSAGE: 'Build failed :bomb:'
# - name: Debug Job

View file

@ -91,9 +91,3 @@ Check out our [roadmap](https://www.notion.so/openreplay/Roadmap-889d2c3d968b478
## License
This monorepo uses several licenses. See [LICENSE](/LICENSE) for more details.
## Contributors
<a href="https://github.com/openreplay/openreplay/graphs/contributors">
<img src="https://contrib.rocks/image?repo=openreplay/openreplay" />
</a>

View file

@ -213,8 +213,9 @@ def get_stages_and_events(filter_d, project_id) -> List[RealDictRow]:
AND ISE.timestamp <= stages_t.stage{i + 1}_timestamp
AND ISS.project_id=%(project_id)s
AND ISE.session_id = stages_t.session_id
AND ISS.type!='custom' -- ignore custom issues because they are massive
{"AND ISS.type IN %(issueTypes)s" if len(filter_issues) > 0 else ""}
LIMIT 20 -- remove the limit to get exact stats
LIMIT 50 -- remove the limit to get exact stats
) AS issues_t ON (TRUE)
) AS stages_and_issues_t INNER JOIN sessions USING(session_id);
"""

View file

@ -1,92 +0,0 @@
chalice:
env:
jwt_secret: SetARandomStringHere
clickhouse:
enabled: false
fromVersion: v1.6.0
global:
domainName: openreplay.local
email:
emailFrom: OpenReplay<do-not-reply@openreplay.com>
emailHost: ""
emailPassword: ""
emailPort: "587"
emailSslCert: ""
emailSslKey: ""
emailUseSsl: "false"
emailUseTls: "true"
emailUser: ""
enterpriseEditionLicense: ""
ingress:
controller:
config:
enable-real-ip: true
force-ssl-redirect: false
max-worker-connections: 0
proxy-body-size: 10m
ssl-redirect: false
extraArgs:
default-ssl-certificate: app/openreplay-ssl
ingressClass: openreplay
ingressClassResource:
name: openreplay
service:
externalTrafficPolicy: Local
kafka:
kafkaHost: kafka.db.svc.cluster.local
kafkaPort: "9092"
kafkaUseSsl: "false"
zookeeperHost: databases-zookeeper.svc.cluster.local
zookeeperNonTLSPort: 2181
postgresql:
postgresqlDatabase: postgres
postgresqlHost: postgresql.db.svc.cluster.local
postgresqlPassword: changeMePassword
postgresqlPort: "5432"
postgresqlUser: postgres
redis:
redisHost: redis-master.db.svc.cluster.local
redisPort: "6379"
s3:
accessKey: changeMeMinioAccessKey
assetsBucket: sessions-assets
endpoint: http://minio.db.svc.cluster.local:9000
recordingsBucket: mobs
region: us-east-1
secretKey: changeMeMinioPassword
sourcemapsBucket: sourcemaps
ingress-nginx:
controller:
config:
enable-real-ip: true
force-ssl-redirect: false
max-worker-connections: 0
proxy-body-size: 10m
ssl-redirect: false
extraArgs:
default-ssl-certificate: app/openreplay-ssl
ingressClass: openreplay
ingressClassResource:
name: openreplay
service:
externalTrafficPolicy: Local
kafka:
kafkaHost: kafka.db.svc.cluster.local
kafkaPort: "9092"
kafkaUseSsl: "false"
zookeeperHost: databases-zookeeper.svc.cluster.local
zookeeperNonTLSPort: 2181
minio:
global:
minio:
accessKey: changeMeMinioAccessKey
secretKey: changeMeMinioPassword
postgresql:
postgresqlDatabase: postgres
postgresqlHost: postgresql.db.svc.cluster.local
postgresqlPassword: changeMePassword
postgresqlPort: "5432"
postgresqlUser: postgres
redis:
redisHost: redis-master.db.svc.cluster.local
redisPort: "6379"

View file

@ -51,6 +51,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st
AND users.deleted_at ISNULL
AND users.tenant_id = %(tenant_id)s
AND (roles.all_projects OR roles_projects.project_id = s.project_id)
LIMIT 1
) AS role_project ON (TRUE)"""
extra_projection = ""
extra_join = ""

View file

@ -220,8 +220,9 @@ def get_stages_and_events(filter_d, project_id) -> List[RealDictRow]:
AND ISE.timestamp <= stages_t.stage{i + 1}_timestamp
AND ISS.project_id=%(project_id)s
AND ISE.session_id = stages_t.session_id
AND ISS.type!='custom' -- ignore custom issues because they are massive
{"AND ISS.type IN %(issueTypes)s" if len(filter_issues) > 0 else ""}
LIMIT 20 -- remove the limit to get exact stats
LIMIT 50 -- remove the limit to get exact stats
) AS issues_t ON (TRUE)
) AS stages_and_issues_t INNER JOIN sessions USING(session_id);
"""

View file

@ -23,4 +23,4 @@ MINIO_SECRET_KEY = ''
# APP and TRACKER VERSIONS
VERSION = '1.9.0'
TRACKER_VERSION = '4.1.6'
TRACKER_VERSION = '4.1.7'

View file

@ -3,18 +3,56 @@ import { connect } from 'react-redux';
import { setAutoplayValues } from 'Duck/sessions';
import { session as sessionRoute } from 'App/routes';
import { Link, Icon, Toggler, Tooltip } from 'UI';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import cn from 'classnames';
import { fetchAutoplaySessions } from 'Duck/search';
import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite';
function Autoplay(props) {
const { previousId, nextId, disabled } = props;
const { player, store } = React.useContext(PlayerContext)
const PER_PAGE = 10;
interface Props extends RouteComponentProps {
previousId: string;
nextId: string;
autoplay: boolean;
defaultList: any;
currentPage: number;
total: number;
setAutoplayValues?: () => void;
toggleAutoplay?: () => void;
latestRequestTime: any;
sessionIds: any;
fetchAutoplaySessions?: (page: number) => Promise<void>;
}
function Autoplay(props: Props) {
const {
previousId,
nextId,
currentPage,
total,
sessionIds,
latestRequestTime,
match: {
// @ts-ignore
params: { sessionId },
},
} = props;
const { player, store } = React.useContext(PlayerContext)
const { autoplay } = store.get()
const disabled = sessionIds.length === 0;
useEffect(() => {
props.setAutoplayValues();
if (latestRequestTime) {
props.setAutoplayValues();
const totalPages = Math.ceil(total / PER_PAGE);
const index = sessionIds.indexOf(sessionId);
// check for the last page and load the next
if (currentPage !== totalPages && index === sessionIds.length - 1) {
props.fetchAutoplaySessions(currentPage + 1).then(props.setAutoplayValues);
}
}
}, []);
return (
@ -66,14 +104,14 @@ function Autoplay(props) {
);
}
const connectAutoplay = connect(
(state) => ({
export default connect(
(state: any) => ({
previousId: state.getIn(['sessions', 'previousId']),
nextId: state.getIn(['sessions', 'nextId']),
currentPage: state.getIn(['search', 'currentPage']) || 1,
total: state.getIn(['sessions', 'total']) || 0,
sessionIds: state.getIn(['sessions', 'sessionIds']) || [],
latestRequestTime: state.getIn(['search', 'latestRequestTime']),
}),
{ setAutoplayValues }
);
export default connectAutoplay(
observer(Autoplay)
);
{ setAutoplayValues, fetchAutoplaySessions }
)(withRouter(observer(Autoplay)))

View file

@ -50,7 +50,7 @@ function Step({ step, ind, isDefault }: { step: IStep; ind: number; isDefault?:
>
<div className="rounded-3xl px-4 bg-gray-lightest relative z-10">{ind + 1}</div>
<div className="w-full">
<div className="flex items-center w-full gap-2">
<div className="flex items-start w-full gap-2">
<div className="px-1 text-disabled-text">{durationFromMs(step.time)}</div>
{/* @ts-ignore */}
<Icon name={step.icon} size={16} color="gray-darkest" className="relative z-10" />

View file

@ -70,7 +70,7 @@ function XRay({ xrayProps, timePointer, stepPickRadius, clearEventSelection, set
<>
<div className="flex items-center justify-between my-2">
<div className=" text-gray-dark py-2">
XRAY
X-RAY
{timePointer > 0 ? (
<span className="text-disabled-text ml-2">
{Duration.fromMillis(selectedTime).toFormat('hh:mm:ss')}
@ -79,14 +79,11 @@ function XRay({ xrayProps, timePointer, stepPickRadius, clearEventSelection, set
</div>
{!shouldShowPointerReset ? (
<div
className="flex items-center gap-2 rounded bg-active-blue px-2 py-1 whitespace-nowrap overflow-hidden text-clip"
className="flex items-center gap-2 rounded bg-active-blue px-2 py-1 whitespace-nowrap overflow-hidden text-clip group"
id="pdf-ignore"
>
<Icon name="info-circle" size={16} />
<div>
Click anywhere on <span className="font-semibold">X-RAY</span> to drilldown and add
steps
</div>
<div>Click anywhere in the graph below to drilldown and add steps</div>
</div>
) : (
<Button id="pdf-ignore" variant="text-primary" onClick={clearEventSelection}>
@ -94,7 +91,11 @@ function XRay({ xrayProps, timePointer, stepPickRadius, clearEventSelection, set
</Button>
)}
</div>
<div className="relative cursor-pointer" onClick={pickEventRadius} ref={xrayContainer}>
<div
className="relative cursor-pointer group-hover:border-dotted hover:border-dotted group-hover:border-gray-dark hover:border-gray-dark border border-transparent"
onClick={pickEventRadius}
ref={xrayContainer}
>
<div
id="pdf-ignore"
style={{

View file

@ -1 +1 @@
export { default } from './LongTasks';
export { default } from './LongTasks.DEPRECATED';

View file

@ -24,7 +24,6 @@ import Storage from '../Storage';
import { ConnectedPerformance } from '../Performance';
import GraphQL from '../GraphQL';
import Exceptions from '../Exceptions/Exceptions';
import LongTasks from '../LongTasks';
import Inspector from '../Inspector';
import Controls from './Controls';
import Overlay from './Overlay';

View file

@ -26,11 +26,7 @@ function NotePopup({
return (
<GuidePopup
title={
<div className="color-gray-dark">
Introducing <span className={''}>Notes</span>
</div>
}
title="Introducing Notes"
description={'Annotate session replays and share your feedback with the rest of your team.'}
>
<Button icon="quotes" variant="text" disabled={tooltipActive} onClick={toggleNotePopup}>

View file

@ -10,6 +10,7 @@
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 200px;
&:hover {
background-color: $active-blue;
color: $teal !important;
@ -34,4 +35,4 @@
overflow: hidden;
text-overflow: ellipsis;
}
}
}

View file

@ -8,19 +8,19 @@ import { filtersMap } from 'Types/filter/newFilter';
export const getMatchingEntries = (searchQuery: string, filters: Record<string, any>) => {
const matchingCategories: string[] = [];
const matchingFilters: Record<string, any> = {};
if (searchQuery.length === 0) return {
const lowerCaseQuery = searchQuery.toLowerCase();
if (lowerCaseQuery.length === 0) return {
matchingCategories: Object.keys(filters),
matchingFilters: filters,
};
Object.keys(filters).forEach(name => {
if (name.toLocaleLowerCase().includes(searchQuery)) {
if (name.toLocaleLowerCase().includes(lowerCaseQuery)) {
matchingCategories.push(name);
matchingFilters[name] = filters[name];
} else {
const filtersQuery = filters[name]
.filter(filterOption => filterOption.label.toLocaleLowerCase().includes(searchQuery))
.filter((filterOption: any) => filterOption.label.toLocaleLowerCase().includes(lowerCaseQuery))
if (filtersQuery.length > 0) matchingFilters[name] = filtersQuery
filtersQuery.length > 0 && matchingCategories.push(name);
@ -64,10 +64,10 @@ function FilterModal(props: Props) {
// console.log(matchingFilters)
return (
<div className={stl.wrapper} style={{ width: '480px', maxHeight: '380px', overflowY: 'auto'}}>
<div className={searchQuery && !isResultEmpty ? 'mb-6' : ''} style={{ columns: "auto 200px" }}>
<div className={searchQuery && !isResultEmpty ? 'mb-6' : ''} style={{ columns: matchingCategories.length > 1 ? 'auto 200px' : 1 }}>
{matchingCategories.map((key) => {
return (
<div className="mb-6" key={key}>
<div className="mb-6 flex flex-col gap-2" key={key}>
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">{key}</div>
<div>
{matchingFilters[key] && matchingFilters[key].map((filter: any) => (

View file

@ -40,13 +40,13 @@ export default function GuidePopup({ children, title, description }: IProps) {
style={{ zIndex: INDEXES.POPUP_GUIDE_BG, opacity: '0.7' }}
></div>
<Tooltip
offset={30}
offset={20}
className="!bg-white rounded text-center shadow !p-6"
title={
<div className="relative">
<div className="font-bold">{title}</div>
<div className="color-gray-medium w-80">{description}</div>
<div className="w-10 h-10 bg-white rotate-45 absolute right-0 left-0 m-auto" style={{ top: '-38px'}} />
<div className="font-bold text-figmaColors-text-primary">{title}</div>
<div className="color-gray-dark w-80">{description}</div>
<div className="w-4 h-4 bg-white rotate-45 absolute right-0 left-0 m-auto" style={{ top: '-28px'}} />
</div>
}
open={true}

View file

@ -4,6 +4,7 @@ import SessionHeader from './components/SessionHeader';
import NotesList from './components/Notes/NoteList';
import { connect } from 'react-redux';
import { fetchList as fetchMembers } from 'Duck/member';
import LatestSessionsMessage from './components/LatestSessionsMessage';
function SessionListContainer({
activeTab,
@ -21,6 +22,7 @@ function SessionListContainer({
<div className="widget-wrapper">
<SessionHeader />
<div className="border-b" />
<LatestSessionsMessage />
{activeTab !== 'notes' ? <SessionList /> : <NotesList members={members} />}
</div>
);

View file

@ -0,0 +1,31 @@
import React from 'react';
import { connect } from 'react-redux';
import { updateCurrentPage } from 'Duck/search';
import { numberWithCommas } from 'App/utils'
interface Props {
latestSessions: any;
updateCurrentPage: (page: number) => void;
}
function LatestSessionsMessage(props: Props) {
const { latestSessions = [] } = props;
const count = latestSessions.length;
return count > 0 ? (
<div
className="bg-amber-50 p-1 flex w-full border-b text-center justify-center link"
style={{ backgroundColor: 'rgb(255 251 235)' }}
onClick={() => props.updateCurrentPage(1)}
>
Show {numberWithCommas(count)} new Sessions
</div>
) : (
<></>
);
}
export default connect(
(state: any) => ({
latestSessions: state.getIn(['search', 'latestList']),
}),
{ updateCurrentPage }
)(LatestSessionsMessage);

View file

@ -0,0 +1 @@
export { default } from './LatestSessionsMessage';

View file

@ -9,24 +9,25 @@ import {
addFilterByKeyAndValue,
updateCurrentPage,
setScrollPosition,
checkForLatestSessions,
} from 'Duck/search';
import useTimeout from 'App/hooks/useTimeout';
import { numberWithCommas } from 'App/utils';
import { fetchListActive as fetchMetadata } from 'Duck/customField';
enum NoContentType {
Bookmarked,
Vaulted,
ToDate,
Bookmarked,
Vaulted,
ToDate,
}
const AUTOREFRESH_INTERVAL = 5 * 60 * 1000;
const PER_PAGE = 10;
let sessionTimeOut: any = null;
interface Props {
loading: boolean;
list: any;
currentPage: number;
pageSize: number;
total: number;
filters: any;
lastPlayedSessionId: string;
@ -39,13 +40,15 @@ interface Props {
fetchMetadata: () => void;
activeTab: any;
isEnterprise?: boolean;
checkForLatestSessions: () => void;
}
function SessionList(props: Props) {
const [noContentType, setNoContentType] = React.useState<NoContentType>(NoContentType.ToDate)
const [noContentType, setNoContentType] = React.useState<NoContentType>(NoContentType.ToDate);
const {
loading,
list,
currentPage,
pageSize,
total,
filters,
lastPlayedSessionId,
@ -60,19 +63,19 @@ function SessionList(props: Props) {
const isVault = isBookmark && isEnterprise;
const NO_CONTENT = React.useMemo(() => {
if (isBookmark && !isEnterprise) {
setNoContentType(NoContentType.Bookmarked)
setNoContentType(NoContentType.Bookmarked);
return {
icon: ICONS.NO_BOOKMARKS,
message: 'No sessions bookmarked.',
};
} else if (isVault) {
setNoContentType(NoContentType.Vaulted)
setNoContentType(NoContentType.Vaulted);
return {
icon: ICONS.NO_SESSIONS_IN_VAULT,
message: 'No sessions found in vault.',
};
}
setNoContentType(NoContentType.ToDate)
setNoContentType(NoContentType.ToDate);
return {
icon: ICONS.NO_SESSIONS,
message: 'No relevant sessions found for the selected time period.',
@ -81,7 +84,7 @@ function SessionList(props: Props) {
useTimeout(() => {
if (!document.hidden) {
props.fetchSessions(null, true);
props.checkForLatestSessions();
}
}, AUTOREFRESH_INTERVAL);
@ -107,7 +110,7 @@ function SessionList(props: Props) {
sessionTimeOut = setTimeout(function () {
if (!document.hidden) {
props.fetchSessions(null, true);
props.checkForLatestSessions();
}
}, 5000);
};
@ -182,15 +185,15 @@ function SessionList(props: Props) {
{total > 0 && (
<div className="flex items-center justify-between p-5">
<div>
Showing <span className="font-medium">{(currentPage - 1) * PER_PAGE + 1}</span> to{' '}
<span className="font-medium">{(currentPage - 1) * PER_PAGE + list.size}</span> of{' '}
Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{' '}
<span className="font-medium">{(currentPage - 1) * pageSize + list.size}</span> of{' '}
<span className="font-medium">{numberWithCommas(total)}</span> sessions.
</div>
<Pagination
page={currentPage}
totalPages={Math.ceil(total / PER_PAGE)}
totalPages={Math.ceil(total / pageSize)}
onPageChange={(page) => props.updateCurrentPage(page)}
limit={PER_PAGE}
limit={pageSize}
debounceRequest={1000}
/>
</div>
@ -210,7 +213,15 @@ export default connect(
total: state.getIn(['sessions', 'total']) || 0,
scrollY: state.getIn(['search', 'scrollY']),
activeTab: state.getIn(['search', 'activeTab']),
pageSize: state.getIn(['search', 'pageSize']),
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee',
}),
{ updateCurrentPage, addFilterByKeyAndValue, setScrollPosition, fetchSessions, fetchMetadata }
{
updateCurrentPage,
addFilterByKeyAndValue,
setScrollPosition,
fetchSessions,
fetchMetadata,
checkForLatestSessions,
}
)(SessionList);

View file

@ -13,7 +13,10 @@ function XRayButton(props: Props) {
const { player: Player } = React.useContext(PlayerContext);
const { isActive } = props;
const [showGuide, setShowGuide] = useState(!localStorage.getItem(FEATURE_KEYS.XRAY));
// const [showGuide, setShowGuide] = useState(!localStorage.getItem(FEATURE_KEYS.XRAY));
const showGuide = false;
const setShowGuide = (anyt: any) => anyt;
useEffect(() => {
if (!showGuide) {
return;

View file

@ -5,16 +5,18 @@ import { array, success, createListUpdater, mergeReducers } from './funcTools/to
import Filter from 'Types/filter';
import SavedFilter from 'Types/filter/savedFilter';
import { errors as errorsRoute, isRoute } from 'App/routes';
import { fetchList as fetchSessionList } from './sessions';
import { fetchList as fetchSessionList, fetchAutoplayList } from './sessions';
import { fetchList as fetchErrorsList } from './errors';
import { FilterCategory, FilterKey } from 'Types/filter/filterType';
import { filtersMap, liveFiltersMap, generateFilterOptions } from 'Types/filter/newFilter';
import { DURATION_FILTER } from 'App/constants/storageKeys';
import Period, { CUSTOM_RANGE } from 'Types/app/period';
const ERRORS_ROUTE = errorsRoute();
const name = 'search';
const idKey = 'searchId';
const PER_PAGE = 10;
const FETCH_LIST = fetchListType(name);
const FETCH_FILTER_SEARCH = fetchListType(`${name}/FILTER_SEARCH`);
@ -33,25 +35,30 @@ const SET_ACTIVE_TAB = `${name}/SET_ACTIVE_TAB`;
const SET_SCROLL_POSITION = `${name}/SET_SCROLL_POSITION`;
const REFRESH_FILTER_OPTIONS = 'filters/REFRESH_FILTER_OPTIONS';
const CHECK_LATEST = fetchListType(`${name}/CHECK_LATEST`);
const UPDATE_LATEST_REQUEST_TIME = 'filters/UPDATE_LATEST_REQUEST_TIME'
function chartWrapper(chart = []) {
return chart.map((point) => ({ ...point, count: Math.max(point.count, 0) }));
}
// function chartWrapper(chart = []) {
// return chart.map((point) => ({ ...point, count: Math.max(point.count, 0) }));
// }
const savedSearchIdKey = 'searchId';
const updateItemInList = createListUpdater(savedSearchIdKey);
const updateInstance = (state, instance) =>
state.getIn(['savedSearch', savedSearchIdKey]) === instance[savedSearchIdKey] ? state.mergeIn(['savedSearch'], SavedFilter(instance)) : state;
// const savedSearchIdKey = 'searchId';
// const updateItemInList = createListUpdater(savedSearchIdKey);
// const updateInstance = (state, instance) =>
// state.getIn(['savedSearch', savedSearchIdKey]) === instance[savedSearchIdKey] ? state.mergeIn(['savedSearch'], SavedFilter(instance)) : state;
const initialState = Map({
filterList: generateFilterOptions(filtersMap),
filterListLive: generateFilterOptions(liveFiltersMap),
list: List(),
latestRequestTime: null,
latestList: List(),
alertMetricId: null,
instance: new Filter({ filters: [] }),
savedSearch: new SavedFilter({}),
filterSearchList: {},
currentPage: 1,
pageSize: PER_PAGE,
activeTab: { name: 'All', type: 'all' },
scrollY: 0,
});
@ -73,6 +80,10 @@ function reducer(state = initialState, action = {}) {
'list',
List(data.map(SavedFilter)).sortBy((i) => i.searchId)
);
case UPDATE_LATEST_REQUEST_TIME:
return state.set('latestRequestTime', Date.now()).set('latestList', [])
case success(CHECK_LATEST):
return state.set('latestList', action.data)
case success(FETCH_FILTER_SEARCH):
const groupedList = action.data.reduce((acc, item) => {
const { projectId, type, value } = item;
@ -131,52 +142,69 @@ export const filterMap = ({ category, value, key, operator, sourceOperator, sour
filters: filters ? filters.map(filterMap) : [],
});
const getFilters = (state) => {
const filter = state.getIn(['search', 'instance']).toData();
const activeTab = state.getIn(['search', 'activeTab']);
if (activeTab.type !== 'all' && activeTab.type !== 'bookmark' && activeTab.type !== 'vault') {
const tmpFilter = filtersMap[FilterKey.ISSUE];
tmpFilter.value = [activeTab.type];
filter.filters = filter.filters.concat(tmpFilter);
}
if (activeTab.type === 'bookmark' || activeTab.type === 'vault') {
filter.bookmarked = true;
}
filter.filters = filter.filters.map(filterMap);
// duration filter from local storage
if (!filter.filters.find((f) => f.type === FilterKey.DURATION)) {
const durationFilter = JSON.parse(localStorage.getItem(DURATION_FILTER) || '{"count": 0}');
let durationValue = parseInt(durationFilter.count);
if (durationValue > 0) {
const value = [0];
durationValue = durationFilter.countType === 'min' ? durationValue * 60 * 1000 : durationValue * 1000;
if (durationFilter.operator === '<') {
value[0] = durationValue;
} else if (durationFilter.operator === '>') {
value[1] = durationValue;
}
filter.filters = filter.filters.concat({
type: FilterKey.DURATION,
operator: 'is',
value,
});
}
}
return filter;
}
export const reduceThenFetchResource =
(actionCreator) =>
(...args) =>
(dispatch, getState) => {
dispatch(actionCreator(...args));
const filter = getState().getIn(['search', 'instance']).toData();
const activeTab = getState().getIn(['search', 'activeTab']);
if (activeTab.type === 'notes') return;
if (activeTab.type !== 'all' && activeTab.type !== 'bookmark' && activeTab.type !== 'vault') {
const tmpFilter = filtersMap[FilterKey.ISSUE];
tmpFilter.value = [activeTab.type];
filter.filters = filter.filters.concat(tmpFilter);
}
if (activeTab.type === 'bookmark' || activeTab.type === 'vault') {
filter.bookmarked = true;
}
filter.filters = filter.filters.map(filterMap);
filter.limit = 10;
const filter = getFilters(getState());
filter.limit = PER_PAGE;
filter.page = getState().getIn(['search', 'currentPage']);
const forceFetch = filter.filters.length === 0 || args[1] === true;
// duration filter from local storage
if (!filter.filters.find((f) => f.type === FilterKey.DURATION)) {
const durationFilter = JSON.parse(localStorage.getItem(DURATION_FILTER) || '{"count": 0}');
let durationValue = parseInt(durationFilter.count);
if (durationValue > 0) {
const value = [0];
durationValue = durationFilter.countType === 'min' ? durationValue * 60 * 1000 : durationValue * 1000;
if (durationFilter.operator === '<') {
value[0] = durationValue;
} else if (durationFilter.operator === '>') {
value[1] = durationValue;
}
filter.filters = filter.filters.concat({
type: FilterKey.DURATION,
operator: 'is',
value,
});
}
// reset the timestamps to latest
if (filter.rangeValue !== CUSTOM_RANGE) {
const period = new Period({ rangeName: filter.rangeValue })
const newTimestamps = period.toJSON();
filter.startDate = newTimestamps.startDate
filter.endDate = newTimestamps.endDate
}
dispatch(updateLatestRequestTime())
return isRoute(ERRORS_ROUTE, window.location.pathname) ? dispatch(fetchErrorsList(filter)) : dispatch(fetchSessionList(filter, forceFetch));
};
@ -353,3 +381,33 @@ export const setScrollPosition = (scrollPosition) => {
scrollPosition,
};
};
export const updateLatestRequestTime = () => {
return {
type: UPDATE_LATEST_REQUEST_TIME
}
}
export const checkForLatestSessions = () => (dispatch, getState) => {
const state = getState();
const filter = getFilters(state);
const latestRequestTime = state.getIn(['search', 'latestRequestTime'])
if (!!latestRequestTime) {
const period = new Period({ rangeName: CUSTOM_RANGE, start: latestRequestTime, end: Date.now() })
const newTimestamps = period.toJSON();
filter.startDate = newTimestamps.startDate
filter.endDate = newTimestamps.endDate
}
return dispatch({
types: array(CHECK_LATEST),
call: (client) => client.post(`/sessions/search/ids`, filter),
});
}
export const fetchAutoplaySessions = (page) => (dispatch, getState) => {
const filter = getFilters(getState());
filter.page = page;
filter.limit = PER_PAGE;
return dispatch(fetchAutoplayList(filter));
}

View file

@ -11,6 +11,7 @@ import { getDateRangeFromValue } from 'App/dateRange';
const name = 'sessions';
const INIT = 'sessions/INIT';
const FETCH_LIST = new RequestTypes('sessions/FETCH_LIST');
const FETCH_AUTOPLAY_LIST = new RequestTypes('sessions/FETCH_AUTOPLAY_LIST');
const FETCH = new RequestTypes('sessions/FETCH');
const FETCH_FAVORITE_LIST = new RequestTypes('sessions/FETCH_FAVORITE_LIST');
const FETCH_LIVE_LIST = new RequestTypes('sessions/FETCH_LIVE_LIST');
@ -96,6 +97,10 @@ const reducer = (state = initialState, action = {}) => {
list.filter(({ favorite }) => favorite)
)
.set('total', total);
case FETCH_AUTOPLAY_LIST.SUCCESS:
let sessionIds = state.get('sessionIds');
sessionIds = sessionIds.concat(action.data.map(i => i.sessionId + ''))
return state.set('sessionIds', sessionIds.filter((i, index) => sessionIds.indexOf(i) === index ))
case SET_AUTOPLAY_VALUES: {
const sessionIds = state.get('sessionIds');
const currentSessionId = state.get('current').sessionId;
@ -257,7 +262,7 @@ function init(session) {
export const fetchList =
(params = {}, force = false) =>
(dispatch, getState) => {
(dispatch) => {
if (!force) { // compare with the last fetched filter
const oldFilters = getSessionFilter();
if (compareJsonObjects(oldFilters, cleanSessionFilters(params))) {
@ -273,6 +278,19 @@ export const fetchList =
});
};
export const fetchAutoplayList =
(params = {}) =>
(dispatch) => {
setSessionFilter(cleanSessionFilters(params));
return dispatch({
types: FETCH_AUTOPLAY_LIST.toArray(),
call: (client) => client.post('/sessions/search/ids', params),
params: cleanParams(params),
});
};
export function fetchErrorStackList(sessionId, errorId) {
return {
types: FETCH_ERROR_STACK.toArray(),
@ -436,4 +454,4 @@ export function updateLastPlayedSession(sessionId) {
type: LAST_PLAYED_SESSION_ID,
sessionId,
};
}
}

View file

@ -1,13 +1,13 @@
import Record from 'Types/Record';
import { List } from 'immutable';
import Watchdog from 'Types/watchdog'
export const issues_types = List([
{ 'type': 'all', 'visible': true, 'order': 0, 'name': 'All', 'icon': '' },
{ 'type': 'js_exception', 'visible': true, 'order': 1, 'name': 'Errors', 'icon': 'funnel/exclamation-circle' },
{ 'type': 'click_rage', 'visible': true, 'order': 2, 'name': 'Click Rage', 'icon': 'funnel/emoji-angry' },
{ 'type': 'crash', 'visible': true, 'order': 3, 'name': 'Crashes', 'icon': 'funnel/file-earmark-break' },
{ 'type': 'memory', 'visible': true, 'order': 4, 'name': 'High Memory', 'icon': 'funnel/sd-card' },
{ 'type': 'bad_request', 'visible': true, 'order': 2, 'name': 'Bad Requests', 'icon': 'funnel/file-medical-alt' },
{ 'type': 'click_rage', 'visible': true, 'order': 3, 'name': 'Click Rage', 'icon': 'funnel/emoji-angry' },
{ 'type': 'crash', 'visible': true, 'order': 4, 'name': 'Crashes', 'icon': 'funnel/file-earmark-break' },
// { 'type': 'memory', 'visible': true, 'order': 4, 'name': 'High Memory', 'icon': 'funnel/sd-card' },
// { 'type': 'vault', 'visible': true, 'order': 5, 'name': 'Vault', 'icon': 'safe' },
// { 'type': 'bookmark', 'visible': true, 'order': 5, 'name': 'Bookmarks', 'icon': 'safe' },
// { 'type': 'bad_request', 'visible': true, 'order': 1, 'name': 'Bad Requests', 'icon': 'funnel/file-medical-alt' },

View file

@ -143,5 +143,6 @@ clickhouse:
postgreql:
enabled: true
# For enterpriseEdition Only
vault:
enabled: false

View file

@ -67,6 +67,8 @@ usr=`whoami`
fi
}
sleep 10
# ## Installing openssl
# sudo apt update &> /dev/null
# sudo apt install openssl -y &> /dev/null
@ -106,6 +108,7 @@ sed_i_wrapper -i "s/accessKey: \"changeMeMinioAccessKey\"/accessKey: \"$(randomP
sed_i_wrapper -i "s/secretKey: \"changeMeMinioPassword\"/secretKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/jwt_secret: \"SetARandomStringHere\"/jwt_secret: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/assistKey: \"SetARandomStringHere\"/assistKey: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/assistJWTSecret: \"SetARandomStringHere\"/assistJWTSecret: \"$(randomPass)\"/g" vars.yaml
sed_i_wrapper -i "s/domainName: \"\"/domainName: \"${DOMAIN_NAME}\"/g" vars.yaml
info "Setting proper permission for shared folder"

View file

@ -51,22 +51,47 @@ spec:
value: "5432"
- name: pg_dbname
value: "{{ .Values.global.postgresql.postgresqlDatabase }}"
- name: ch_host
value: "{{ .Values.global.clickhouse.chHost }}"
- name: ch_port
value: "{{ .Values.global.clickhouse.service.webPort }}"
- name: pg_user
value: '{{ .Values.global.postgresql.postgresqlUser }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: SITE_URL
value: 'https://{{ .Values.global.domainName }}'
- name: S3_HOST
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
value: 'https://{{ .Values.global.domainName }}:{{ .Values.global.ingress.controller.service.ports.https}}'
{{- else}}
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: S3_SECRET
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_DEFAULT_REGION
value: '{{ .Values.global.s3.region }}'
- name: EMAIL_HOST

View file

@ -98,8 +98,6 @@ autoscaling:
# targetMemoryUtilizationPercentage: 80
env:
ch_host: clickhouse-openreplay-clickhouse.db.svc.cluster.local
ch_port: 9000
PYTHONUNBUFFERED: '0'

View file

@ -44,9 +44,23 @@ spec:
{{- end}}
env:
- name: AWS_ACCESS_KEY_ID
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: AWS_SECRET_ACCESS_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: S3_BUCKET_ASSETS
value: {{ .Values.global.s3.assetsBucket }}
- name: LICENSE_KEY
@ -69,7 +83,7 @@ spec:
# 4. Using AWS itself.
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
- name: ASSETS_ORIGIN
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
# Local minio Installation
value: 'https://{{ .Values.global.domainName }}:{{.Values.global.ingress.controller.service.ports.https}}/{{.Values.global.s3.assetsBucket}}'
{{- else if contains "amazonaws.com" .Values.global.s3.endpoint }}

View file

@ -50,15 +50,31 @@ spec:
- name: AWS_DEFAULT_REGION
value: "{{ .Values.global.s3.region }}"
- name: S3_HOST
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
value: 'https://{{ .Values.global.domainName }}:{{ .Values.global.ingress.controller.service.ports.https}}'
{{- else}}
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: S3_SECRET
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: REDIS_URL
value: {{ .Values.global.redis.redisHost }}
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: '{{ $val }}'

View file

@ -94,7 +94,6 @@ autoscaling:
# targetMemoryUtilizationPercentage: 80
env:
REDIS_URL: "redis://redis-master.db.svc.cluster.local:6379"
debug: 0
uws: false
redis: false

View file

@ -43,6 +43,14 @@ spec:
{{- .Values.healthCheck | toYaml | nindent 10}}
{{- end}}
env:
- name: ch_host
value: "{{ .Values.global.clickhouse.chHost }}"
- name: ch_port
value: "{{ .Values.global.clickhouse.service.webPort }}"
- name: sourcemaps_reader
value: "http://sourcemaps-reader-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9000/sourcemaps/%s/sourcemaps"
- name: ASSIST_URL
value: "http://assist-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s"
- name: ASSIST_JWT_SECRET
value: {{ .Values.global.assistJWTSecret }}
- name: ASSIST_KEY
@ -60,19 +68,40 @@ spec:
- name: pg_user
value: '{{ .Values.global.postgresql.postgresqlUser }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: SITE_URL
value: 'https://{{ .Values.global.domainName }}'
- name: S3_HOST
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
value: 'https://{{ .Values.global.domainName }}:{{ .Values.global.ingress.controller.service.ports.https}}'
{{- else}}
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: S3_SECRET
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_DEFAULT_REGION
value: '{{ .Values.global.s3.region }}'
- name: sessions_region

View file

@ -91,8 +91,6 @@ autoscaling:
# targetMemoryUtilizationPercentage: 80
env:
ch_host: clickhouse-openreplay-clickhouse.db.svc.cluster.local
ch_port: 9000
captcha_server: ''
captcha_key: ''
async_Token: ''

View file

@ -43,6 +43,8 @@ spec:
{{- .Values.healthCheck | toYaml | nindent 10}}
{{- end}}
env:
- name: CLICKHOUSE_STRING
value: '{{ .Values.global.clickhouse.chHost }}:{{.Values.global.clickhouse.service.webPort}}/{{.Values.env.ch_db}}'
- name: LICENSE_KEY
value: '{{ .Values.global.enterpriseEditionLicense }}'
- name: REDIS_STRING
@ -51,10 +53,19 @@ spec:
value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}'
- name: KAFKA_USE_SSL
value: '{{ .Values.global.kafka.kafkaUseSsl }}'
- name: POSTGRES_STRING
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:{{ .Values.global.postgresql.postgresqlPassword }}@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: QUICKWIT_ENABLED
value: '{{ .Values.global.quickwit.enabled }}'
- name: POSTGRES_STRING
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: '{{ $val }}'

View file

@ -98,7 +98,7 @@ autoscaling:
# targetMemoryUtilizationPercentage: 80
env:
CLICKHOUSE_STRING: tcp://clickhouse-openreplay-clickhouse.db.svc.cluster.local:9000/default
ch_db: default
nodeSelector: {}

View file

@ -51,8 +51,17 @@ spec:
value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}'
- name: KAFKA_USE_SSL
value: '{{ .Values.global.kafka.kafkaUseSsl }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: POSTGRES_STRING
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:{{ .Values.global.postgresql.postgresqlPassword }}@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: '{{ $val }}'

View file

@ -44,9 +44,23 @@ spec:
{{- end}}
env:
- name: AWS_ACCESS_KEY_ID
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: AWS_SECRET_ACCESS_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_REGION
value: '{{ .Values.global.s3.region }}'
- name: LICENSE_KEY
@ -57,8 +71,17 @@ spec:
value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}'
- name: KAFKA_USE_SSL
value: '{{ .Values.global.kafka.kafkaUseSsl }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: POSTGRES_STRING
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:{{ .Values.global.postgresql.postgresqlPassword }}@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
# We need to check what is the object store endpoint.
# There can be 4 options
# 1. Using minio inside kube clster
@ -67,7 +90,7 @@ spec:
# 4. Using AWS itself.
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
- name: ASSETS_ORIGIN
{{- if eq .Values.global.s3.endpoint "frontend://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
# Local minio Installation
value: 'frontends://{{ .Values.global.domainName }}:{{.Values.global.ingress.controller.service.ports.https}}/{{.Values.global.s3.assetsBucket}}'
{{- else if contains "amazonaws.com" .Values.global.s3.endpoint }}

View file

@ -44,9 +44,23 @@ spec:
{{- end}}
env:
- name: AWS_ACCESS_KEY_ID
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: AWS_SECRET_ACCESS_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_REGION
value: '{{ .Values.global.s3.region }}'
- name: LICENSE_KEY
@ -57,8 +71,17 @@ spec:
value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}'
- name: KAFKA_USE_SSL
value: '{{ .Values.global.kafka.kafkaUseSsl }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: POSTGRES_STRING
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:{{ .Values.global.postgresql.postgresqlPassword }}@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
# We need to check what is the object store endpoint.
# There can be 4 options
# 1. Using minio inside kube clster
@ -67,7 +90,7 @@ spec:
# 4. Using AWS itself.
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
- name: ASSETS_ORIGIN
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
# Local minio Installation
value: 'https://{{ .Values.global.domainName }}:{{.Values.global.ingress.controller.service.ports.https}}/{{.Values.global.s3.assetsBucket}}'
{{- else if contains "amazonaws.com" .Values.global.s3.endpoint }}

View file

@ -36,7 +36,7 @@ spec:
path: /ingest/(.*)
{{- end }}
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
## TODO:
## Frontend service from minio will be migrated to nginx atomic container.
## This ingress is just a workaround.
@ -45,7 +45,7 @@ apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minio
namespace: db
namespace: "{{(split "." .Values.global.s3.endpoint)._1}}"
spec:
ingressClassName: "{{ tpl .Values.ingress.className . }}"
rules:

View file

@ -51,8 +51,17 @@ spec:
value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}'
- name: KAFKA_USE_SSL
value: '{{ .Values.global.kafka.kafkaUseSsl }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: POSTGRES_STRING
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:{{ .Values.global.postgresql.postgresqlPassword }}@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: '{{ $val }}'

View file

@ -46,7 +46,14 @@ spec:
- name: ASSIST_KEY
value: {{ .Values.global.assistKey }}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: '{{ $val }}'

View file

@ -48,9 +48,8 @@ spec:
env:
- name: AWS_DEFAULT_REGION
value: "{{ .Values.global.s3.region }}"
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
- name: QW_S3_ENDPOINT
value: 'http://minio.db.svc.cluster.local:9000'
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: AWS_ACCESS_KEY_ID
value: {{ .Values.global.s3.accessKey }}

View file

@ -19,10 +19,8 @@ spec:
env:
- name: AWS_DEFAULT_REGION
value: "{{ .Values.global.s3.region }}"
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
- name: QW_S3_ENDPOINT
value: 'http://minio.db.svc.cluster.local:9000'
{{- end}}
value: '{{.Values.global.s3.endpoint}}'
- name: AWS_ACCESS_KEY_ID
value: {{ .Values.global.s3.accessKey }}
- name: AWS_SECRET_ACCESS_KEY

View file

@ -96,7 +96,6 @@ autoscaling:
# targetMemoryUtilizationPercentage: 80
env:
REDIS_URL: "redis://redis-master.db.svc.cluster.local:6379"
debug: 0
uws: false
redis: false

View file

@ -59,7 +59,7 @@ spec:
# 4. Using AWS itself.
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
- name: ASSETS_ORIGIN
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
# Local minio Installation
value: 'https://{{ .Values.global.domainName }}:{{.Values.global.ingress.controller.service.ports.https}}/{{.Values.global.s3.assetsBucket}}'
{{- else if contains "amazonaws.com" .Values.global.s3.endpoint }}

View file

@ -67,9 +67,9 @@ spec:
# 4. Using AWS itself.
# AWS uses bucketname.endpoint/object while others use endpoint/bucketname/object
- name: ASSETS_ORIGIN
{{- if eq .Values.global.s3.endpoint "sourcemapreader://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
# Local minio Installation
value: 'sourcemapreaders://{{ .Values.global.domainName }}:{{.Values.global.controller.service.ports.https}}/{{.Values.global.s3.assetsBucket}}'
value: 'sourcemapreaders://{{ .Values.global.domainName }}:{{.Values.global.ingress.controller.service.ports.https}}/{{.Values.global.s3.assetsBucket}}'
{{- else if contains "amazonaws.com" .Values.global.s3.endpoint }}
# AWS S3
# Ref: sourcemapreaders://stackoverflow.com/questions/53634583/go-template-split-string-by-delimiter

View file

@ -44,9 +44,23 @@ spec:
{{- end}}
env:
- name: AWS_ACCESS_KEY_ID
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: AWS_SECRET_ACCESS_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_ENDPOINT
value: '{{ .Values.global.s3.endpoint }}'
- name: AWS_REGION_WEB

View file

@ -31,19 +31,40 @@ spec:
- name: pg_user
value: '{{ .Values.global.postgresql.postgresqlUser }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: SITE_URL
value: 'https://{{ .Values.global.domainName }}'
- name: S3_HOST
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
value: 'https://{{ .Values.global.domainName }}:{{ .Values.global.ingress.controller.service.ports.https}}'
{{- else}}
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: S3_SECRET
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_DEFAULT_REGION
value: '{{ .Values.global.s3.region }}'
- name: sessions_region

View file

@ -31,19 +31,40 @@ spec:
- name: pg_user
value: '{{ .Values.global.postgresql.postgresqlUser }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: SITE_URL
value: 'https://{{ .Values.global.domainName }}'
- name: S3_HOST
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
value: 'https://{{ .Values.global.domainName }}:{{ .Values.global.ingress.controller.service.ports.https}}'
{{- else}}
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: S3_SECRET
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_DEFAULT_REGION
value: '{{ .Values.global.s3.region }}'
- name: sessions_region

View file

@ -31,19 +31,40 @@ spec:
- name: pg_user
value: '{{ .Values.global.postgresql.postgresqlUser }}'
- name: pg_password
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
- name: SITE_URL
value: 'https://{{ .Values.global.domainName }}'
- name: S3_HOST
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
value: 'https://{{ .Values.global.domainName }}:{{ .Values.global.ingress.controller.service.ports.https}}'
{{- else}}
value: '{{ .Values.global.s3.endpoint }}'
{{- end}}
- name: S3_KEY
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: access-key
{{- else }}
value: {{ .Values.global.s3.accessKey }}
{{- end }}
- name: S3_SECRET
{{- if .Values.global.s3.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.s3.existingSecret }}
key: secret-key
{{- else }}
value: {{ .Values.global.s3.secretKey }}
{{- end }}
- name: AWS_DEFAULT_REGION
value: '{{ .Values.global.s3.region }}'
- name: sessions_region

View file

@ -55,7 +55,6 @@ sessionsCleaner:
# Common env values are from chalice for the crons
chalice:
env:
ch_host: clickhouse-openreplay-clickhouse.db.svc.cluster.local
ch_port: 9000
captcha_server: ''
captcha_key: ''

View file

@ -11,7 +11,7 @@ function migrate() {
echo "Migrating clickhouse version $version"
# For now, we can ignore the clickhouse db inject errors.
# TODO: Better error handling in script
clickhouse-client -h clickhouse-openreplay-clickhouse.db.svc.cluster.local --port 9000 --multiquery < ${clickhousedir}/${version}/${version}.sql || true
clickhouse-client -h ${CH_HOST} --port ${CH_PORT} --multiquery < ${clickhousedir}/${version}/${version}.sql || true
done
}
@ -19,7 +19,7 @@ function init() {
echo "Initializing clickhouse"
for file in `ls ${clickhousedir}/create/*.sql`; do
echo "Injecting $file"
clickhouse-client -h clickhouse-openreplay-clickhouse.db.svc.cluster.local --port 9000 --multiquery < $file || true
clickhouse-client -h ${CH_HOST} --port ${CH_PORT} --multiquery < $file || true
done
}

View file

@ -7,7 +7,8 @@ cd /tmp
buckets=("mobs" "sessions-assets" "sourcemaps" "sessions-mobile-assets" "quickwit" "vault-data")
mc alias set minio http://minio.db.svc.cluster.local:9000 $MINIO_ACCESS_KEY $MINIO_SECRET_KEY
mc alias set minio $MINIO_HOST $MINIO_ACCESS_KEY $MINIO_SECRET_KEY
function init() {
echo "Initializing minio"

View file

@ -7,7 +7,7 @@ if [ "x$VERBOSE" == "x1" ]; then
set -x
fi
export VAULT_ADDR=http://databases-vault.db.svc.cluster.local:8200
export VAULT_ADDR=${VAULT_ADDR}
# Check vault is already initialized, if so return

View file

@ -50,7 +50,7 @@ spec:
error_connection=1
while [ $exit_count -le 20 ];do
nc -zv clickhouse-openreplay-clickhouse.db.svc.cluster.local 9000 -w 1
nc -zv {{.Values.global.clickhouse.chHost}} 9000 -w 1
if [ $? -ne 0 ]; then
echo "[info] clickhouse is not up; retrying in 5 seconds"
sleep 4
@ -91,7 +91,14 @@ spec:
- name: PGUSER
value: "{{ .Values.global.postgresql.postgresqlUser }}"
- name: PGPASSWORD
value: "{{ .Values.global.postgresql.postgresqlPassword }}"
{{- if .Values.global.postgresql.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.global.postgresql.existingSecret }}
key: postgresql-postgres-password
{{- else }}
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
{{- end}}
image: bitnami/postgresql:13.3.0-debian-10-r53
command:
- /bin/bash
@ -103,7 +110,7 @@ spec:
mountPath: /opt/openreplay
- name: dbmigrationscript
mountPath: /opt/migrations/
{{- if eq .Values.global.s3.endpoint "http://minio.db.svc.cluster.local:9000" }}
{{- if contains "minio" .Values.global.s3.endpoint }}
- name: minio
image: bitnami/minio:2020.10.9-debian-10-r6
env:
@ -119,6 +126,8 @@ spec:
value: "{{ .Values.minio.global.minio.accessKey }}"
- name: MINIO_SECRET_KEY
value: "{{ .Values.minio.global.minio.secretKey }}"
- name: MINIO_HOST
value: "{{ .Values.global.s3.endpoint }}"
command:
- /bin/bash
- /opt/migrations/dbops.sh
@ -147,6 +156,8 @@ spec:
value: "{{ .Values.global.postgresql.postgresqlUser }}"
- name: PGPASSWORD
value: "{{ .Values.global.postgresql.postgresqlPassword }}"
- name: VAULT_ADDR
value: "{{ .Values.global.vault.vaultHost }}"
image: hashicorp/vault:1.12.0
command:
- /bin/sh
@ -208,6 +219,10 @@ spec:
value: "{{ .Values.fromVersion }}"
- name: CHART_APP_VERSION
value: "{{ .Chart.AppVersion }}"
- name: CH_HOST
value: "{{.Values.global.clickhouse.chHost}}"
- name: CH_PORT
value: "{{.Values.global.clickhouse.service.webPort}}"
command:
- /bin/bash
- /opt/migrations/dbops.sh

View file

@ -81,3 +81,26 @@ ingress-nginx:
## repository:
tag: "v1.3.0"
digest: ""
# For enterpriseEdition Only
vault: &vault
vaultHost: databases-vault.db.svc.cluster.local:8200
annotations:
vault.hashicorp.com/agent-cache-enable: "true"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-token: "true"
vault.hashicorp.com/template-static-secret-render-interval: 2m
# vault.hashicorp.com/log-level: debug
vault.hashicorp.com/agent-run-as-same-user: "true"
vault.hashicorp.com/agent-inject-command-processor.properties: |
pkill -TERM openreplay
vault.hashicorp.com/role: pgaccess
vault.hashicorp.com/agent-inject-secret-processor.properties: database/creds/db-app
vault.hashicorp.com/agent-inject-template-processor.properties: |
{{- with secret "database/creds/db-app" -}}
POSTGRES_STRING=postgres://{{.Data.username}}:{{.Data.password}}@postgresql.db.svc.cluster.local:5432/postgres
{{- end -}}
global:
vault: *vault
clusterDomain: "svc.cluster.local"

View file

@ -17,9 +17,12 @@ postgresql: &postgres
# asdf
# cpu: 2
clickhouse:
clickhouse: &clickhouse
# For enterpriseEdition
enabled: false
chHost: clickhouse-openreplay-clickhouse.db.svc.cluster.local
service:
webPort: 9000
quickwit: &quickwit
# For enterpriseEdition
@ -91,6 +94,7 @@ ingress-nginx: &ingress-nginx
force-ssl-redirect: false
proxy-body-size: 10m
# Application specific variables
global:
ingress: *ingress-nginx
@ -98,6 +102,8 @@ global:
kafka: *kafka
redis: *redis
quickwit: *quickwit
clickhouse: *clickhouse
# Registry URL from where the OR images should be pulled.
openReplayContainerRegistry: "public.ecr.aws/p1t3u8a3"
# secret key to inject to assist and peers service
assistKey: "SetARandomStringHere"
@ -172,21 +178,3 @@ chalice:
# cpu: 512m
# memory: 2056Mi
# For enterpriseEdition Only
vault:
enabled: false
annotations:
vault.hashicorp.com/agent-cache-enable: "true"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-token: "true"
vault.hashicorp.com/template-static-secret-render-interval: 2m
# vault.hashicorp.com/log-level: debug
vault.hashicorp.com/agent-run-as-same-user: "true"
vault.hashicorp.com/agent-inject-command-processor.properties: |
pkill -TERM openreplay
vault.hashicorp.com/role: pgaccess
vault.hashicorp.com/agent-inject-secret-processor.properties: database/creds/db-app
vault.hashicorp.com/agent-inject-template-processor.properties: |
{{- with secret "database/creds/db-app" -}}
POSTGRES_STRING=postgres://{{.Data.username}}:{{.Data.password}}@postgresql.db.svc.cluster.local:5432/postgres
{{- end -}}

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "4.1.7",
"version": "4.1.8",
"keywords": [
"logging",
"replay"

View file

@ -1,6 +1,6 @@
import type Message from './messages.gen.js'
import { Timestamp, Metadata, UserID } from './messages.gen.js'
import { now, deprecationWarn } from '../utils.js'
import { now, adjustTimeOrigin, deprecationWarn } from '../utils.js'
import Nodes from './nodes.js'
import Observer from './observer/top_observer.js'
import Sanitizer from './sanitizer.js'
@ -369,6 +369,7 @@ export default class App {
this.sessionStorage.removeItem(this.options.session_reset_key)
}
}
private _start(startOpts: StartOptions = {}, resetByWorker = false): Promise<StartPromiseReturn> {
if (!this.worker) {
return Promise.resolve(UnsuccessfulStart('No worker found: perhaps, CSP is not set.'))
@ -381,6 +382,7 @@ export default class App {
)
}
this.activityState = ActivityState.Starting
adjustTimeOrigin()
if (startOpts.sessionHash) {
this.session.applySessionHash(startOpts.sessionHash)

View file

@ -226,13 +226,13 @@ export default function (app: App, opts: Partial<Options>): void {
paintBlocks === null
? 0
: calculateSpeedIndex(firstContentfulPaint || firstPaint, paintBlocks)
const { domContentLoadedEventEnd, navigationStart } = performance.timing
const timeToInteractive =
interactiveWindowTickTime === null
? Math.max(
interactiveWindowStartTime,
firstContentfulPaint,
performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart ||
0,
domContentLoadedEventEnd - navigationStart || 0,
)
: 0
app.send(

View file

@ -1,9 +1,10 @@
import type App from '../app/index.js'
import { getTimeOrigin } from '../utils.js'
import { SetPageLocation, SetViewportSize, SetPageVisibility } from '../app/messages.gen.js'
export default function (app: App): void {
let url: string, width: number, height: number
let navigationStart = performance.timing.navigationStart
let navigationStart: number
const sendSetPageLocation = app.safe(() => {
const { URL } = document
@ -30,6 +31,7 @@ export default function (app: App): void {
app.attachStartCallback(() => {
url = ''
navigationStart = getTimeOrigin()
width = height = -1
sendSetPageLocation()
sendSetViewportSize()

View file

@ -6,12 +6,19 @@ export const IS_FIREFOX = IN_BROWSER && navigator.userAgent.match(/firefox|fxios
export const MAX_STR_LEN = 1e5
const navigationStart: number | false =
IN_BROWSER && (performance.timing.navigationStart || performance.timeOrigin)
// performance.now() is buggy in some browsers
// Buggy to use `performance.timeOrigin || performance.timing.navigationStart`
// https://github.com/mdn/content/issues/4713
// Maybe move to timer/ticker
let timeOrigin: number = IN_BROWSER ? Date.now() - performance.now() : 0
export function adjustTimeOrigin() {
timeOrigin = Date.now() - performance.now()
}
export function getTimeOrigin() {
return timeOrigin
}
export const now: () => number =
IN_BROWSER && performance.now() && navigationStart
? () => Math.round(performance.now() + navigationStart)
IN_BROWSER && !!performance.now
? () => Math.round(performance.now() + timeOrigin)
: () => Date.now()
export const stars: (str: string) => string =