main
alex 2023-07-02 22:09:40 +02:00
parent cc59493b04
commit 16f3cbf1d2
4 changed files with 163 additions and 3 deletions

View File

@ -140,6 +140,10 @@
"userProfile.column.expiresAt": "Läuft ab am",
"userProfile.column.action": "Maßnahme",
"userProfile.column.action.signOut": "Abmelden",
"userProfile.column.createdAt": "Erstellt am",
"userProfile.column.usageCount": "Anzahl der Nutzungen",
"userProfile.column.name": "Name",
"userProfile.column.token": "Token",
"userProfile.changeAvatarError.notification.message": "Ihr Avatar konnte nicht geändert werden",
"userProfile.changeAvatarError.notification.description": "Avatar muss kleiner sein als {{MAX_AVATAR_SIZE}} MB sein",
"userProfile.form.username": "Benutzername",
@ -149,6 +153,11 @@
"userProfile.form.repeatNewPassword": "Neues Passwort wiederholen",
"userProfile.form.language": "Sprache",
"userProfile.header.yourSessions": "Ihre Sitzungen",
"userProfile.header.yourApiKeys": "Ihre API Schlüssel",
"userProfile.button.createApiKey": "Neuen API Schlüssel erstellen",
"userProfile.button.createApiKey.popconfirm.title": "Name für den neuen API Schlüssel",
"userProfile.button.createApiKey.popconfirm.okText": "Erstellen",
"userProfile.button.copyToClipboard.notification": "API Token in die Zwischenablage kopiert",
"scanners.column.name": "Name",
"scanners.column.usedBy": "Verwendet von",
"scanners.column.lastUsed": "Zuletzt verwendet",

View File

@ -140,6 +140,10 @@
"userProfile.column.expiresAt": "Expires at",
"userProfile.column.action": "Action",
"userProfile.column.action.signOut": "Sign out",
"userProfile.column.createdAt": "Created at",
"userProfile.column.usageCount": "Usage count",
"userProfile.column.name": "Name",
"userProfile.column.token": "Token",
"userProfile.changeAvatarError.notification.message": "Your avatar could not be changed",
"userProfile.changeAvatarError.notification.description": "Avatar must be smaller than {{MAX_AVATAR_SIZE}} MB",
"userProfile.form.username": "Username",
@ -149,6 +153,11 @@
"userProfile.form.repeatNewPassword": "Repeat new password",
"userProfile.form.language": "Language",
"userProfile.header.yourSessions": "Your sessions",
"userProfile.header.yourApiKeys": "Your API keys",
"userProfile.button.createApiKey": "Create new API key",
"userProfile.button.createApiKey.popconfirm.title": "Name for the new API key",
"userProfile.button.createApiKey.popconfirm.okText": "Create",
"userProfile.button.copyToClipboard.notification": "API token copied to clipboard",
"scanners.column.name": "Name",
"scanners.column.usedBy": "Used by",
"scanners.column.lastUsed": "Last used",

View File

@ -4,6 +4,7 @@ import {
Col,
Form,
Input,
Popconfirm,
Row,
Select,
Space,
@ -26,6 +27,12 @@ import {
} from "../../utils";
import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next";
import {
CopyOutlined,
EyeInvisibleOutlined,
EyeOutlined,
KeyOutlined,
} from "@ant-design/icons";
export default function UserProfile() {
const webSocketContext = useContext(WebSocketContext);
@ -36,8 +43,10 @@ export default function UserProfile() {
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [repeatedNewPassword, setRepeatedNewPassword] = useState("");
const [newApiKeyName, setNewApikeyName] = useState("");
const [showApiKeyPassword, setShowApiKeyPassword] = useState(false);
const getTableColumns = () => {
const getSessionTableColumns = () => {
return [
{
title: t("userProfile.column.userAgent"),
@ -91,7 +100,81 @@ export default function UserProfile() {
];
};
const getTableItems = () => {
const getApiKeyTableColumns = () => {
return [
{
title: t("userProfile.column.name"),
dataIndex: "name",
key: "name",
},
{
title: t("userProfile.column.token"),
dataIndex: "token",
key: "token",
render: (text) => (
<Space>
{showApiKeyPassword ? text : "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}
{showApiKeyPassword ? (
<EyeInvisibleOutlined
onClick={() => setShowApiKeyPassword(false)}
/>
) : (
<EyeOutlined onClick={() => setShowApiKeyPassword(true)} />
)}
{showApiKeyPassword && (
<CopyOutlined
onClick={() => {
navigator.clipboard.writeText(text);
setShowApiKeyPassword(false);
notificationApi["info"]({
message: t(
"userProfile.button.copyToClipboard.notification"
),
});
}}
/>
)}
</Space>
),
},
{
title: t("userProfile.column.usageCount"),
dataIndex: "usageCount",
key: "usageCount",
},
{
title: t("userProfile.column.lastUsed"),
dataIndex: "lastUsed",
key: "lastUsed",
},
{
title: t("userProfile.column.createdAt"),
dataIndex: "createdAt",
key: "createdAt",
},
];
};
const getApiKeyTableItems = () => {
let items = [];
webSocketContext.User.ApiKeys.forEach((apiKey) => {
items.push({
key: apiKey.Id,
name: apiKey.Name,
token: apiKey.Token,
usageCount: apiKey.UsageCount,
createdAt: FormatDatetime(apiKey.CreatedAt),
lastUsed: FormatDatetime(apiKey.LastUsed),
});
});
return items;
};
const getSessionTableItems = () => {
let items = [];
webSocketContext.User.Sessions.sort(
@ -178,6 +261,15 @@ export default function UserProfile() {
});
};
const onCreateNewApiKeyConfirm = () => {
webSocketContext.SendSocketMessage(
SentMessagesCommands.CreateNewUserApiKey,
{ Name: newApiKeyName }
);
setNewApikeyName("");
};
return (
<>
{notificationContextHolder}
@ -325,7 +417,46 @@ export default function UserProfile() {
{webSocketContext.User.Sessions.length})
</h1>
<Table columns={getTableColumns()} dataSource={getTableItems()} />
<Table
columns={getSessionTableColumns()}
dataSource={getSessionTableItems()}
/>
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<h1 style={{ fontWeight: "bold" }}>
{t("userProfile.header.yourApiKeys")} (
{webSocketContext.User.ApiKeys.length})
</h1>
<Popconfirm
placement="topRight"
title={t("userProfile.button.createApiKey.popconfirm.title")}
description={
<Input
placeholder="Name"
value={newApiKeyName}
onChange={(e) => setNewApikeyName(e.target.value)}
/>
}
okText={t("userProfile.button.createApiKey.popconfirm.okText")}
cancelText={t("buttonCancel")}
onConfirm={() => onCreateNewApiKeyConfirm()}
>
<Button icon={<KeyOutlined />}>
{t("userProfile.button.createApiKey")}
</Button>
</Popconfirm>
</div>
<Table
columns={getApiKeyTableColumns()}
dataSource={getApiKeyTableItems()}
/>
</>
);
}

View File

@ -144,6 +144,7 @@ let webSocketContextPreview = {
Sessions: [],
Permissions: [],
RoleId: "",
ApiKeys: [],
},
AllUsers: [],
AllRoles: [],
@ -191,6 +192,7 @@ const ReceivedMessagesCommands = {
AllUsersUserDeleted: 27,
AllUsersUserDeactivation: 28,
GroupTasksCategoryGroupChanges: 29,
NewUserApiKeyCreated: 30,
};
// commands sent to the backend server
@ -213,6 +215,7 @@ export const SentMessagesCommands = {
ScannersDisconnectScanner: 16,
GroupTasksCheckingForCategoryGroupChanges: 17,
HandleUserActionTaskStep: 18,
CreateNewUserApiKey: 19,
};
export function WebSocketProvider({
@ -951,7 +954,15 @@ export function WebSocketProvider({
return newArr;
});
}
break;
case ReceivedMessagesCommands.NewUserApiKeyCreated:
setUser((user) => {
const updatedUser = { ...user };
updatedUser.ApiKeys.push(body);
return updatedUser;
});
break;
default: