0

It builds

This commit is contained in:
Gabe Kangas 2020-11-01 00:01:37 -07:00
parent 27f4b8b158
commit 9b89955bb7
17 changed files with 107 additions and 84 deletions

3
web/.env.production Normal file
View File

@ -0,0 +1,3 @@
NEXT_PUBLIC_ADMIN_USERNAME=admin
NEXT_PUBLIC_ADMIN_STREAMKEY=abc123
NEXT_PUBLIC_API_HOST=/

1
web/.gitignore vendored
View File

@ -2,3 +2,4 @@ node_modules
.env*.local .env*.local
.next .next
out

View File

@ -3,7 +3,7 @@ import 'antd/dist/antd.compact.css';
import "../styles/globals.scss"; import "../styles/globals.scss";
import { AppProps } from 'next/app'; 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'; import MainLayout from './components/main-layout';

View File

@ -1,5 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { BroadcastStatusContext } from './utils/broadcast-status-context'; import { BroadcastStatusContext } from '../utils/broadcast-status-context';
export default function BroadcastInfo() { export default function BroadcastInfo() {

View File

@ -9,20 +9,23 @@ interface ToolTipProps {
const defaultProps = { const defaultProps = {
active: false, active: false,
payload: {}, payload: Object,
unit: "" unit: "",
}; };
interface ChartProps { interface TimedValue {
data: [{ time: Date;
time: string, value: Number;
}],
color: string,
unit: string,
dataCollections?: any,
} }
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) { function CustomizedTooltip(props: ToolTipProps) {
const { active, payload, unit } = props; const { active, payload, unit } = props;
@ -41,13 +44,23 @@ function CustomizedTooltip(props: ToolTipProps) {
CustomizedTooltip.defaultProps = defaultProps; CustomizedTooltip.defaultProps = defaultProps;
export default function Chart({ data, color, unit, dataCollections }: ChartProps) { export default function Chart({ data, color, unit, dataCollections }: ChartProps) {
if (!data && !dataCollections) {
return null;
}
const timeFormatter = (tick: string) => { const timeFormatter = (tick: string) => {
return timeFormat("%I:%M")(new Date(tick)); return timeFormat("%I:%M")(new Date(tick));
}; };
let ticks = data.map((item) => item?.time); let ticks
if (dataCollections) { if (dataCollections.length > 0) {
ticks = dataCollections[0].data?.map((collection) => collection?.time); ticks = dataCollections[0].data?.map(function (collection) {
return collection?.time;
})
} else if (data?.length > 0){
ticks = data?.map(function (item) {
return item?.time;
});
} }
return ( return (

View File

@ -19,13 +19,18 @@ function renderColumnLevel(text, entry) {
return <div style={style}>{text}</div>; return <div style={style}>{text}</div>;
} }
function renderMessage(text, entry) { function renderMessage(text) {
return ( return (
<Linkify>{text}</Linkify> <Linkify>{text}</Linkify>
) )
} }
export default function LogTable({ logs, pageSize }) { interface Props {
logs: object[],
pageSize: number
}
export default function LogTable({ logs, pageSize }: Props) {
const columns = [ const columns = [
{ {
title: "Level", title: "Level",

View File

@ -9,14 +9,13 @@ import {
LineChartOutlined, LineChartOutlined,
CloseCircleOutlined, CloseCircleOutlined,
PlayCircleFilled, PlayCircleFilled,
StopFilled,
MinusSquareFilled, MinusSquareFilled,
} from '@ant-design/icons'; } from '@ant-design/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import OwncastLogo from './logo'; 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'; import adminStyles from '../../styles/styles.module.css';

View File

@ -1,16 +1,22 @@
/* eslint-disable no-array-constructor */
import React, { useState, useEffect } from 'react'; 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 {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 Chart from './components/chart';
import StatisticItem from "./components/statistic"; import StatisticItem from "./components/statistic";
interface TimedValue {
time: Date,
value: Number
}
export default function HardwareInfo() { export default function HardwareInfo() {
const [hardwareStatus, setHardwareStatus] = useState({ const [hardwareStatus, setHardwareStatus] = useState({
cpu: 0, cpu: Array<TimedValue>(),
memory: 0, memory: Array<TimedValue>(),
disk: 0, disk: Array<TimedValue>(),
message: '', message: "",
}); });
const getHardwareStatus = async () => { const getHardwareStatus = async () => {

View File

@ -11,7 +11,7 @@ import React, { useState, useEffect, useContext } from "react";
import { Row, Skeleton, Empty, Typography } from "antd"; import { Row, Skeleton, Empty, Typography } from "antd";
import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons"; import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons";
import { formatDistanceToNow, formatRelative } from "date-fns"; 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 StatisticItem from "./components/statistic"
import LogTable from "./components/log-table"; import LogTable from "./components/log-table";
@ -21,8 +21,8 @@ import {
LOGS_WARN, LOGS_WARN,
fetchData, fetchData,
FETCH_INTERVAL, FETCH_INTERVAL,
} from "./utils/apis"; } from "../utils/apis";
import { formatIPAddress, isEmptyObject } from "./utils/format"; import { formatIPAddress, isEmptyObject } from "../utils/format";
const { Title } = Typography; const { Title } = Typography;
@ -60,7 +60,22 @@ export default function Stats() {
// Pull in the server config so we can show config overview. // 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 [logs, setLogs] = useState([]);
const getConfig = async () => { const getConfig = async () => {
@ -108,6 +123,7 @@ export default function Stats() {
: `${setting.audioBitrate} kbps`; : `${setting.audioBitrate} kbps`;
return ( return (
// eslint-disable-next-line react/no-array-index-key
<Row gutter={[16, 16]} key={index}> <Row gutter={[16, 16]} key={index}>
<StatisticItem <StatisticItem
title="Outbound Video Stream" title="Outbound Video Stream"

View File

@ -4,7 +4,7 @@ import LogTable from "./components/log-table"
import { import {
LOGS_ALL, LOGS_ALL,
fetchData, fetchData,
} from "./utils/apis"; } from "../utils/apis";
const FETCH_INTERVAL = 5 * 1000; // 5 sec const FETCH_INTERVAL = 5 * 1000; // 5 sec

View File

@ -1,9 +1,10 @@
/* eslint-disable react/prop-types */
import React, { useState, useEffect } from "react"; 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"; import KeyValueTable from "./components/key-value-table";
function Storage({ config }) { function Storage({ config }) {
if (!config) { if (!config || !config.s3) {
return null; return null;
} }
@ -46,7 +47,7 @@ function Storage({ config }) {
} }
export default function ServerConfig() { export default function ServerConfig() {
const [config, setConfig] = useState(); const [config, setConfig] = useState({});
const getInfo = async () => { const getInfo = async () => {
try { try {

View File

@ -1,7 +1,8 @@
/* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Table, Typography, Input } from 'antd'; import { Table, Typography, Input } from 'antd';
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from './utils/apis'; import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from '../utils/apis';
import { isEmptyObject } from './utils/format'; import { isEmptyObject } from '../utils/format';
import KeyValueTable from "./components/key-value-table"; import KeyValueTable from "./components/key-value-table";
const { Title } = Typography; const { Title } = Typography;
@ -27,6 +28,10 @@ function SocialHandles({ config }) {
}, },
]; ];
if (!config.instanceDetails?.socialHandles) {
return null;
}
return ( return (
<div> <div>
<Title>Social Handles</Title> <Title>Social Handles</Title>
@ -40,7 +45,6 @@ function SocialHandles({ config }) {
} }
function InstanceDetails({ config }) { function InstanceDetails({ config }) {
console.log(config)
if (!config || isEmptyObject(config)) { if (!config || isEmptyObject(config)) {
return null; return null;
} }
@ -102,7 +106,7 @@ function InstanceDetails({ config }) {
} }
function PageContent({ config }) { function PageContent({ config }) {
if (!config) { if (!config?.instanceDetails?.extraPageContent) {
return null; return null;
} }
return ( return (
@ -117,7 +121,7 @@ function PageContent({ config }) {
} }
export default function ServerConfig() { export default function ServerConfig() {
const [config, setConfig] = useState(); const [config, setConfig] = useState({});
const getInfo = async () => { const getInfo = async () => {
try { try {

View File

@ -1,12 +1,13 @@
/* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Table, Typography } from 'antd'; 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; const { Title } = Typography;
function VideoVariants({ config }) { function VideoVariants({ config }) {
if (!config) { if (!config || !config.videoSettings) {
return null; return null;
} }
@ -80,7 +81,7 @@ function VideoVariants({ config }) {
} }
export default function VideoConfig() { export default function VideoConfig() {
const [config, setConfig] = useState(); const [config, setConfig] = useState({});
const getInfo = async () => { const getInfo = async () => {
try { try {

View File

@ -1,18 +1,20 @@
/* eslint-disable react/prop-types */
import React, { useState, useEffect, useContext } from 'react'; import React, { useState, useEffect, useContext } from 'react';
import { timeFormat } from "d3-time-format"; import { timeFormat } from "d3-time-format";
import { Table, Row } from "antd"; import { Table, Row } from "antd";
import { formatDistanceToNow } from "date-fns"; import { formatDistanceToNow } from "date-fns";
import { UserOutlined} from "@ant-design/icons"; import { UserOutlined} from "@ant-design/icons";
import { SortOrder } from "antd/lib/table/interface";
import Chart from "./components/chart"; import Chart from "./components/chart";
import StatisticItem from "./components/statistic"; import StatisticItem from "./components/statistic";
import { BroadcastStatusContext } from './utils/broadcast-status-context'; import { BroadcastStatusContext } from '../utils/broadcast-status-context';
import { import {
CONNECTED_CLIENTS, CONNECTED_CLIENTS,
STREAM_STATUS, VIEWERS_OVER_TIME, STREAM_STATUS, VIEWERS_OVER_TIME,
fetchData, fetchData,
} from "./utils/apis"; } from "../utils/apis";
const FETCH_INTERVAL = 5 * 60 * 1000; // 5 mins const FETCH_INTERVAL = 5 * 60 * 1000; // 5 mins
@ -78,14 +80,14 @@ export default function ViewersOverTime() {
key: "username", key: "username",
render: (username) => username || "-", render: (username) => username || "-",
sorter: (a, b) => a.username - b.username, sorter: (a, b) => a.username - b.username,
sortDirections: ["descend", "ascend"], sortDirections: ["descend", "ascend"] as SortOrder[],
}, },
{ {
title: "Messages sent", title: "Messages sent",
dataIndex: "messageCount", dataIndex: "messageCount",
key: "messageCount", key: "messageCount",
sorter: (a, b) => a.messageCount - b.messageCount, sorter: (a, b) => a.messageCount - b.messageCount,
sortDirections: ["descend", "ascend"], sortDirections: ["descend", "ascend"] as SortOrder[],
}, },
{ {
title: "Connected Time", 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 ( return (
<div> <div>
<h2>Current Viewers</h2> <h2>Current Viewers</h2>