expiration check

main
alex 2023-10-21 18:39:20 +02:00
parent f56a4235e7
commit 71c6a72308
5 changed files with 236 additions and 9 deletions

View File

@ -29,7 +29,8 @@
"copiedToClipboard": "In die Zwischenablage kopiert", "copiedToClipboard": "In die Zwischenablage kopiert",
"copyToClipboard": "In die Zwischenablage kopieren", "copyToClipboard": "In die Zwischenablage kopieren",
"show": "Anzeigen", "show": "Anzeigen",
"hide": "Verbergen" "hide": "Verbergen",
"reload": "Neu laden"
} }
}, },
"sideMenu": { "sideMenu": {
@ -386,7 +387,6 @@
"repeatNewPassword": "Neues Passwort wiederholen", "repeatNewPassword": "Neues Passwort wiederholen",
"language": "Sprache" "language": "Sprache"
}, },
"button": { "button": {
"createApiKey": { "createApiKey": {
"title": "Neuen API-Schlüssel erstellen", "title": "Neuen API-Schlüssel erstellen",
@ -396,7 +396,14 @@
} }
} }
}, },
"icon": { "viewApiDoc": "Api-Dokumentation anschauen" } "icon": { "viewApiDoc": "Api-Dokumentation anschauen" },
"telegram": {
"title": "Telegram Benachrichtigungen",
"disconnectPopconfirm": {
"title": "Sind Sie sicher, dass Sie die Verbindung zu Telegram trennen wollen?",
"description": "Sie werden keine Benachrichtigungen mehr erhalten"
}
}
}, },
"scanners": { "scanners": {
"column": { "column": {

View File

@ -29,7 +29,8 @@
"copiedToClipboard": "Copied to clipboard", "copiedToClipboard": "Copied to clipboard",
"copyToClipboard": "Copy to clipboard", "copyToClipboard": "Copy to clipboard",
"show": "Show", "show": "Show",
"hide": "Hide" "hide": "Hide",
"reload": "Reload"
} }
}, },
"sideMenu": { "sideMenu": {
@ -415,7 +416,21 @@
} }
} }
}, },
"icon": { "viewApiDoc": "View API Documentation" } "icon": { "viewApiDoc": "View API Documentation" },
"telegram": {
"title": "Telegram notifications",
"subscribeButton": "Subscribe",
"checkStatusButton": "Check Status",
"subscribed": "Subscribed",
"disconnectPopconfirm": {
"title": "Are you sure you want to disconnect your Telegram account?",
"description": "You will no longer receive notifications via Telegram"
},
"verificationPopover": {
"title": "Verification",
"description": "Add the bot on your telegram account and send the command {{command}} to the bot"
}
}
}, },
"scanners": { "scanners": {
"column": { "column": {

View File

@ -1,7 +1,9 @@
import { import {
CopyOutlined, CopyOutlined,
DisconnectOutlined,
EyeInvisibleOutlined, EyeInvisibleOutlined,
EyeOutlined, EyeOutlined,
ReloadOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Tooltip } from "antd"; import { Tooltip } from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -37,3 +39,13 @@ export function MyShowHiddenIcon({ setIsHidden, isHidden }) {
</Tooltip> </Tooltip>
); );
} }
export function MyReloadIcon({ onClick }) {
const { t } = useTranslation();
return (
<Tooltip title={t("common.text.reload")}>
<ReloadOutlined onClick={onClick} />
</Tooltip>
);
}

View File

@ -5,6 +5,7 @@ import {
Form, Form,
Input, Input,
Popconfirm, Popconfirm,
Popover,
Row, Row,
Select, Select,
Space, Space,
@ -14,7 +15,7 @@ import {
Upload, Upload,
notification, notification,
} from "antd"; } from "antd";
import { useEffect, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { import {
Constants, Constants,
EncodeStringToBase64, EncodeStringToBase64,
@ -24,18 +25,29 @@ import {
hasPermission, hasPermission,
isEmailValid, isEmailValid,
myFetch, myFetch,
myFetchContentType,
wsConnectionCustomEventName, wsConnectionCustomEventName,
} from "../../utils"; } from "../../utils";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { FileTextOutlined, KeyOutlined } from "@ant-design/icons"; import {
DisconnectOutlined,
FileTextOutlined,
InfoCircleOutlined,
KeyOutlined,
NotificationOutlined,
} from "@ant-design/icons";
import { MyUserAvatar } from "../../Components/MyAvatar"; import { MyUserAvatar } from "../../Components/MyAvatar";
import { useUserProfileContext } from "../../Contexts/UserProfileContext"; import { useUserProfileContext } from "../../Contexts/UserProfileContext";
import { useWebSocketContext } from "../../Contexts/WebSocketContext"; import { useWebSocketContext } from "../../Contexts/WebSocketContext";
import { useAppContext } from "../../Contexts/AppContext"; import { useAppContext } from "../../Contexts/AppContext";
import { useSideBarContext } from "../../Contexts/SideBarContext"; import { useSideBarContext } from "../../Contexts/SideBarContext";
import { SentMessagesCommands } from "../../Handlers/WebSocketMessageHandler"; import { SentMessagesCommands } from "../../Handlers/WebSocketMessageHandler";
import { MyCopyIcon, MyShowHiddenIcon } from "../../Components/MyIcon"; import {
MyCopyIcon,
MyReloadIcon,
MyShowHiddenIcon,
} from "../../Components/MyIcon";
export default function UserProfile() { export default function UserProfile() {
const webSocketContext = useWebSocketContext(); const webSocketContext = useWebSocketContext();
@ -53,6 +65,13 @@ export default function UserProfile() {
const [repeatedNewPassword, setRepeatedNewPassword] = useState(""); const [repeatedNewPassword, setRepeatedNewPassword] = useState("");
const [newApiKeyName, setNewApikeyName] = useState(""); const [newApiKeyName, setNewApikeyName] = useState("");
const [showApiKeyPassword, setShowApiKeyPassword] = useState(false); const [showApiKeyPassword, setShowApiKeyPassword] = useState(false);
const [
telegramNotificationSubscribedStatus,
setTelegramNotificationSubscribedStatus,
] = useState(null);
const statusInterval = useRef(null);
const telegramVerifyCodeRequestTime = useRef(null);
const getSessionTableColumns = () => { const getSessionTableColumns = () => {
return [ return [
@ -271,6 +290,165 @@ export default function UserProfile() {
setNewApikeyName(""); setNewApikeyName("");
}; };
const telegramVerifyStatusRequest = (updateOnlyOnVerified) =>
myFetch(
`/status/${appContext.userId.current}`,
"GET",
null,
{},
myFetchContentType.JSON,
Constants.TELEGRAM_BOT_MANAGER_ADDRESS
).then((data) => {
// manual check verify status by clicking reload icon
if (!updateOnlyOnVerified) {
setTelegramNotificationSubscribedStatus(data.Status);
}
// used for auto check verify status
if (data.Status === true) {
setTelegramNotificationSubscribedStatus(data.Status);
clearInterval(statusInterval.current);
telegramVerifyCodeRequestTime.current = null;
}
});
const TelegramNotificationSubscribedStatusItem = () => {
// check verified status
if (telegramNotificationSubscribedStatus === null) {
return (
<Button
style={{ width: "100%" }}
onClick={() => telegramVerifyStatusRequest()}
>
{t("userProfile.telegram.checkStatusButton")}
</Button>
);
}
// unsubscribe
if (telegramNotificationSubscribedStatus === true) {
return (
<Space>
<span>{t("userProfile.telegram.subscribed")}</span>
<Popconfirm
title={t("userProfile.telegram.disconnectPopconfirm.title")}
description={t(
"userProfile.telegram.disconnectPopconfirm.description"
)}
okText={t("common.button.confirm")}
cancelText={t("common.button.cancel")}
onConfirm={() => {
myFetch(
`/verifycode/${appContext.userId.current}`,
"DELETE",
null,
{},
myFetchContentType.JSON,
Constants.TELEGRAM_BOT_MANAGER_ADDRESS
).then(() => {
setTelegramNotificationSubscribedStatus(null);
});
}}
>
<DisconnectOutlined />
</Popconfirm>
</Space>
);
}
// subscribe
if (telegramNotificationSubscribedStatus === false) {
return (
<Button
icon={<NotificationOutlined />}
style={{ width: "100%" }}
onClick={() =>
myFetch(
`/verifycode/${appContext.userId.current}`,
"GET",
null,
{},
myFetchContentType.JSON,
Constants.TELEGRAM_BOT_MANAGER_ADDRESS
).then((data) => {
setTelegramNotificationSubscribedStatus(data.Code);
})
}
>
{t("userProfile.telegram.subscribeButton")}
</Button>
);
}
// verification code
return (
<Space>
<Popover
placement="left"
title={t("userProfile.telegram.verificationPopover.title")}
overlayStyle={{ maxWidth: 300 }}
content={
<Space direction="vertical">
<Trans
i18nKey="userProfile.telegram.verificationPopover.description"
values={{
command: `<strong>/verify ${telegramNotificationSubscribedStatus}</strong>`,
}}
/>
<div style={{ textAlign: "center" }}>
<img
src={`${Constants.TELEGRAM_BOT_MANAGER_CONTENT_ADDRESS}qrcode.png`}
width={200}
/>
</div>
</Space>
}
>
<InfoCircleOutlined style={{ color: Constants.COLORS.ICON_INFO }} />
</Popover>
<span>{telegramNotificationSubscribedStatus}</span>
<MyCopyIcon
text={`/verify ${telegramNotificationSubscribedStatus}`}
notificationApi={notificationApi}
/>
<MyReloadIcon onClick={() => telegramVerifyStatusRequest()} />
</Space>
);
};
useEffect(() => {
if (
telegramNotificationSubscribedStatus !== true &&
telegramNotificationSubscribedStatus !== false &&
telegramNotificationSubscribedStatus !== null
) {
telegramVerifyCodeRequestTime.current = new Date();
statusInterval.current = setInterval(() => {
// check expiration time
if (
telegramVerifyCodeRequestTime.current !== null &&
new Date() - telegramVerifyCodeRequestTime.current >
Constants.GLOBALS.TELEGRAM_VERIFY_CODE_EXPIRATION_TIME
) {
clearInterval(statusInterval.current);
telegramVerifyCodeRequestTime.current = null;
setTelegramNotificationSubscribedStatus(false);
return;
}
telegramVerifyStatusRequest(true);
}, 1000);
}
return () => clearInterval(statusInterval.current);
}, [telegramNotificationSubscribedStatus]);
useEffect(() => { useEffect(() => {
const userProfileRequest = () => const userProfileRequest = () =>
myFetch("/user/profile", "GET").then((data) => { myFetch("/user/profile", "GET").then((data) => {
@ -342,6 +520,10 @@ export default function UserProfile() {
onChange={(e) => i18n.changeLanguage(e)} onChange={(e) => i18n.changeLanguage(e)}
/> />
</Form.Item> </Form.Item>
<Form.Item label={t("userProfile.telegram.title")}>
<TelegramNotificationSubscribedStatusItem />
</Form.Item>
</Col> </Col>
</Row> </Row>

View File

@ -14,6 +14,8 @@ let wsAddress = "";
let logApiAddress = ""; let logApiAddress = "";
let roboticsApiAddress = ""; let roboticsApiAddress = "";
var roboticsSwaggerAddress = ""; var roboticsSwaggerAddress = "";
var telegramBotManagerAddress = "";
var telegramBotManagerStaticContentAddress = "";
if (window.location.hostname === "localhost" && window.location.port === "") { if (window.location.hostname === "localhost" && window.location.port === "") {
// for docker container testing on localhost // for docker container testing on localhost
@ -23,6 +25,8 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
logApiAddress = "http://localhost/lm/v1/log"; logApiAddress = "http://localhost/lm/v1/log";
roboticsApiAddress = "http://localhost/rcm/v1"; roboticsApiAddress = "http://localhost/rcm/v1";
roboticsSwaggerAddress = "http://localhost/rcm/swagger/index.html"; roboticsSwaggerAddress = "http://localhost/rcm/swagger/index.html";
telegramBotManagerAddress = "http://localhost/tnm/v1";
telegramBotManagerStaticContentAddress = "http://localhost/tnm/";
} else if (window.location.hostname === "localhost") { } else if (window.location.hostname === "localhost") {
// programming on localhost // programming on localhost
apiAddress = "http://localhost:50050/v1"; apiAddress = "http://localhost:50050/v1";
@ -31,6 +35,8 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
logApiAddress = "http://127.0.0.1:50110/v1/log"; logApiAddress = "http://127.0.0.1:50110/v1/log";
roboticsApiAddress = "http://localhost:50055/v1"; roboticsApiAddress = "http://localhost:50055/v1";
roboticsSwaggerAddress = "http://localhost:50055/swagger/index.html"; roboticsSwaggerAddress = "http://localhost:50055/swagger/index.html";
telegramBotManagerAddress = "http://localhost:50056/v1";
telegramBotManagerStaticContentAddress = "http://localhost:50056/";
/*} else if (window.location.hostname === "192.168.178.93") { /*} else if (window.location.hostname === "192.168.178.93") {
apiAddress = "http://192.168.178.93:50050/v1"; apiAddress = "http://192.168.178.93:50050/v1";
staticContentAddress = "http://192.168.178.93:50050/"; staticContentAddress = "http://192.168.178.93:50050/";
@ -43,6 +49,8 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
logApiAddress = `${window.location.protocol}${window.location.hostname}/lm/v1/log`; logApiAddress = `${window.location.protocol}${window.location.hostname}/lm/v1/log`;
roboticsApiAddress = `${window.location.protocol}${window.location.hostname}/rcm/v1`; roboticsApiAddress = `${window.location.protocol}${window.location.hostname}/rcm/v1`;
roboticsSwaggerAddress = `${window.location.protocol}${window.location.hostname}/rcm/swagger/index.html`; roboticsSwaggerAddress = `${window.location.protocol}${window.location.hostname}/rcm/swagger/index.html`;
telegramBotManagerAddress = `${window.location.protocol}${window.location.hostname}/tnm/v1`;
telegramBotManagerStaticContentAddress = `${window.location.protocol}${window.location.hostname}/tnm/`;
} }
export const Constants = { export const Constants = {
@ -66,6 +74,8 @@ export const Constants = {
LOG_API_ADDRESS: logApiAddress, LOG_API_ADDRESS: logApiAddress,
ROBOTICS_API_ADDRESS: roboticsApiAddress, // robot-control-manager ROBOTICS_API_ADDRESS: roboticsApiAddress, // robot-control-manager
ROBOTICS_SWAGGER_ADDRESS: roboticsSwaggerAddress, ROBOTICS_SWAGGER_ADDRESS: roboticsSwaggerAddress,
TELEGRAM_BOT_MANAGER_ADDRESS: telegramBotManagerAddress,
TELEGRAM_BOT_MANAGER_CONTENT_ADDRESS: telegramBotManagerStaticContentAddress,
ROUTE_PATHS: { ROUTE_PATHS: {
EQUIPMENT_DOCUMENTATION: "/equipment-documentation", EQUIPMENT_DOCUMENTATION: "/equipment-documentation",
EQUIPMENT_DOCUMENTATION_VIEW: "/equipment-documentation/", EQUIPMENT_DOCUMENTATION_VIEW: "/equipment-documentation/",
@ -113,6 +123,7 @@ export const Constants = {
MAX_USER_API_KEY_NAME_LENGTH: 30, MAX_USER_API_KEY_NAME_LENGTH: 30,
ROBOTICS_ROBOTS_PAGINATION_LIMIT: 5, ROBOTICS_ROBOTS_PAGINATION_LIMIT: 5,
ROBOTICS_UNAUTHORIZED_PAGINATION_LIMIT: 5, ROBOTICS_UNAUTHORIZED_PAGINATION_LIMIT: 5,
TELEGRAM_VERIFY_CODE_EXPIRATION_TIME: 10 * 60 * 1000, // 10 minutes
}, },
MAX_AVATAR_SIZE: 5 * 1024 * 1024, MAX_AVATAR_SIZE: 5 * 1024 * 1024,
ACCEPTED_AVATAR_FILE_TYPES: [ ACCEPTED_AVATAR_FILE_TYPES: [