2022-11-29 07:22:26 +03:00
|
|
|
import React, { useContext, useState } from 'react';
|
|
|
|
import { Table, Space, Button, Typography, Alert, Input, Form } from 'antd';
|
2023-01-16 09:31:36 +03:00
|
|
|
import dynamic from 'next/dynamic';
|
2023-01-10 07:57:29 +03:00
|
|
|
import { ServerStatusContext } from '../../../../utils/server-status-context';
|
2022-11-29 07:22:26 +03:00
|
|
|
|
2023-01-10 07:57:29 +03:00
|
|
|
import { fetchData, UPDATE_STREAM_KEYS } from '../../../../utils/apis';
|
2022-11-29 07:22:26 +03:00
|
|
|
|
2022-12-28 05:48:21 +03:00
|
|
|
const { Paragraph } = Typography;
|
2022-11-29 07:22:26 +03:00
|
|
|
const { Item } = Form;
|
|
|
|
|
2023-01-16 09:31:36 +03:00
|
|
|
// Lazy loaded components
|
|
|
|
|
|
|
|
const DeleteOutlined = dynamic(() => import('@ant-design/icons/DeleteOutlined'), {
|
|
|
|
ssr: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
const EyeOutlined = dynamic(() => import('@ant-design/icons/EyeOutlined'), {
|
|
|
|
ssr: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
const PlusOutlined = dynamic(() => import('@ant-design/icons/PlusOutlined'), {
|
|
|
|
ssr: false,
|
|
|
|
});
|
|
|
|
|
2022-11-29 07:22:26 +03:00
|
|
|
const saveKeys = async (keys, setError) => {
|
|
|
|
try {
|
|
|
|
await fetchData(UPDATE_STREAM_KEYS, {
|
|
|
|
method: 'POST',
|
|
|
|
auth: true,
|
|
|
|
data: { value: keys },
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
setError(error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-08 04:17:04 +03:00
|
|
|
const generateRndKey = () => {
|
|
|
|
let defaultKey = '';
|
|
|
|
let isValidStreamKey = false;
|
|
|
|
const streamKeyRegex = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#$%^&*]).{8,192}$/;
|
|
|
|
const s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
|
|
|
|
|
|
|
|
while (!isValidStreamKey) {
|
|
|
|
const temp = Array.apply(20, Array(30))
|
|
|
|
.map(() => s.charAt(Math.floor(Math.random() * s.length)))
|
|
|
|
.join('');
|
|
|
|
if (streamKeyRegex.test(temp)) {
|
|
|
|
isValidStreamKey = true;
|
|
|
|
defaultKey = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return defaultKey;
|
|
|
|
};
|
|
|
|
|
2022-11-29 07:22:26 +03:00
|
|
|
const AddKeyForm = ({ setShowAddKeyForm, setFieldInConfigState, streamKeys, setError }) => {
|
|
|
|
const handleAddKey = (newkey: any) => {
|
|
|
|
const updatedKeys = [...streamKeys, newkey];
|
|
|
|
|
|
|
|
setFieldInConfigState({
|
|
|
|
fieldName: 'streamKeys',
|
|
|
|
value: updatedKeys,
|
|
|
|
});
|
|
|
|
|
|
|
|
saveKeys(updatedKeys, setError);
|
|
|
|
|
|
|
|
setShowAddKeyForm(false);
|
|
|
|
};
|
|
|
|
|
2023-02-06 07:08:14 +03:00
|
|
|
// Default auto-generated key
|
2023-02-08 04:17:04 +03:00
|
|
|
const defaultKey = generateRndKey();
|
2023-02-06 07:08:14 +03:00
|
|
|
|
2022-11-29 07:22:26 +03:00
|
|
|
return (
|
|
|
|
<Form layout="inline" autoComplete="off" onFinish={handleAddKey}>
|
|
|
|
<Item label="Key" name="key" tooltip="The key you provide your broadcasting software">
|
2023-02-06 07:08:14 +03:00
|
|
|
<Input placeholder="def456" defaultValue={defaultKey} />
|
2022-11-29 07:22:26 +03:00
|
|
|
</Item>
|
|
|
|
<Item label="Comment" name="comment" tooltip="For remembering why you added this key">
|
|
|
|
<Input placeholder="My OBS Key" />
|
|
|
|
</Item>
|
|
|
|
|
|
|
|
<Button type="primary" htmlType="submit">
|
|
|
|
Add
|
|
|
|
</Button>
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const AddKeyButton = ({ setShowAddKeyForm }) => (
|
|
|
|
<Button type="default" onClick={() => setShowAddKeyForm(true)}>
|
|
|
|
<PlusOutlined />
|
|
|
|
</Button>
|
|
|
|
);
|
|
|
|
|
|
|
|
const StreamKeys = () => {
|
|
|
|
const serverStatusData = useContext(ServerStatusContext);
|
|
|
|
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
|
|
|
const { streamKeys } = serverConfig;
|
|
|
|
const [showAddKeyForm, setShowAddKeyForm] = useState(false);
|
|
|
|
const [showKeyMap, setShowKeyMap] = useState({});
|
|
|
|
const [error, setError] = useState(null);
|
|
|
|
|
|
|
|
const handleDeleteKey = keyToRemove => {
|
|
|
|
const newKeys = streamKeys.filter(k => k !== keyToRemove);
|
|
|
|
setFieldInConfigState({
|
|
|
|
fieldName: 'streamKeys',
|
|
|
|
value: newKeys,
|
|
|
|
});
|
|
|
|
saveKeys(newKeys, setError);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleToggleShowKey = key => {
|
|
|
|
setShowKeyMap({
|
|
|
|
...showKeyMap,
|
|
|
|
[key]: !showKeyMap[key],
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const columns = [
|
|
|
|
{
|
|
|
|
title: 'Key',
|
|
|
|
dataIndex: 'key',
|
|
|
|
key: 'key',
|
|
|
|
render: text => (
|
|
|
|
<Space direction="horizontal">
|
|
|
|
<Paragraph copyable>{showKeyMap[text] ? text : '**********'}</Paragraph>
|
|
|
|
|
|
|
|
<Button
|
|
|
|
type="link"
|
|
|
|
style={{ top: '-7px' }}
|
|
|
|
icon={<EyeOutlined />}
|
|
|
|
onClick={() => handleToggleShowKey(text)}
|
|
|
|
/>
|
|
|
|
</Space>
|
|
|
|
),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
title: 'Comment',
|
|
|
|
dataIndex: 'comment',
|
|
|
|
key: 'comment',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
title: '',
|
|
|
|
key: 'delete',
|
|
|
|
render: text => <Button onClick={() => handleDeleteKey(text)} icon={<DeleteOutlined />} />,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<Paragraph>
|
|
|
|
A streaming key is used with your broadcasting software to authenticate itself to Owncast.
|
|
|
|
Most people will only need one. However, if you share a server with others or you want
|
|
|
|
different keys for different broadcasting sources you can add more here.
|
|
|
|
</Paragraph>
|
|
|
|
<Paragraph>
|
|
|
|
These keys are unrelated to the admin password and will not grant you access to make changes
|
|
|
|
to Owncast's configuration.
|
|
|
|
</Paragraph>
|
|
|
|
<Paragraph>
|
|
|
|
Read more about broadcasting at{' '}
|
|
|
|
<a
|
|
|
|
href="https://owncast.online/docs/broadcasting/?source=admin"
|
|
|
|
target="_blank"
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
>
|
|
|
|
the documentation
|
|
|
|
</a>
|
|
|
|
.
|
|
|
|
</Paragraph>
|
|
|
|
|
|
|
|
<Space direction="vertical" style={{ width: '70%' }}>
|
|
|
|
{error && <Alert type="error" message="Saving Keys Error" description={error} />}
|
|
|
|
|
|
|
|
{streamKeys.length === 0 && (
|
|
|
|
<Alert
|
|
|
|
message="No stream keys!"
|
|
|
|
description="You will not be able to stream until you create at least one stream key and add it to your broadcasting software."
|
|
|
|
type="error"
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
|
|
|
<Table
|
|
|
|
rowKey="key"
|
|
|
|
columns={columns}
|
|
|
|
dataSource={streamKeys}
|
|
|
|
pagination={false}
|
|
|
|
// eslint-disable-next-line react/no-unstable-nested-components
|
|
|
|
footer={() =>
|
|
|
|
showAddKeyForm ? (
|
|
|
|
<AddKeyForm
|
|
|
|
setShowAddKeyForm={setShowAddKeyForm}
|
|
|
|
streamKeys={streamKeys}
|
|
|
|
setFieldInConfigState={setFieldInConfigState}
|
|
|
|
setError={setError}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<AddKeyButton setShowAddKeyForm={setShowAddKeyForm} />
|
|
|
|
)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
<br />
|
|
|
|
</Space>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
export default StreamKeys;
|