@ -1,21 +1,28 @@
import React , { useState , useEffect } from "react" ;
import React , { useState , useEffect } from 'react' ;
import { Table , Tag , Space , Button , Modal , Checkbox , Input , Typography , Tooltip } from 'antd' ;
import { Table , Tag , Space , Button , Modal , Checkbox , Input , Typography , Tooltip } from 'antd' ;
import { DeleteOutlined , EyeTwoTone , EyeInvisibleOutlined } from '@ant-design/icons' ;
import { DeleteOutlined , EyeTwoTone , EyeInvisibleOutlined } from '@ant-design/icons' ;
const { Title , Paragraph , Text } = Typography ;
const { Title , Paragraph , Text } = Typography ;
import format from 'date-fns/format'
import format from 'date-fns/format' ;
import {
import { fetchData , ACCESS_TOKENS , DELETE_ACCESS_TOKEN , CREATE_ACCESS_TOKEN } from '../utils/apis' ;
fetchData ,
ACCESS_TOKENS ,
DELETE_ACCESS_TOKEN ,
CREATE_ACCESS_TOKEN ,
} from "../utils/apis" ;
const availableScopes = {
const availableScopes = {
'CAN_SEND_SYSTEM_MESSAGES' : { name : 'System messages' , description : 'You can send official messages on behalf of the system' , color : 'purple' } ,
CAN_SEND_SYSTEM_MESSAGES : {
'CAN_SEND_MESSAGES' : { name : 'User chat messages' , description : 'You can send messages on behalf of a username' , color : 'green' } ,
name : 'System messages' ,
'HAS_ADMIN_ACCESS' : { name : 'Has admin access' , description : 'Can perform administrative actions such as moderation, get server statuses, etc' , color : 'red' } ,
description : 'You can send official messages on behalf of the system' ,
color : 'purple' ,
} ,
CAN_SEND_MESSAGES : {
name : 'User chat messages' ,
description : 'You can send messages on behalf of a username' ,
color : 'green' ,
} ,
HAS_ADMIN_ACCESS : {
name : 'Has admin access' ,
description : 'Can perform administrative actions such as moderation, get server statuses, etc' ,
color : 'red' ,
} ,
} ;
} ;
function convertScopeStringToTag ( scopeString ) {
function convertScopeStringToTag ( scopeString ) {
@ -27,9 +34,7 @@ function convertScopeStringToTag(scopeString) {
return (
return (
< Tooltip key = { scopeString } title = { scope . description } >
< Tooltip key = { scopeString } title = { scope . description } >
< Tag color = { scope . color } >
< Tag color = { scope . color } > { scope . name } < / Tag >
{ scope . name }
< / Tag >
< / Tooltip >
< / Tooltip >
) ;
) ;
}
}
@ -39,7 +44,7 @@ function NewTokenModal(props) {
const [ name , setName ] = useState ( '' ) ;
const [ name , setName ] = useState ( '' ) ;
const scopes = Object . keys ( availableScopes ) . map ( function ( key ) {
const scopes = Object . keys ( availableScopes ) . map ( function ( key ) {
return { value : key , label : availableScopes [ key ] . description }
return { value : key , label : availableScopes [ key ] . description } ;
} ) ;
} ) ;
function onChange ( checkedValues ) {
function onChange ( checkedValues ) {
@ -55,7 +60,7 @@ function NewTokenModal(props) {
}
}
const okButtonProps = {
const okButtonProps = {
disabled : selectedScopes.length === 0 || name === ''
disabled : selectedScopes.length === 0 || name === '' ,
} ;
} ;
function selectAll() {
function selectAll() {
@ -63,16 +68,30 @@ function NewTokenModal(props) {
}
}
return (
return (
< Modal title = "Create New Access token" visible = { props . visible } onOk = { saveToken } onCancel = { props . onCancel } okButtonProps = { okButtonProps } >
< Modal
< p > < Input value = { name } placeholder = "Access token name/description" onChange = { ( input ) = > setName ( input . currentTarget . value ) } / > < / p >
title = "Create New Access token"
visible = { props . visible }
onOk = { saveToken }
onCancel = { props . onCancel }
okButtonProps = { okButtonProps }
>
< p >
< Input
value = { name }
placeholder = "Access token name/description"
onChange = { input = > setName ( input . currentTarget . value ) }
/ >
< / p >
< p >
< p >
Select the permissions this access token will have . It cannot be edited after it ' s created .
Select the permissions this access token will have . It cannot be edited after it ' s created .
< / p >
< / p >
< Checkbox.Group options = { scopes } value = { selectedScopes } onChange = { onChange } / >
< Checkbox.Group options = { scopes } value = { selectedScopes } onChange = { onChange } / >
< Button onClick = { selectAll } > Select all < / Button >
< Button type = "text" size = "small" onClick = { selectAll } >
Select all
< / Button >
< / Modal >
< / Modal >
)
) ;
}
}
export default function AccessTokens() {
export default function AccessTokens() {
@ -87,7 +106,7 @@ export default function AccessTokens() {
< Space size = "middle" >
< Space size = "middle" >
< Button onClick = { ( ) = > handleDeleteToken ( record . token ) } icon = { < DeleteOutlined / > } / >
< Button onClick = { ( ) = > handleDeleteToken ( record . token ) } icon = { < DeleteOutlined / > } / >
< / Space >
< / Space >
)
) ,
} ,
} ,
{
{
title : 'Name' ,
title : 'Name' ,
@ -98,9 +117,7 @@ export default function AccessTokens() {
title : 'Token' ,
title : 'Token' ,
dataIndex : 'token' ,
dataIndex : 'token' ,
key : 'token' ,
key : 'token' ,
render : ( text , record ) = > (
render : ( text , record ) = > < Input.Password size = "small" bordered = { false } value = { text } / > ,
< Input.Password size = "small" bordered = { false } value = { text } / >
)
} ,
} ,
{
{
title : 'Scopes' ,
title : 'Scopes' ,
@ -118,7 +135,7 @@ export default function AccessTokens() {
title : 'Last Used' ,
title : 'Last Used' ,
dataIndex : 'lastUsed' ,
dataIndex : 'lastUsed' ,
key : 'lastUsed' ,
key : 'lastUsed' ,
render : ( lastUsed ) = > {
render : lastUsed = > {
if ( ! lastUsed ) {
if ( ! lastUsed ) {
return 'Never' ;
return 'Never' ;
}
}
@ -143,7 +160,10 @@ export default function AccessTokens() {
async function handleDeleteToken ( token ) {
async function handleDeleteToken ( token ) {
try {
try {
const result = await fetchData ( DELETE_ACCESS_TOKEN , { method : 'POST' , data : { token : token } } ) ;
const result = await fetchData ( DELETE_ACCESS_TOKEN , {
method : 'POST' ,
data : { token : token } ,
} ) ;
getAccessTokens ( ) ;
getAccessTokens ( ) ;
} catch ( error ) {
} catch ( error ) {
handleError ( error ) ;
handleError ( error ) ;
@ -152,7 +172,10 @@ export default function AccessTokens() {
async function handleSaveToken ( name : string , scopes : string [ ] ) {
async function handleSaveToken ( name : string , scopes : string [ ] ) {
try {
try {
const newToken = await fetchData ( CREATE_ACCESS_TOKEN , { method : 'POST' , data : { name : name , scopes : scopes } } ) ;
const newToken = await fetchData ( CREATE_ACCESS_TOKEN , {
method : 'POST' ,
data : { name : name , scopes : scopes } ,
} ) ;
setTokens ( tokens . concat ( newToken ) ) ;
setTokens ( tokens . concat ( newToken ) ) ;
} catch ( error ) {
} catch ( error ) {
handleError ( error ) ;
handleError ( error ) ;
@ -160,7 +183,7 @@ export default function AccessTokens() {
}
}
function handleError ( error ) {
function handleError ( error ) {
console . error ( "error" , error ) ;
console . error ( 'error' , error ) ;
alert ( error ) ;
alert ( error ) ;
}
}
@ -181,16 +204,25 @@ export default function AccessTokens() {
< div >
< div >
< Title > Access Tokens < / Title >
< Title > Access Tokens < / Title >
< Paragraph >
< Paragraph >
Access tokens are used to allow external , 3 rd party tools to perform specific actions on your Owncast server .
Access tokens are used to allow external , 3 rd party tools to perform specific actions on
They should be kept secure and never included in client code , instead they should be kept on a server that you control .
your Owncast server . They should be kept secure and never included in client code , instead
they should be kept on a server that you control .
< / Paragraph >
< / Paragraph >
< Paragraph >
< Paragraph >
Read more about how to use these tokens , with examples , at < a href = "https://owncast.online/docs/integrations/" > our documentation < / a > .
Read more about how to use these tokens , with examples , at { ' ' }
< a href = "https://owncast.online/docs/integrations/" > our documentation < / a > .
< / Paragraph >
< / Paragraph >
< Table rowKey = "token" columns = { columns } dataSource = { tokens } pagination = { false } / >
< Table rowKey = "token" columns = { columns } dataSource = { tokens } pagination = { false } / >
< Button onClick = { showCreateTokenModal } > Create Access Token < / Button >
< br / >
< NewTokenModal visible = { isTokenModalVisible } onOk = { handleTokenModalSaveButton } onCancel = { handleTokenModalCancel } / >
< Button type = "primary" onClick = { showCreateTokenModal } >
Create Access Token
< / Button >
< NewTokenModal
visible = { isTokenModalVisible }
onOk = { handleTokenModalSaveButton }
onCancel = { handleTokenModalCancel }
/ >
< / div >
< / div >
) ;
) ;
}
}