Merge pull request #935 from getmaxun/api-key-create
feat: display api key creation date
This commit is contained in:
@@ -7,6 +7,7 @@ interface UserAttributes {
|
|||||||
password: string;
|
password: string;
|
||||||
api_key_name?: string | null;
|
api_key_name?: string | null;
|
||||||
api_key?: string | null;
|
api_key?: string | null;
|
||||||
|
api_key_created_at?: Date | null;
|
||||||
proxy_url?: string | null;
|
proxy_url?: string | null;
|
||||||
proxy_username?: string | null;
|
proxy_username?: string | null;
|
||||||
proxy_password?: string | null;
|
proxy_password?: string | null;
|
||||||
@@ -20,6 +21,7 @@ class User extends Model<UserAttributes, UserCreationAttributes> implements User
|
|||||||
public password!: string;
|
public password!: string;
|
||||||
public api_key_name!: string | null;
|
public api_key_name!: string | null;
|
||||||
public api_key!: string | null;
|
public api_key!: string | null;
|
||||||
|
public api_key_created_at!: Date | null;
|
||||||
public proxy_url!: string | null;
|
public proxy_url!: string | null;
|
||||||
public proxy_username!: string | null;
|
public proxy_username!: string | null;
|
||||||
public proxy_password!: string | null;
|
public proxy_password!: string | null;
|
||||||
@@ -53,6 +55,10 @@ User.init(
|
|||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
api_key_created_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
proxy_url: {
|
proxy_url: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
|||||||
@@ -255,8 +255,9 @@ router.post(
|
|||||||
return res.status(400).json({ message: "API key already exists" });
|
return res.status(400).json({ message: "API key already exists" });
|
||||||
}
|
}
|
||||||
const apiKey = genAPIKey();
|
const apiKey = genAPIKey();
|
||||||
|
const createdAt = new Date();
|
||||||
|
|
||||||
await user.update({ api_key: apiKey });
|
await user.update({ api_key: apiKey, api_key_created_at: createdAt })
|
||||||
|
|
||||||
capture("maxun-oss-api-key-created", {
|
capture("maxun-oss-api-key-created", {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
@@ -266,6 +267,7 @@ router.post(
|
|||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: "API key generated successfully",
|
message: "API key generated successfully",
|
||||||
api_key: apiKey,
|
api_key: apiKey,
|
||||||
|
api_key_created_at: createdAt,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res
|
return res
|
||||||
@@ -290,7 +292,7 @@ router.get(
|
|||||||
|
|
||||||
const user = await User.findByPk(req.user.id, {
|
const user = await User.findByPk(req.user.id, {
|
||||||
raw: true,
|
raw: true,
|
||||||
attributes: ["api_key"],
|
attributes: ["api_key", "api_key_created_at"]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -305,6 +307,7 @@ router.get(
|
|||||||
ok: true,
|
ok: true,
|
||||||
message: "API key fetched successfully",
|
message: "API key fetched successfully",
|
||||||
api_key: user.api_key || null,
|
api_key: user.api_key || null,
|
||||||
|
api_key_created_at: user.api_key_created_at || null,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('API Key fetch error:', error);
|
console.error('API Key fetch error:', error);
|
||||||
@@ -336,7 +339,7 @@ router.delete(
|
|||||||
return res.status(404).json({ message: "API Key not found" });
|
return res.status(404).json({ message: "API Key not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
await User.update({ api_key: null }, { where: { id: req.user.id } });
|
await User.update({ api_key: null, api_key_created_at: null }, { where: { id: req.user.id } });
|
||||||
|
|
||||||
capture("maxun-oss-api-key-deleted", {
|
capture("maxun-oss-api-key-deleted", {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const ApiKeyManager = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [apiKey, setApiKey] = useState<string | null>(null);
|
const [apiKey, setApiKey] = useState<string | null>(null);
|
||||||
const [apiKeyName, setApiKeyName] = useState<string>(t('apikey.default_name'));
|
const [apiKeyName, setApiKeyName] = useState<string>(t('apikey.default_name'));
|
||||||
|
const [apiKeyCreatedAt, setApiKeyCreatedAt] = useState<string | null>(null);
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [showKey, setShowKey] = useState<boolean>(false);
|
const [showKey, setShowKey] = useState<boolean>(false);
|
||||||
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
||||||
@@ -44,6 +45,7 @@ const ApiKeyManager = () => {
|
|||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`${apiUrl}/auth/api-key`);
|
const { data } = await axios.get(`${apiUrl}/auth/api-key`);
|
||||||
setApiKey(data.api_key);
|
setApiKey(data.api_key);
|
||||||
|
setApiKeyCreatedAt(data.api_key_created_at);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
notify('error', t('apikey.notifications.fetch_error', { error: error.message }));
|
notify('error', t('apikey.notifications.fetch_error', { error: error.message }));
|
||||||
} finally {
|
} finally {
|
||||||
@@ -60,7 +62,7 @@ const ApiKeyManager = () => {
|
|||||||
try {
|
try {
|
||||||
const { data } = await axios.post(`${apiUrl}/auth/generate-api-key`);
|
const { data } = await axios.post(`${apiUrl}/auth/generate-api-key`);
|
||||||
setApiKey(data.api_key);
|
setApiKey(data.api_key);
|
||||||
|
setApiKeyCreatedAt(data.api_key_created_at);
|
||||||
notify('success', t('apikey.notifications.generate_success'));
|
notify('success', t('apikey.notifications.generate_success'));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
notify('error', t('apikey.notifications.generate_error', { error: error.message }));
|
notify('error', t('apikey.notifications.generate_error', { error: error.message }));
|
||||||
@@ -74,6 +76,7 @@ const ApiKeyManager = () => {
|
|||||||
try {
|
try {
|
||||||
await axios.delete(`${apiUrl}/auth/delete-api-key`);
|
await axios.delete(`${apiUrl}/auth/delete-api-key`);
|
||||||
setApiKey(null);
|
setApiKey(null);
|
||||||
|
setApiKeyCreatedAt(null);
|
||||||
notify('success', t('apikey.notifications.delete_success'));
|
notify('success', t('apikey.notifications.delete_success'));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
notify('error', t('apikey.notifications.delete_error', { error: error.message }));
|
notify('error', t('apikey.notifications.delete_error', { error: error.message }));
|
||||||
@@ -128,12 +131,13 @@ const ApiKeyManager = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
{apiKey ? (
|
{apiKey ? (
|
||||||
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}>
|
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}>
|
||||||
<Table>
|
<Table sx={{ tableLayout: 'fixed', width: '100%' }}>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>{t('apikey.table.name')}</TableCell>
|
<TableCell>{t('apikey.table.name')}</TableCell>
|
||||||
<TableCell>{t('apikey.table.key')}</TableCell>
|
<TableCell>{t('apikey.table.key')}</TableCell>
|
||||||
<TableCell>{t('apikey.table.actions')}</TableCell>
|
{apiKeyCreatedAt && <TableCell>Created On</TableCell>}
|
||||||
|
<TableCell align="center" sx={{ width: 160 }}>{t('apikey.table.actions')}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -144,7 +148,16 @@ const ApiKeyManager = () => {
|
|||||||
{showKey ? `${apiKey?.substring(0, 10)}...` : '**********'}
|
{showKey ? `${apiKey?.substring(0, 10)}...` : '**********'}
|
||||||
</Box>
|
</Box>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
{apiKeyCreatedAt && (
|
||||||
|
<TableCell>
|
||||||
|
{new Date(apiKeyCreatedAt).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})}
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
<TableCell align="right" sx={{ width: 160 }}>
|
||||||
<Tooltip title={t('apikey.actions.copy')}>
|
<Tooltip title={t('apikey.actions.copy')}>
|
||||||
<IconButton onClick={copyToClipboard}>
|
<IconButton onClick={copyToClipboard}>
|
||||||
<ContentCopy />
|
<ContentCopy />
|
||||||
|
|||||||
Reference in New Issue
Block a user