Compare commits
13
Commits
3ff31ecbf6
..
v2.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc2c3d7f54 | ||
|
|
8b89f31178 | ||
|
|
c7933dfa3b | ||
|
|
c1bb628f22 | ||
|
|
941f9105d6 | ||
|
|
0070b0e7a0 | ||
|
|
63d3e2db07 | ||
|
|
9c17b2a495 | ||
|
|
929ec5ef10 | ||
|
|
67c704009e | ||
|
|
831de8b610 | ||
|
|
fd43e84c0c | ||
|
|
1ce8d33b0d |
@@ -0,0 +1,14 @@
|
|||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
**/dist-ssr
|
||||||
|
.git
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
**/.env
|
||||||
|
**/.env.*
|
||||||
|
**/.DS_Store
|
||||||
|
**/*.log
|
||||||
|
docs
|
||||||
|
pics
|
||||||
|
icon
|
||||||
|
no-as-a-service
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
DB_PASSWORD=
|
||||||
|
BACKEND_URL=
|
||||||
|
MAIL_HOST=
|
||||||
|
MAIL_PORT=
|
||||||
|
MAIL_USER=
|
||||||
|
MAIL_PASSWORD=
|
||||||
|
MAIL_SENDEES_CONTACT=
|
||||||
|
MAIL_SENDEES=
|
||||||
|
NAAS_URL=
|
||||||
|
SECRET_KEY=
|
||||||
+3
-1
@@ -11,6 +11,8 @@ dist/
|
|||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
docker-compose.override.yml
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
@@ -119,6 +121,6 @@ ToDo.txt
|
|||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
# psd files from footage
|
# psd files from footage
|
||||||
footage/*.psd
|
pics/*.psd
|
||||||
|
|
||||||
icon/
|
icon/
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Docker
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
You have created an empty `docker-compose.override.yml` file in the root directory of this repository.
|
||||||
|
|
||||||
|
## Docker Network
|
||||||
|
|
||||||
|
If you want to run the stack in a docker network, you need to change the `BACKEND_HOST` variable in the root `.env` file
|
||||||
|
to `stockhome-backend`. To add a docker network, create a `docker-compose.override.yml` file in the root directory of
|
||||||
|
this repository with the following content:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
admin-frontend:
|
||||||
|
networks:
|
||||||
|
- proxynet
|
||||||
|
|
||||||
|
backend:
|
||||||
|
networks:
|
||||||
|
- proxynet
|
||||||
|
|
||||||
|
usr-frontend:
|
||||||
|
networks:
|
||||||
|
- proxynet
|
||||||
|
|
||||||
|
no-as-a-service:
|
||||||
|
networks:
|
||||||
|
- proxynet
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
networks:
|
||||||
|
- proxynet
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxynet:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
To start off I recommend to set the `BACKEND_HOST` variable to `localhost` and start the stack without a docker network.
|
||||||
|
After you have started the stack and everything is working, you can add a docker network.
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
### Change Exposed Ports
|
||||||
|
|
||||||
|
If you want to change the exposed ports, you can do this in the `docker-compose.yml` file like so:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
|
backend:
|
||||||
|
ports:
|
||||||
|
- "8004:8004"
|
||||||
|
|
||||||
|
usr-frontend:
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remove Exposed Ports
|
||||||
|
|
||||||
|
If you want to remove the exposed ports, you can do this in the `docker-compose.override.yml` file like so:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
ports: !override [ ]
|
||||||
|
|
||||||
|
backend:
|
||||||
|
ports: !override [ ]
|
||||||
|
|
||||||
|
usr-frontend:
|
||||||
|
ports: !override [ ]
|
||||||
|
```
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
FROM node:22-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
|
||||||
RUN npm ci
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM nginx:alpine AS runner
|
|
||||||
|
|
||||||
WORKDIR /usr/share/nginx/html
|
|
||||||
COPY --from=builder /app/dist .
|
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { ChakraProvider, defaultSystem } from "@chakra-ui/react";
|
|
||||||
import * as React from "react";
|
|
||||||
import type { ReactNode } from "react";
|
|
||||||
import { ColorModeProvider as ThemeColorModeProvider } from "./color-mode";
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<ChakraProvider value={defaultSystem}>
|
|
||||||
<ColorModeProvider>{children}</ColorModeProvider>
|
|
||||||
</ChakraProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { defineConfig } from "vite";
|
|
||||||
import tailwindcss from "@tailwindcss/vite";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [tailwindcss()],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
"@": path.resolve(__dirname, "src"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
host: "0.0.0.0",
|
|
||||||
allowedHosts: ["insta.the1s.de"],
|
|
||||||
port: 8101,
|
|
||||||
watch: { usePolling: true },
|
|
||||||
hmr: {
|
|
||||||
host: "insta.the1s.de",
|
|
||||||
port: 8101,
|
|
||||||
protocol: "wss",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -19,17 +19,21 @@ A small full‑stack system to log in, view available items, reserve them for a
|
|||||||
## Contents
|
## Contents
|
||||||
|
|
||||||
- Frontend: [frontend/](frontend)
|
- Frontend: [frontend/](frontend)
|
||||||
- Vite/Tailwind config: [frontend/vite.config.ts](frontend/vite.config.ts), [frontend/tailwind.config.js](frontend/tailwind.config.js)
|
- Vite/Tailwind
|
||||||
- App entry: [frontend/src/main.tsx](frontend/src/main.tsx), [frontend/src/App.tsx](frontend/src/App.tsx)
|
config: [frontend/vite.config.ts](frontend/vite.config.ts), [frontend/tailwind.config.js](frontend/tailwind.config.js)
|
||||||
- UI: [frontend/src/layout/Layout.tsx](frontend/src/layout/Layout.tsx), [frontend/src/components](frontend/src/components)
|
- App entry: [frontend/src/main.tsx](frontend/src/main.tsx), [frontend/src/App.tsx](frontend/src/App.tsx)
|
||||||
- Data/utilities: [frontend/src/utils/fetchData.ts](frontend/src/utils/fetchData.ts), [frontend/src/utils/userHandler.ts](frontend/src/utils/userHandler.ts), [frontend/src/utils/toastify.ts](frontend/src/utils/toastify.ts)
|
-
|
||||||
|
UI: [frontend/src/layout/Layout.tsx](frontend/src/layout/Layout.tsx), [frontend/src/components](frontend/src/components)
|
||||||
|
-
|
||||||
|
Data/utilities: [frontend/src/utils/fetchData.ts](frontend/src/utils/fetchData.ts), [frontend/src/utils/userHandler.ts](frontend/src/utils/userHandler.ts), [frontend/src/utils/toastify.ts](frontend/src/utils/toastify.ts)
|
||||||
- Backend: [backend/](backend)
|
- Backend: [backend/](backend)
|
||||||
- Server: [backend/server.js](backend/server.js)
|
- Server: [backend/server.js](backend/server.js)
|
||||||
- Routes: [backend/routes/api.js](backend/routes/api.js), [backend/routes/apiV2.js](backend/routes/apiV2.js)
|
- Routes: [backend/routes/api.js](backend/routes/api.js), [backend/routes/apiV2.js](backend/routes/apiV2.js)
|
||||||
- DB + services: [backend/services/database.js](backend/services/database.js), [backend/services/tokenService.js](backend/services/tokenService.js)
|
- DB +
|
||||||
- Schema/seed: [backend/scheme.sql](backend/scheme.sql)
|
services: [backend/services/database.js](backend/services/database.js), [backend/services/tokenService.js](backend/services/tokenService.js)
|
||||||
|
- Schema/seed: [backend/scheme.sql](backend/scheme.sql)
|
||||||
- Docs: [docs/](docs)
|
- Docs: [docs/](docs)
|
||||||
- API docs (see below): [docs/backend_API_docs/README.md](docs/backend_API_docs/README.md)
|
- API docs (see below): [docs/backend_API_docs/README.md](docs/backend_API_docs/README.md)
|
||||||
|
|
||||||
## Features (high‑level)
|
## Features (high‑level)
|
||||||
|
|
||||||
@@ -41,7 +45,8 @@ A small full‑stack system to log in, view available items, reserve them for a
|
|||||||
Key frontend utilities:
|
Key frontend utilities:
|
||||||
|
|
||||||
- [`utils.fetchData.fetchAllData`](frontend/src/utils/fetchData.ts): loads items, loans, and user loans after login.
|
- [`utils.fetchData.fetchAllData`](frontend/src/utils/fetchData.ts): loads items, loans, and user loans after login.
|
||||||
- [`utils.fetchData.getBorrowableItems`](frontend/src/utils/fetchData.ts): fetches borrowable items for the selected time range.
|
- [`utils.fetchData.getBorrowableItems`](frontend/src/utils/fetchData.ts): fetches borrowable items for the selected
|
||||||
|
time range.
|
||||||
- [`utils.userHandler.createLoan`](frontend/src/utils/userHandler.ts): creates a new loan for selected items.
|
- [`utils.userHandler.createLoan`](frontend/src/utils/userHandler.ts): creates a new loan for selected items.
|
||||||
- [`utils.userHandler.handleDeleteLoan`](frontend/src/utils/userHandler.ts): deletes a loan and syncs local state.
|
- [`utils.userHandler.handleDeleteLoan`](frontend/src/utils/userHandler.ts): deletes a loan and syncs local state.
|
||||||
- [`utils.toastify.myToast`](frontend/src/utils/toastify.ts): toast notifications.
|
- [`utils.toastify.myToast`](frontend/src/utils/toastify.ts): toast notifications.
|
||||||
@@ -54,20 +59,18 @@ UI flow (main screens):
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
- Scripts: see [frontend/package.json](frontend/package.json) and [backend/package.json](backend/package.json)
|
- The NAAS (no-as-a-service) and the mysql database are running in a docker container
|
||||||
- Frontend: `npm run dev`, `npm run build`, `npm run preview`, `npm run lint`
|
- `frontend`, `backend` and `admin` are both in a vite dev server and can be started by `npm run dev` in the root
|
||||||
- Backend: `npm start`
|
directory.
|
||||||
- Linting: ESLint configured via [frontend/eslint.config.js](frontend/eslint.config.js)
|
|
||||||
- TypeScript configs: [frontend/tsconfig.app.json](frontend/tsconfig.app.json), [frontend/tsconfig.node.json](frontend/tsconfig.node.json)
|
|
||||||
|
|
||||||
## Configuration notes
|
## Configuration notes
|
||||||
|
|
||||||
- Vite/Tailwind integration via [frontend/vite.config.ts](frontend/vite.config.ts) and `@tailwindcss/vite`; CSS entry uses `@import "tailwindcss"` in [frontend/src/index.css](frontend/src/index.css).
|
- Vite/Tailwind integration via [frontend/vite.config.ts](frontend/vite.config.ts) and `@tailwindcss/vite`; CSS entry
|
||||||
|
uses `@import "tailwindcss"` in [frontend/src/index.css](frontend/src/index.css).
|
||||||
- Toasts wired in [frontend/src/main.tsx](frontend/src/main.tsx) with `react-toastify`.
|
- Toasts wired in [frontend/src/main.tsx](frontend/src/main.tsx) with `react-toastify`.
|
||||||
- Local state is stored in `localStorage` keys: `allItems`, `allLoans`, `userLoans`, `borrowableItems`. Cross‑component updates are signaled via window events from [`utils.fetchData`](frontend/src/utils/fetchData.ts).
|
- Local state is stored in `localStorage` keys: `allItems`, `allLoans`, `userLoans`, `borrowableItems`. Cross‑component
|
||||||
|
updates are signaled via window events from [`utils.fetchData`](frontend/src/utils/fetchData.ts).
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
|
|
||||||
Refer to the dedicated API docs:
|
Refer to the dedicated [API-Docs](docs/backend_API_docs/README.md)
|
||||||
|
|
||||||
`docs/backend_API_docs/README.md`
|
|
||||||
|
|||||||
+14
-7
@@ -1,19 +1,26 @@
|
|||||||
FROM node:18 as builder
|
# Build context is the repo root (npm workspaces share one root package-lock.json)
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm ci
|
COPY frontend/package.json frontend/
|
||||||
|
COPY admin/package.json admin/
|
||||||
|
COPY backend/package.json backend/
|
||||||
|
RUN npm ci -w admin
|
||||||
|
|
||||||
COPY . .
|
ARG VITE_BACKEND_URL
|
||||||
RUN npm run build
|
ENV VITE_BACKEND_URL=$VITE_BACKEND_URL
|
||||||
|
|
||||||
|
COPY admin/ admin/
|
||||||
|
RUN npm run build -w admin
|
||||||
|
|
||||||
FROM nginx:alpine AS runner
|
FROM nginx:alpine AS runner
|
||||||
|
|
||||||
WORKDIR /usr/share/nginx/html
|
WORKDIR /usr/share/nginx/html
|
||||||
COPY --from=builder /app/dist .
|
COPY --from=builder /app/admin/dist .
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY admin/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /backend/ {
|
location /backend/ {
|
||||||
proxy_pass http://borrow_system-backend_v2:8102/;
|
proxy_pass http://borrow-system_backend:8004/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
||||||
|
|||||||
Generated
-6066
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "admin",
|
"name": "admin",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "v1.3.2 (dev)",
|
"version": "1.3.2-dev",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -8,13 +8,9 @@ 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",
|
||||||
allowedHosts: ["admin.insta.the1s.de"],
|
port: 8003,
|
||||||
port: 8103,
|
watch: {
|
||||||
watch: { usePolling: true },
|
usePolling: true,
|
||||||
hmr: {
|
|
||||||
host: "admin.insta.the1s.de",
|
|
||||||
port: 8103,
|
|
||||||
protocol: "wss",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Build context is the repo root (npm workspaces share one root package-lock.json)
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
COPY frontend/package.json frontend/
|
||||||
|
COPY admin/package.json admin/
|
||||||
|
COPY backend/package.json backend/
|
||||||
|
RUN npm ci -w backend --omit=dev
|
||||||
|
|
||||||
|
COPY backend/ backend/
|
||||||
|
|
||||||
|
# Generate info.json from the workspace package.json versions, without the "-dev" suffix
|
||||||
|
RUN node -e ' \
|
||||||
|
const fs = require("fs"); \
|
||||||
|
const v = (p) => "v" + JSON.parse(fs.readFileSync(p + "/package.json")).version.replace(/-dev$/, ""); \
|
||||||
|
fs.writeFileSync("backend/info.json", JSON.stringify({ \
|
||||||
|
"backend-info": { version: v("backend") }, \
|
||||||
|
"frontend-info": { version: v("frontend") }, \
|
||||||
|
"admin-panel-info": { version: v("admin") } \
|
||||||
|
}, null, 4)); \
|
||||||
|
'
|
||||||
|
|
||||||
|
WORKDIR /app/backend
|
||||||
|
EXPOSE 8004
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"backend-info": {
|
||||||
|
"version": "v2.2 (dev)"
|
||||||
|
},
|
||||||
|
"frontend-info": {
|
||||||
|
"version": "v2.2 (dev)"
|
||||||
|
},
|
||||||
|
"admin-panel-info": {
|
||||||
|
"version": "v1.4 (dev)"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "backendv2",
|
"name": "backend",
|
||||||
"version": "v2.1.1 (dev)",
|
"version": "2.1.1-dev",
|
||||||
|
"private": true,
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"dev": "node --watch server.js",
|
||||||
"start": "node server.js"
|
"start": "NODE_ENV=production node server.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -18,6 +19,6 @@
|
|||||||
"express-rate-limit": "^8.4.1",
|
"express-rate-limit": "^8.4.1",
|
||||||
"jose": "^6.0.12",
|
"jose": "^6.0.12",
|
||||||
"mysql2": "^3.14.3",
|
"mysql2": "^3.14.3",
|
||||||
"nodemailer": "^8.0.6"
|
"nodemailer": "^10.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -12,7 +12,7 @@ const pool = mysql
|
|||||||
.promise();
|
.promise();
|
||||||
|
|
||||||
export const getAllLoans = async () => {
|
export const getAllLoans = async () => {
|
||||||
const [rows] = await pool.query("SELECT * FROM loans");
|
const [rows] = await pool.query("SELECT * FROM loans WHERE deleted_admin = 0;");
|
||||||
return { success: true, data: rows };
|
return { success: true, data: rows };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,14 +119,6 @@ export const setTakeDateV2 = async (loanCode) => {
|
|||||||
return { message: "Failed to set take date", success: false };
|
return { message: "Failed to set take date", success: false };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllLoansV2 = async () => {
|
|
||||||
const [result] = await pool.query("SELECT * FROM loans;");
|
|
||||||
if (result.length > 0) {
|
|
||||||
return { success: true, data: result };
|
|
||||||
}
|
|
||||||
return { success: false };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const openDoor = async (doorKey) => {
|
export const openDoor = async (doorKey) => {
|
||||||
const [result] = await pool.query(
|
const [result] = await pool.query(
|
||||||
"SELECT safe_nr, id FROM items WHERE door_key = ?;",
|
"SELECT safe_nr, id FROM items WHERE door_key = ?;",
|
||||||
+17
-22
@@ -63,11 +63,9 @@ export const createLoanInDatabase = async (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemNames = itemIds
|
const itemNames = itemIds.map(
|
||||||
.map(
|
(id) => itemsRows.find((r) => Number(r.id) === Number(id)).item_name,
|
||||||
(id) => itemsRows.find((r) => Number(r.id) === Number(id))?.item_name,
|
);
|
||||||
)
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
// Build lockers array (unique, only 2-digit numbers from safe_nr)
|
// Build lockers array (unique, only 2-digit numbers from safe_nr)
|
||||||
const lockers = [
|
const lockers = [
|
||||||
@@ -109,27 +107,24 @@ export const createLoanInDatabase = async (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate unique loan_code (retry a few times)
|
let index = false;
|
||||||
let loanCode = null;
|
let candidate;
|
||||||
for (let i = 0; i < 6; i++) {
|
let loanCode;
|
||||||
const candidate = Math.floor(100000 + Math.random() * 899999); // 6 digits
|
|
||||||
const [exists] = await conn.query(
|
// Generates 6-digit loan code
|
||||||
"SELECT 1 FROM loans WHERE loan_code = ? LIMIT 1",
|
do {
|
||||||
|
candidate = Math.floor(100000 + Math.random() * 899999);
|
||||||
|
|
||||||
|
const [rows] = await conn.query(
|
||||||
|
"SELECT 1 FROM loans where loan_code = ? LIMIT 1",
|
||||||
[candidate],
|
[candidate],
|
||||||
);
|
);
|
||||||
if (exists.length === 0) {
|
|
||||||
|
if (rows.length == 0) {
|
||||||
|
index = true;
|
||||||
loanCode = candidate;
|
loanCode = candidate;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
} while (index === false);
|
||||||
if (!loanCode) {
|
|
||||||
await conn.rollback();
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
code: "SERVER_ERROR",
|
|
||||||
message: "Failed to generate unique loan code",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert loan (now includes lockers)
|
// Insert loan (now includes lockers)
|
||||||
const [insertRes] = await conn.query(
|
const [insertRes] = await conn.query(
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import cors from "cors";
|
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" with { type: "json" };
|
||||||
import { authenticate } from "./services/authentication.js";
|
import { authenticate } from "./services/authentication.js";
|
||||||
import { rateLimit } from "express-rate-limit";
|
import { rateLimit } from "express-rate-limit";
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
FROM node:20-alpine
|
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
WORKDIR /backend
|
|
||||||
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm ci --omit=dev
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
EXPOSE 8004
|
|
||||||
CMD ["npm", "start"]
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"backend-info": {
|
|
||||||
"version": "v2.2"
|
|
||||||
},
|
|
||||||
"frontend-info": {
|
|
||||||
"version": "v2.2"
|
|
||||||
},
|
|
||||||
"admin-panel-info": {
|
|
||||||
"version": "v1.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
-1141
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
|||||||
# Changelog for upcoming version: vX.X
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
|
|
||||||
## New features
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Improvements
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Fixed bugs
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## New version numbers
|
|
||||||
|
|
||||||
**Backend:** vX.X
|
|
||||||
|
|
||||||
**Frontend:** vX.X
|
|
||||||
|
|
||||||
**Admin panel:** vX.X
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
-[Theis](https://portfolio-theis.de)
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
services:
|
||||||
|
# usr-frontend_v2:
|
||||||
|
# container_name: borrow_system-usr-frontend
|
||||||
|
# build: ./FrontendV2
|
||||||
|
# ports:
|
||||||
|
# - "8001:80"
|
||||||
|
# restart: always
|
||||||
|
|
||||||
|
# admin-frontend:
|
||||||
|
# container_name: borrow_system-admin-frontend
|
||||||
|
# build: ./admin
|
||||||
|
# ports:
|
||||||
|
# - "8003:80"
|
||||||
|
# restart: always
|
||||||
|
|
||||||
|
# backend_v2:
|
||||||
|
# container_name: borrow_system-backend_v2
|
||||||
|
# build: ./backend
|
||||||
|
# ports:
|
||||||
|
# - "8004:8004"
|
||||||
|
# environment:
|
||||||
|
# NODE_ENV: production
|
||||||
|
# DB_HOST: mysql_v2
|
||||||
|
# DB_USER: root
|
||||||
|
# DB_PASSWORD: ${DB_PASSWORD_V2}
|
||||||
|
# DB_NAME: borrow_system_new
|
||||||
|
# depends_on:
|
||||||
|
# - mysql_v2
|
||||||
|
# restart: always
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
container_name: borrow-system_mysql
|
||||||
|
image: mysql:8.0
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD_V2}
|
||||||
|
MYSQL_DATABASE: borrow_system_new
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
volumes:
|
||||||
|
- mysql-v2-data:/var/lib/mysql
|
||||||
|
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
|
||||||
|
ports:
|
||||||
|
- "3310:3306"
|
||||||
|
|
||||||
|
no-as-a-service:
|
||||||
|
container_name: borrow-system_naas
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
build:
|
||||||
|
context: ./no-as-a-service
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
mysql-v2-data:
|
||||||
+38
-34
@@ -1,41 +1,51 @@
|
|||||||
services:
|
services:
|
||||||
usr-frontend_v2:
|
usr-frontend:
|
||||||
container_name: borrow_system-usr-frontend
|
container_name: borrow-system_usr-frontend
|
||||||
networks:
|
build:
|
||||||
- proxynet
|
context: .
|
||||||
build: ./FrontendV2
|
dockerfile: frontend/Dockerfile
|
||||||
restart: unless-stopped
|
args:
|
||||||
|
VITE_BACKEND_URL: ${BACKEND_URL}
|
||||||
|
restart: always
|
||||||
|
|
||||||
admin-frontend:
|
admin-frontend:
|
||||||
container_name: borrow_system-admin-frontend
|
container_name: borrow-system_admin-frontend
|
||||||
networks:
|
build:
|
||||||
- proxynet
|
context: .
|
||||||
build: ./admin
|
dockerfile: admin/Dockerfile
|
||||||
restart: unless-stopped
|
args:
|
||||||
|
VITE_BACKEND_URL: ${BACKEND_URL}
|
||||||
|
restart: always
|
||||||
|
|
||||||
backend_v2:
|
backend:
|
||||||
container_name: borrow_system-backend_v2
|
container_name: borrow-system_backend
|
||||||
networks:
|
build:
|
||||||
- proxynet
|
context: .
|
||||||
build: ./backendV2
|
dockerfile: backend/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
DB_HOST: mysql_v2
|
DB_HOST: borrow-system_mysql
|
||||||
DB_USER: root
|
DB_USER: root
|
||||||
DB_PASSWORD: ${DB_PASSWORD_V2}
|
DB_PASSWORD: ${DB_PASSWORD}
|
||||||
DB_NAME: borrow_system_new
|
DB_NAME: borrow_system_new
|
||||||
|
MAIL_HOST: ${MAIL_HOST}
|
||||||
|
MAIL_PORT: ${MAIL_PORT}
|
||||||
|
MAIL_USER: ${MAIL_USER}
|
||||||
|
MAIL_PASSWORD: ${MAIL_PASSWORD}
|
||||||
|
MAIL_SENDEES_CONTACT: ${MAIL_SENDEES_CONTACT}
|
||||||
|
MAIL_SENDEES: ${MAIL_SENDEES}
|
||||||
|
NAAS_URL: ${NAAS_URL}
|
||||||
|
SECRET_KEY: ${SECRET_KEY}
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql_v2
|
- mysql
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
|
|
||||||
mysql_v2:
|
mysql:
|
||||||
container_name: borrow_system-mysql-v2
|
container_name: borrow-system_mysql
|
||||||
networks:
|
|
||||||
- proxynet
|
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD_V2}
|
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
|
||||||
MYSQL_DATABASE: borrow_system_new
|
MYSQL_DATABASE: borrow_system_new
|
||||||
TZ: Europe/Berlin
|
TZ: Europe/Berlin
|
||||||
volumes:
|
volumes:
|
||||||
@@ -43,9 +53,7 @@ services:
|
|||||||
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
|
- ./mysql-timezone.cnf:/etc/mysql/conf.d/timezone.cnf:ro
|
||||||
|
|
||||||
no-as-a-service:
|
no-as-a-service:
|
||||||
container_name: borrow_system-naas
|
container_name: borrow-system_naas
|
||||||
networks:
|
|
||||||
- proxynet
|
|
||||||
build:
|
build:
|
||||||
context: ./no-as-a-service
|
context: ./no-as-a-service
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@@ -53,8 +61,4 @@ services:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mysql-data:
|
mysql-data:
|
||||||
mysql-v2-data:
|
mysql-v2-data:
|
||||||
|
|
||||||
networks:
|
|
||||||
proxynet:
|
|
||||||
external: true
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Build context is the repo root (npm workspaces share one root package-lock.json)
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
COPY frontend/package.json frontend/
|
||||||
|
COPY admin/package.json admin/
|
||||||
|
COPY backend/package.json backend/
|
||||||
|
RUN npm ci -w frontend
|
||||||
|
|
||||||
|
ARG VITE_BACKEND_URL
|
||||||
|
ENV VITE_BACKEND_URL=$VITE_BACKEND_URL
|
||||||
|
|
||||||
|
COPY frontend/ frontend/
|
||||||
|
RUN npm run build -w frontend
|
||||||
|
|
||||||
|
FROM nginx:alpine AS runner
|
||||||
|
|
||||||
|
WORKDIR /usr/share/nginx/html
|
||||||
|
COPY --from=builder /app/frontend/dist .
|
||||||
|
|
||||||
|
COPY frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -14,7 +14,7 @@ server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /backend/ {
|
location /backend/ {
|
||||||
proxy_pass http://borrow_system-backend_v2:8004/;
|
proxy_pass http://borrow-system_backend:8004/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "admin",
|
"name": "frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "v2.1.2 (dev)",
|
"version": "2.1.2-dev",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
|
"prettier": "^3.9.10",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"typescript-eslint": "^8.39.0",
|
"typescript-eslint": "^8.39.0",
|
||||||
"vite": "^7.1.0",
|
"vite": "^7.1.0",
|
||||||
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
|
||||||
|
import {
|
||||||
|
ColorModeProvider,
|
||||||
|
type ColorModeProviderProps,
|
||||||
|
} from "./color-mode"
|
||||||
|
|
||||||
|
export function Provider(props: ColorModeProviderProps) {
|
||||||
|
return (
|
||||||
|
<ChakraProvider value={defaultSystem}>
|
||||||
|
<ColorModeProvider {...props} />
|
||||||
|
</ChakraProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -14,5 +14,5 @@ createRoot(document.getElementById("root")!).render(
|
|||||||
<Provider>
|
<Provider>
|
||||||
<App />
|
<App />
|
||||||
</Provider>
|
</Provider>
|
||||||
</StrictMode>
|
</StrictMode>,
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import svgr from "vite-plugin-svgr";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), svgr(), tailwindcss(), tsconfigPaths()],
|
||||||
|
server: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8001,
|
||||||
|
watch: {
|
||||||
|
usePolling: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[mysqld]
|
||||||
|
default-time-zone = Europe/Berlin
|
||||||
+1
-1
Submodule no-as-a-service updated: 764062a307...e6b4218394
+2989
-1703
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "borrow-system",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"private": true,
|
||||||
|
"workspaces": [
|
||||||
|
"backend",
|
||||||
|
"frontend",
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"directories": {
|
||||||
|
"doc": "docs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"concurrently": "^10.0.6",
|
||||||
|
"prettier": "^3.9.6"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev:docker": "docker compose -f docker-compose.dev.yml up -d",
|
||||||
|
"dev": "npm run dev:docker && concurrently --kill-others \"npm:dev:frontend\" \"npm:dev:backend\" \"npm:dev:admin\"",
|
||||||
|
"dev:frontend": "npm run dev -w frontend",
|
||||||
|
"dev:backend": "npm run dev -w backend",
|
||||||
|
"dev:admin": "npm run dev -w admin",
|
||||||
|
"dev:stop": "docker compose -f docker-compose.dev.yml down"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.the1s.de/theis.gaedigk/borrow-system.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user