Merge branch 'master' of github.com:owncast/owncast-admin
This commit is contained in:
commit
3f1f96a768
@ -1,3 +1 @@
|
||||
NEXT_PUBLIC_ADMIN_USERNAME=admin
|
||||
NEXT_PUBLIC_ADMIN_STREAMKEY=abc123
|
||||
NEXT_PUBLIC_API_HOST=/
|
@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
basePath: "/admin",
|
||||
trailingSlash: true,
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import styles from '../../styles/styles.module.css';
|
||||
|
||||
interface ToolTipProps {
|
||||
active?: boolean,
|
||||
payload?: object,
|
||||
payload?: {name: string, payload: {value: string, time: Date}}[],
|
||||
unit?: string
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ interface TimedValue {
|
||||
|
||||
interface ChartProps {
|
||||
data?: TimedValue[],
|
||||
title?: string,
|
||||
color: string,
|
||||
unit: string,
|
||||
dataCollections?: any[],
|
||||
@ -31,19 +32,24 @@ function CustomizedTooltip(props: ToolTipProps) {
|
||||
const { active, payload, unit } = props;
|
||||
if (active && payload && payload[0]) {
|
||||
const time = payload[0].payload ? timeFormat("%I:%M")(new Date(payload[0].payload.time)) : "";
|
||||
|
||||
const tooltipDetails = payload.map(data => {
|
||||
return <div className="label" key={data.name}>
|
||||
{data.payload.value}{unit} {data.name}
|
||||
</div>
|
||||
});
|
||||
return (
|
||||
<div className="custom-tooltip">
|
||||
<p className="label">
|
||||
<strong>{time}</strong> {payload[0].payload.value} {unit}
|
||||
</p>
|
||||
</div>
|
||||
<span className="custom-tooltip">
|
||||
<strong>{time}</strong>
|
||||
{tooltipDetails}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
CustomizedTooltip.defaultProps = defaultProps;
|
||||
|
||||
export default function Chart({ data, color, unit, dataCollections }: ChartProps) {
|
||||
export default function Chart({ data, title, color, unit, dataCollections }: ChartProps) {
|
||||
if (!data && !dataCollections) {
|
||||
return null;
|
||||
}
|
||||
@ -67,6 +73,18 @@ export default function Chart({ data, color, unit, dataCollections }: ChartProps
|
||||
});
|
||||
}
|
||||
|
||||
const line = data ? (
|
||||
<Line
|
||||
type="natural"
|
||||
dataKey="value"
|
||||
stroke={color}
|
||||
dot={null}
|
||||
strokeWidth={3}
|
||||
legendType="square"
|
||||
name={title}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className={styles.lineChartContainer}>
|
||||
<LineChart width={chartWidth} height={chartHeight} data={data}>
|
||||
@ -87,23 +105,18 @@ export default function Chart({ data, color, unit, dataCollections }: ChartProps
|
||||
/>
|
||||
<Tooltip content={<CustomizedTooltip unit={unit} />} />
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="value"
|
||||
stroke={color}
|
||||
dot={null}
|
||||
strokeWidth={3}
|
||||
/>
|
||||
{line}
|
||||
{dataCollections?.map((s) => (
|
||||
<Line
|
||||
dataKey="value"
|
||||
data={s.data}
|
||||
name={s.name}
|
||||
key={s.name}
|
||||
type="monotone"
|
||||
type="natural"
|
||||
stroke={s.color}
|
||||
dot={null}
|
||||
strokeWidth={3}
|
||||
legendType="square"
|
||||
/>
|
||||
))}
|
||||
</LineChart>
|
||||
|
@ -77,7 +77,6 @@ export default function LogTable({ logs, pageSize }: Props) {
|
||||
rowKey={(row) => row.time}
|
||||
pagination={{ pageSize: pageSize || 20 }}
|
||||
/>
|
||||
;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import Link from 'next/link';
|
||||
import { differenceInSeconds } from "date-fns";
|
||||
import { useRouter } from 'next/router';
|
||||
import { Layout, Menu } from 'antd';
|
||||
import { Layout, Menu, Popover } from 'antd';
|
||||
|
||||
import {
|
||||
SettingOutlined,
|
||||
@ -38,6 +38,11 @@ export default function MainLayout(props) {
|
||||
|
||||
const streamDurationString = online ? parseSecondsToDurationString(differenceInSeconds(new Date(), new Date(broadcaster.time))) : "";
|
||||
|
||||
const content = (
|
||||
<div>
|
||||
<img src="/thumbnail.jpg" width="200px" />
|
||||
</div>
|
||||
);
|
||||
const statusIcon = online ? <PlayCircleFilled /> : <MinusSquareFilled />;
|
||||
const statusMessage = online ? `Online ${streamDurationString}` : "Offline";
|
||||
|
||||
@ -145,10 +150,12 @@ export default function MainLayout(props) {
|
||||
|
||||
<Layout>
|
||||
<Header className={adminStyles.header}>
|
||||
<Popover content={content} title="Thumbnail" trigger="hover">
|
||||
<div className={adminStyles.statusIndicatorContainer}>
|
||||
<span className={adminStyles.statusLabel}>{statusMessage}</span>
|
||||
<span className={adminStyles.statusIcon}>{statusIcon}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
</Header>
|
||||
<Content className={adminStyles.contentMain}>{children}</Content>
|
||||
|
||||
|
@ -1,25 +1,57 @@
|
||||
import { Statistic, Card, Col} from "antd";
|
||||
import { Typography, Statistic, Card, Col, Progress} from "antd";
|
||||
const { Text } = Typography;
|
||||
|
||||
interface ItemProps {
|
||||
title: string,
|
||||
value: string,
|
||||
prefix: JSX.Element,
|
||||
color: string,
|
||||
progress?: boolean,
|
||||
centered: boolean,
|
||||
};
|
||||
|
||||
export default function StatisticItem(props: ItemProps) {
|
||||
const { title, value, prefix } = props;
|
||||
const valueStyle = { color: "#334", fontSize: "1.8rem" };
|
||||
const View = props.progress ? ProgressView : StatisticView;
|
||||
const style = props.centered ? {display: 'flex', alignItems: 'center', justifyContent: 'center'} : {};
|
||||
|
||||
return (
|
||||
<Col span={8}>
|
||||
<Card>
|
||||
<Statistic
|
||||
title={title}
|
||||
value={value}
|
||||
valueStyle={valueStyle}
|
||||
prefix={prefix}
|
||||
/>
|
||||
<div style={style}>
|
||||
<View {...props} />
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
function ProgressView({title, value, prefix, color}) {
|
||||
const endColor = value > 90 ? 'red' : color;
|
||||
const content = (
|
||||
<div>
|
||||
{prefix}
|
||||
<div><Text type="secondary">{title}</Text></div>
|
||||
<div><Text type="secondary">{value}%</Text></div>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<Progress type="dashboard" percent={value} width={120} strokeColor={{
|
||||
'0%': color,
|
||||
'90%': endColor,
|
||||
}} format={percent => content} />
|
||||
)
|
||||
}
|
||||
|
||||
function StatisticView({title, value, prefix, color}) {
|
||||
const valueStyle = { fontSize: "1.8rem" };
|
||||
|
||||
return (
|
||||
<Statistic
|
||||
title={title}
|
||||
value={value}
|
||||
valueStyle={valueStyle}
|
||||
prefix={prefix}
|
||||
/>
|
||||
)
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
/* eslint-disable no-array-constructor */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { BulbOutlined, LaptopOutlined, SaveOutlined } from "@ant-design/icons";
|
||||
import { Row } from "antd";
|
||||
import {LaptopOutlined, BulbOutlined, SaveOutlined} from "@ant-design/icons"
|
||||
import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { fetchData, FETCH_INTERVAL, HARDWARE_STATS } from '../utils/apis';
|
||||
import Chart from './components/chart';
|
||||
import StatisticItem from "./components/statistic";
|
||||
|
||||
@ -55,17 +55,17 @@ export default function HardwareInfo() {
|
||||
const series = [
|
||||
{
|
||||
name: "CPU",
|
||||
color: "#FF7700",
|
||||
color: "#B63FFF",
|
||||
data: hardwareStatus.cpu,
|
||||
},
|
||||
{
|
||||
name: "Memory",
|
||||
color: "#004777",
|
||||
color: "#2087E2",
|
||||
data: hardwareStatus.memory,
|
||||
},
|
||||
{
|
||||
name: "Disk",
|
||||
color: "#A9E190",
|
||||
color: "#FF7700",
|
||||
data: hardwareStatus.disk,
|
||||
},
|
||||
];
|
||||
@ -76,19 +76,28 @@ const series = [
|
||||
<h2>Hardware Info</h2>
|
||||
<Row gutter={[16, 16]}>
|
||||
<StatisticItem
|
||||
title="CPU used"
|
||||
value={`${currentCPUUsage} %`}
|
||||
prefix={<LaptopOutlined />}
|
||||
title={series[0].name}
|
||||
value={`${currentCPUUsage}`}
|
||||
prefix={<LaptopOutlined style={{color: series[0].color }}/>}
|
||||
color={series[0].color}
|
||||
progress
|
||||
centered
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Memory used"
|
||||
value={`${currentRamUsage} %`}
|
||||
prefix={<BulbOutlined />}
|
||||
title={series[1].name}
|
||||
value={`${currentRamUsage}`}
|
||||
prefix={<BulbOutlined style={{color: series[1].color }} />}
|
||||
color={series[1].color}
|
||||
progress
|
||||
centered
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Disk used"
|
||||
value={`${currentDiskUsage} %`}
|
||||
prefix={<SaveOutlined />}
|
||||
title={series[2].name}
|
||||
value={`${currentDiskUsage}`}
|
||||
prefix={<SaveOutlined style={{color: series[2].color }} />}
|
||||
color={series[2].color}
|
||||
progress
|
||||
centered
|
||||
/>
|
||||
</Row>
|
||||
|
||||
@ -96,18 +105,6 @@ const series = [
|
||||
<Chart dataCollections={series} color="#FF7700" unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
<p>cpu:[], disk: [], memory: []; value = %age.</p>
|
||||
<p>the times should be the same for each, though milliseconds differ</p>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid blue",
|
||||
height: "300px",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(hardwareStatus)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -8,7 +8,11 @@ TODO: Link each overview value to the sub-page that focuses on it.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
<<<<<<< HEAD
|
||||
import { Row, Skeleton, Typography } from "antd";
|
||||
=======
|
||||
import { Row, Col, Skeleton, Result, List, Typography, Card } from "antd";
|
||||
>>>>>>> 4cdf5b73baa0584a0e6b2f586c27ca53923c65c7
|
||||
import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons";
|
||||
import { formatDistanceToNow, formatRelative } from "date-fns";
|
||||
import { ServerStatusContext } from "../utils/server-status-context";
|
||||
@ -27,6 +31,10 @@ import { formatIPAddress, isEmptyObject } from "../utils/format";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
>>>>>>> 4cdf5b73baa0584a0e6b2f586c27ca53923c65c7
|
||||
|
||||
<<<<<<< HEAD
|
||||
export default function Home() {
|
||||
@ -105,6 +113,9 @@ export default function Stats() {
|
||||
);
|
||||
}
|
||||
|
||||
const logTable = logs.length > 0 ? <LogTable logs={logs} pageSize={5} /> : null
|
||||
console.log(logs)
|
||||
|
||||
if (!broadcaster) {
|
||||
return <Offline />;
|
||||
}
|
||||
@ -122,17 +133,18 @@ export default function Stats() {
|
||||
title="Outbound Video Stream"
|
||||
value={`${setting.videoBitrate} kbps ${setting.framerate} fps`}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Outbound Audio Stream"
|
||||
value={audioSetting}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
||||
const logTable = logs.length > 0 ? <LogTable logs={logs} pageSize={5} /> : null
|
||||
const { viewerCount, sessionMaxViewerCount } = stats;
|
||||
const streamVideoDetailString = `${streamDetails.videoCodec} ${streamDetails.videoBitrate} kbps ${streamDetails.width}x${streamDetails.height}`;
|
||||
const streamAudioDetailString = `${streamDetails.audioCodec} ${streamDetails.audioBitrate} kpbs`;
|
||||
@ -148,16 +160,19 @@ export default function Stats() {
|
||||
)}`}
|
||||
value={formatDistanceToNow(new Date(broadcaster.time))}
|
||||
prefix={<ClockCircleOutlined />}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Viewers"
|
||||
value={viewerCount}
|
||||
prefix={<UserOutlined />}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Peak viewer count"
|
||||
value={sessionMaxViewerCount}
|
||||
prefix={<UserOutlined />}
|
||||
color="#334"
|
||||
/>
|
||||
</Row>
|
||||
|
||||
@ -166,16 +181,19 @@ export default function Stats() {
|
||||
title="Input"
|
||||
value={formatIPAddress(remoteAddr)}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Inbound Video Stream"
|
||||
value={streamVideoDetailString}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Inbound Audio Stream"
|
||||
value={streamAudioDetailString}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
</Row>
|
||||
|
||||
@ -186,15 +204,81 @@ export default function Stats() {
|
||||
title="Stream key"
|
||||
value={config.streamKey}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Directory registration enabled"
|
||||
value={config.yp.enabled.toString()}
|
||||
prefix={null}
|
||||
color="#334"
|
||||
/>
|
||||
</Row>
|
||||
|
||||
{logTable}
|
||||
</div>
|
||||
);
|
||||
|
||||
function Offline() {
|
||||
const data = [
|
||||
{
|
||||
title: "Send some test content",
|
||||
content: (
|
||||
<div>
|
||||
Test your server with any video you have around. Pass it to the test script and start streaming it.
|
||||
<blockquote>
|
||||
<em>./test/ocTestStream.sh yourVideo.mp4</em>
|
||||
</blockquote>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Use your broadcasting software",
|
||||
content: (
|
||||
<div>
|
||||
<a href="https://owncast.online/docs/broadcasting/">Learn how to point your existing software to your new server and start streaming your content.</a>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "Chat is disabled",
|
||||
content: "Chat will continue to be disabled until you begin a live stream."
|
||||
},
|
||||
{
|
||||
title: "Embed your video onto other sites",
|
||||
content: (
|
||||
<div>
|
||||
<a href="https://owncast.online/docs/embed">Learn how you can add your Owncast stream to other sites you control.</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<Result
|
||||
icon={<OwncastLogo />}
|
||||
title="No stream is active."
|
||||
subTitle="You should start one."
|
||||
/>
|
||||
|
||||
<List
|
||||
grid={{
|
||||
gutter: 16,
|
||||
xs: 1,
|
||||
sm: 2,
|
||||
md: 2,
|
||||
lg: 6,
|
||||
xl: 3,
|
||||
xxl: 3,
|
||||
}}
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<Card title={item.title}>{item.content}</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
{logTable}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ export default function Offline() {
|
||||
title: "Send some test content",
|
||||
content: (
|
||||
<div>
|
||||
With any video you have around you can pass it to the test script and start streaming it.
|
||||
Test your server with any video you have around. Pass it to the test script and start streaming it.
|
||||
<blockquote>
|
||||
<em>./test/ocTestStream.sh yourVideo.mp4</em>
|
||||
</blockquote>
|
||||
@ -23,8 +23,17 @@ export default function Offline() {
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "Something else",
|
||||
title: "Chat is disabled",
|
||||
content: "Chat will continue to be disabled until you begin a live stream."
|
||||
},
|
||||
{
|
||||
title: "Embed your video onto other sites",
|
||||
content: (
|
||||
<div>
|
||||
<a href="https://owncast.online/docs/embed">Learn how you can add your Owncast stream to other sites you control.</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
@ -39,9 +48,9 @@ export default function Offline() {
|
||||
gutter: 16,
|
||||
xs: 1,
|
||||
sm: 2,
|
||||
md: 4,
|
||||
lg: 4,
|
||||
xl: 6,
|
||||
md: 2,
|
||||
lg: 6,
|
||||
xl: 3,
|
||||
xxl: 3,
|
||||
}}
|
||||
dataSource={data}
|
||||
@ -51,6 +60,7 @@ export default function Offline() {
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
{logTable}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ function Storage({ config }) {
|
||||
return (
|
||||
<h3>
|
||||
Local storage is being used. Enable external S3 storage if you want
|
||||
to use it.
|
||||
to use it. TODO: Make this message somewhat more informative. Point to documentation or something.
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
@ -74,20 +74,7 @@ export default function ServerConfig() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Server Config</h2>
|
||||
<p>
|
||||
Display this data all pretty, most things will be editable in the
|
||||
future, not now.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid pink",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<Storage config={config} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -149,24 +149,9 @@ export default function ServerConfig() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Server Config</h2>
|
||||
<p>
|
||||
Display this data all pretty, most things will be editable in the
|
||||
future, not now.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid pink",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<InstanceDetails config={config} />
|
||||
<SocialHandles config={config} />
|
||||
<PageContent config={config} />
|
||||
|
||||
{JSON.stringify(config)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ export default function Logs() {
|
||||
<a href={release.html_url}>{release.name}</a>
|
||||
</Title>
|
||||
<Title level={5}>{new Date(release.created_at).toDateString()}</Title>
|
||||
<ReactMarkdown>{release.body}</ReactMarkdown>;<h3>Downloads</h3>
|
||||
<ReactMarkdown>{release.body}</ReactMarkdown><h3>Downloads</h3>
|
||||
<AssetTable {...release.assets} />
|
||||
</div>
|
||||
);
|
||||
@ -68,6 +68,6 @@ function AssetTable(assets) {
|
||||
},
|
||||
];
|
||||
|
||||
return <Table dataSource={data} columns={columns} rowKey="id" size="large" />;
|
||||
return <Table dataSource={data} columns={columns} rowKey="id" size="large" pagination={false} />
|
||||
}
|
||||
|
||||
|
@ -109,20 +109,7 @@ export default function VideoConfig() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Server Config</h2>
|
||||
<p>
|
||||
Display this data all pretty, most things will be editable in the
|
||||
future, not now.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid pink",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<VideoVariants config={config} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -109,22 +109,25 @@ export default function ViewersOverTime() {
|
||||
title="Current viewers"
|
||||
value={viewerCount.toString()}
|
||||
prefix={<UserOutlined />}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Peak viewers this session"
|
||||
value={sessionPeakViewerCount.toString()}
|
||||
prefix={<UserOutlined />}
|
||||
color="#334"
|
||||
/>
|
||||
<StatisticItem
|
||||
title="Peak viewers overall"
|
||||
value={overallPeakViewerCount.toString()}
|
||||
prefix={<UserOutlined />}
|
||||
color="#334"
|
||||
/>
|
||||
</Row>
|
||||
<div className="chart-container">
|
||||
<Chart data={viewerInfo} color="#ff84d8" unit="" />
|
||||
<Chart title="Viewers" data={viewerInfo} color="#2087E2" unit="" />
|
||||
</div>
|
||||
<Table dataSource={clients} columns={columns} />;
|
||||
<Table dataSource={clients} columns={columns} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -37,16 +37,19 @@ export const LOGS_WARN = `${API_LOCATION}logs/warnings`;
|
||||
const GITHUB_RELEASE_URL = "https://api.github.com/repos/owncast/owncast/releases/latest";
|
||||
|
||||
export async function fetchData(url) {
|
||||
const encoded = btoa(`${ADMIN_USERNAME}:${ADMIN_STREAMKEY}`);
|
||||
let options: RequestInit = {};
|
||||
|
||||
if (ADMIN_USERNAME && ADMIN_STREAMKEY) {
|
||||
const encoded = btoa(`${ADMIN_USERNAME}:${ADMIN_STREAMKEY}`);
|
||||
options.headers = {
|
||||
'Authorization': `Basic ${encoded}`
|
||||
}
|
||||
options.mode = 'cors';
|
||||
options.credentials = 'include'
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Authorization': `Basic ${encoded}`,
|
||||
},
|
||||
mode: 'cors',
|
||||
credentials: 'include',
|
||||
});
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
const message = `An error has occured: ${response.status}`;
|
||||
throw new Error(message);
|
||||
|
Loading…
x
Reference in New Issue
Block a user