Compare commits

..

32 Commits

Author SHA1 Message Date
theis.gaedigk 55c44cc639 Merge branch 'dev_demo' into debian12Demo 2026-04-18 14:59:57 +02:00
theis.gaedigk 34f133f94c Merge branch 'dev' into demoDev 2026-04-18 14:58:58 +02:00
theis.gaedigk 4155982aac edited version infos 2026-04-18 14:57:51 +02:00
theis.gaedigk 59e05e5c4c changed license 2026-04-15 19:37:59 +02:00
theis.gaedigk 2facdab011 added icon 2026-03-26 23:03:19 +01:00
theis.gaedigk 04db0bd7e0 edited icon 2026-03-26 23:01:05 +01:00
theis.gaedigk 40dd34bcac changed icon 2026-03-26 22:25:46 +01:00
theis.gaedigk 939d3e89c5 added footage and updated gitignore 2026-03-17 20:35:42 +01:00
theis.gaedigk a355c42964 added license 2026-02-23 21:37:54 +01:00
theis.gaedigk a3df8172e2 Merge branch 'demoDev' into debian12Demo 2026-02-22 23:50:32 +01:00
theis.gaedigk 98834a9270 Merge branch 'dev' into demoDev 2026-02-22 23:50:00 +01:00
theis.gaedigk c00f720af4 Merge branch 'demoDev' into debian12Demo 2026-02-20 16:34:21 +01:00
theis.gaedigk 8d04465705 Merge branch 'dev' into demoDev 2026-02-20 16:33:54 +01:00
theis.gaedigk 06cb298a38 Merge branch 'demoDev' into debian12Demo 2026-02-09 15:55:52 +01:00
theis.gaedigk 8589971dc8 added scheme to demo branch 2026-02-09 15:55:27 +01:00
theis.gaedigk 6ec8e19737 Merge branch 'dev' into demoDev 2026-02-09 15:51:25 +01:00
theis.gaedigk d29c793b6b Merge branch 'demoDev' into debian12Demo 2026-02-07 17:50:53 +01:00
theis.gaedigk 9f44a4796d Merge branch 'dev' into demoDev 2026-02-07 17:50:34 +01:00
theis.gaedigk c97cc8b538 Merge branch 'demoDev' into debian12Demo 2026-02-07 17:44:12 +01:00
theis.gaedigk dc0a68f7f1 fixed version info 2026-02-07 17:42:21 +01:00
theis.gaedigk fe3a06e5ce Merge branch 'dev' into demoDev 2026-02-07 17:41:57 +01:00
theis.gaedigk 776fab749d Merge branch 'demoDev' into debian12Demo 2026-02-04 13:48:28 +01:00
theis.gaedigk 179f5686d1 Merge branch 'dev' into demoDev 2026-02-04 13:47:33 +01:00
theis.gaedigk 83b43f4c83 fixed bug again 2026-02-01 16:30:18 +01:00
theis.gaedigk 5d9cee63ab fixed bug: cannot start container 2026-02-01 16:28:47 +01:00
theis.gaedigk 0b203d838c added sth for demo distr 2026-02-01 16:26:58 +01:00
theis.gaedigk ae1888fe90 added secret user 2026-02-01 16:20:00 +01:00
theis.gaedigk f1c02910e6 changed docker config and other 2026-02-01 16:03:16 +01:00
theis.gaedigk d33b288956 chengd nginx config 2026-02-01 16:00:12 +01:00
theis.gaedigk 5e2a426401 edited docker config 2026-02-01 15:50:31 +01:00
theis.gaedigk 022aa669e8 Merge branch 'demoDev' into debian12Demo 2026-02-01 15:49:05 +01:00
theis.gaedigk 28373e0231 changed version info 2026-02-01 15:47:51 +01:00
25 changed files with 146 additions and 139 deletions
+6 -1
View File
@@ -116,4 +116,9 @@ ToDo.txt
# only in development branch
next-env.d.ts
next-env.d.ts
# psd files from footage
footage/*.psd
icon/
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

+5 -1
View File
@@ -2,7 +2,11 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link
rel="icon"
type="image/png"
href="/icon_borrow-system-frontend_dark.png"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ausleihsystem</title>
</head>
+1 -1
View File
@@ -14,7 +14,7 @@ server {
}
location /backend/ {
proxy_pass http://borrow_system-backend_v2:8102/;
proxy_pass http://demo_borrow_system-backend_v2:8102/;
}
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "admin",
"private": true,
"version": "0.0.0",
"version": "v2.1.2 (dev)",
"type": "module",
"scripts": {
"dev": "vite",
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-shapes-icon lucide-shapes"><path d="M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z"/><rect x="3" y="14" width="7" height="7" rx="1"/><circle cx="17.5" cy="17.5" r="3.5"/></svg>

Before

Width:  |  Height:  |  Size: 420 B

-2
View File
@@ -16,7 +16,6 @@ import { Flex } from "@chakra-ui/react";
import { Footer } from "./components/footer/Footer";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { API_BASE } from "@/config/api.config";
import { ContactPage } from "./pages/ContactPage";
const queryClient = new QueryClient();
@@ -81,7 +80,6 @@ function App() {
<Route path="/" element={<HomePage />} />
<Route path="/my-loans" element={<MyLoansPage />} />
<Route path="/landingpage" element={<Landingpage />} />
<Route path="/contact" element={<ContactPage />} />
</Route>
<Route path="/login" element={<LoginPage />} />
-22
View File
@@ -22,7 +22,6 @@ import {
MoreVertical,
Languages,
Table,
ContactRound,
} from "lucide-react";
import { useUserContext } from "@/states/Context";
import { useState } from "react";
@@ -154,16 +153,6 @@ export const Header = () => {
</HStack>
}
/>
<Menu.Item
value="contact"
onSelect={() => navigate("/contact", { replace: true })}
children={
<HStack gap={3}>
<ContactRound size={16} />
<Text as="span">{t("contact")}</Text>
</HStack>
}
/>
<Menu.Separator />
<Menu.Item
value="logout"
@@ -289,17 +278,6 @@ export const Header = () => {
</HStack>
</Button>
</a>
<Button
variant={"outline"}
onClick={() => navigate("/contact", { replace: true })}
>
<HStack gap={2}>
<ContactRound size={18} />
<Text as="span">{t("contact")}</Text>
</HStack>
</Button>
<Button onClick={logout} variant="outline" colorScheme="red">
<HStack gap={2}>
<LogOut size={18} />
-84
View File
@@ -1,84 +0,0 @@
import {
Field,
Textarea,
Button,
Alert,
Container,
Text,
} from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { useState } from "react";
import { API_BASE } from "@/config/api.config";
import Cookies from "js-cookie";
import { Header } from "@/components/Header";
interface Alert {
type: "info" | "warning" | "success" | "error" | "neutral";
headline: string;
text: string;
}
export const ContactPage = () => {
const { t } = useTranslation();
const [message, setMessage] = useState("");
const [alert, setAlert] = useState<Alert | null>(null);
const sendMessage = async () => {
// Logic to send the message
const result = await fetch(`${API_BASE}/api/users/contact`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ message }),
});
if (result.ok) {
setAlert({
type: "success",
headline: t("contactPage_successHeadline"),
text: t("contactPage_successText"),
});
setMessage("");
} else {
setAlert({
type: "error",
headline: t("contactPage_errorHeadline"),
text: t("contactPage_errorText"),
});
}
};
return (
<Container className="px-6 sm:px-8 pt-10">
<Header />
<Field.Root invalid={message === ""}>
<Field.Label>
<Text>{t("contactPage_messageDescription")}</Text>
<Field.RequiredIndicator />
</Field.Label>
<Textarea
placeholder={t("contactPage_messagePlaceholder")}
variant="subtle"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
{message === "" && (
<Field.ErrorText>{t("contactPage_messageErrorText")}</Field.ErrorText>
)}
</Field.Root>
{alert && (
<Alert.Root status={alert.type}>
<Alert.Indicator />
<Alert.Content>
<Alert.Title>{alert.headline}</Alert.Title>
<Alert.Description>{alert.text}</Alert.Description>
</Alert.Content>
</Alert.Root>
)}
<Button onClick={sendMessage}>{t("contactPage_sendButton")}</Button>
</Container>
);
};
+1 -1
View File
@@ -68,7 +68,7 @@
"admin-status": "Admin-Status",
"first-name": "Vorname",
"last-name": "Nachname",
"app-title": "Ausleihsystem",
"app-title": "Ausleihsystem (demo)",
"last-borrowed-person": "Zuletzt ausgeliehen von",
"currently-borrowed-by": "Derzeit ausgeliehen von",
"back": "Zurückgehen",
+1 -1
View File
@@ -68,7 +68,7 @@
"admin-status": "Admin status",
"first-name": "First name",
"last-name": "Last name",
"app-title": "Borrow System",
"app-title": "Borrow System (demo)",
"last-borrowed-person": "Last borrowed by",
"currently-borrowed-by": "Currently borrowed by",
"back": "Go back",
+6
View File
@@ -0,0 +1,6 @@
Copyright (c) 2026 Theis Gaedigk
All rights reserved.
This source code is not to be copied, modified, or distributed in any form
without explicit written permission from the author.
+1 -1
View File
@@ -14,7 +14,7 @@ server {
}
location /backend/ {
proxy_pass http://borrow_system-backend_v2:8102/;
proxy_pass http://demo_borrow_system-backend_v2:8102/;
}
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "admin",
"private": true,
"version": "0.0.0",
"version": "v1.3.2 (dev)",
"type": "module",
"scripts": {
"dev": "vite",
@@ -47,4 +47,4 @@
"vite": "^7.1.0",
"vite-tsconfig-paths": "^5.1.4"
}
}
}
+3 -3
View File
@@ -1,11 +1,11 @@
{
"backend-info": {
"version": "v2.1.1"
"version": "v2.1.1 (demo)"
},
"frontend-info": {
"version": "v2.1.2"
"version": "v2.1.2 (demo)"
},
"admin-panel-info": {
"version": "v1.3.2"
"version": "v1.3.2 (demo)"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "backendv2",
"version": "1.0.0",
"version": "v2.1.1 (dev)",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
@@ -29,14 +29,14 @@ export const createUser = async (
};
export const deleteUserById = async (userId) => {
const [result] = await pool.query("DELETE FROM users WHERE id = ?", [userId]);
const [result] = await pool.query("DELETE FROM users WHERE id = ? AND secret_user = false", [userId]);
if (result.affectedRows > 0) return { success: true };
return { success: false };
};
export const changePassword = async (username, newPassword) => {
const [result] = await pool.query(
"UPDATE users SET password = ?, entry_updated_at = NOW() WHERE username = ?",
"UPDATE users SET password = ?, entry_updated_at = NOW() WHERE username = ? AND secret_user = false",
[newPassword, username],
);
if (result.affectedRows > 0) return { success: true };
@@ -52,7 +52,7 @@ export const editUserById = async (
is_admin,
) => {
const [result] = await pool.query(
"UPDATE users SET first_name = ?, last_name = ?, role = ?, email = ?, is_admin = ?, entry_updated_at = NOW() WHERE id = ?",
"UPDATE users SET first_name = ?, last_name = ?, role = ?, email = ?, is_admin = ?, entry_updated_at = NOW() WHERE id = ? AND secret_user = false",
[first_name, last_name, role, email, is_admin, userId],
);
if (result.affectedRows > 0) return { success: true };
@@ -61,7 +61,7 @@ export const editUserById = async (
export const getAllUsers = async () => {
const [result] = await pool.query(
"SELECT id, username, first_name, last_name, role, email, is_admin, entry_created_at, entry_updated_at FROM users",
"SELECT id, username, first_name, last_name, role, email, is_admin, entry_created_at, entry_updated_at FROM users WHERE secret_user = false",
);
if (result.length > 0) return { success: true, data: result };
return { success: false };
@@ -69,7 +69,7 @@ export const getAllUsers = async () => {
export const getUserById = async (userId) => {
const [rows] = await pool.query(
"SELECT id, username, first_name, last_name, role, email, is_admin FROM users WHERE id = ?",
"SELECT id, username, first_name, last_name, role, email, is_admin FROM users WHERE id = ? AND secret_user = false",
[userId],
);
if (rows.length === 0) {
+100
View File
@@ -0,0 +1,100 @@
USE borrow_system_new;
-- USERS
INSERT INTO users (username, password, email, first_name, last_name, role, is_admin)
VALUES
('user1', 'passwordhash1', 'user1@example.com', 'First1', 'Last1', 1, false),
('user2', 'passwordhash2', 'user2@example.com', 'First2', 'Last2', 1, false),
('user3', 'passwordhash3', 'user3@example.com', 'First3', 'Last3', 2, false),
('admin1', 'passwordhash4', 'admin1@example.com', 'Admin', 'One', 9, true),
('admin2', 'passwordhash5', 'admin2@example.com', 'Admin', 'Two', 9, true);
-- ITEMS
INSERT INTO items (item_name, can_borrow_role, in_safe, safe_nr, door_key, last_borrowed_person, currently_borrowing)
VALUES
('Item1', 1, true, 1, 101, NULL, NULL),
('Item2', 1, true, 2, 102, 'user1', 'user1'),
('Item3', 2, true, 3, 103, 'user2', NULL),
('Item4', 1, false, NULL, NULL, NULL, NULL),
('Item5', 2, false, NULL, NULL, 'user3', 'user3');
-- LOANS
INSERT INTO loans (
username,
lockers,
loan_code,
start_date,
end_date,
take_date,
returned_date,
created_at,
loaned_items_id,
loaned_items_name,
deleted,
note
)
VALUES
(
'user1',
JSON_ARRAY('Locker1', 'Locker2'),
'123456',
'2026-02-01 09:00:00',
'2026-02-10 17:00:00',
'2026-02-01 09:15:00',
NULL,
'2026-02-01 09:00:00',
JSON_ARRAY(1, 2),
JSON_ARRAY('Item1', 'Item2'),
false,
'Erste allgemeine Ausleihe'
),
(
'user2',
JSON_ARRAY('Locker3'),
'234567',
'2026-02-02 10:00:00',
'2026-02-05 16:00:00',
'2026-02-02 10:05:00',
'2026-02-05 15:30:00',
'2026-02-02 10:00:00',
JSON_ARRAY(3),
JSON_ARRAY('Item3'),
false,
'Zurückgegeben vor Enddatum'
),
(
'user3',
JSON_ARRAY(),
'345678',
'2026-02-03 08:30:00',
'2026-02-15 18:00:00',
NULL,
NULL,
'2026-02-03 08:30:00',
JSON_ARRAY(5),
JSON_ARRAY('Item5'),
false,
'Noch ausgeliehen'
),
(
'user1',
JSON_ARRAY('Locker4'),
'456789',
'2025-12-01 09:00:00',
'2025-12-03 17:00:00',
'2025-12-01 09:10:00',
'2025-12-03 16:45:00',
'2025-12-01 09:00:00',
JSON_ARRAY(1),
JSON_ARRAY('Item1'),
true,
'Alte, gelöschte Ausleihe'
);
-- API KEYS
INSERT INTO apiKeys (api_key, entry_name)
VALUES
('10000001', 'Entry1'),
('10000002', 'Entry2'),
('10000003', 'Entry3'),
('10000004', 'Entry4');
+1
View File
@@ -11,6 +11,7 @@ CREATE TABLE users (
is_admin bool NOT NULL DEFAULT false,
entry_created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
entry_updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
secret_user bool NOT NULL DEFAULT false,
PRIMARY KEY (id)
) ENGINE=InnoDB;
+12 -12
View File
@@ -1,35 +1,35 @@
services:
usr-frontend_v2:
container_name: borrow_system-usr-frontend
demo_usr_frontend:
container_name: demo_borrow_system-usr-frontend
networks:
- proxynet
build: ./FrontendV2
restart: unless-stopped
admin-frontend:
container_name: borrow_system-admin-frontend
demo_admin_frontend:
container_name: demo_borrow_system-admin-frontend
networks:
- proxynet
build: ./admin
restart: unless-stopped
backend_v2:
container_name: borrow_system-backend_v2
demo_backend_v2:
container_name: demo_borrow_system-backend_v2
networks:
- proxynet
build: ./backendV2
environment:
NODE_ENV: production
DB_HOST: mysql_v2
DB_HOST: demo_mysql_v2
DB_USER: root
DB_PASSWORD: ${DB_PASSWORD_V2}
DB_NAME: borrow_system_new
depends_on:
- mysql_v2
- demo_mysql_v2
restart: unless-stopped
mysql_v2:
container_name: borrow_system-mysql-v2
demo_mysql_v2:
container_name: demo_borrow_system-mysql-v2
networks:
- proxynet
image: mysql:8.0
@@ -39,12 +39,12 @@ services:
MYSQL_DATABASE: borrow_system_new
TZ: Europe/Berlin
volumes:
- mysql-v2-data:/var/lib/mysql
- demo_mysql-v2-data:/var/lib/mysql
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
volumes:
mysql-data:
mysql-v2-data:
demo_mysql-v2-data:
networks:
proxynet: