Session list - redesign (#621)
* change(ui) - removed env * change(ui) - no content component updates * feat(ui) - session list - wip * feat(ui) - session list - wip * feat(ui) - session list - wip * feat(ui) - session list - wip * fix(ui) - live session list key * feat(ui) - session list - wip * feat(ui) - session list - wip * feat(backend): set default size of first part of session mob file to 1mb * feat(backend): added extra information for db metrics * fix(ui) - siteform loader, trash btn project exists check, IconButton replace Co-authored-by: Alexander Zavorotynskiy <zavorotynskiy@pm.me>
This commit is contained in:
parent
361f990486
commit
78d7df72a5
24 changed files with 401 additions and 84 deletions
|
|
@ -31,7 +31,7 @@ const LiveSessionPure = lazy(() => import('Components/Session/LiveSession'));
|
|||
const OnboardingPure = lazy(() => import('Components/Onboarding/Onboarding'));
|
||||
const ClientPure = lazy(() => import('Components/Client/Client'));
|
||||
const AssistPure = lazy(() => import('Components/Assist'));
|
||||
const BugFinderPure = lazy(() => import('Components/BugFinder/BugFinder'));
|
||||
const BugFinderPure = lazy(() => import('Components/Overview'));
|
||||
const DashboardPure = lazy(() => import('Components/Dashboard/NewDashboard'));
|
||||
const ErrorsPure = lazy(() => import('Components/Errors/Errors'));
|
||||
const FunnelDetailsPure = lazy(() => import('Components/Funnels/FunnelDetails'));
|
||||
|
|
|
|||
28
frontend/app/components/Overview/Overview.tsx
Normal file
28
frontend/app/components/Overview/Overview.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import withPageTitle from 'HOCs/withPageTitle';
|
||||
import NoSessionsMessage from 'Shared/NoSessionsMessage';
|
||||
import MainSearchBar from 'Shared/MainSearchBar';
|
||||
import SessionSearch from 'Shared/SessionSearch';
|
||||
import SessionListContainer from 'Shared/SessionListContainer/SessionListContainer';
|
||||
|
||||
function Overview() {
|
||||
return (
|
||||
<div className="page-margin container-90 flex relative">
|
||||
<div className="flex-1 flex">
|
||||
<div className={'w-full mx-auto'} style={{ maxWidth: '1300px' }}>
|
||||
<NoSessionsMessage />
|
||||
|
||||
<div className="mb-5">
|
||||
<MainSearchBar />
|
||||
<SessionSearch />
|
||||
|
||||
<div className="my-4" />
|
||||
<SessionListContainer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withPageTitle('Sessions - OpenReplay')(Overview);
|
||||
1
frontend/app/components/Overview/index.ts
Normal file
1
frontend/app/components/Overview/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from './Overview';
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React, { Fragment, useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { NoContent, Loader, Pagination } from 'UI';
|
||||
import { List } from 'immutable';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import SessionList from './components/SessionList';
|
||||
import SessionHeader from './components/SessionHeader';
|
||||
|
||||
interface Props {}
|
||||
function SessionListContainer(props: Props) {
|
||||
return (
|
||||
<div className="widget-wrapper">
|
||||
<SessionHeader />
|
||||
<div className="p-4">
|
||||
<SessionList />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SessionListContainer;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
function NoContentMessage({ activeTab }: any) {
|
||||
return <div>{getNoContentMessage(activeTab)}</div>;
|
||||
}
|
||||
|
||||
export default connect((state: any) => ({
|
||||
activeTab: state.getIn(['search', 'activeTab']),
|
||||
}))(NoContentMessage);
|
||||
|
||||
function getNoContentMessage(activeTab: any) {
|
||||
let str = 'No recordings found';
|
||||
if (activeTab.type !== 'all') {
|
||||
str += ' with ' + activeTab.name;
|
||||
return str;
|
||||
}
|
||||
|
||||
return str + '!';
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import React from 'react';
|
||||
import { numberWithCommas } from 'App/utils';
|
||||
import { applyFilter } from 'Duck/search';
|
||||
import Period from 'Types/app/period';
|
||||
import SelectDateRange from 'Shared/SelectDateRange';
|
||||
import SessionTags from '../SessionTags';
|
||||
import { connect } from 'react-redux';
|
||||
import SessionSort from '../SessionSort';
|
||||
|
||||
interface Props {
|
||||
listCount: number;
|
||||
filter: any;
|
||||
applyFilter: (filter: any) => void;
|
||||
}
|
||||
function SessionHeader(props: Props) {
|
||||
const { listCount, filter: { startDate, endDate, rangeValue } } = props;
|
||||
const period = Period({ start: startDate, end: endDate, rangeName: rangeValue });
|
||||
|
||||
const onDateChange = (e: any) => {
|
||||
const dateValues = e.toJSON();
|
||||
props.applyFilter(dateValues);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center p-4 justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-3 text-lg">
|
||||
<span className="font-bold">Sessions</span> <span className="color-gray-medium ml-2">{listCount}</span>
|
||||
</div>
|
||||
<SessionTags />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<SelectDateRange period={period} onChange={onDateChange} />
|
||||
<div className="mx-2" />
|
||||
<SessionSort />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
filter: state.getIn(['search', 'instance']),
|
||||
listCount: numberWithCommas(state.getIn(['sessions', 'total'])),
|
||||
}),
|
||||
{ applyFilter }
|
||||
)(SessionHeader);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './SessionHeader';
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
import SessionItem from 'Shared/SessionItem';
|
||||
import { NoContent, Loader, Pagination } from 'UI';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import NoContentMessage from '../NoContentMessage';
|
||||
import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage, setScrollPosition } from 'Duck/search';
|
||||
|
||||
interface Props {
|
||||
loading: boolean;
|
||||
list: any;
|
||||
currentPage: number;
|
||||
total: number;
|
||||
filters: any;
|
||||
lastPlayedSessionId: string;
|
||||
metaList: any;
|
||||
scrollY: number;
|
||||
addFilterByKeyAndValue: (key: string, value: any, operator?: string) => void;
|
||||
updateCurrentPage: (page: number) => void;
|
||||
setScrollPosition: (scrollPosition: number) => void;
|
||||
fetchSessions: () => void;
|
||||
}
|
||||
function SessionList(props: Props) {
|
||||
const { loading, list, currentPage, total, filters, lastPlayedSessionId, metaList } = props;
|
||||
const _filterKeys = filters.map((i: any) => i.key);
|
||||
const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID);
|
||||
|
||||
useEffect(() => {
|
||||
const { scrollY } = props;
|
||||
window.scrollTo(0, scrollY);
|
||||
if (total === 0) {
|
||||
props.fetchSessions()
|
||||
}
|
||||
|
||||
return () => {
|
||||
props.setScrollPosition(window.scrollY);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onUserClick = (userId: any) => {
|
||||
if (userId) {
|
||||
props.addFilterByKeyAndValue(FilterKey.USERID, userId);
|
||||
} else {
|
||||
props.addFilterByKeyAndValue(FilterKey.USERID, '', 'isUndefined');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Loader loading={loading}>
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex items-center justify-center flex-col">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size={170} />
|
||||
<div className="mt-2" />
|
||||
<NoContentMessage />
|
||||
</div>
|
||||
}
|
||||
subtext={<div>Please try changing your search parameters.</div>}
|
||||
show={!loading && list.size === 0}
|
||||
>
|
||||
{list.map((session: any) => (
|
||||
<React.Fragment key={session.sessionId}>
|
||||
<SessionItem
|
||||
session={session}
|
||||
hasUserFilter={hasUserFilter}
|
||||
onUserClick={onUserClick}
|
||||
metaList={metaList}
|
||||
lastPlayedSessionId={lastPlayedSessionId}
|
||||
/>
|
||||
<div className="border-b" />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</NoContent>
|
||||
|
||||
{total > 0 && (
|
||||
<div className="w-full flex items-center justify-center py-6">
|
||||
<Pagination
|
||||
page={currentPage}
|
||||
totalPages={Math.ceil(total / 10)}
|
||||
onPageChange={(page) => props.updateCurrentPage(page)}
|
||||
limit={10}
|
||||
debounceRequest={1000}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
list: state.getIn(['sessions', 'list']),
|
||||
filters: state.getIn(['search', 'instance', 'filters']),
|
||||
lastPlayedSessionId: state.getIn(['sessions', 'lastPlayedSessionId']),
|
||||
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
|
||||
loading: state.getIn(['sessions', 'loading']),
|
||||
currentPage: state.getIn(['search', 'currentPage']) || 1,
|
||||
total: state.getIn(['sessions', 'total']) || 0,
|
||||
scrollY: state.getIn(['search', 'scrollY']),
|
||||
}),
|
||||
{ updateCurrentPage, addFilterByKeyAndValue, setScrollPosition, fetchSessions }
|
||||
)(SessionList);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './SessionList';
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import Select from 'Shared/Select';
|
||||
import { sort } from 'Duck/sessions';
|
||||
import { applyFilter } from 'Duck/search';
|
||||
|
||||
const sortOptionsMap = {
|
||||
'startTs-desc': 'Newest',
|
||||
'startTs-asc': 'Oldest',
|
||||
'eventsCount-asc': 'Events Ascending',
|
||||
'eventsCount-desc': 'Events Descending',
|
||||
};
|
||||
|
||||
const sortOptions = Object.entries(sortOptionsMap).map(([value, label]) => ({ value, label }));
|
||||
|
||||
interface Props {
|
||||
filter: any;
|
||||
options: any;
|
||||
applyFilter: (filter: any) => void;
|
||||
sort: (sort: string, sign: number) => void;
|
||||
}
|
||||
|
||||
function SessionSort(props: Props) {
|
||||
const { sort, order } = props.filter;
|
||||
const onSort = ({ value }: any) => {
|
||||
value = value.value;
|
||||
const [sort, order] = value.split('-');
|
||||
const sign = order === 'desc' ? -1 : 1;
|
||||
props.applyFilter({ order, sort });
|
||||
props.sort(sort, sign);
|
||||
};
|
||||
|
||||
const defaultOption = `${sort}-${order}`;
|
||||
return <Select name="sortSessions" plain right options={sortOptions} onChange={onSort} defaultValue={defaultOption} />;
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
filter: state.getIn(['search', 'instance']),
|
||||
}),
|
||||
{ sort, applyFilter }
|
||||
)(SessionSort);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './SessionSort';
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.dropdown {
|
||||
display: flex !important;
|
||||
padding: 4px 6px;
|
||||
border-radius: 3px;
|
||||
color: $gray-darkest;
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
background-color: $gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdownTrigger {
|
||||
padding: 4px 8px;
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
background-color: $gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdownIcon {
|
||||
margin-top: 2px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import React from 'react';
|
||||
import { setActiveTab } from 'Duck/search';
|
||||
import { connect } from 'react-redux';
|
||||
import { issues_types } from 'Types/session/issue';
|
||||
import { Icon } from 'UI';
|
||||
import cn from 'classnames';
|
||||
|
||||
interface Props {
|
||||
setActiveTab: typeof setActiveTab;
|
||||
activeTab: any;
|
||||
tags: any;
|
||||
total: number;
|
||||
}
|
||||
function SessionTags(props: Props) {
|
||||
const { activeTab, tags, total } = props;
|
||||
const disable = activeTab.type === 'all' && total === 0;
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{tags &&
|
||||
tags.map((tag: any, index: any) => (
|
||||
<div key={index}>
|
||||
<TagItem
|
||||
onClick={() => props.setActiveTab(tag)}
|
||||
label={tag.name}
|
||||
isActive={activeTab.type === tag.type}
|
||||
icon={tag.icon}
|
||||
disabled={disable && tag.type !== 'all'}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: any) => {
|
||||
const isEnterprise = state.getIn(['user', 'account', 'edition']) === 'ee';
|
||||
return {
|
||||
activeTab: state.getIn(['search', 'activeTab']),
|
||||
tags: issues_types.filter((tag: any) => (isEnterprise ? tag.type !== 'bookmark' : tag.type !== 'vault')),
|
||||
total: state.getIn(['sessions', 'total']) || 0,
|
||||
};
|
||||
},
|
||||
{
|
||||
setActiveTab,
|
||||
}
|
||||
)(SessionTags);
|
||||
|
||||
function TagItem({ isActive, onClick, label, icon = '', disabled = false }: any) {
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={cn('transition group rounded ml-2 px-2 py-1 flex items-center uppercase text-sm hover:bg-teal hover:text-white', {
|
||||
'bg-teal text-white': isActive,
|
||||
'bg-active-blue color-teal': !isActive,
|
||||
'disabled': disabled,
|
||||
})}
|
||||
>
|
||||
{icon && <Icon name={icon} color="teal" size="14" className={cn('group-hover:fill-white mr-2', { 'fill-white': isActive })} />}
|
||||
<span className="leading-none">{label}</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './SessionTags';
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Icon } from 'UI';
|
||||
import styles from './noContent.module.css';
|
||||
|
||||
export default ({
|
||||
title = <div>No data available.</div>,
|
||||
subtext,
|
||||
icon,
|
||||
iconSize = 100,
|
||||
size,
|
||||
show = true,
|
||||
children = null,
|
||||
empty = false,
|
||||
image = null,
|
||||
style = {},
|
||||
}) => (!show ? children :
|
||||
<div className={ `${ styles.wrapper } ${ size && styles[ size ] }` } style={style}>
|
||||
{
|
||||
icon && <Icon name={icon} size={iconSize} />
|
||||
}
|
||||
{ title && <div className={ styles.title }>{ title }</div> }
|
||||
{
|
||||
subtext &&
|
||||
<div className={ styles.subtext }>{ subtext }</div>
|
||||
}
|
||||
{
|
||||
image && <div className="mt-4 flex justify-center">{ image } </div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
29
frontend/app/components/ui/NoContent/NoContent.tsx
Normal file
29
frontend/app/components/ui/NoContent/NoContent.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import { Icon } from 'UI';
|
||||
import styles from './noContent.module.css';
|
||||
|
||||
interface Props {
|
||||
title?: any;
|
||||
subtext?: any;
|
||||
icon?: string;
|
||||
iconSize?: number;
|
||||
size?: number;
|
||||
show?: boolean;
|
||||
children?: any;
|
||||
image?: any;
|
||||
style?: any;
|
||||
}
|
||||
export default function NoContent(props: Props) {
|
||||
const { title = '', subtext = '', icon, iconSize, size, show, children, image, style } = props;
|
||||
|
||||
return !show ? (
|
||||
children
|
||||
) : (
|
||||
<div className={`${styles.wrapper} ${size && styles[size]}`} style={style}>
|
||||
{icon && <Icon name={icon} size={iconSize} />}
|
||||
{title && <div className={styles.title}>{title}</div>}
|
||||
{subtext && <div className={styles.subtext}>{subtext}</div>}
|
||||
{image && <div className="mt-4 flex justify-center">{image} </div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -45,15 +45,3 @@
|
|||
height: 166px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: block;
|
||||
margin: auto;
|
||||
background-image: svg-load(empty-state.svg, fill=#CCC);
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center center;
|
||||
width: 166px;
|
||||
height: 166px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,13 +137,13 @@ export const reduceThenFetchResource =
|
|||
const filter = getState().getIn(['search', 'instance']).toData();
|
||||
|
||||
const activeTab = getState().getIn(['search', 'activeTab']);
|
||||
if (activeTab.type !== 'all' && activeTab.type !== 'bookmark') {
|
||||
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') {
|
||||
if (activeTab.type === 'bookmark' || activeTab.type === 'vault') {
|
||||
filter.bookmarked = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,8 @@
|
|||
input.no-focus:focus {
|
||||
outline: none !important;
|
||||
border: solid thin transparent !important;
|
||||
}
|
||||
|
||||
.widget-wrapper {
|
||||
@apply rounded border bg-white;
|
||||
}
|
||||
|
|
@ -3,15 +3,18 @@ import { List } from 'immutable';
|
|||
import Watchdog from 'Types/watchdog'
|
||||
|
||||
export const issues_types = List([
|
||||
{ 'type': 'js_exception', 'visible': true, 'order': 0, 'name': 'Errors', 'icon': 'funnel/exclamation-circle' },
|
||||
{ 'type': 'bad_request', 'visible': true, 'order': 1, 'name': 'Bad Requests', 'icon': 'funnel/file-medical-alt' },
|
||||
{ 'type': 'missing_resource', 'visible': true, 'order': 2, 'name': 'Missing Images', 'icon': 'funnel/image' },
|
||||
{ 'type': 'click_rage', 'visible': true, 'order': 3, 'name': 'Click Rage', 'icon': 'funnel/emoji-angry' },
|
||||
{ 'type': 'dead_click', 'visible': true, 'order': 4, 'name': 'Dead Clicks', 'icon': 'funnel/dizzy' },
|
||||
{ 'type': 'memory', 'visible': true, 'order': 5, 'name': 'High Memory', 'icon': 'funnel/sd-card' },
|
||||
{ 'type': 'cpu', 'visible': true, 'order': 6, 'name': 'High CPU', 'icon': 'funnel/cpu' },
|
||||
{ 'type': 'crash', 'visible': true, 'order': 7, 'name': 'Crashes', 'icon': 'funnel/file-earmark-break' },
|
||||
{ 'type': 'custom', 'visible': false, 'order': 8, 'name': 'Custom', 'icon': 'funnel/exclamation-circle' }
|
||||
{ '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': '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' },
|
||||
// { 'type': 'missing_resource', 'visible': true, 'order': 2, 'name': 'Missing Images', 'icon': 'funnel/image' },
|
||||
// { 'type': 'dead_click', 'visible': true, 'order': 4, 'name': 'Dead Clicks', 'icon': 'funnel/dizzy' },
|
||||
// { 'type': 'cpu', 'visible': true, 'order': 6, 'name': 'High CPU', 'icon': 'funnel/cpu' },
|
||||
// { 'type': 'custom', 'visible': false, 'order': 8, 'name': 'Custom', 'icon': 'funnel/exclamation-circle' }
|
||||
]).map(Watchdog)
|
||||
|
||||
export const issues_types_map = {}
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
require('dotenv').config()
|
||||
|
||||
// TODO: (the problem is during the build time the frontend is isolated)
|
||||
//const trackerInfo = require('../tracker/tracker/package.json');
|
||||
|
||||
const oss = {
|
||||
name: 'oss',
|
||||
PRODUCTION: true,
|
||||
SENTRY_ENABLED: false,
|
||||
SENTRY_URL: "",
|
||||
CAPTCHA_ENABLED: process.env.CAPTCHA_ENABLED === 'true',
|
||||
CAPTCHA_SITE_KEY: process.env.CAPTCHA_SITE_KEY,
|
||||
ORIGIN: () => 'window.location.origin',
|
||||
API_EDP: () => 'window.location.origin + "/api"',
|
||||
ASSETS_HOST: () => 'window.location.origin + "/assets"',
|
||||
VERSION: '1.7.0',
|
||||
SOURCEMAP: true,
|
||||
MINIO_ENDPOINT: process.env.MINIO_ENDPOINT,
|
||||
MINIO_PORT: process.env.MINIO_PORT,
|
||||
MINIO_USE_SSL: process.env.MINIO_USE_SSL,
|
||||
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
||||
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
|
||||
ICE_SERVERS: process.env.ICE_SERVERS,
|
||||
TRACKER_VERSION: '3.5.15' // trackerInfo.version,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
oss,
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue