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=/
|
1
web/.gitignore
vendored
1
web/.gitignore
vendored
@ -2,3 +2,4 @@ node_modules
|
|||||||
.env*.local
|
.env*.local
|
||||||
|
|
||||||
.next
|
.next
|
||||||
|
out
|
@ -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';
|
||||||
|
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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 (
|
||||||
|
@ -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",
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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 () => {
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user