Compare commits

..

37 Commits

Author SHA1 Message Date
theis.gaedigk 5c7a96912b Merge branch 'dev' into debian12 2026-04-18 15:00:51 +02:00
theis.gaedigk 195f270064 Merge branch 'dev' into debian12 2026-02-22 23:46:35 +01:00
theis.gaedigk 3d592c5c76 Merge branch 'dev' into debian12 2026-02-22 23:44:46 +01:00
theis.gaedigk 08104d32db Merge branch 'dev' into debian12 2026-02-20 16:30:34 +01:00
theis.gaedigk cc7c024892 Merge branch 'dev' into debian12 2026-02-07 17:48:44 +01:00
theis.gaedigk 3eb452aeab fixed version 2026-02-07 17:41:09 +01:00
theis.gaedigk f46a654184 Merge branch 'dev' into debian12 2026-02-07 17:40:47 +01:00
theis.gaedigk 863409aed9 Merge branch 'dev' into debian12 2026-02-04 13:47:04 +01:00
theis.gaedigk 052137a697 removed ports 2026-02-01 15:50:52 +01:00
theis.gaedigk 2f3583ccd0 Merge branch 'dev' into debian12 2026-01-28 18:26:08 +01:00
theis.gaedigk 9da72cc5bf Merge branch 'dev' into debian12 2026-01-28 13:06:19 +01:00
theis.gaedigk c633627b7c Merge branch 'dev' into debian12 2026-01-28 12:44:25 +01:00
theis.gaedigk 5259c41b13 Merge branch 'dev' into debian12 2026-01-27 21:29:08 +01:00
theis.gaedigk 3d9e3814fe edited docker 2026-01-27 10:33:32 +01:00
theis.gaedigk b44edb2b1d chnaged config 2026-01-16 17:17:15 +01:00
theis.gaedigk a72fabc0a0 Merge branch 'dev' into debian12 2026-01-16 17:11:30 +01:00
theis.gaedigk 1406f28f86 Merge branch 'dev' into debian12 2026-01-07 15:06:51 +01:00
theis.gaedigk 38d1091e9b Merge branch 'dev' into debian12 2025-11-30 21:23:22 +01:00
theis.gaedigk f82efecb8c edited docker config 2025-11-30 21:21:21 +01:00
theis.gaedigk 1f12bc8839 t 2025-11-30 21:17:36 +01:00
theis.gaedigk f19750f6f3 edited port config 2025-11-30 21:12:14 +01:00
theis.gaedigk 808b3fd5c4 Merge branch 'dev' into debian12 2025-11-30 21:07:32 +01:00
theis.gaedigk 0891598eb9 changed version info 2025-11-25 17:30:56 +01:00
theis.gaedigk 39ff02f2e7 Merge branch 'dev' into debian12 2025-11-25 17:11:27 +01:00
theis.gaedigk cc67fb4f85 changed version info 2025-11-24 15:35:03 +01:00
theis.gaedigk 75ff4aadc1 fixed color bug 2025-11-24 14:16:55 +01:00
theis.gaedigk 6f998d07c1 Merge branch 'dev' into debian12 2025-11-23 21:52:34 +01:00
theis.gaedigk f2bb326040 Merge branch 'dev' into debian12 2025-11-23 21:40:11 +01:00
theis.gaedigk 8c701db900 changed ports 2025-11-23 21:11:23 +01:00
theis.gaedigk d1664338a6 add networks configuration for frontend and backend services in docker-compose 2025-11-23 21:06:12 +01:00
theis.gaedigk 1a2624cd9e again 2025-11-23 20:34:19 +01:00
theis.gaedigk a138190cc6 fixed bugs 2025-11-23 20:32:14 +01:00
theis.gaedigk 993e0cd74b fixed bugs 2025-11-23 20:29:31 +01:00
theis.gaedigk dab004a7b6 changed docker config 2025-11-23 20:26:27 +01:00
theis.gaedigk d039336f39 Merge branch 'dev' into debian12 2025-11-23 20:20:41 +01:00
theis.gaedigk 4c781e9325 changed ports 2025-11-23 20:12:41 +01:00
theis.gaedigk 451e6b3646 published v2 2025-11-23 20:11:36 +01:00
13 changed files with 510 additions and 644 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ server {
} }
location /backend/ { location /backend/ {
proxy_pass http://borrow_system-backend_v2:8004/; proxy_pass http://borrow_system-backend_v2:8102/;
} }
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
+257 -325
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -12,7 +12,6 @@
"dependencies": { "dependencies": {
"@chakra-ui/react": "^3.28.0", "@chakra-ui/react": "^3.28.0",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@lottiefiles/dotlottie-react": "^0.19.0",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.11",
"@tanstack/react-query": "^5.90.5", "@tanstack/react-query": "^5.90.5",
"i18next": "^25.6.0", "i18next": "^25.6.0",
-28
View File
@@ -1,28 +0,0 @@
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
export const unlockAnimation = () => {
return (
<DotLottieReact
src="https://lottie.host/f839baa1-9c64-44c4-9386-f0e4c87ab208/2Iw1m4k86d.lottie"
autoplay
/>
);
};
export const approvalAnimation = () => {
return (
<DotLottieReact
src="https://lottie.host/b7257009-9e3f-43e2-8112-a176f4696e4c/iQxxqAVOGX.lottie"
autoplay
/>
);
};
export const logoutAnimation = () => {
return (
<DotLottieReact
src="https://lottie.host/4975758c-de38-4d15-9f74-927709751d32/v8FtKpnD1y.lottie"
autoplay
/>
);
};
+17 -9
View File
@@ -1,15 +1,23 @@
"use client" "use client";
import { ChakraProvider, defaultSystem } from "@chakra-ui/react" import { ChakraProvider, defaultSystem } from "@chakra-ui/react";
import { import * as React from "react";
ColorModeProvider, import type { ReactNode } from "react";
type ColorModeProviderProps, import { ColorModeProvider as ThemeColorModeProvider } from "./color-mode";
} from "./color-mode"
export function Provider(props: ColorModeProviderProps) { export interface ColorModeProviderProps {
children: React.ReactNode;
}
export function ColorModeProvider({ children }: ColorModeProviderProps) {
// Wrap children with the real color-mode provider
return <ThemeColorModeProvider>{children}</ThemeColorModeProvider>;
}
export function Provider({ children }: { children: ReactNode }) {
return ( return (
<ChakraProvider value={defaultSystem}> <ChakraProvider value={defaultSystem}>
<ColorModeProvider {...props} /> <ColorModeProvider>{children}</ColorModeProvider>
</ChakraProvider> </ChakraProvider>
) );
} }
+137 -157
View File
@@ -18,7 +18,6 @@ import { borrowAbleItemsAtom } from "@/states/Atoms";
import { createLoan } from "@/utils/Fetcher"; 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";
export interface User { export interface User {
username: string; username: string;
@@ -28,8 +27,6 @@ export interface User {
export const HomePage = () => { export const HomePage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showAnimation, setShowAnimation] = useState(false);
const [borrowableItems, setBorrowableItems] = useAtom(borrowAbleItemsAtom); const [borrowableItems, setBorrowableItems] = useAtom(borrowAbleItemsAtom);
const [startDate, setStartDate] = useState(""); const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState(""); const [endDate, setEndDate] = useState("");
@@ -49,172 +46,155 @@ export const HomePage = () => {
setSelectedItems((prevSelected) => setSelectedItems((prevSelected) =>
prevSelected.includes(itemId) prevSelected.includes(itemId)
? prevSelected.filter((id) => id !== itemId) ? prevSelected.filter((id) => id !== itemId)
: [...prevSelected, itemId], : [...prevSelected, itemId]
); );
}; };
const showApprovalAnimation = (seconds: number) => {
const milliseconds = seconds * 1000;
setShowAnimation(true);
window.setTimeout(() => {
setShowAnimation(false);
}, milliseconds);
};
return ( return (
<> <Container className="px-6 sm:px-8 pt-10">
{showAnimation && ( <Header />
<div className="fixed inset-0 z-9999 flex items-center justify-center pointer-events-none"> {isMsg && (
<div>{approvalAnimation()}</div> <MyAlert
</div> status={msgStatus}
title={msgTitle}
description={msgDescription}
/>
)} )}
<Container className="px-6 sm:px-8 pt-10"> <Stack as="main">
<Header /> <Text>{t("timezone-info")}</Text>
{isMsg && ( <label htmlFor="startDate">
<MyAlert <strong>
status={msgStatus} <Text>{t("start-date")}</Text>
title={msgTitle} </strong>
description={msgDescription} </label>
/> <Input
)} id="startDate"
<Stack as="main"> placeholder={t("start-date")}
<Text>{t("timezone-info")}</Text> type="datetime-local"
<label htmlFor="startDate"> value={startDate}
<strong> onChange={(e) => setStartDate(e.target.value)}
<Text>{t("start-date")}</Text> />
</strong> <label htmlFor="endDate">
</label> <strong>
<Input <Text>{t("end-date")}</Text>
id="startDate" </strong>
placeholder={t("start-date")} </label>
type="datetime-local" <Input
value={startDate} id="endDate"
onChange={(e) => setStartDate(e.target.value)} placeholder={t("end-date")}
/> type="datetime-local"
<label htmlFor="endDate"> value={endDate}
<strong> onChange={(e) => setEndDate(e.target.value)}
<Text>{t("end-date")}</Text> />
</strong> <Button
</label> onClick={async () => {
<Input setIsLoadingA(true);
id="endDate" if (!startDate || !endDate) {
placeholder={t("end-date")} setMsgStatus("error");
type="datetime-local" setMsgTitle(t("missing-fields"));
value={endDate} setMsgDescription(t("missing-fields-desc"));
onChange={(e) => setEndDate(e.target.value)} setIsMsg(true);
/> setIsLoadingA(false);
<Button return;
onClick={async () => { }
setIsLoadingA(true); await getBorrowableItems(startDate, endDate).then((response) => {
if (!startDate || !endDate) { setIsLoadingA(false);
if (response && response.status === "error") {
setMsgStatus("error"); setMsgStatus("error");
setMsgTitle(t("missing-fields")); setMsgTitle(response.title || t("error"));
setMsgDescription(t("missing-fields-desc")); setMsgDescription(response.description || t("unknown-error"));
setIsMsg(true); setIsMsg(true);
setIsLoadingA(false);
return; return;
} }
await getBorrowableItems(startDate, endDate).then((response) => { setBorrowableItems(response.data);
setIsLoadingA(false); setIsMsg(false);
if (response && response.status === "error") { });
setMsgStatus("error"); }}
setMsgTitle(response.title || t("error")); >
setMsgDescription(response.description || t("unknown-error")); {t("get-borrowable-items")}
setIsMsg(true); </Button>
return; {isLoadingA && (
} <VStack colorPalette="teal">
setBorrowableItems(response.data); <Spinner color="colorPalette.600" />
setIsMsg(false); <Text color="colorPalette.600">{t("loading")}</Text>
}); </VStack>
}} )}
> {borrowableItems.length > 0 && (
{t("get-borrowable-items")} <Table.ScrollArea borderWidth="1px" rounded="md">
</Button> <Table.Root size="sm" stickyHeader>
{isLoadingA && ( <Table.Header>
<VStack colorPalette="teal"> <Table.Row bg="bg.subtle">
<Spinner color="colorPalette.600" /> <Table.ColumnHeader></Table.ColumnHeader>
<Text color="colorPalette.600">{t("loading")}</Text> <Table.ColumnHeader>{t("item")}</Table.ColumnHeader>
</VStack> </Table.Row>
)} </Table.Header>
{borrowableItems.length > 0 && (
<Table.ScrollArea borderWidth="1px" rounded="md">
<Table.Root size="sm" stickyHeader>
<Table.Header>
<Table.Row bg="bg.subtle">
<Table.ColumnHeader></Table.ColumnHeader>
<Table.ColumnHeader>{t("item")}</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body> <Table.Body>
{borrowableItems.map((item) => ( {borrowableItems.map((item) => (
<Table.Row key={item.id}> <Table.Row key={item.id}>
<Table.Cell> <Table.Cell>
<input <input
onChange={() => handleCheckboxChange(item.id)} onChange={() => handleCheckboxChange(item.id)}
type="checkbox" type="checkbox"
name={item.id} name={item.id}
id={item.id} id={item.id}
/> />
</Table.Cell>
<Table.Cell>{item.item_name}</Table.Cell>
</Table.Row>
))}
<Table.Row>
<Table.Cell colSpan={2}>
<InputGroup
endElement={
<Span color="fg.muted" textStyle="xs">
{note.length} / {MAX_CHARACTERS}
</Span>
}
>
<Input
placeholder={t("optional-note")}
value={note}
maxLength={MAX_CHARACTERS}
onChange={(e) => {
setNote(
e.currentTarget.value.slice(0, MAX_CHARACTERS),
);
}}
/>
</InputGroup>
</Table.Cell> </Table.Cell>
<Table.Cell>{item.item_name}</Table.Cell>
</Table.Row> </Table.Row>
</Table.Body> ))}
</Table.Root> <Table.Row>
</Table.ScrollArea> <Table.Cell colSpan={2}>
)} <InputGroup
{selectedItems.length >= 1 && ( endElement={
<Button <Span color="fg.muted" textStyle="xs">
onClick={() => {note.length} / {MAX_CHARACTERS}
createLoan(selectedItems, startDate, endDate, note).then( </Span>
(response) => { }
if (response.status === "error") { >
setMsgStatus("error"); <Input
setMsgTitle(response.title || t("error")); placeholder={t("optional-note")}
setMsgDescription( value={note}
response.description || t("unknown-error"), maxLength={MAX_CHARACTERS}
); onChange={(e) => {
setIsMsg(true); setNote(
return; e.currentTarget.value.slice(0, MAX_CHARACTERS)
} );
showApprovalAnimation(3); }}
setMsgStatus("success"); />
setMsgTitle(t("success")); </InputGroup>
setMsgDescription(t("loan-success")); </Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
</Table.ScrollArea>
)}
{selectedItems.length >= 1 && (
<Button
onClick={() =>
createLoan(selectedItems, startDate, endDate, note).then(
(response) => {
if (response.status === "error") {
setMsgStatus("error");
setMsgTitle(response.title || t("error"));
setMsgDescription(
response.description || t("unknown-error")
);
setIsMsg(true); setIsMsg(true);
}, return;
) }
} setMsgStatus("success");
> setMsgTitle(t("success"));
{t("create-loan")} setMsgDescription(t("loan-success"));
</Button> setIsMsg(true);
)} }
</Stack> )
</Container> }
</> >
{t("create-loan")}
</Button>
)}
</Stack>
</Container>
); );
}; };
+52 -92
View File
@@ -4,47 +4,26 @@ import { Button, Card, Field, Input, Stack } from "@chakra-ui/react";
import { setIsLoggedInAtom, triggerLogoutAtom } from "@/states/Atoms"; import { setIsLoggedInAtom, triggerLogoutAtom } from "@/states/Atoms";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { useNavigate, useLocation } from "react-router-dom"; import { Navigate, useNavigate, useLocation } from "react-router-dom";
import { PasswordInput } from "@/components/ui/password-input"; import { PasswordInput } from "@/components/ui/password-input";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { API_BASE } from "@/config/api.config"; import { API_BASE } from "@/config/api.config";
import { unlockAnimation } from "@/components/dotLottie";
import { logoutAnimation } from "@/components/dotLottie";
export const LoginPage = () => { export const LoginPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [isLoggedIn, setIsLoggedIn] = useAtom(setIsLoggedInAtom); const [isLoggedIn, setIsLoggedIn] = useAtom(setIsLoggedInAtom);
const [triggerLogout, setTriggerLogout] = useAtom(triggerLogoutAtom); const [triggerLogout, setTriggerLogout] = useAtom(triggerLogoutAtom);
const [showAnimation, setShowAnimation] = useState(false);
const [showLogout, setShowLogout] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const from = location.state?.from?.pathname || "/"; const from = location.state?.from?.pathname || "/";
useEffect(() => { useEffect(() => {
if (triggerLogout) { if (isLoggedIn) {
setShowLogout(true);
window.setTimeout(() => {
setShowLogout(false);
}, 4500);
}
if (!isLoggedIn) return;
// Existing sessions should redirect immediately, fresh logins wait for animation.
if (!showAnimation) {
navigate(from, { replace: true }); navigate(from, { replace: true });
return; window.location.reload(); // if deleted, the user context is not updated in time
} }
}, [isLoggedIn, navigate, from]);
const timeoutId = window.setTimeout(() => {
navigate(from, { replace: true });
window.location.reload(); // keeps user context in sync after login
}, 3000);
return () => window.clearTimeout(timeoutId);
}, [isLoggedIn, showAnimation, navigate, from]);
const loginFnc = async (username: string, password: string) => { const loginFnc = async (username: string, password: string) => {
const response = await fetch(`${API_BASE}/api/users/login`, { const response = await fetch(`${API_BASE}/api/users/login`, {
@@ -63,8 +42,6 @@ export const LoginPage = () => {
}; };
} }
setShowAnimation(true);
Cookies.set("token", data.token); Cookies.set("token", data.token);
setIsLoggedIn(true); setIsLoggedIn(true);
return { success: true }; return { success: true };
@@ -85,75 +62,58 @@ export const LoginPage = () => {
return; return;
} }
setTriggerLogout(false); setTriggerLogout(false);
navigate(from, { replace: true });
}; };
if (isLoggedIn) {
return <Navigate to={from} replace />;
}
return ( return (
<> <div className="flex flex-1 items-center justify-center p-4">
{showAnimation && ( <form onSubmit={(e) => e.preventDefault()}>
<div className="fixed inset-0 z-9999 flex items-center justify-center pointer-events-none"> <Card.Root maxW="sm">
<div>{unlockAnimation()}</div> <Card.Header>
</div> <Card.Title>{t("login")}</Card.Title>
)} <Card.Description>{t("enter-credentials")}</Card.Description>
</Card.Header>
{showLogout && ( <Card.Body>
<div className="fixed inset-0 z-9999 flex items-center justify-center pointer-events-none"> <Stack gap="4" w="full">
<div>{logoutAnimation()}</div> <Field.Root>
</div> <Field.Label>{t("username")}</Field.Label>
)} <Input
value={username}
<div className="flex flex-1 items-center justify-center p-4"> onChange={(e) => setUsername(e.target.value)}
<form onSubmit={(e) => e.preventDefault()}>
<Card.Root maxW="sm">
<Card.Header>
<Card.Title>{t("login")}</Card.Title>
<Card.Description>{t("enter-credentials")}</Card.Description>
</Card.Header>
<Card.Body>
<Stack gap="4" w="full">
<Field.Root>
<Field.Label>{t("username")}</Field.Label>
<Input
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</Field.Root>
<Field.Root>
<Field.Label>{t("password")}</Field.Label>
<PasswordInput
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</Field.Root>
</Stack>
</Card.Body>
<Card.Footer justifyContent="flex-end">
{isError && (
<MyAlert
status="error"
title={errorMsg}
description={errorDsc}
/> />
)} </Field.Root>
<Button <Field.Root>
type="submit" <Field.Label>{t("password")}</Field.Label>
onClick={() => handleLogin()} <PasswordInput
variant="solid" value={password}
> onChange={(e) => setPassword(e.target.value)}
Login
</Button>
</Card.Footer>
<Card.Footer justifyContent="flex-end">
{triggerLogout && (
<MyAlert
status="success"
title={t("logout-success")}
description={t("logout-success-desc")}
/> />
)} </Field.Root>
</Card.Footer> </Stack>
</Card.Root> </Card.Body>
</form> <Card.Footer justifyContent="flex-end">
</div> {isError && (
</> <MyAlert status="error" title={errorMsg} description={errorDsc} />
)}
<Button type="submit" onClick={() => handleLogin()} variant="solid">
Login
</Button>
</Card.Footer>
<Card.Footer justifyContent="flex-end">
{triggerLogout && (
<MyAlert
status="success"
title={t("logout-success")}
description={t("logout-success-desc")}
/>
)}
</Card.Footer>
</Card.Root>
</form>
</div>
); );
}; };
+14 -7
View File
@@ -1,16 +1,23 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import tsconfigPaths from "vite-tsconfig-paths"; import path from "node:path";
export default defineConfig({ export default defineConfig({
plugins: [react(), svgr(), tailwindcss(), tsconfigPaths()], plugins: [tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
server: { server: {
host: "0.0.0.0", host: "0.0.0.0",
port: 8001, allowedHosts: ["insta.the1s.de"],
watch: { port: 8101,
usePolling: true, watch: { usePolling: true },
hmr: {
host: "insta.the1s.de",
port: 8101,
protocol: "wss",
}, },
}, },
}); });
+1 -1
View File
@@ -14,7 +14,7 @@ server {
} }
location /backend/ { location /backend/ {
proxy_pass http://borrow_system-backend_v2:8004/; proxy_pass http://borrow_system-backend_v2:8102/;
} }
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
+7 -3
View File
@@ -8,9 +8,13 @@ export default defineConfig({
plugins: [react(), svgr(), tailwindcss(), tsconfigPaths()], plugins: [react(), svgr(), tailwindcss(), tsconfigPaths()],
server: { server: {
host: "0.0.0.0", host: "0.0.0.0",
port: 8003, allowedHosts: ["admin.insta.the1s.de"],
watch: { port: 8103,
usePolling: true, watch: { usePolling: true },
hmr: {
host: "admin.insta.the1s.de",
port: 8103,
protocol: "wss",
}, },
}, },
}); });
+3 -3
View File
@@ -1,11 +1,11 @@
{ {
"backend-info": { "backend-info": {
"version": "v2.1.1 (dev)" "version": "v2.1.1"
}, },
"frontend-info": { "frontend-info": {
"version": "v2.1.2 (dev)" "version": "v2.1.2"
}, },
"admin-panel-info": { "admin-panel-info": {
"version": "v1.3.2 (dev)" "version": "v1.3.2"
} }
} }
+1 -1
View File
@@ -20,7 +20,7 @@ import apiRouter from "./routes/api/api.route.js";
env.config(); env.config();
const app = express(); const app = express();
const port = 8004; const port = 8102;
app.use(cors()); app.use(cors());
// Body-Parser VOR den Routen registrieren // Body-Parser VOR den Routen registrieren
+20 -16
View File
@@ -1,23 +1,23 @@
services: services:
# usr-frontend_v2: usr-frontend_v2:
# container_name: borrow_system-usr-frontend container_name: borrow_system-usr-frontend
# build: ./FrontendV2 networks:
# ports: - proxynet
# - "8001:80" build: ./FrontendV2
# restart: unless-stopped restart: unless-stopped
# admin-frontend: admin-frontend:
# container_name: borrow_system-admin-frontend container_name: borrow_system-admin-frontend
# build: ./admin networks:
# ports: - proxynet
# - "8003:80" build: ./admin
# restart: unless-stopped restart: unless-stopped
backend_v2: backend_v2:
container_name: borrow_system-backend_v2 container_name: borrow_system-backend_v2
networks:
- proxynet
build: ./backendV2 build: ./backendV2
ports:
- "8004:8004"
environment: environment:
NODE_ENV: production NODE_ENV: production
DB_HOST: mysql_v2 DB_HOST: mysql_v2
@@ -30,6 +30,8 @@ services:
mysql_v2: mysql_v2:
container_name: borrow_system-mysql-v2 container_name: borrow_system-mysql-v2
networks:
- proxynet
image: mysql:8.0 image: mysql:8.0
restart: unless-stopped restart: unless-stopped
environment: environment:
@@ -39,9 +41,11 @@ services:
volumes: volumes:
- mysql-v2-data:/var/lib/mysql - mysql-v2-data:/var/lib/mysql
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro - ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
ports:
- "3310:3306"
volumes: volumes:
mysql-data: mysql-data:
mysql-v2-data: mysql-v2-data:
networks:
proxynet:
external: true