It builds
This commit is contained in:
parent
27f4b8b158
commit
9b89955bb7
3
web/.env.production
Normal file
3
web/.env.production
Normal file
@ -0,0 +1,3 @@
|
||||
NEXT_PUBLIC_ADMIN_USERNAME=admin
|
||||
NEXT_PUBLIC_ADMIN_STREAMKEY=abc123
|
||||
NEXT_PUBLIC_API_HOST=/
|
3
web/.gitignore
vendored
3
web/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
node_modules
|
||||
.env*.local
|
||||
|
||||
.next
|
||||
.next
|
||||
out
|
@ -3,7 +3,7 @@ import 'antd/dist/antd.compact.css';
|
||||
import "../styles/globals.scss";
|
||||
|
||||
import { AppProps } from 'next/app';
|
||||
import BroadcastStatusProvider from './utils/broadcast-status-context';
|
||||
import BroadcastStatusProvider from '../utils/broadcast-status-context';
|
||||
import MainLayout from './components/main-layout';
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { BroadcastStatusContext } from './utils/broadcast-status-context';
|
||||
import { BroadcastStatusContext } from '../utils/broadcast-status-context';
|
||||
|
||||
|
||||
export default function BroadcastInfo() {
|
||||
|
@ -9,20 +9,23 @@ interface ToolTipProps {
|
||||
|
||||
const defaultProps = {
|
||||
active: false,
|
||||
payload: {},
|
||||
unit: ""
|
||||
payload: Object,
|
||||
unit: "",
|
||||
};
|
||||
|
||||
interface ChartProps {
|
||||
data: [{
|
||||
time: string,
|
||||
}],
|
||||
color: string,
|
||||
unit: string,
|
||||
dataCollections?: any,
|
||||
interface TimedValue {
|
||||
time: Date;
|
||||
value: Number;
|
||||
}
|
||||
|
||||
|
||||
interface ChartProps {
|
||||
// eslint-disable-next-line react/require-default-props
|
||||
data?: TimedValue[],
|
||||
color: string,
|
||||
unit: string,
|
||||
// eslint-disable-next-line react/require-default-props
|
||||
dataCollections?: any[],
|
||||
}
|
||||
|
||||
function CustomizedTooltip(props: ToolTipProps) {
|
||||
const { active, payload, unit } = props;
|
||||
@ -41,13 +44,23 @@ function CustomizedTooltip(props: ToolTipProps) {
|
||||
CustomizedTooltip.defaultProps = defaultProps;
|
||||
|
||||
export default function Chart({ data, color, unit, dataCollections }: ChartProps) {
|
||||
if (!data && !dataCollections) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timeFormatter = (tick: string) => {
|
||||
return timeFormat("%I:%M")(new Date(tick));
|
||||
};
|
||||
|
||||
let ticks = data.map((item) => item?.time);
|
||||
if (dataCollections) {
|
||||
ticks = dataCollections[0].data?.map((collection) => collection?.time);
|
||||
let ticks
|
||||
if (dataCollections.length > 0) {
|
||||
ticks = dataCollections[0].data?.map(function (collection) {
|
||||
return collection?.time;
|
||||
})
|
||||
} else if (data?.length > 0){
|
||||
ticks = data?.map(function (item) {
|
||||
return item?.time;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -19,13 +19,18 @@ function renderColumnLevel(text, entry) {
|
||||
return <div style={style}>{text}</div>;
|
||||
}
|
||||
|
||||
function renderMessage(text, entry) {
|
||||
function renderMessage(text) {
|
||||
return (
|
||||
<Linkify>{text}</Linkify>
|
||||
)
|
||||
}
|
||||
|
||||
export default function LogTable({ logs, pageSize }) {
|
||||
interface Props {
|
||||
logs: object[],
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
export default function LogTable({ logs, pageSize }: Props) {
|
||||
const columns = [
|
||||
{
|
||||
title: "Level",
|
||||
|
@ -9,14 +9,13 @@ import {
|
||||
LineChartOutlined,
|
||||
CloseCircleOutlined,
|
||||
PlayCircleFilled,
|
||||
StopFilled,
|
||||
MinusSquareFilled,
|
||||
} from '@ant-design/icons';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
||||
import OwncastLogo from './logo';
|
||||
import { BroadcastStatusContext } from '../utils/broadcast-status-context';
|
||||
import { BroadcastStatusContext } from '../../utils/broadcast-status-context';
|
||||
|
||||
import adminStyles from '../../styles/styles.module.css';
|
||||
|
||||
|
@ -1,16 +1,22 @@
|
||||
/* eslint-disable no-array-constructor */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Skeleton, Empty, Typography } from "antd";
|
||||
import { Row } from "antd";
|
||||
import {LaptopOutlined, BulbOutlined, SaveOutlined} from "@ant-design/icons"
|
||||
import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||
import Chart from './components/chart';
|
||||
import StatisticItem from "./components/statistic";
|
||||
|
||||
interface TimedValue {
|
||||
time: Date,
|
||||
value: Number
|
||||
}
|
||||
|
||||
export default function HardwareInfo() {
|
||||
const [hardwareStatus, setHardwareStatus] = useState({
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
disk: 0,
|
||||
message: '',
|
||||
cpu: Array<TimedValue>(),
|
||||
memory: Array<TimedValue>(),
|
||||
disk: Array<TimedValue>(),
|
||||
message: "",
|
||||
});
|
||||
|
||||
const getHardwareStatus = async () => {
|
||||
|
@ -11,7 +11,7 @@ import React, { useState, useEffect, useContext } from "react";
|
||||
import { Row, Skeleton, Empty, Typography } from "antd";
|
||||
import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons";
|
||||
import { formatDistanceToNow, formatRelative } from "date-fns";
|
||||
import { BroadcastStatusContext } from "./utils/broadcast-status-context";
|
||||
import { BroadcastStatusContext } from "../utils/broadcast-status-context";
|
||||
import StatisticItem from "./components/statistic"
|
||||
import LogTable from "./components/log-table";
|
||||
|
||||
@ -21,8 +21,8 @@ import {
|
||||
LOGS_WARN,
|
||||
fetchData,
|
||||
FETCH_INTERVAL,
|
||||
} from "./utils/apis";
|
||||
import { formatIPAddress, isEmptyObject } from "./utils/format";
|
||||
} from "../utils/apis";
|
||||
import { formatIPAddress, isEmptyObject } from "../utils/format";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
@ -60,7 +60,22 @@ export default function Stats() {
|
||||
|
||||
|
||||
// Pull in the server config so we can show config overview.
|
||||
const [config, setConfig] = useState([]);
|
||||
const [config, setConfig] = useState({
|
||||
streamKey: "",
|
||||
yp: {
|
||||
enabled: false,
|
||||
},
|
||||
videoSettings: {
|
||||
videoQualityVariants: [
|
||||
{
|
||||
audioPassthrough: false,
|
||||
videoBitrate: 0,
|
||||
audioBitrate: 0,
|
||||
framerate: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const [logs, setLogs] = useState([]);
|
||||
|
||||
const getConfig = async () => {
|
||||
@ -108,6 +123,7 @@ export default function Stats() {
|
||||
: `${setting.audioBitrate} kbps`;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<Row gutter={[16, 16]} key={index}>
|
||||
<StatisticItem
|
||||
title="Outbound Video Stream"
|
||||
|
@ -4,7 +4,7 @@ import LogTable from "./components/log-table"
|
||||
import {
|
||||
LOGS_ALL,
|
||||
fetchData,
|
||||
} from "./utils/apis";
|
||||
} from "../utils/apis";
|
||||
|
||||
const FETCH_INTERVAL = 5 * 1000; // 5 sec
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from "./utils/apis";
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from "../utils/apis";
|
||||
import KeyValueTable from "./components/key-value-table";
|
||||
|
||||
function Storage({ config }) {
|
||||
if (!config) {
|
||||
if (!config || !config.s3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -46,7 +47,7 @@ function Storage({ config }) {
|
||||
}
|
||||
|
||||
export default function ServerConfig() {
|
||||
const [config, setConfig] = useState();
|
||||
const [config, setConfig] = useState({});
|
||||
|
||||
const getInfo = async () => {
|
||||
try {
|
||||
|
@ -1,7 +1,8 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Table, Typography, Input } from 'antd';
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
import { isEmptyObject } from './utils/format';
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||
import { isEmptyObject } from '../utils/format';
|
||||
import KeyValueTable from "./components/key-value-table";
|
||||
|
||||
const { Title } = Typography;
|
||||
@ -27,20 +28,23 @@ function SocialHandles({ config }) {
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title>Social Handles</Title>
|
||||
<Table
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
dataSource={config.instanceDetails.socialHandles}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
if (!config.instanceDetails?.socialHandles) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title>Social Handles</Title>
|
||||
<Table
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
dataSource={config.instanceDetails.socialHandles}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InstanceDetails({ config }) {
|
||||
console.log(config)
|
||||
if (!config || isEmptyObject(config)) {
|
||||
return null;
|
||||
}
|
||||
@ -102,7 +106,7 @@ function InstanceDetails({ config }) {
|
||||
}
|
||||
|
||||
function PageContent({ config }) {
|
||||
if (!config) {
|
||||
if (!config?.instanceDetails?.extraPageContent) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
@ -117,7 +121,7 @@ function PageContent({ config }) {
|
||||
}
|
||||
|
||||
export default function ServerConfig() {
|
||||
const [config, setConfig] = useState();
|
||||
const [config, setConfig] = useState({});
|
||||
|
||||
const getInfo = async () => {
|
||||
try {
|
||||
|
@ -1,12 +1,13 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Table, Typography } from 'antd';
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
|
||||
function VideoVariants({ config }) {
|
||||
if (!config) {
|
||||
if (!config || !config.videoSettings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -80,7 +81,7 @@ function VideoVariants({ config }) {
|
||||
}
|
||||
|
||||
export default function VideoConfig() {
|
||||
const [config, setConfig] = useState();
|
||||
const [config, setConfig] = useState({});
|
||||
|
||||
const getInfo = async () => {
|
||||
try {
|
||||
|
@ -1,18 +1,20 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { timeFormat } from "d3-time-format";
|
||||
import { Table, Row } from "antd";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { UserOutlined} from "@ant-design/icons";
|
||||
import { SortOrder } from "antd/lib/table/interface";
|
||||
import Chart from "./components/chart";
|
||||
import StatisticItem from "./components/statistic";
|
||||
|
||||
import { BroadcastStatusContext } from './utils/broadcast-status-context';
|
||||
import { BroadcastStatusContext } from '../utils/broadcast-status-context';
|
||||
|
||||
import {
|
||||
CONNECTED_CLIENTS,
|
||||
STREAM_STATUS, VIEWERS_OVER_TIME,
|
||||
fetchData,
|
||||
} from "./utils/apis";
|
||||
} from "../utils/apis";
|
||||
|
||||
const FETCH_INTERVAL = 5 * 60 * 1000; // 5 mins
|
||||
|
||||
@ -78,14 +80,14 @@ export default function ViewersOverTime() {
|
||||
key: "username",
|
||||
render: (username) => username || "-",
|
||||
sorter: (a, b) => a.username - b.username,
|
||||
sortDirections: ["descend", "ascend"],
|
||||
sortDirections: ["descend", "ascend"] as SortOrder[],
|
||||
},
|
||||
{
|
||||
title: "Messages sent",
|
||||
dataIndex: "messageCount",
|
||||
key: "messageCount",
|
||||
sorter: (a, b) => a.messageCount - b.messageCount,
|
||||
sortDirections: ["descend", "ascend"],
|
||||
sortDirections: ["descend", "ascend"] as SortOrder[],
|
||||
},
|
||||
{
|
||||
title: "Connected Time",
|
||||
@ -106,34 +108,6 @@ export default function ViewersOverTime() {
|
||||
},
|
||||
];
|
||||
|
||||
const timeFormatter = (tick) => {
|
||||
return timeFormat("%H:%M")(new Date(tick));
|
||||
};
|
||||
|
||||
const CustomizedTooltip = (props) => {
|
||||
const { active, payload, label } = props;
|
||||
if (active) {
|
||||
const numViewers = payload && payload[0] && payload[0].value;
|
||||
const time = timeFormatter(label);
|
||||
const message = `${numViewers} viewer(s) at ${time}`;
|
||||
return (
|
||||
<div className="custom-tooltip">
|
||||
<p className="label">{message}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
geo data looks like this
|
||||
"geo": {
|
||||
"countryCode": "US",
|
||||
"regionName": "California",
|
||||
"timeZone": "America/Los_Angeles"
|
||||
}
|
||||
*/
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Current Viewers</h2>
|
||||
|
Loading…
x
Reference in New Issue
Block a user