diff --git a/web/.env.production b/web/.env.production
new file mode 100644
index 000000000..d307f0c91
--- /dev/null
+++ b/web/.env.production
@@ -0,0 +1,3 @@
+NEXT_PUBLIC_ADMIN_USERNAME=admin
+NEXT_PUBLIC_ADMIN_STREAMKEY=abc123
+NEXT_PUBLIC_API_HOST=/
\ No newline at end of file
diff --git a/web/.gitignore b/web/.gitignore
index 358a850a1..a0b5b377f 100644
--- a/web/.gitignore
+++ b/web/.gitignore
@@ -1,4 +1,5 @@
node_modules
.env*.local
-.next
\ No newline at end of file
+.next
+out
\ No newline at end of file
diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx
index 958b72559..d09fdfcc3 100644
--- a/web/pages/_app.tsx
+++ b/web/pages/_app.tsx
@@ -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';
diff --git a/web/pages/broadcast-info.tsx b/web/pages/broadcast-info.tsx
index 465b48de5..5c3ad36bd 100644
--- a/web/pages/broadcast-info.tsx
+++ b/web/pages/broadcast-info.tsx
@@ -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() {
diff --git a/web/pages/components/chart.tsx b/web/pages/components/chart.tsx
index 7d98cf137..67dcd0380 100644
--- a/web/pages/components/chart.tsx
+++ b/web/pages/components/chart.tsx
@@ -9,21 +9,24 @@ interface ToolTipProps {
const defaultProps = {
active: false,
- payload: {},
- unit: ""
+ payload: Object,
+ unit: "",
};
+interface TimedValue {
+ time: Date;
+ value: Number;
+}
+
interface ChartProps {
- data: [{
- time: string,
- }],
+ // eslint-disable-next-line react/require-default-props
+ data?: TimedValue[],
color: string,
unit: string,
- dataCollections?: any,
+ // eslint-disable-next-line react/require-default-props
+ dataCollections?: any[],
}
-
-
function CustomizedTooltip(props: ToolTipProps) {
const { active, payload, unit } = props;
if (active && payload && payload[0]) {
@@ -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 (
diff --git a/web/pages/components/log-table.tsx b/web/pages/components/log-table.tsx
index 5ef74fddc..86469aa6f 100644
--- a/web/pages/components/log-table.tsx
+++ b/web/pages/components/log-table.tsx
@@ -19,13 +19,18 @@ function renderColumnLevel(text, entry) {
return
{text}
;
}
-function renderMessage(text, entry) {
+function renderMessage(text) {
return (
{text}
)
}
-export default function LogTable({ logs, pageSize }) {
+interface Props {
+ logs: object[],
+ pageSize: number
+}
+
+export default function LogTable({ logs, pageSize }: Props) {
const columns = [
{
title: "Level",
diff --git a/web/pages/components/main-layout.tsx b/web/pages/components/main-layout.tsx
index 91c5eadab..052cc3bc3 100644
--- a/web/pages/components/main-layout.tsx
+++ b/web/pages/components/main-layout.tsx
@@ -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';
diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx
index 3ffb151d0..9688421ea 100644
--- a/web/pages/hardware-info.tsx
+++ b/web/pages/hardware-info.tsx
@@ -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(),
+ memory: Array(),
+ disk: Array(),
+ message: "",
});
const getHardwareStatus = async () => {
diff --git a/web/pages/index.tsx b/web/pages/index.tsx
index e3fbd5141..963ce6310 100644
--- a/web/pages/index.tsx
+++ b/web/pages/index.tsx
@@ -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
{
try {
diff --git a/web/pages/update-server-config.tsx b/web/pages/update-server-config.tsx
index 8573b4048..1b77d74ea 100644
--- a/web/pages/update-server-config.tsx
+++ b/web/pages/update-server-config.tsx
@@ -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 (
-
- );
+ if (!config.instanceDetails?.socialHandles) {
+ return null;
+ }
+
+ return (
+
+ );
}
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 {
diff --git a/web/pages/video-config.tsx b/web/pages/video-config.tsx
index 53479d2de..faef9a149 100644
--- a/web/pages/video-config.tsx
+++ b/web/pages/video-config.tsx
@@ -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 {
diff --git a/web/pages/viewer-info.tsx b/web/pages/viewer-info.tsx
index 40aae806d..bc2d9adae 100644
--- a/web/pages/viewer-info.tsx
+++ b/web/pages/viewer-info.tsx
@@ -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 (
-
- );
- }
- return null;
- };
-
- /*
-geo data looks like this
- "geo": {
- "countryCode": "US",
- "regionName": "California",
- "timeZone": "America/Los_Angeles"
- }
-*/
-
return (
Current Viewers
diff --git a/web/pages/utils/apis.ts b/web/utils/apis.ts
similarity index 100%
rename from web/pages/utils/apis.ts
rename to web/utils/apis.ts
diff --git a/web/pages/utils/broadcast-status-context.tsx b/web/utils/broadcast-status-context.tsx
similarity index 100%
rename from web/pages/utils/broadcast-status-context.tsx
rename to web/utils/broadcast-status-context.tsx
diff --git a/web/pages/utils/format.ts b/web/utils/format.ts
similarity index 100%
rename from web/pages/utils/format.ts
rename to web/utils/format.ts