Merge pull request #181 from openreplay/codeql

Codeql
This commit is contained in:
Mehdi Osman 2021-09-22 11:27:32 +02:00 committed by GitHub
commit b552e04267
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 385 additions and 140 deletions

View file

@ -63,7 +63,7 @@ OpenReplay can be deployed anywhere. Follow our step-by-step guides for deployin
## OpenReplay Cloud
For those who want to simply use OpenReplay as a service, [sign up](https://asayer.io/register.html) for a free account on our cloud offering.
For those who want to simply use OpenReplay as a service, [sign up](https://app.openreplay.com/signup) for a free account on our cloud offering.
## Community Support

71
codeql-analysis.yml Normal file
View file

@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ dev ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ dev ]
schedule:
- cron: '30 23 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go', 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View file

@ -25,10 +25,9 @@ import { LAST_7_DAYS } from 'Types/app/period';
import { resetFunnel } from 'Duck/funnels';
import { resetFunnelFilters } from 'Duck/funnelFilters'
import NoSessionsMessage from '../shared/NoSessionsMessage';
import TrackerUpdateMessage from '../shared/TrackerUpdateMessage';
import LiveSessionList from './LiveSessionList'
const AUTOREFRESH_INTERVAL = 10 * 60 * 1000;
const weakEqual = (val1, val2) => {
if (!!val1 === false && !!val2 === false) return true;
if (!val1 !== !val2) return false;
@ -37,7 +36,6 @@ const weakEqual = (val1, val2) => {
@withLocationHandlers()
@connect(state => ({
shouldAutorefresh: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 0,
filter: state.getIn([ 'filters', 'appliedFilter' ]),
showLive: state.getIn([ 'user', 'account', 'appearance', 'sessionsLive' ]),
variables: state.getIn([ 'customFields', 'list' ]),
@ -93,12 +91,6 @@ export default class BugFinder extends React.PureComponent {
this.props.resetFunnel();
this.props.resetFunnelFilters();
this.autorefreshIntervalId = setInterval(() => {
if (this.props.shouldAutorefresh) {
props.applyFilter();
}
}, AUTOREFRESH_INTERVAL);
props.fetchFunnelsList(LAST_7_DAYS)
}
@ -129,10 +121,6 @@ export default class BugFinder extends React.PureComponent {
}.bind(this));
}
componentWillUnmount() {
clearInterval(this.autorefreshIntervalId);
}
setActiveTab = tab => {
this.props.setActiveTab(tab);
}
@ -151,6 +139,7 @@ export default class BugFinder extends React.PureComponent {
/>
</div>
<div className={cn("side-menu-margined", stl.searchWrapper) }>
<TrackerUpdateMessage />
<NoSessionsMessage />
<div
data-hidden={ activeTab === 'live' || activeTab === 'favorite' }

View file

@ -5,24 +5,44 @@ import { NoContent, Loader } from 'UI';
import { List, Map } from 'immutable';
import SessionItem from 'Shared/SessionItem';
const AUTOREFRESH_INTERVAL = 1 * 60 * 1000
interface Props {
loading: Boolean,
list?: List<any>,
fetchList: (params) => void,
applyFilter: () => void,
filters: List<any>
}
function LiveSessionList(props: Props) {
const { loading, list, filters } = props;
var timeoutId;
useEffect(() => {
props.fetchList(filters.toJS());
timeout();
return () => {
clearTimeout(timeoutId)
}
}, [])
const timeout = () => {
timeoutId = setTimeout(() => {
props.fetchList(filters.toJS());
timeout();
}, AUTOREFRESH_INTERVAL);
}
return (
<div>
<NoContent
title={"No live sessions!"}
title={"No live sessions."}
subtext={
<span>
See how to <a target="_blank" className="link" href="https://docs.openreplay.com/plugins/assist">{'enable Assist'}</a> if you haven't yet done so.
</span>
}
image={<img src="/img/live-sessions.png" style={{ width: '70%', marginBottom: '30px' }}/>}
show={ !loading && list && list.size === 0}
>

View file

@ -4,12 +4,14 @@ import { applyFilter, addAttribute, addEvent } from 'Duck/filters';
import SessionItem from 'Shared/SessionItem';
import SessionListHeader from './SessionListHeader';
import { KEYS } from 'Types/filter/customFilter';
import styles from './sessionList.css';
const ALL = 'all';
const PER_PAGE = 10;
const AUTOREFRESH_INTERVAL = 3 * 60 * 1000;
var timeoutId;
@connect(state => ({
shouldAutorefresh: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 0,
savedFilters: state.getIn([ 'filters', 'list' ]),
loading: state.getIn([ 'sessions', 'loading' ]),
activeTab: state.getIn([ 'sessions', 'activeTab' ]),
@ -27,6 +29,7 @@ export default class SessionList extends React.PureComponent {
}
constructor(props) {
super(props);
this.timeout();
}
componentDidUpdate(prevProps) {
@ -47,6 +50,15 @@ export default class SessionList extends React.PureComponent {
this.props.applyFilter()
}
timeout = () => {
timeoutId = setTimeout(function () {
if (this.props.shouldAutorefresh) {
this.props.applyFilter();
}
this.timeout();
}.bind(this), AUTOREFRESH_INTERVAL);
}
getNoContentMessage = activeTab => {
let str = "No recordings found";
if (activeTab.type !== 'all') {
@ -57,6 +69,10 @@ export default class SessionList extends React.PureComponent {
return str + '!';
}
componentWillUnmount() {
clearTimeout(timeoutId)
}
renderActiveTabContent(list) {
const {
loading,

View file

@ -7,6 +7,7 @@ import { fetchWatchdogStatus } from 'Duck/watchdogs';
import { setActiveFlow, clearEvents } from 'Duck/filters';
import { setActiveTab } from 'Duck/sessions';
import { issues_types } from 'Types/session/issue'
import NewBadge from 'Shared/NewBadge';
function SessionsMenu(props) {
const {
@ -75,11 +76,15 @@ function SessionsMenu(props) {
<div className={stl.divider} />
<div className="my-3">
<SideMenuitem
title="Assist"
title={ <div className="flex items-center">
<div>Assist</div>
<div className="ml-2">{ <NewBadge />}</div>
</div> }
iconName="person"
active={activeTab.type === 'live'}
onClick={() => onMenuItemClick({ name: 'Assist', type: 'live' })}
/>
</div>
<div className={stl.divider} />

View file

@ -1,60 +1,53 @@
import Highlight from 'react-highlight'
import ToggleContent from 'Shared/ToggleContent'
import DocLink from 'Shared/DocLink/DocLink';
import AssistScript from './AssistScript'
import AssistNpm from './AssistNpm'
import { Tabs } from 'UI';
import { useState } from 'react';
const NPM = 'NPM'
const SCRIPT = 'SCRIPT'
const TABS = [
{ key: SCRIPT, text: SCRIPT },
{ key: NPM, text: NPM },
]
const AssistDoc = (props) => {
const { projectKey } = props;
const [activeTab, setActiveTab] = useState(SCRIPT)
const renderActiveTab = () => {
switch (activeTab) {
case SCRIPT:
return <AssistScript projectKey={projectKey} />
case NPM:
return <AssistNpm projectKey={projectKey} />
}
return null;
}
return (
<div className="p-4">
<div>OpenReplay Assist allows you to support your users by seeing their live screen and instantly hopping on call (WebRTC) with them without requiring any 3rd-party screen sharing software.</div>
<div className="font-bold my-2">Installation</div>
<Highlight className="js">
{`npm i @openreplay/tracker-assist`}
</Highlight>
<div className="font-bold my-2">Usage</div>
<p>Initialize the tracker then load the @openreplay/tracker-assist plugin.</p>
<div className="py-3" />
<div className="mb-4" />
<div className="font-bold my-2">Usage</div>
<ToggleContent
label="Is SSR?"
first={
<Highlight className="js">
{`import Tracker from '@openreplay/tracker';
import trackerAssist from '@openreplay/tracker-assist';
const tracker = new Tracker({
projectKey: PROJECT_KEY,
});
tracker.start();
tracker.use(trackerAssist(options)); // check the list of available options below`}
</Highlight>
}
second={
<Highlight className="js">
{`import OpenReplay from '@openreplay/tracker/cjs';
import trackerFetch from '@openreplay/tracker-assist/cjs';
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
});
const trackerAssist = tracker.use(trackerAssist(options)); // check the list of available options below
//...
function MyApp() {
useEffect(() => { // use componentDidMount in case of React Class Component
tracker.start();
}, [])
//...
}`}
</Highlight>
}
<Tabs
tabs={ TABS }
active={ activeTab } onClick={ (tab) => setActiveTab(tab) }
/>
<div className="font-bold my-2">Options</div>
<Highlight className="js">
{`trackerAssist({
confirmText: string;
})`}
</Highlight>
<div className="py-5">
{ renderActiveTab() }
</div>
<DocLink className="mt-4" label="Install Assist" url="https://docs.openreplay.com/installation/assist" />
</div>

View file

@ -0,0 +1,53 @@
import React from 'react';
import Highlight from 'react-highlight'
import ToggleContent from 'Shared/ToggleContent'
function AssistNpm(props) {
return (
<div>
<p>Initialize the tracker then load the @openreplay/tracker-assist plugin.</p>
<div className="font-bold my-2">Usage</div>
<ToggleContent
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import Tracker from '@openreplay/tracker';
import trackerAssist from '@openreplay/tracker-assist';
const tracker = new Tracker({
projectKey: '${props.projectKey}',
});
tracker.start();
tracker.use(trackerAssist(options)); // check the list of available options below`}
</Highlight>
}
second={
<Highlight className="js">
{`import OpenReplay from '@openreplay/tracker/cjs';
import trackerFetch from '@openreplay/tracker-assist/cjs';
const tracker = new OpenReplay({
projectKey: '${props.projectKey}'
});
const trackerAssist = tracker.use(trackerAssist(options)); // check the list of available options below
//...
function MyApp() {
useEffect(() => { // use componentDidMount in case of React Class Component
tracker.start();
}, [])
//...
}`}
</Highlight>
}
/>
<div className="font-bold my-2">Options</div>
<Highlight className="js">
{`trackerAssist({
confirmText: string;
})`}
</Highlight>
</div>
);
}
export default AssistNpm;

View file

@ -0,0 +1,33 @@
import React from 'react';
import Highlight from 'react-highlight'
function AssistScript(props) {
return (
<div>
<p>If your OpenReplay tracker is set up using the JS snippet, then simply replace the .../openreplay.js occurrence with .../openreplay-assist.js. Below is an example of how the script should like after the change:</p>
<div className="py-3" />
<Highlight className="js">
{`<!-- OpenReplay Tracking Code -->
<script>
(function(A,s,a,y,e,r){
r=window.OpenReplay=[s,r,e,[y-1]];
s=document.createElement('script');s.src=a;s.async=!A;
document.getElementsByTagName('head')[0].appendChild(s);
r.start=function(v){r.push([0])};
r.stop=function(v){r.push([1])};
r.setUserID=function(id){r.push([2,id])};
r.setUserAnonymousID=function(id){r.push([3,id])};
r.setMetadata=function(k,v){r.push([4,k,v])};
r.event=function(k,p,i){r.push([5,k,p,i])};
r.issue=function(k,p){r.push([6,k,p])};
r.isActive=function(){return false};
r.getSessionToken=function(){};
})(0, "${props.projectKey}", "//static.openreplay.com/3.3.1/openreplay-assist.js",1,28);
</script>`}
</Highlight>
</div>
);
}
export default AssistScript;

View file

@ -3,6 +3,7 @@ import ToggleContent from 'Shared/ToggleContent'
import DocLink from 'Shared/DocLink/DocLink';
const FetchDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<div>This plugin allows you to capture fetch payloads and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues.</div>
@ -18,14 +19,14 @@ const FetchDoc = (props) => {
<div className="font-bold my-2">Usage</div>
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import tracker from '@openreplay/tracker';
import trackerFetch from '@openreplay/tracker-fetch';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.start();
//...
@ -40,7 +41,7 @@ fetch('https://api.openreplay.com/').then(response => console.log(response.json(
import trackerFetch from '@openreplay/tracker-fetch/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
//...
function SomeFunctionalComponent() {

View file

@ -3,6 +3,7 @@ import DocLink from 'Shared/DocLink/DocLink';
import ToggleContent from 'Shared/ToggleContent';
const GraphQLDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<p>This plugin allows you to capture GraphQL requests and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues.</p>
@ -19,14 +20,14 @@ const GraphQLDoc = (props) => {
<div className="py-3" />
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import OpenReplay from '@openreplay/tracker';
import trackerGraphQL from '@openreplay/tracker-graphql';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY,
projectKey: '${projectKey}'
});
tracker.start();
//...
@ -39,7 +40,7 @@ export const recordGraphQL = tracker.use(trackerGraphQL());`}
import trackerGraphQL from '@openreplay/tracker-graphql/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
//...
function SomeFunctionalComponent() {

View file

@ -75,7 +75,7 @@ const TITLE = {
[ ASSIST ] : 'Assist',
}
const DOCS = [REDUX, VUE, GRAPHQL, NGRX, FETCH, MOBX, PROFILER]
const DOCS = [REDUX, VUE, GRAPHQL, NGRX, FETCH, MOBX, PROFILER, ASSIST]
const integrations = [ 'sentry', 'datadog', 'stackdriver', 'rollbar', 'newrelic', 'bugsnag', 'cloudwatch', 'elasticsearch', 'sumologic', 'issues' ];
@ -87,12 +87,14 @@ const integrations = [ 'sentry', 'datadog', 'stackdriver', 'rollbar', 'newrelic'
state.getIn([ name, 'list' ]).size > 0;
props.loading = props.loading || state.getIn([ name, 'fetchRequest', 'loading']);
})
const site = state.getIn([ 'site', 'instance' ]);
return {
...props,
issues: state.getIn([ 'issues', 'list']).first() || {},
slackChannelListExists: state.getIn([ 'slack', 'list' ]).size > 0,
tenantId: state.getIn([ 'user', 'client', 'tenantId' ]),
jwt: state.get('jwt')
jwt: state.get('jwt'),
projectKey: site ? site.projectKey : ''
};
}, {
fetchList,
@ -142,7 +144,9 @@ export default class Integrations extends React.PureComponent {
}
}
renderModalContent() {
renderModalContent() {
const { projectKey } = this.props;
switch (this.state.modalContent) {
case SENTRY:
return <SentryForm onClose={ this.closeModal } />;
@ -172,21 +176,21 @@ export default class Integrations extends React.PureComponent {
case JIRA:
return <JiraForm onClose={ this.closeModal } />;
case REDUX:
return <ReduxDoc onClose={ this.closeModal } />
return <ReduxDoc onClose={ this.closeModal } projectKey={projectKey} />
case VUE:
return <VueDoc onClose={ this.closeModal } />
return <VueDoc onClose={ this.closeModal } projectKey={projectKey} />
case GRAPHQL:
return <GraphQLDoc onClose={ this.closeModal } />
return <GraphQLDoc onClose={ this.closeModal } projectKey={projectKey} />
case NGRX:
return <NgRxDoc onClose={ this.closeModal } />
return <NgRxDoc onClose={ this.closeModal } projectKey={projectKey} />
case FETCH:
return <FetchDoc onClose={ this.closeModal } />
return <FetchDoc onClose={ this.closeModal } projectKey={projectKey} />
case MOBX:
return <MobxDoc onClose={ this.closeModal } />
return <MobxDoc onClose={ this.closeModal } projectKey={projectKey} />
case PROFILER:
return <ProfilerDoc onClose={ this.closeModal } />
return <ProfilerDoc onClose={ this.closeModal } projectKey={projectKey} />
case ASSIST:
return <AssistDoc onClose={ this.closeModal } />
return <AssistDoc onClose={ this.closeModal } projectKey={projectKey} />
default:
return null;
}

View file

@ -3,6 +3,7 @@ import ToggleContent from 'Shared/ToggleContent'
import DocLink from 'Shared/DocLink/DocLink';
const MobxDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<div>This plugin allows you to capture MobX events and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues.</div>
@ -18,14 +19,14 @@ const MobxDoc = (props) => {
<div className="font-bold my-2">Usage</div>
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import OpenReplay from '@openreplay/tracker';
import trackerMobX from '@openreplay/tracker-mobx';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.use(trackerMobX(<options>)); // check list of available options below
tracker.start();`}
@ -37,7 +38,7 @@ tracker.start();`}
import trackerMobX from '@openreplay/tracker-mobx/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.use(trackerMobX(<options>)); // check list of available options below
//...

View file

@ -3,6 +3,7 @@ import ToggleContent from 'Shared/ToggleContent'
import DocLink from 'Shared/DocLink/DocLink';
const NgRxDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<div>This plugin allows you to capture NgRx actions/state and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues.</div>
@ -18,7 +19,7 @@ const NgRxDoc = (props) => {
<div className="font-bold my-2">Usage</div>
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import { StoreModule } from '@ngrx/store';
@ -27,7 +28,7 @@ import OpenReplay from '@openreplay/tracker';
import trackerNgRx from '@openreplay/tracker-ngrx';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.start();
//...
@ -47,7 +48,7 @@ import OpenReplay from '@openreplay/tracker/cjs';
import trackerNgRx from '@openreplay/tracker-ngrx/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
//...
function SomeFunctionalComponent() {

View file

@ -3,6 +3,7 @@ import ToggleContent from 'Shared/ToggleContent'
import DocLink from 'Shared/DocLink/DocLink';
const ProfilerDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<div>The profiler plugin allows you to measure your JS functions' performance and capture both arguments and result for each function call.</div>
@ -18,14 +19,14 @@ const ProfilerDoc = (props) => {
<div className="font-bold my-2">Usage</div>
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import OpenReplay from '@openreplay/tracker';
import trackerProfiler from '@openreplay/tracker-profiler';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.start();
//...
@ -42,7 +43,7 @@ const fn = profiler('call_name')(() => {
import trackerProfiler from '@openreplay/tracker-profiler/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
//...
function SomeFunctionalComponent() {

View file

@ -3,6 +3,7 @@ import ToggleContent from '../../../shared/ToggleContent';
import DocLink from 'Shared/DocLink/DocLink';
const ReduxDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<div>This plugin allows you to capture Redux actions/state and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues.</div>
@ -17,7 +18,7 @@ const ReduxDoc = (props) => {
<p>Initialize the tracker then put the generated middleware into your Redux chain.</p>
<div className="py-3" />
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import { applyMiddleware, createStore } from 'redux';
@ -25,7 +26,7 @@ import OpenReplay from '@openreplay/tracker';
import trackerRedux from '@openreplay/tracker-redux';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.start();
//...
@ -42,7 +43,7 @@ import OpenReplay from '@openreplay/tracker/cjs';
import trackerRedux from '@openreplay/tracker-redux/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
//...
function SomeFunctionalComponent() {

View file

@ -3,6 +3,7 @@ import ToggleContent from '../../../shared/ToggleContent';
import DocLink from 'Shared/DocLink/DocLink';
const VueDoc = (props) => {
const { projectKey } = props;
return (
<div className="p-4">
<div>This plugin allows you to capture VueX mutations/state and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues.</div>
@ -18,7 +19,7 @@ const VueDoc = (props) => {
<ToggleContent
label="Is SSR?"
label="Server-Side-Rendered (SSR)?"
first={
<Highlight className="js">
{`import Vuex from 'vuex'
@ -26,7 +27,7 @@ import OpenReplay from '@openreplay/tracker';
import trackerVuex from '@openreplay/tracker-vuex';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
tracker.start();
//...
@ -43,7 +44,7 @@ import OpenReplay from '@openreplay/tracker/cjs';
import trackerVuex from '@openreplay/tracker-vuex/cjs';
//...
const tracker = new OpenReplay({
projectKey: PROJECT_KEY
projectKey: '${projectKey}'
});
//...
function SomeFunctionalComponent() {

View file

@ -27,7 +27,7 @@ const LIMIT_WARNING = 'You have reached users limit.';
fetchList,
generateInviteLink
})
@withPageTitle('Manage Users - OpenReplay Preferences')
@withPageTitle('Users - OpenReplay Preferences')
class ManageUsers extends React.PureComponent {
state = { showModal: false, remaining: this.props.account.limits.teamMember.remaining, invited: false }
@ -50,7 +50,7 @@ class ManageUsers extends React.PureComponent {
deleteHandler = async (user) => {
if (await confirm({
header: 'Manage Users',
header: 'Users',
confirmation: `Are you sure you want to remove this user?`
})) {
this.props.deleteMember(user.id).then(() => {

View file

@ -72,7 +72,7 @@ function PreferencesMenu({ activeTab, appearance, history }) {
<div className="mb-4">
<SideMenuitem
active={ activeTab === CLIENT_TABS.MANAGE_USERS }
title="Manage Users"
title="Users"
iconName="users"
onClick={() => setTab(CLIENT_TABS.MANAGE_USERS) }
/>

View file

@ -2,6 +2,7 @@ import { connect } from 'react-redux';
import { Input, Button, Label } from 'UI';
import { save, edit, update , fetchList } from 'Duck/site';
import { pushNewSite, setSiteId } from 'Duck/user';
import { withRouter } from 'react-router-dom';
import styles from './siteForm.css';
@connect(state => ({
@ -17,6 +18,7 @@ import styles from './siteForm.css';
fetchList,
setSiteId
})
@withRouter
export default class NewSiteForm extends React.PureComponent {
state = {
existsError: false,
@ -24,7 +26,7 @@ export default class NewSiteForm extends React.PureComponent {
onSubmit = e => {
e.preventDefault();
const { site, siteList } = this.props;
const { site, siteList, location: { pathname } } = this.props;
if (!site.exists() && siteList.some(({ name }) => name === site.name)) {
return this.setState({ existsError: true });
}
@ -39,20 +41,21 @@ export default class NewSiteForm extends React.PureComponent {
const site = sites.last();
this.props.pushNewSite(site)
this.props.setSiteId(site.id)
if (!pathname.includes('/client')) {
this.props.setSiteId(site.id)
}
this.props.onClose(null, site)
});
}
}
edit = ({ target: { name, value } }) => {
if (value.includes(' ')) return; // TODO: site validation
this.setState({ existsError: false });
this.props.edit({ [ name ]: value });
}
render() {
const { site, loading, onClose } = this.props;
const { site, loading } = this.props;
return (
<form className={ styles.formWrapper } onSubmit={ this.onSubmit }>
<div className={ styles.content }>

View file

@ -34,6 +34,7 @@
.activeLink {
cursor: pointer;
pointer-events: default;
text-decoration: underline;
& label {
color: #000000 !important;
text-decoration: underline;

View file

@ -22,7 +22,6 @@ class TrackingCodeModal extends React.PureComponent {
}
renderActiveTab = () => {
console.log('rendering...')
switch (this.state.activeTab) {
case PROJECT:
return <ProjectCodeSnippet />

View file

@ -4,9 +4,10 @@ import PageInsightsPanel from '../Session_/PageInsightsPanel/PageInsightsPanel'
import { Controls as PlayerControls } from 'Player';
import { Tabs } from 'UI';
import { connectPlayer } from 'Player';
import NewBadge from 'Shared/NewBadge';
const EVENTS = 'Events';
const HEATMAPS = 'Heatmaps';
const HEATMAPS = 'Click Map';
const TABS = [ EVENTS, HEATMAPS ].map(tab => ({ text: tab, key: tab }));
@ -29,12 +30,15 @@ export default function RightBlock() {
}
return (
<div style={{ width: '270px', height: 'calc(100vh- 50px)'}} className="flex flex-col">
<Tabs
tabs={ TABS }
active={ activeTab }
onClick={ (tab) => setActiveTab(tab) }
border={ true }
/>
<div className="relative">
<Tabs
tabs={ TABS }
active={ activeTab }
onClick={ (tab) => setActiveTab(tab) }
border={ true }
/>
<div className="absolute" style={{ left: '160px', top: '13px' }}>{ <NewBadge />}</div>
</div>
{
renderActiveTab(activeTab)
}

View file

@ -42,7 +42,9 @@ export default class EventsBlock extends React.PureComponent {
this.props.setEventFilter({ query: value, filter })
setTimeout(() => {
this.scroller.current.scrollToRow(0);
if (!this.scroller.current) return;
this.scroller.current.scrollToRow(0);
}, 100)
}
@ -54,7 +56,9 @@ export default class EventsBlock extends React.PureComponent {
this.scroller.current.forceUpdateGrid();
setTimeout(() => {
this.scroller.current.scrollToRow(0);
if (!this.scroller.current) return;
this.scroller.current.scrollToRow(0);
}, 100)
}
@ -176,6 +180,7 @@ export default class EventsBlock extends React.PureComponent {
userNumericHash,
userDisplayName,
userId,
revId,
userAnonymousId
},
filteredEvents
@ -191,6 +196,7 @@ export default class EventsBlock extends React.PureComponent {
userNumericHash={userNumericHash}
userDisplayName={userDisplayName}
userId={userId}
revId={revId}
userAnonymousId={userAnonymousId}
/>

View file

@ -6,7 +6,7 @@ import Metadata from '../Metadata'
import { withRequest } from 'HOCs'
import SessionList from '../Metadata/SessionList'
function UserCard({ className, userNumericHash, userDisplayName, similarSessions, userId, userAnonymousId, request, loading }) {
function UserCard({ className, userNumericHash, userDisplayName, similarSessions, userId, userAnonymousId, request, loading, revId }) {
const [showUserSessions, setShowUserSessions] = useState(false)
const hasUserDetails = !!userId || !!userAnonymousId;
@ -29,6 +29,11 @@ function UserCard({ className, userNumericHash, userDisplayName, similarSessions
</TextEllipsis>
</div>
</div>
{revId && (
<div className="border-t py-2 px-3">
<span className="font-medium">Rev ID:</span> {revId}
</div>
)}
<div className="border-t">
<Metadata />
</div>

View file

@ -14,7 +14,7 @@ import BottomBlock from '../BottomBlock';
@connect(state => ({
session: state.getIn([ 'sessions', 'current' ]),
errorStack: state.getIn([ 'sessions', 'errorStack' ]),
sourceMapUploaded: state.getIn([ 'sessions', 'sourceMapUploaded' ]),
sourcemapUploaded: state.getIn([ 'sessions', 'sourcemapUploaded' ]),
loading: state.getIn([ 'sessions', 'fetchErrorStackList', 'loading' ])
}), { fetchErrorStackList })
export default class Exceptions extends React.PureComponent {
@ -33,7 +33,7 @@ export default class Exceptions extends React.PureComponent {
closeModal = () => this.setState({ currentError: null})
render() {
const { exceptions, loading, errorStack, sourceMapUploaded } = this.props;
const { exceptions, loading, errorStack, sourcemapUploaded } = this.props;
const { filter, currentError } = this.state;
const filterRE = getRE(filter, 'i');
@ -63,7 +63,7 @@ export default class Exceptions extends React.PureComponent {
show={ !loading && errorStack.size === 0 }
title="Nothing found!"
>
<ErrorDetails error={ currentError.name } errorStack={errorStack} sourceMapUploaded={sourceMapUploaded} />
<ErrorDetails error={ currentError.name } errorStack={errorStack} sourcemapUploaded={sourcemapUploaded} />
</NoContent>
</Loader>
</div>

View file

@ -399,7 +399,7 @@ export default class Controls extends React.Component {
icon="tachometer-slow"
/>
}
{ !live && showLongtasks &&
{/* { !live && showLongtasks &&
<ControlButton
disabled={ disabled }
onClick={ () => toggleBottomBlock(LONGTASKS) }
@ -407,7 +407,7 @@ export default class Controls extends React.Component {
label="Long Tasks"
icon="business-time"
/>
}
} */}
<div className={ styles.divider } />
{ !live &&
<React.Fragment>

View file

@ -26,11 +26,24 @@ export default class SignupForm extends React.Component {
email: '',
projectName: '',
organizationName: '',
reload: false,
};
static getDerivedStateFromProps(props, state) {
if (props.errors && props.errors.size > 0 && state.reload) {
recaptchaRef.current.reset();
return {
reload: false
}
}
return null;
}
handleSubmit = (token) => {
const { tenantId, fullname, password, email, projectName, organizationName, auth } = this.state;
this.props.signup({ tenantId, fullname, password, email, projectName, organizationName, auth, 'g-recaptcha-response': token })
this.setState({ reload: true })
}
write = ({ target: { value, name } }) => this.setState({ [ name ]: value })

View file

@ -0,0 +1,40 @@
import React from 'react'
import { Icon } from 'UI'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom';
import { onboarding as onboardingRoute } from 'App/routes'
import { withSiteId } from 'App/routes';
const TrackerUpdateMessage= (props) => {
// const { site } = props;
const { site, sites, match: { params: { siteId } } } = props;
const activeSite = sites.find(s => s.id == siteId);
const hasSessions = !!activeSite && !activeSite.recorded;
const needUpdate = !hasSessions && site.trackerVersion !== window.ENV.TRACKER_VERSION;
return needUpdate ? (
<>
{(
<div>
<div
className="rounded text-sm flex items-center justify-between mb-4"
style={{ height: '42px', backgroundColor: 'rgba(255, 239, 239, 1)', border: 'solid thin rgba(221, 181, 181, 1)'}}
>
<div className="flex items-center w-full">
<div className="flex-shrink-0 w-8 flex justify-center">
<Icon name="info-circle" size="14" color="gray-darkest" />
</div>
<div className="ml-2color-gray-darkest mr-auto">
Please <a href="#" className="link" onClick={() => props.history.push(withSiteId(onboardingRoute('installing'), siteId))}>update</a> your tracker (Asayer) to the latest OpenReplay version ({window.ENV.TRACKER_VERSION}) to benefit from all new features we recently shipped.
</div>
</div>
</div>
</div>
)}
</>
) : ''
}
export default connect(state => ({
site: state.getIn([ 'site', 'instance' ]),
sites: state.getIn([ 'site', 'list' ])
}))(withRouter(TrackerUpdateMessage))

View file

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

View file

@ -19,7 +19,6 @@ import { funnel as funnelRoute, withSiteId } from 'App/routes';
import Event, { TYPES } from 'Types/filter/event';
import FunnelMenuItem from 'Components/Funnels/FunnelMenuItem';
import FunnelSaveModal from 'Components/Funnels/FunnelSaveModal';
import NewBadge from 'Shared/NewBadge';
import { blink as setBlink } from 'Duck/funnels';
const DEFAULT_VISIBLE = 3;
@ -98,7 +97,6 @@ class SavedSearchList extends React.Component {
onClick={ this.createHandler }
/>
)}
<div className="ml-2">{ <NewBadge />}</div>
</div>
</div>
{ funnels.size === 0 &&

View file

@ -161,7 +161,6 @@ export default class AssistManager {
if (['peer-unavailable', 'network', 'webrtc'].includes(e.type)) {
if (this.peer && this.connectionAttempts++ < MAX_RECONNECTION_COUNT) {
this.setStatus(ConnectionStatus.Connecting);
console.log("peerunavailable")
this.connectToPeer();
} else {
this.setStatus(ConnectionStatus.Disconnected);
@ -175,7 +174,6 @@ export default class AssistManager {
peer.on("open", () => {
if (this.peeropened) { return; }
this.peeropened = true;
console.log('peeropen')
this.connectToPeer();
});
});
@ -186,11 +184,9 @@ export default class AssistManager {
if (!this.peer) { return; }
this.setStatus(ConnectionStatus.Connecting);
const id = this.peerID;
console.log("trying to connect to", id)
const conn = this.peer.connect(id, { serialization: 'json', reliable: true});
conn.on('open', () => {
window.addEventListener("beforeunload", ()=>conn.open &&conn.send("unload"));
console.log("peer connected")
let i = 0;
let firstMessage = true;
@ -252,7 +248,6 @@ export default class AssistManager {
const onDataClose = () => {
this.initiateCallEnd();
this.setStatus(ConnectionStatus.Connecting);
console.log('closed peer conn. Reconnecting...')
this.connectToPeer();
}
@ -263,7 +258,6 @@ export default class AssistManager {
// }, 3000);
conn.on('close', onDataClose);// Does it work ?
conn.on("error", (e) => {
console.log("PeerJS connection error", e);
this.setStatus(ConnectionStatus.Error);
})
}
@ -290,12 +284,10 @@ export default class AssistManager {
private notifyCallEnd() {
const dataConn = this.dataConnection;
if (dataConn) {
console.log("notifyCallEnd send")
dataConn.send("call_end");
}
}
private initiateCallEnd = () => {
console.log('initiateCallEnd')
this.forceCallEnd();
this.notifyCallEnd();
this.onCallEnd?.();
@ -349,7 +341,6 @@ export default class AssistManager {
if (!this.peer || getState().calling !== CallingState.False) { return null; }
update({ calling: CallingState.Requesting });
console.log('calling...')
const call = this.peer.call(this.peerID, localStream);
call.on('stream', stream => {
@ -393,12 +384,10 @@ export default class AssistManager {
}
clear() {
console.log('clearing', this.peerID)
this.initiateCallEnd();
this.dataCheckIntervalID && clearInterval(this.dataCheckIntervalID);
if (this.peer) {
this.peer.connections[this.peerID]?.forEach(c => c.open && c.close());
console.log("destroying peer...")
this.peer.disconnect();
this.peer.destroy();
this.peer = null;

View file

@ -1,7 +1,7 @@
import Record from 'Types/Record';
import Target from 'Types/target';
import { camelCased } from 'App/utils';
// import { getEventIcon } from 'Types/filter';
import { getEventIcon } from 'Types/filter';
const CLICK = 'CLICK';
const INPUT = 'INPUT';
@ -105,6 +105,6 @@ export default Record({
operator: event.operator || getOperatorDefault(event.type),
// value: target ? target.label : event.value,
value: typeof value === 'string' ? [value] : value,
icon: 'filters/metadata'
icon: event.type ? getEventIcon(event.type) : 'filters/metadata'
}),
})

View file

@ -124,6 +124,7 @@ export const getEventIcon = (filter) => {
type = type || key;
if (type === KEYS.USER_COUNTRY) return 'map-marker-alt';
if (type === KEYS.USER_BROWSER) return 'window';
if (type === KEYS.USERBROWSER) return 'window';
if (type === KEYS.PLATFORM) return 'window';
if (type === TYPES.CLICK) return 'filters/click';

View file

@ -75,6 +75,7 @@ export default Record({
crashes: [],
socket: null,
isIOS: false,
revId: ''
}, {
fromJS:({
startTs=0,

View file

@ -30,11 +30,11 @@ Installation components are separated by namespaces.
**Scripts:**
- **install.sh**
Installs OpenReplay in a single node machine, for trial runs / demo.
Installs OpenReplay in a single node machine.
This script is a wrapper around the `install.sh` with [k3s](https://k3s.io/) as kubernetes distro.
Note: As of now this script support only ubuntu, as we've to install some packages to enable `NFS`.
Note: As of now this script support only Ubuntu, as we've to install some packages to enable `NFS`.
- **kube-install.sh:**

View file

@ -43,3 +43,4 @@ env:
KAFKA_SERVERS: kafka.db.svc.cluster.local:9092
KAFKA_USE_SSL: false
LICENSE_KEY: ""
FS_CLEAN_HRS: 24

View file

@ -4,10 +4,10 @@ This is the frontend of the OpenReplay web app (internet).
## Endpoints
- /streaming -> ios-proxy
- /api -> chalice
- /http -> http
- / -> frontend (in minio)
- /assets -> sessions-assets bucket in minio
- /minio -> minio api endpoint
- /ingest -> events ingestor
- /assist -> live sessions and webRTC
- /grafana -> monitoring (Enterprise Edition only)

View file

@ -52,14 +52,6 @@ data:
proxy_set_header Host $host;
proxy_pass $target;
}
location /streaming/ {
set $target http://ios-proxy-openreplay.app.svc.cluster.local; rewrite ^/streaming/(.*) /$1 break;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_pass $target;
}
location /api/ {
rewrite ^/api/(.*) /$1 break;
proxy_http_version 1.1;