📦 chore(frontend): add moment package as a dependency to handle date and time formatting
🐛 fix(headerComponent): add onClick event handler to navigate to "/account/api-keys" when button is clicked 🐛 fix(API): add missing API functions for getting, creating, and deleting API keys 🐛 fix(SecretKeyModal): add functionality to create a new API key and set the value in the input field 🔧 fix(ApiKeysPage): remove unused imports and refactor code to improve readability and performance ✨ feat(ApiKeysPage): add support for fetching and displaying API keys for the logged-in user 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables 🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
This commit is contained in:
parent
37cdd41ec9
commit
ad8cbddd91
7 changed files with 120 additions and 175 deletions
9
src/frontend/package-lock.json
generated
9
src/frontend/package-lock.json
generated
|
|
@ -45,6 +45,7 @@
|
|||
"esbuild": "^0.17.18",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.233.0",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-cookie": "^4.1.1",
|
||||
|
|
@ -7726,6 +7727,14 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
"esbuild": "^0.17.18",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.233.0",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-cookie": "^4.1.1",
|
||||
|
|
|
|||
|
|
@ -146,7 +146,11 @@ export default function Header() {
|
|||
/>
|
||||
</div>
|
||||
</AlertDropdown>
|
||||
<button>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/account/api-keys");
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Key"
|
||||
className="side-bar-button-size text-muted-foreground hover:text-accent-foreground"
|
||||
|
|
|
|||
|
|
@ -467,3 +467,41 @@ export async function updateUser(user_id: string, user: Users) {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApiKey(user_id: String) {
|
||||
try {
|
||||
const res = await api.get(`${BASE_URL_API}api_key/${user_id}`);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createApiKey(user_id: string) {
|
||||
try {
|
||||
const res = await api.post(`${BASE_URL_API}api_key/${user_id}`);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function deleteApiKey(user_id: string) {
|
||||
try {
|
||||
const res = await api.delete(`${BASE_URL_API}api_key/${user_id}`);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Button } from "../../components/ui/button";
|
|||
import { Input } from "../../components/ui/input";
|
||||
import { CONTROL_NEW_API_KEY } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { createApiKey } from "../../controllers/API";
|
||||
import {
|
||||
ApiKeyInputType,
|
||||
ApiKeyType,
|
||||
|
|
@ -20,13 +21,11 @@ export default function SecretKeyModal({
|
|||
children,
|
||||
icon,
|
||||
data,
|
||||
index,
|
||||
onConfirm,
|
||||
}: ApiKeyType) {
|
||||
const Icon: any = nodeIconsLucide[icon];
|
||||
const [open, setOpen] = useState(false);
|
||||
const [apiKeyName, setApiKeyName] = useState(data?.apikeyname ?? "");
|
||||
const [apiKeyValue, setApiKeyValue] = useState("Value");
|
||||
const [apiKeyValue, setApiKeyValue] = useState("");
|
||||
const [inputState, setInputState] =
|
||||
useState<ApiKeyInputType>(CONTROL_NEW_API_KEY);
|
||||
const [renderKey, setRenderKey] = useState(false);
|
||||
|
|
@ -49,7 +48,7 @@ export default function SecretKeyModal({
|
|||
|
||||
function resetForm() {
|
||||
setApiKeyName("");
|
||||
setApiKeyValue("Value");
|
||||
setApiKeyValue("");
|
||||
}
|
||||
|
||||
const handleCopyClick = async () => {
|
||||
|
|
@ -68,6 +67,14 @@ export default function SecretKeyModal({
|
|||
}
|
||||
};
|
||||
|
||||
function handleAddNewKey() {
|
||||
createApiKey(data)
|
||||
.then((res) => {
|
||||
setApiKeyValue(res["api_key"]);
|
||||
})
|
||||
.catch((err) => {});
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseModal size="small-h-full" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
|
|
@ -122,6 +129,7 @@ export default function SecretKeyModal({
|
|||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
setRenderKey(true);
|
||||
handleAddNewKey();
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
|
|
@ -13,165 +12,55 @@ import {
|
|||
} from "../../components/ui/table";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import {
|
||||
addUser,
|
||||
deleteUser,
|
||||
getUsersPage,
|
||||
updateUser,
|
||||
} from "../../controllers/API";
|
||||
import { deleteApiKey, getApiKey } from "../../controllers/API";
|
||||
import ConfirmationModal from "../../modals/ConfirmationModal";
|
||||
import SecretKeyModal from "../../modals/SecretKeyModal";
|
||||
import { UserInputType } from "../../types/components";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
export default function ApiKeysPage() {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const [size, setPageSize] = useState(10);
|
||||
const [index, setPageIndex] = useState(0);
|
||||
const [loadingUsers, setLoadingUsers] = useState(true);
|
||||
const [loadingKeys, setLoadingKeys] = useState(true);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const { userData } = useContext(AuthContext);
|
||||
const [totalRowsCount, setTotalRowsCount] = useState(0);
|
||||
|
||||
const userList = useRef([]);
|
||||
const [userId, setUserId] = useState("");
|
||||
const keysList = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
getUsers();
|
||||
getKeys();
|
||||
}, 500);
|
||||
}, []);
|
||||
}, [userData]);
|
||||
|
||||
const [filterUserList, setFilterUserList] = useState(userList.current);
|
||||
|
||||
function getUsers() {
|
||||
setLoadingUsers(true);
|
||||
getUsersPage(index, size)
|
||||
.then((users) => {
|
||||
setTotalRowsCount(users["total_count"]);
|
||||
userList.current = users["users"];
|
||||
setFilterUserList(users["users"]);
|
||||
setLoadingUsers(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingUsers(false);
|
||||
});
|
||||
}
|
||||
|
||||
function handleChangePagination(pageIndex: number, pageSize: number) {
|
||||
setLoadingUsers(true);
|
||||
getUsersPage(pageIndex, pageSize)
|
||||
.then((users) => {
|
||||
setTotalRowsCount(users["total_count"]);
|
||||
userList.current = users["users"];
|
||||
setFilterUserList(users["users"]);
|
||||
setLoadingUsers(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingUsers(false);
|
||||
});
|
||||
}
|
||||
|
||||
function resetFilter() {
|
||||
setPageIndex(0);
|
||||
setPageSize(10);
|
||||
getUsers();
|
||||
}
|
||||
|
||||
function handleFilterUsers(input: string) {
|
||||
setInputValue(input);
|
||||
|
||||
if (input === "") {
|
||||
setFilterUserList(userList.current);
|
||||
} else {
|
||||
const filteredList = userList.current.filter((user) =>
|
||||
user.username.toLowerCase().includes(input.toLowerCase())
|
||||
);
|
||||
setFilterUserList(filteredList);
|
||||
function getKeys() {
|
||||
setLoadingKeys(true);
|
||||
if (userData) {
|
||||
getApiKey(userData.id)
|
||||
.then((keys) => {
|
||||
keysList.current = keys["api_keys"];
|
||||
setUserId(keys["user_id"]);
|
||||
setLoadingKeys(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingKeys(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleDeleteUser(user) {
|
||||
deleteUser(user.id)
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! User deleted!",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on delete user",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
function resetFilter() {
|
||||
getKeys();
|
||||
}
|
||||
|
||||
function handleEditUser(userId, user) {
|
||||
updateUser(userId, user)
|
||||
function handleDeleteKey(keys) {
|
||||
deleteApiKey(keys)
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! User edited!",
|
||||
title: "Success! Key deleted!",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on edit user",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleDisableUser(check, userId, user) {
|
||||
const userEdit = cloneDeep(user);
|
||||
userEdit.is_active = !check;
|
||||
|
||||
updateUser(userId, userEdit)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! User edited!",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on edit user",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleSuperUserEdit(check, userId, user) {
|
||||
const userEdit = cloneDeep(user);
|
||||
userEdit.is_superuser = !check;
|
||||
updateUser(userId, userEdit)
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! User edited!",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on edit user",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleNewUser(user: UserInputType) {
|
||||
addUser(user)
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! New user added!",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on add new user",
|
||||
title: "Error on delete key",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
});
|
||||
});
|
||||
|
|
@ -188,6 +77,12 @@ export default function ApiKeysPage() {
|
|||
);
|
||||
}
|
||||
|
||||
function getIdKeyHidden(apiKey: string) {
|
||||
const firstTwoChars = apiKey.slice(0, 2);
|
||||
const lastFourChars = apiKey.slice(-4);
|
||||
return firstTwoChars + "..." + lastFourChars;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{userData && (
|
||||
|
|
@ -211,7 +106,7 @@ export default function ApiKeysPage() {
|
|||
<div className="flex items-center space-x-2"></div>
|
||||
</div>
|
||||
|
||||
{userList.current.length === 0 && !loadingUsers && (
|
||||
{keysList.current.length === 0 && !loadingKeys && (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2>There's no users registered :)</h2>
|
||||
|
|
@ -219,21 +114,21 @@ export default function ApiKeysPage() {
|
|||
</>
|
||||
)}
|
||||
<>
|
||||
{loadingUsers && (
|
||||
{loadingKeys && (
|
||||
<div>
|
||||
<strong>Loading...</strong>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
"max-h-[15rem] min-h-[15rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||
(loadingUsers ? " border-0" : "")
|
||||
"max-h-[15rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||
(loadingKeys ? " border-0" : "")
|
||||
}
|
||||
>
|
||||
<Table className={"table-fixed bg-muted outline-1"}>
|
||||
<TableHeader
|
||||
className={
|
||||
loadingUsers
|
||||
loadingKeys
|
||||
? "hidden"
|
||||
: "table-fixed bg-muted outline-1"
|
||||
}
|
||||
|
|
@ -256,37 +151,31 @@ export default function ApiKeysPage() {
|
|||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{!loadingUsers && (
|
||||
{!loadingKeys && (
|
||||
<TableBody>
|
||||
{filterUserList.map((user, index) => (
|
||||
{keysList.current.map((api_keys, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.id}>
|
||||
<ShadTooltip content={api_keys.name}>
|
||||
<span className="cursor-default">
|
||||
{user.id}
|
||||
{api_keys.name}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
<span className="cursor-default">
|
||||
{getIdKeyHidden(api_keys.id)}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2 ">
|
||||
{
|
||||
new Date(user.create_at)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
{moment(api_keys.create_at).format(
|
||||
"YYYY-MM-DD HH:mm"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
{
|
||||
new Date(user.updated_at)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
{moment(api_keys.last_used_at).format(
|
||||
"YYYY-MM-DD HH:mm"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
|
|
@ -294,14 +183,14 @@ export default function ApiKeysPage() {
|
|||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
modalContent="Are you sure you want to delete this key? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
data={api_keys.id}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
onConfirm={(index, keys) => {
|
||||
handleDeleteKey(keys);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Delete" side="top">
|
||||
|
|
@ -327,9 +216,7 @@ export default function ApiKeysPage() {
|
|||
cancelText="Cancel"
|
||||
confirmationText="Create secret key"
|
||||
icon={"Key"}
|
||||
onConfirm={(index, user) => {
|
||||
handleNewUser(user);
|
||||
}}
|
||||
data={userId}
|
||||
>
|
||||
<Button>
|
||||
<IconComponent name="Plus" className="mr-1 h-5 w-5" />
|
||||
|
|
|
|||
|
|
@ -241,8 +241,6 @@ export type ApiKeyType = {
|
|||
children: ReactElement;
|
||||
icon: string;
|
||||
data?: any;
|
||||
index?: number;
|
||||
onConfirm: (index, data) => void;
|
||||
};
|
||||
|
||||
export type ApiKeyInputType = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue