Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a3c948386 | |||
| 6fb03530df | |||
| d2c36e71be |
Generated
+3
-3
@@ -5224,9 +5224,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.11",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.11.tgz",
|
||||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
"integrity": "sha512-5dDj8+lmvA8XB78SmzGI8NlQoksv7IfutGWeVZxiixHbO+p4LDPT3wuG/D9sM/wrjZZ9I+Siy/e117vbFPxSZg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Alert, Stack, VStack, Spinner, Text, Heading } from "@chakra-ui/react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { API_BASE } from "@/config/api.config";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
export const DeactivatedServices = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [deactivatedServices, setDeactivatedServices] = useState<
|
||||||
|
{ function_name: string }[]
|
||||||
|
>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchDeactivatedServices = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${API_BASE}/api/users/deactivated-services`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${Cookies.get("token") || ""}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setDeactivatedServices(data);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch deactivated services");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching deactivated services:", error);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchDeactivatedServices();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{deactivatedServices.length >= 1 && (
|
||||||
|
<Stack gap="2">
|
||||||
|
<Heading size={"xl"}>{t("deactivated-services")}</Heading>
|
||||||
|
{isLoading && (
|
||||||
|
<VStack colorPalette="teal">
|
||||||
|
<Spinner color="colorPalette.600" />
|
||||||
|
<Text color="colorPalette.600">{t("loading")}</Text>
|
||||||
|
</VStack>
|
||||||
|
)}
|
||||||
|
{deactivatedServices.length >= 1 &&
|
||||||
|
deactivatedServices.map((item) => (
|
||||||
|
<Alert.Root key={item.function_name} status="warning">
|
||||||
|
<Alert.Indicator />
|
||||||
|
<Alert.Title>
|
||||||
|
{item.function_name} {t("is-deactivated")}
|
||||||
|
</Alert.Title>
|
||||||
|
</Alert.Root>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -69,6 +69,7 @@ export const Header = () => {
|
|||||||
className="mb-6"
|
className="mb-6"
|
||||||
position="relative"
|
position="relative"
|
||||||
pr={{ base: 10, md: 0 }} // Platz für den Mobile-Button rechts
|
pr={{ base: 10, md: 0 }} // Platz für den Mobile-Button rechts
|
||||||
|
marginBottom={1}
|
||||||
>
|
>
|
||||||
{/* Mobile: Drei-Punkte-Button, vertikal zentriert im Header */}
|
{/* Mobile: Drei-Punkte-Button, vertikal zentriert im Header */}
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ export const ContactPage = () => {
|
|||||||
text: t("contactPage_successText"),
|
text: t("contactPage_successText"),
|
||||||
});
|
});
|
||||||
setMessage("");
|
setMessage("");
|
||||||
|
} else if (result.status === 503) {
|
||||||
|
setAlert({
|
||||||
|
type: "error",
|
||||||
|
headline: t("serviceDeactivatedHeadline"),
|
||||||
|
text: t("contactPage_serviceDeactivatedText"),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
setAlert({
|
setAlert({
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { createLoan } from "@/utils/Fetcher";
|
|||||||
import { Header } from "@/components/Header";
|
import { Header } from "@/components/Header";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { approvalAnimation } from "@/components/dotLottie";
|
import { approvalAnimation } from "@/components/dotLottie";
|
||||||
|
import { DeactivatedServices } from "@/components/DeactivatedServices";
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -71,6 +72,7 @@ export const HomePage = () => {
|
|||||||
)}
|
)}
|
||||||
<Container className="px-6 sm:px-8 pt-10">
|
<Container className="px-6 sm:px-8 pt-10">
|
||||||
<Header />
|
<Header />
|
||||||
|
<DeactivatedServices />
|
||||||
{isMsg && (
|
{isMsg && (
|
||||||
<MyAlert
|
<MyAlert
|
||||||
status={msgStatus}
|
status={msgStatus}
|
||||||
|
|||||||
@@ -79,6 +79,16 @@ const Landingpage: React.FC = () => {
|
|||||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (loanRes.status === 503) {
|
||||||
|
setError(
|
||||||
|
"error",
|
||||||
|
t("serviceDeactivatedHeadline"),
|
||||||
|
t("loan_page_serviceDeactivatedText"),
|
||||||
|
);
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const loanData = await loanRes.json();
|
const loanData = await loanRes.json();
|
||||||
if (Array.isArray(loanData)) {
|
if (Array.isArray(loanData)) {
|
||||||
setLoans(loanData);
|
setLoans(loanData);
|
||||||
|
|||||||
@@ -52,6 +52,13 @@ export const MyLoansPage = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
if (res.status === 503) {
|
||||||
|
setMsgStatus("error");
|
||||||
|
setMsgTitle(t("serviceDeactivatedHeadline"));
|
||||||
|
setMsgDescription(t("loan_page_serviceDeactivatedText"));
|
||||||
|
setIsMsg(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setMsgStatus("error");
|
setMsgStatus("error");
|
||||||
setMsgTitle(t("error"));
|
setMsgTitle(t("error"));
|
||||||
setMsgDescription(t("error-fetching-loans"));
|
setMsgDescription(t("error-fetching-loans"));
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ export const getBorrowableItems = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
if (response.status === 503) {
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
status: "error",
|
||||||
|
title: "Service deactivated",
|
||||||
|
description:
|
||||||
|
"The loan service is currently deactivated. Please try again later.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: null,
|
data: null,
|
||||||
status: "error",
|
status: "error",
|
||||||
@@ -60,6 +70,16 @@ export const createLoan = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
if (response.status === 503) {
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
status: "error",
|
||||||
|
title: "Service deactivated",
|
||||||
|
description:
|
||||||
|
"The loan service is currently deactivated. Please try again later.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: null,
|
data: null,
|
||||||
status: "error",
|
status: "error",
|
||||||
|
|||||||
@@ -94,5 +94,10 @@
|
|||||||
"naas-error": "Fehler mit no-as-a-service",
|
"naas-error": "Fehler mit no-as-a-service",
|
||||||
"naas-error-desc": "Ein Fehler ist beim Kommunizieren mit no-as-a-service aufgetreten.",
|
"naas-error-desc": "Ein Fehler ist beim Kommunizieren mit no-as-a-service aufgetreten.",
|
||||||
"naas-header": "Eine gute Möglichkeit, nein zu sagen...",
|
"naas-header": "Eine gute Möglichkeit, nein zu sagen...",
|
||||||
"error-deleting-loan-507": "Die Ausleihe kann nicht gelöscht werden, da sie noch nicht zurückgegeben wurde."
|
"error-deleting-loan-507": "Die Ausleihe kann nicht gelöscht werden, da sie noch nicht zurückgegeben wurde.",
|
||||||
|
"serviceDeactivatedHeadline": "Service deaktiviert",
|
||||||
|
"contactPage_serviceDeactivatedText": "Der Kontaktservice ist derzeit deaktiviert. Bitte versuchen Sie es später erneut.",
|
||||||
|
"loan_page_serviceDeactivatedText": "Der Ausleihservice ist derzeit deaktiviert. Bitte versuchen Sie es später erneut.",
|
||||||
|
"is-deactivated": "ist deaktiviert.",
|
||||||
|
"deactivated-services": "Deaktivierte Services"
|
||||||
}
|
}
|
||||||
@@ -94,5 +94,10 @@
|
|||||||
"naas-error": "Error with no-as-a-service",
|
"naas-error": "Error with no-as-a-service",
|
||||||
"naas-error-desc": "An error occurred while communicating with no-as-a-service.",
|
"naas-error-desc": "An error occurred while communicating with no-as-a-service.",
|
||||||
"naas-header": "A good way to say no...",
|
"naas-header": "A good way to say no...",
|
||||||
"error-deleting-loan-507": "The loan cannot be deleted because it has not been returned yet."
|
"error-deleting-loan-507": "The loan cannot be deleted because it has not been returned yet.",
|
||||||
|
"serviceDeactivatedHeadline": "Service deactivated",
|
||||||
|
"contactPage_serviceDeactivatedText": "The contact service is currently deactivated. Please try again later.",
|
||||||
|
"loan_page_serviceDeactivatedText": "The loan service is currently deactivated. Please try again later.",
|
||||||
|
"is-deactivated": "is deactivated.",
|
||||||
|
"deactivated-services": "Deactivated services"
|
||||||
}
|
}
|
||||||
Generated
+30
-2
@@ -1,18 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "backendv2",
|
"name": "backendv2",
|
||||||
"version": "1.0.0",
|
"version": "v2.1.1 (dev)",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "backendv2",
|
"name": "backendv2",
|
||||||
"version": "1.0.0",
|
"version": "v2.1.1 (dev)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
|
"express-rate-limit": "^8.4.1",
|
||||||
"jose": "^6.0.12",
|
"jose": "^6.0.12",
|
||||||
"mysql2": "^3.14.3",
|
"mysql2": "^3.14.3",
|
||||||
"nodemailer": "^7.0.6"
|
"nodemailer": "^7.0.6"
|
||||||
@@ -349,6 +350,24 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express-rate-limit": {
|
||||||
|
"version": "8.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz",
|
||||||
|
"integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ip-address": "10.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/express-rate-limit"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": ">= 4.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/filelist": {
|
"node_modules/filelist": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
@@ -527,6 +546,15 @@
|
|||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/ip-address": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
|||||||
@@ -15,8 +15,9 @@
|
|||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
|
"express-rate-limit": "^8.4.1",
|
||||||
"jose": "^6.0.12",
|
"jose": "^6.0.12",
|
||||||
"mysql2": "^3.14.3",
|
"mysql2": "^3.14.3",
|
||||||
"nodemailer": "^7.0.6"
|
"nodemailer": "^7.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const pool = mysql
|
|||||||
export const loginFunc = async (username, password) => {
|
export const loginFunc = async (username, password) => {
|
||||||
const [result] = await pool.query(
|
const [result] = await pool.query(
|
||||||
"SELECT * FROM users WHERE username = ? AND password = ?",
|
"SELECT * FROM users WHERE username = ? AND password = ?",
|
||||||
[username, password]
|
[username, password],
|
||||||
);
|
);
|
||||||
if (result.length > 0) return { success: true, data: result[0] };
|
if (result.length > 0) return { success: true, data: result[0] };
|
||||||
return { success: false };
|
return { success: false };
|
||||||
@@ -40,7 +40,7 @@ export const changePassword = async (username, oldPassword, newPassword) => {
|
|||||||
// get user current password
|
// get user current password
|
||||||
const [user] = await pool.query(
|
const [user] = await pool.query(
|
||||||
"SELECT * FROM users WHERE username = ? AND password = ?",
|
"SELECT * FROM users WHERE username = ? AND password = ?",
|
||||||
[username, oldPassword]
|
[username, oldPassword],
|
||||||
);
|
);
|
||||||
if (user.length === 0) return { success: false };
|
if (user.length === 0) return { success: false };
|
||||||
|
|
||||||
@@ -48,8 +48,16 @@ export const changePassword = async (username, oldPassword, newPassword) => {
|
|||||||
|
|
||||||
const [result] = await pool.query(
|
const [result] = await pool.query(
|
||||||
"UPDATE users SET password = ? WHERE username = ?",
|
"UPDATE users SET password = ? WHERE username = ?",
|
||||||
[newPassword, username]
|
[newPassword, username],
|
||||||
);
|
);
|
||||||
if (result.affectedRows > 0) return { success: true };
|
if (result.affectedRows > 0) return { success: true };
|
||||||
return { success: false };
|
return { success: false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDeactivatedServices = async () => {
|
||||||
|
const [rows] = await pool.query("SELECT function_name FROM functions WHERE active = 0;");
|
||||||
|
if (rows.length > 0) {
|
||||||
|
return { success: true, data: rows };
|
||||||
|
}
|
||||||
|
return { success: false };
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ const user_frontend_service = "User Frontend";
|
|||||||
const contact_form_service = "Contact Form Service";
|
const contact_form_service = "Contact Form Service";
|
||||||
|
|
||||||
// database funcs import
|
// database funcs import
|
||||||
import { loginFunc, changePassword } from "./database/userMgmt.database.js";
|
import {
|
||||||
|
loginFunc,
|
||||||
|
changePassword,
|
||||||
|
getDeactivatedServices,
|
||||||
|
} from "./database/userMgmt.database.js";
|
||||||
import { sendMail } from "./services/mailer_v2.js";
|
import { sendMail } from "./services/mailer_v2.js";
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@@ -63,4 +67,13 @@ router.post(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get("/deactivated-services", authenticate, async (req, res) => {
|
||||||
|
const result = await getDeactivatedServices();
|
||||||
|
if (result.success) {
|
||||||
|
res.status(200).json(result.data);
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ message: "Failed to fetch deactivated services" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
+17
-5
@@ -3,6 +3,23 @@ import cors from "cors";
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import info from "./info.json" assert { type: "json" };
|
import info from "./info.json" assert { type: "json" };
|
||||||
import { authenticate } from "./services/authentication.js";
|
import { authenticate } from "./services/authentication.js";
|
||||||
|
import { rateLimit } from "express-rate-limit";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
const app = express();
|
||||||
|
const port = 8004;
|
||||||
|
const naasURL = process.env.NAAS_URL;
|
||||||
|
|
||||||
|
const limiter = rateLimit({
|
||||||
|
windowMs: 1 * 60 * 1000, // 1 minute
|
||||||
|
limit: 50, // Limit each IP to 50 requests per `window` (here, per 1 minute).
|
||||||
|
standardHeaders: "draft-8", // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
|
||||||
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
|
||||||
|
ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
|
||||||
|
// store: ... , // Redis, Memcached, etc. See below.
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(limiter);
|
||||||
|
|
||||||
// frontend routes
|
// frontend routes
|
||||||
import loansMgmtRouter from "./routes/app/loanMgmt.route.js";
|
import loansMgmtRouter from "./routes/app/loanMgmt.route.js";
|
||||||
@@ -19,11 +36,6 @@ import serverConfMgmtRouter from "./routes/admin/serverConfMgmt.route.js";
|
|||||||
// API routes
|
// API routes
|
||||||
import apiRouter from "./routes/api/api.route.js";
|
import apiRouter from "./routes/api/api.route.js";
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
const app = express();
|
|
||||||
const port = 8004;
|
|
||||||
const naasURL = process.env.NAAS_URL;
|
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
// Body-Parser VOR den Routen registrieren
|
// Body-Parser VOR den Routen registrieren
|
||||||
app.use(express.json({ limit: "10mb" }));
|
app.use(express.json({ limit: "10mb" }));
|
||||||
|
|||||||
Reference in New Issue
Block a user