// Dependencies import { DateTime } from 'luxon' import orderBy from 'lodash/orderBy.js' import { shortDate } from '@freesewing/utils' import { apikeyLevels } from '@freesewing/config' // Context import { ModalContext } from '@freesewing/react/context/Modal' import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus' // Hooks import React, { useState, useEffect, useContext } from 'react' import { useAccount } from '@freesewing/react/hooks/useAccount' import { useBackend } from '@freesewing/react/hooks/useBackend' import { useSelection } from '@freesewing/react/hooks/useSelection' // Components import { TableWrapper } from '@freesewing/react/components/Table' import { Link as WebLink } from '@freesewing/react/components/Link' import { PlusIcon, RightIcon, TrashIcon } from '@freesewing/react/components/Icon' import { Uuid } from '@freesewing/react/components/Uuid' import { Popout } from '@freesewing/react/components/Popout' import { ModalWrapper } from '@freesewing/react/components/Modal' import { NumberCircle } from '@freesewing/react/components/Number' import { StringInput, Fieldset, ListInput } from '@freesewing/react/components/Input' import { CopyToClipboardButton } from '@freesewing/react/components/Button' import { TimeAgo, TimeToGo } from '@freesewing/react/components/Time' import { KeyVal } from '@freesewing/react/components/KeyVal' const fields = { id: 'Key', name: 'Name', calls: 'Calls', level: 'Level', createdAt: 'Created', expiresAt: 'Expires', } /** * A component to mange the user's API keys * * @component * @param {object} props - All component props * @param {JSX.Element} [props.Link] - An optional framework-specific Link component * @returns {JSX.Element} */ export const Apikeys = ({ Link = false }) => { if (!Link) Link = WebLink // State const [apikeys, setApikeys] = useState([]) const [refresh, setRefresh] = useState(0) const [order, setOrder] = useState('id') const [desc, setDesc] = useState(false) // Hooks const backend = useBackend() const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext) const { count, selection, setSelection, toggle, toggleAll } = useSelection(apikeys) // Context const { setModal } = useContext(ModalContext) // Effects useEffect(() => { const getApikeys = async () => { setLoadingStatus([true, 'Loading API keys from backend']) const [status, body] = await backend.getApikeys() if (status === 200) { setApikeys(body.apikeys) setLoadingStatus([true, 'API keys loaded', true, true]) } else setLoadingStatus([false, 'Failed to load API keys from backend', true, true]) } getApikeys() }, [refresh]) // Helper to delete one or more patterns const removeSelectedApikeys = async () => { let i = 0 for (const key in selection) { i++ await backend.removeApikey(key) setLoadingStatus([ true, , ]) } setSelection({}) setRefresh(refresh + 1) setLoadingStatus([true, 'Nailed it', true, true]) } return ( <>
{Object.keys(fields).map((field) => ( ))} {orderBy(apikeys, order, desc ? 'desc' : 'asc').map((apikey, i) => ( {Object.keys(fields) .slice(1, 4) .map((field) => ( ))} ))}
toggle(apikey.id)} /> {shortDate(apikey.createdAt)} {shortDate(apikey.expiresAt)}
) } const ApiKey = ({ apikey }) => ( <>

API Key

) const NewApikey = ({ onCreate = false }) => { // State const [name, setName] = useState('') const [level, setLevel] = useState(1) const [expires, setExpires] = useState(Date.now()) const [apikey, setApikey] = useState(false) // Hooks const { account } = useAccount() const backend = useBackend() // Context const { setLoadingStatus } = useContext(LoadingStatusContext) const levels = account.role === 'admin' ? [0, 1, 2, 3, 4, 5, 6, 7, 8] : [0, 1, 2, 3, 4] const createKey = async () => { setLoadingStatus([true, 'Creating new API key']) const [status, body] = await backend.createApikey({ name, level, expiresIn: Math.floor((expires.valueOf() - Date.now().valueOf()) / 1000), }) if (status === 201 && body.result === 'created') { setLoadingStatus([true, 'API key created', true, true]) setApikey(body.apikey) if (typeof onCreate === 'function') onCreate(body.apikey) } else setLoadingStatus([true, 'An error occured. Please report this', true, false]) } return (

New API key {apikey ? `: ${apikey.name}` : ''}

{apikey ? ( ) : ( <> val.length > 0} placeholder={'Alicia Key'} /> ({ val: l, label: (
{apikeyLevels[l]}
), }))} current={level} update={setLevel} />
)}
) } const ShowNewApikey = ({ apikey }) => (
} color="secondary" /> } color="secondary" />
Key
{apikey.key}
Secret
{apikey.secret}
This is the only time you can see the key secret, make sure to copy it.
) const ExpiryPicker = ({ expires, setExpires }) => { const [days, setDays] = useState(1) // Run update when component mounts useEffect(() => update(days), []) const update = (evt) => { const value = typeof evt === 'number' ? evt : evt.target.value setExpires(DateTime.now().plus({ days: value })) setDays(value) } return (
} >
) }