2024-10-03 02:47:29 +05:30
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
Box,
|
|
|
|
|
Button,
|
|
|
|
|
Typography,
|
|
|
|
|
IconButton,
|
2024-10-03 04:52:29 +05:30
|
|
|
CircularProgress,
|
2024-10-03 05:06:11 +05:30
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableContainer,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableRow,
|
|
|
|
|
Tooltip,
|
2024-10-03 05:09:23 +05:30
|
|
|
Paper,
|
2026-02-03 11:19:46 +05:30
|
|
|
Dialog,
|
|
|
|
|
DialogTitle,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogContentText,
|
|
|
|
|
DialogActions,
|
2024-10-03 02:47:29 +05:30
|
|
|
} from '@mui/material';
|
2025-02-08 15:44:36 +05:30
|
|
|
import { ContentCopy, Visibility, VisibilityOff, Delete } from '@mui/icons-material';
|
2024-10-03 02:47:29 +05:30
|
|
|
import styled from 'styled-components';
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
import { useGlobalInfoStore } from '../../context/globalInfo';
|
2024-11-01 08:25:33 +05:30
|
|
|
import { apiUrl } from '../../apiConfig';
|
2024-12-20 21:13:44 +05:30
|
|
|
import { useTranslation } from 'react-i18next';
|
2024-10-03 02:47:29 +05:30
|
|
|
|
|
|
|
|
const Container = styled(Box)`
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-top: 50px;
|
2025-11-05 22:19:28 +05:30
|
|
|
margin-left: 70px;
|
|
|
|
|
margin-right: 70px;
|
2024-10-03 02:47:29 +05:30
|
|
|
`;
|
|
|
|
|
|
2024-10-03 03:29:38 +05:30
|
|
|
const ApiKeyManager = () => {
|
2024-12-20 21:13:44 +05:30
|
|
|
const { t } = useTranslation();
|
2024-10-03 02:47:29 +05:30
|
|
|
const [apiKey, setApiKey] = useState<string | null>(null);
|
2024-12-20 21:13:44 +05:30
|
|
|
const [apiKeyName, setApiKeyName] = useState<string>(t('apikey.default_name'));
|
2025-12-30 00:34:10 +05:30
|
|
|
const [apiKeyCreatedAt, setApiKeyCreatedAt] = useState<string | null>(null);
|
2024-10-03 02:47:29 +05:30
|
|
|
const [loading, setLoading] = useState<boolean>(true);
|
2024-10-03 05:06:11 +05:30
|
|
|
const [showKey, setShowKey] = useState<boolean>(false);
|
2024-10-03 02:47:29 +05:30
|
|
|
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
2026-02-03 11:19:46 +05:30
|
|
|
|
|
|
|
|
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState<boolean>(false);
|
|
|
|
|
|
2024-10-03 02:51:08 +05:30
|
|
|
const { notify } = useGlobalInfoStore();
|
2024-10-03 02:47:29 +05:30
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const fetchApiKey = async () => {
|
|
|
|
|
try {
|
2024-11-01 08:25:33 +05:30
|
|
|
const { data } = await axios.get(`${apiUrl}/auth/api-key`);
|
2024-10-03 02:47:29 +05:30
|
|
|
setApiKey(data.api_key);
|
2025-12-30 00:34:10 +05:30
|
|
|
setApiKeyCreatedAt(data.api_key_created_at);
|
2024-10-03 05:27:15 +05:30
|
|
|
} catch (error: any) {
|
2025-02-24 21:45:16 +05:30
|
|
|
notify('error', t('apikey.notifications.fetch_error', { error: error.message }));
|
2024-10-03 02:47:29 +05:30
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fetchApiKey();
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const generateApiKey = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
2024-11-01 08:25:33 +05:30
|
|
|
const { data } = await axios.post(`${apiUrl}/auth/generate-api-key`);
|
2025-02-24 21:45:16 +05:30
|
|
|
setApiKey(data.api_key);
|
2025-12-30 00:34:10 +05:30
|
|
|
setApiKeyCreatedAt(data.api_key_created_at);
|
2025-02-24 22:00:07 +05:30
|
|
|
notify('success', t('apikey.notifications.generate_success'));
|
2024-10-03 05:27:15 +05:30
|
|
|
} catch (error: any) {
|
2025-02-24 21:45:16 +05:30
|
|
|
notify('error', t('apikey.notifications.generate_error', { error: error.message }));
|
2024-10-03 02:47:29 +05:30
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-03 05:06:11 +05:30
|
|
|
const deleteApiKey = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
2025-02-24 21:45:16 +05:30
|
|
|
await axios.delete(`${apiUrl}/auth/delete-api-key`);
|
|
|
|
|
setApiKey(null);
|
2025-12-30 00:34:10 +05:30
|
|
|
setApiKeyCreatedAt(null);
|
2025-02-24 21:45:16 +05:30
|
|
|
notify('success', t('apikey.notifications.delete_success'));
|
2024-10-03 05:27:15 +05:30
|
|
|
} catch (error: any) {
|
2025-02-24 21:45:16 +05:30
|
|
|
notify('error', t('apikey.notifications.delete_error', { error: error.message }));
|
2024-10-03 05:06:11 +05:30
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
2026-02-03 11:19:46 +05:30
|
|
|
setConfirmDeleteOpen(false);
|
2024-10-03 05:06:11 +05:30
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-24 21:45:16 +05:30
|
|
|
const copyToClipboard = () => {
|
|
|
|
|
if (apiKey) {
|
|
|
|
|
navigator.clipboard.writeText(apiKey);
|
2024-10-03 02:47:29 +05:30
|
|
|
setCopySuccess(true);
|
|
|
|
|
setTimeout(() => setCopySuccess(false), 2000);
|
2025-02-24 21:45:16 +05:30
|
|
|
notify('info', t('apikey.notifications.copy_success'));
|
2024-10-03 02:47:29 +05:30
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-03 11:19:46 +05:30
|
|
|
const handleDeleteClick = () => {
|
|
|
|
|
setConfirmDeleteOpen(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDeleteCancel = () => {
|
|
|
|
|
setConfirmDeleteOpen(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDeleteConfirm = () => {
|
|
|
|
|
deleteApiKey();
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-29 22:32:30 +05:30
|
|
|
if (loading) {
|
|
|
|
|
return (
|
|
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
height: '100vh',
|
2024-11-29 22:33:02 +05:30
|
|
|
width: '100vw',
|
2024-11-29 22:32:30 +05:30
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<CircularProgress />
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-10-03 02:47:29 +05:30
|
|
|
|
2024-10-03 01:56:52 +05:30
|
|
|
return (
|
2025-11-05 20:54:48 +05:30
|
|
|
<Container sx={{ alignSelf: 'flex-start' }}>
|
|
|
|
|
<Typography variant="body1" sx={{ marginBottom: '40px' }}>
|
|
|
|
|
Start by creating an API key below. Then,
|
2026-02-03 11:19:46 +05:30
|
|
|
<a
|
|
|
|
|
href={`${apiUrl}/api-docs/`}
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
style={{ textDecoration: 'none', marginLeft: '5px', marginRight: '5px' }}
|
|
|
|
|
>
|
2025-11-05 20:54:48 +05:30
|
|
|
test your API
|
|
|
|
|
</a>
|
2026-02-03 11:19:46 +05:30
|
|
|
or read the{' '}
|
|
|
|
|
<a
|
|
|
|
|
href="https://docs.maxun.dev/category/api-docs"
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
style={{ textDecoration: 'none' }}
|
|
|
|
|
>
|
2025-11-05 20:54:48 +05:30
|
|
|
API documentation
|
2026-02-03 11:19:46 +05:30
|
|
|
</a>{' '}
|
|
|
|
|
for setup instructions.
|
2025-11-05 20:54:48 +05:30
|
|
|
</Typography>
|
2026-02-03 11:19:46 +05:30
|
|
|
|
2025-11-05 20:54:48 +05:30
|
|
|
<Typography
|
|
|
|
|
variant="h6"
|
|
|
|
|
gutterBottom
|
|
|
|
|
component="div"
|
|
|
|
|
style={{ marginBottom: '20px', textAlign: 'left', width: '100%' }}
|
|
|
|
|
>
|
|
|
|
|
{t('apikey.title')}
|
|
|
|
|
</Typography>
|
2026-02-03 11:19:46 +05:30
|
|
|
|
2025-11-05 20:54:48 +05:30
|
|
|
{apiKey ? (
|
|
|
|
|
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}>
|
2025-12-30 00:34:58 +05:30
|
|
|
<Table sx={{ tableLayout: 'fixed', width: '100%' }}>
|
2025-11-05 20:54:48 +05:30
|
|
|
<TableHead>
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableCell>{t('apikey.table.name')}</TableCell>
|
|
|
|
|
<TableCell>{t('apikey.table.key')}</TableCell>
|
2025-12-30 00:37:37 +05:30
|
|
|
{apiKeyCreatedAt && <TableCell>Created On</TableCell>}
|
2026-02-03 11:19:46 +05:30
|
|
|
<TableCell align="center" sx={{ width: 160 }}>
|
|
|
|
|
{t('apikey.table.actions')}
|
|
|
|
|
</TableCell>
|
2025-11-05 20:54:48 +05:30
|
|
|
</TableRow>
|
|
|
|
|
</TableHead>
|
|
|
|
|
<TableBody>
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableCell>{apiKeyName}</TableCell>
|
|
|
|
|
<TableCell>
|
2025-11-05 22:18:49 +05:30
|
|
|
<Box sx={{ fontFamily: 'monospace', width: '20ch' }}>
|
|
|
|
|
{showKey ? `${apiKey?.substring(0, 10)}...` : '**********'}
|
2025-11-05 20:54:48 +05:30
|
|
|
</Box>
|
|
|
|
|
</TableCell>
|
2025-12-30 00:34:10 +05:30
|
|
|
{apiKeyCreatedAt && (
|
|
|
|
|
<TableCell>
|
|
|
|
|
{new Date(apiKeyCreatedAt).toLocaleDateString('en-US', {
|
|
|
|
|
month: 'short',
|
|
|
|
|
day: 'numeric',
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
})}
|
|
|
|
|
</TableCell>
|
|
|
|
|
)}
|
2025-12-30 00:34:36 +05:30
|
|
|
<TableCell align="right" sx={{ width: 160 }}>
|
2025-11-05 20:54:48 +05:30
|
|
|
<Tooltip title={t('apikey.actions.copy')}>
|
|
|
|
|
<IconButton onClick={copyToClipboard}>
|
|
|
|
|
<ContentCopy />
|
|
|
|
|
</IconButton>
|
|
|
|
|
</Tooltip>
|
2026-02-03 11:19:46 +05:30
|
|
|
|
2025-11-05 20:54:48 +05:30
|
|
|
<Tooltip title={showKey ? t('apikey.actions.hide') : t('apikey.actions.show')}>
|
|
|
|
|
<IconButton onClick={() => setShowKey(!showKey)}>
|
|
|
|
|
{showKey ? <VisibilityOff /> : <Visibility />}
|
|
|
|
|
</IconButton>
|
|
|
|
|
</Tooltip>
|
2026-02-03 11:19:46 +05:30
|
|
|
|
2025-11-05 20:54:48 +05:30
|
|
|
<Tooltip title={t('apikey.actions.delete')}>
|
2026-02-03 11:19:46 +05:30
|
|
|
<IconButton onClick={handleDeleteClick} color="error">
|
2025-11-05 20:54:48 +05:30
|
|
|
<Delete />
|
|
|
|
|
</IconButton>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
</TableContainer>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<Typography>{t('apikey.no_key_message')}</Typography>
|
2026-02-03 11:19:46 +05:30
|
|
|
<Button
|
|
|
|
|
onClick={generateApiKey}
|
|
|
|
|
variant="contained"
|
|
|
|
|
color="primary"
|
|
|
|
|
sx={{ marginTop: '20px' }}
|
|
|
|
|
>
|
2025-11-05 20:54:48 +05:30
|
|
|
{t('apikey.generate_button')}
|
|
|
|
|
</Button>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2026-02-03 11:19:46 +05:30
|
|
|
|
|
|
|
|
<Dialog open={confirmDeleteOpen} onClose={handleDeleteCancel}>
|
|
|
|
|
<DialogTitle>Delete API Key</DialogTitle>
|
|
|
|
|
<DialogContent>
|
|
|
|
|
<DialogContentText>
|
|
|
|
|
Are you sure you want to delete this API key? This action cannot be undone and
|
|
|
|
|
will immediately invalidate the key.
|
|
|
|
|
</DialogContentText>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
<DialogActions>
|
2026-02-03 11:22:50 +05:30
|
|
|
<Button color='inherit' onClick={handleDeleteCancel}>
|
2026-02-03 11:19:46 +05:30
|
|
|
Cancel
|
|
|
|
|
</Button>
|
|
|
|
|
<Button onClick={handleDeleteConfirm} color="error" variant="contained">
|
|
|
|
|
Delete
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogActions>
|
|
|
|
|
</Dialog>
|
2025-11-05 20:54:48 +05:30
|
|
|
</Container>
|
|
|
|
|
);
|
2026-02-03 11:19:46 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default ApiKeyManager;
|