// Dependencies import { useState, useEffect, useContext } from 'react' import { useTranslation } from 'next-i18next' import { DateTime } from 'luxon' import { CopyToClipboard } from 'react-copy-to-clipboard' import { shortDate, formatNumber } from 'shared/utils.mjs' // Context import { LoadingStatusContext } from 'shared/context/loading-status-context.mjs' // Hooks import { useAccount } from 'shared/hooks/use-account.mjs' import { useBackend } from 'shared/hooks/use-backend.mjs' import { useRouter } from 'next/router' // Components import { BackToAccountButton, DisplayRow, NumberBullet } from './shared.mjs' import { Popout } from 'shared/components/popout/index.mjs' import { LeftIcon, PlusIcon, CopyIcon, RightIcon, TrashIcon } from 'shared/components/icons.mjs' import { Link, linkClasses } from 'shared/components/link.mjs' import { StringInput, ListInput, FormControl } from 'shared/components/inputs.mjs' import { DynamicMdx } from 'shared/components/mdx/dynamic.mjs' export const ns = ['account', 'status'] const ExpiryPicker = ({ t, expires, setExpires }) => { const router = useRouter() const { locale } = router const [months, setMonths] = useState(1) // Run update when component mounts useEffect(() => update(months), []) const update = (evt) => { const value = typeof evt === 'number' ? evt : evt.target.value setExpires(DateTime.now().plus({ months: value })) setMonths(value) } return ( <>
{t('keyExpiresDesc')} {shortDate(locale, expires)} ) } const CopyInput = ({ text }) => { const { t } = useTranslation(['status']) const { setLoadingStatus } = useContext(LoadingStatusContext) const [copied, setCopied] = useState(false) const showCopied = () => { setCopied(true) setLoadingStatus([true, t('copiedToClipboard'), true, true]) window.setTimeout(() => setCopied(false), 2000) } return (
) } export const Apikey = ({ apikey, setId }) => { const { t } = useTranslation(ns) const router = useRouter() const { locale } = router return apikey ? (
{apikey.name} {shortDate(locale, apikey.createdAt)} {shortDate(locale, apikey.expiresAt)} {apikey.key}
) : null } const ShowKey = ({ apikey, t, clear }) => { const router = useRouter() const { locale } = router return (
{t('keySecretWarning')} {apikey.name} {shortDate(locale, apikey.createdAt)} {shortDate(locale, apikey.expiresAt)}
) } const NewKey = ({ account, setGenerate, backend }) => { const [name, setName] = useState('') const [level, setLevel] = useState(1) const [expires, setExpires] = useState(Date.now()) const [apikey, setApikey] = useState(false) const { setLoadingStatus } = useContext(LoadingStatusContext) const { t, i18n } = useTranslation(ns) const docs = {} for (const option of ['name', 'expiry', 'level']) { docs[option] = ( ) } const levels = account.role === 'admin' ? [0, 1, 2, 3, 4, 5, 6, 7, 8] : [0, 1, 2, 3, 4] const createKey = async () => { setLoadingStatus([true, 'processingUpdate']) const result = await backend.createApikey({ name, level, expiresIn: Math.floor((expires.valueOf() - Date.now().valueOf()) / 1000), }) if (result.success) { setLoadingStatus([true, 'nailedIt', true, true]) setApikey(result.data.apikey) } else setLoadingStatus([true, 'backendError', true, false]) } const clear = () => { setApikey(false) setGenerate(false) setName('') setLevel(1) } return (
{apikey ? ( ) : ( <> val.length > 0} placeholder={'Alicia Key'} /> ({ val: l, label: (
{t(`keyLevel${l}`)}
), }))} current={level} update={setLevel} />
)}
) } // Component for the 'new/apikey' page export const NewApikey = () => { // Hooks const { account } = useAccount() const backend = useBackend() // State const [generate, setGenerate] = useState(false) const [added, setAdded] = useState(0) // Helper method to force refresh const keyAdded = () => setAdded(added + 1) return (
) } // Component for the account/apikeys page export const Apikeys = ({ setId }) => { const router = useRouter() const { locale } = router // Hooks const { account } = useAccount() const backend = useBackend() const { t } = useTranslation(ns) const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext) // State const [keys, setKeys] = useState([]) const [selected, setSelected] = useState({}) const [refresh, setRefresh] = useState(0) // Helper var to see how many are selected const selCount = Object.keys(selected).length // Effects useEffect(() => { const getApikeys = async () => { const result = await backend.getApikeys() if (result.success) setKeys(result.data.apikeys) } getApikeys() }, [refresh]) // Helper method to toggle single selection const toggleSelect = (id) => { const newSelected = { ...selected } if (newSelected[id]) delete newSelected[id] else newSelected[id] = 1 setSelected(newSelected) } // Helper method to toggle select all const toggleSelectAll = () => { if (selCount === keys.length) setSelected({}) else { const newSelected = {} for (const key of keys) newSelected[key.id] = 1 setSelected(newSelected) } } // Helper to delete one or more apikeys const removeSelectedApikeys = async () => { let i = 0 for (const key in selected) { i++ await backend.removeApikey(key) setLoadingStatus([ true, , ]) } setSelected({}) setRefresh(refresh + 1) setLoadingStatus([true, 'nailedIt', true, true]) } return (

{t('newApikey')}

{keys.map((apikey, i) => ( ))}
{t('keyName')} {t('keyLevel')} 🔐 {t('keyExpires')} {t('apiCalls')}
toggleSelect(apikey.id)} /> {apikey.level} ({t(`keyLevel${apikey.level}`)}) {shortDate(locale, apikey.expiresAt, false)} {formatNumber(apikey.calls)}
{account.control < 5 ? (
{t('keyDocsTitle')}

{t('keyDocsMsg')}

FreeSewing.dev

) : null}
) }