Compare commits

..

4 Commits

Author SHA1 Message Date
4d0096c064 Zastavení serveru pomocí SIGINT 2023-07-22 19:39:13 +02:00
347cbc7228 Úprava socket.io pro relativní URL serveru 2023-07-22 19:38:44 +02:00
3c0e8b2297 Deduplikace typů a sloučení kontejnerů
- Zavedení yarn workspaces
- Sloučení klienta a serveru do jednoho Docker kontejneru
- Společný dockerfile, builder
- Zbavení se nginx (není již potřeba)
2023-07-22 19:37:03 +02:00
0d0c5cb946 Aktualizace .gitignore 2023-07-22 19:30:31 +02:00
37 changed files with 477 additions and 11512 deletions

1
.gitignore vendored
View File

@ -10,7 +10,6 @@ node_modules
# production # production
/build /build
dist
# misc # misc
.DS_Store .DS_Store

46
Dockerfile Normal file
View File

@ -0,0 +1,46 @@
# Builder
FROM node:18-alpine3.18 as builder
ENV LANG cs_CZ.UTF-8
WORKDIR /build
COPY package.json .
COPY yarn.lock .
COPY server/package.json ./server/
COPY client/package.json ./client/
RUN yarn install --frozen-lockfile
COPY server/tsconfig.json ./server/
COPY server/src ./server/src/
COPY client/tsconfig.json ./client/
COPY client/src ./client/src
COPY client/public ./client/public
COPY types ./types/
WORKDIR /build/server
RUN yarn build
WORKDIR /build/client
RUN yarn build
# Runner
FROM node:18-alpine3.18
ENV NODE_ENV production
WORKDIR /app
COPY --from=builder /build/node_modules ./node_modules
COPY --from=builder /build/server/dist ./
COPY --from=builder /build/client/build ./public
COPY /server/.env.production ./server/src
RUN echo "Test root" && ls -la
EXPOSE 3000
CMD [ "node", "./server/src/index.js" ]

View File

@ -1,15 +1,13 @@
# Luncher # Luncher
Aplikace pro profesionální management obědů. Aplikace pro profesionální management obědů.
Aplikace sestává ze tří (čtyř) modulů. Aplikace sestává ze tří modulů.
- api - types
- společné Typescript definice, pro objekty posílané mezi serverem a klientem - společné TypeScript definice, pro objekty posílané mezi serverem a klientem
- server - server
- backend psaný v [node.js](https://nodejs.dev) - backend psaný v [node.js](https://nodejs.dev)
- client - client
- frontend psaný v [React.js](https://react.dev) - frontend psaný v [React.js](https://react.dev)
- [nginx](https://nginx.org)
- proxy pro snadné propojení Docker kontejnerů pod jednou URL
## Spuštění pro vývoj ## Spuštění pro vývoj
### Závislosti ### Závislosti
@ -19,7 +17,6 @@ Aplikace sestává ze tří (čtyř) modulů.
### Spuštění na *nix platformách ### Spuštění na *nix platformách
- Nainstalovat závislosti viz předchozí bod - Nainstalovat závislosti viz předchozí bod
- Zkopírovat `client/.env.template` do `client/.env.development` a upravit dle potřeby
- Zkopírovat `server/.env.template` do `server/.env.development` a upravit dle potřeby - Zkopírovat `server/.env.template` do `server/.env.development` a upravit dle potřeby
- Spustit `./run_dev.sh`. Na jiných platformách se lze inspirovat jeho obsahem, postup by měl být víceméně stejný. - Spustit `./run_dev.sh`. Na jiných platformách se lze inspirovat jeho obsahem, postup by měl být víceméně stejný.
@ -28,9 +25,6 @@ Aplikace sestává ze tří (čtyř) modulů.
- [Docker](https://www.docker.com) - [Docker](https://www.docker.com)
- [Docker Compose](https://docs.docker.com/compose) - [Docker Compose](https://docs.docker.com/compose)
### Spuštění s nginx
- `docker compose up --build -d`
### Spuštení s traefik ### Spuštení s traefik
- `docker compose -f compose-traefik.yml up --build -d` - `docker compose -f compose-traefik.yml up --build -d`
@ -55,8 +49,7 @@ Aplikace sestává ze tří (čtyř) modulů.
- [ ] Ceny krabic za pizzu jsou napevno v kódu - problém, pokud se někdy změní - [ ] Ceny krabic za pizzu jsou napevno v kódu - problém, pokud se někdy změní
- [ ] Předvyplnění poslední vybrané hodnoty občas nefunguje, viz komentář - [ ] Předvyplnění poslední vybrané hodnoty občas nefunguje, viz komentář
- [ ] Nasazení nové verze v Docker smaže veškerá data (protože data.json není venku) - [ ] Nasazení nové verze v Docker smaže veškerá data (protože data.json není venku)
- [ ] Vylepšit dokumentaci projektu - [x] Vylepšit dokumentaci projektu
- [ ] Popsat nginx
- [x] Popsat závislosti, co je nutné provést před vývojem a postup spuštění pro vývoj - [x] Popsat závislosti, co je nutné provést před vývojem a postup spuštění pro vývoj
- [x] Popsat dostupné env - [x] Popsat dostupné env
- [x] Přesunout autentizaci na server (JWT?) - [x] Přesunout autentizaci na server (JWT?)
@ -69,7 +62,7 @@ Aplikace sestává ze tří (čtyř) modulů.
- [ ] Možnost náhledu na jiné dny v týdnu (např. pomocí šipek) - [ ] Možnost náhledu na jiné dny v týdnu (např. pomocí šipek)
- [ ] Možnost zadat si oběd dopředu na následující dny v týdnu - [ ] Možnost zadat si oběd dopředu na následující dny v týdnu
- [x] Zbavit se Food API, potřebnou funkcionalitu zahrnout do serveru - [x] Zbavit se Food API, potřebnou funkcionalitu zahrnout do serveru
- [ ] Vyřešit API mezi serverem a klientem, aby nebyl v obou projektech duplicitní kód (viz types.ts a Types.tsx) - [x] Vyřešit API mezi serverem a klientem, aby nebyl v obou projektech duplicitní kód (viz types.ts a Types.tsx)
- [ ] Mazat z databáze předchozí dny, aktuálně je to k ničemu - [ ] Mazat z databáze předchozí dny, aktuálně je to k ničemu
- [ ] Vylepšit parsery restaurací - [ ] Vylepšit parsery restaurací
- [ ] Sladovnická - [ ] Sladovnická

View File

@ -1 +0,0 @@
# Společné Typescript definice serveru a klienta

View File

@ -1,14 +0,0 @@
{
"name": "@luncher/api",
"version": "1.0.0",
"main": "src/index.ts",
"scripts": {
"build": "yarn clean && tsc -p .",
"build:watch": "tsc -p . --watch",
"clean": "rm -rf dist"
},
"types": "dist/index.d.ts",
"devDependencies": {
"typescript": "^5.0.2"
}
}

View File

@ -1 +0,0 @@
export * as Types from './Types';

View File

@ -1,9 +0,0 @@
{
"compilerOptions": {
"declaration": true,
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}

View File

@ -1,8 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
typescript@^5.0.2:
version "5.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==

View File

@ -1,3 +0,0 @@
**/node_modules
**/npm-debug.log
build

View File

@ -1,3 +0,0 @@
# Veřejná URL, na které bude dostupný klient (typicky přes proxy).
# Pro vývoj není potřeba, bude použita výchozí hodnota http://127.0.0.1:3001
# PUBLIC_URL=http://example:3001

2
client/.gitignore vendored
View File

@ -1,2 +1,2 @@
build build
.env.production src/types

View File

@ -1,23 +0,0 @@
FROM node:18-alpine3.18 AS builder
COPY package.json .
COPY yarn.lock .
COPY tsconfig.json .
COPY .env.production .
RUN yarn install
COPY ./src ./src
COPY ./public ./public
RUN yarn build
FROM node:18-alpine3.18
ENV NODE_ENV production
WORKDIR /app
COPY --from=builder /build .
EXPOSE 3000
RUN yarn global add serve && yarn
CMD ["serve", "-s", "."]

View File

@ -1,2 +0,0 @@
#!/bin/bash
docker build -t luncher-client .

View File

@ -1,8 +1,9 @@
{ {
"name": "luncher-client", "name": "@luncher/client",
"version": "0.1.0", "version": "0.1.0",
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"homepage": ".",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0",
@ -16,7 +17,6 @@
"@types/react": "^18.0.33", "@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"bootstrap": "^5.2.3", "bootstrap": "^5.2.3",
"@luncher/api": "1.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.7.2", "react-bootstrap": "^2.7.2",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -30,8 +30,9 @@
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "copy-types": "cp -r ../types ./src",
"build": "react-scripts build", "start": "yarn copy-types && react-scripts start",
"build": "yarn copy-types && react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },

View File

@ -1,4 +1,4 @@
import { PizzaOrder } from "@luncher/api/dist/Types"; import { PizzaOrder } from "./types";
import { getBaseUrl, getToken } from "./Utils"; import { getBaseUrl, getToken } from "./Utils";
async function request<TResponse>( async function request<TResponse>(

View File

@ -14,7 +14,7 @@ import './App.css';
import { SelectSearchOption } from 'react-select-search'; import { SelectSearchOption } from 'react-select-search';
import { faTrashCan } from '@fortawesome/free-regular-svg-icons'; import { faTrashCan } from '@fortawesome/free-regular-svg-icons';
import { useBank } from './context/bank'; import { useBank } from './context/bank';
import { ClientData, Restaurants, Food, Pizza, Order, Locations, PizzaOrder, PizzaDayState } from '@luncher/api/dist/Types'; import { ClientData, Restaurants, Food, Pizza, Order, Locations, PizzaOrder, PizzaDayState } from './types';
const EVENT_CONNECT = "connect" const EVENT_CONNECT = "connect"
@ -127,7 +127,7 @@ function App() {
const handlePizzaChange = async (value: SelectedOptionValue | SelectedOptionValue[]) => { const handlePizzaChange = async (value: SelectedOptionValue | SelectedOptionValue[]) => {
if (auth?.login && pizzy) { if (auth?.login && pizzy) {
if (!(value instanceof String)) { if (!(typeof value === 'string')) {
throw Error('Nepodporovaný typ hodnoty'); throw Error('Nepodporovaný typ hodnoty');
} }
const s = value.split('|'); const s = value.split('|');

View File

@ -3,7 +3,7 @@ import { Table } from "react-bootstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTrashCan } from "@fortawesome/free-regular-svg-icons"; import { faTrashCan } from "@fortawesome/free-regular-svg-icons";
import { useAuth } from "../context/auth"; import { useAuth } from "../context/auth";
import { Order, PizzaDayState, PizzaOrder } from "@luncher/api/dist/Types"; import { Order, PizzaDayState, PizzaOrder } from "../types";
export default function PizzaOrderList({ state, orders, onDelete }: { state: PizzaDayState, orders: Order[], onDelete: (pizzaOrder: PizzaOrder) => void }) { export default function PizzaOrderList({ state, orders, onDelete }: { state: PizzaDayState, orders: Order[], onDelete: (pizzaOrder: PizzaOrder) => void }) {
const auth = useAuth(); const auth = useAuth();

View File

@ -2,8 +2,10 @@ import React from 'react';
import socketio from "socket.io-client"; import socketio from "socket.io-client";
import { getBaseUrl } from "../Utils"; import { getBaseUrl } from "../Utils";
// Hack pro socket.io, který si s tečkou neumí poradit
const socketUrl = getBaseUrl() === '.' ? '/' : getBaseUrl();
// Záměrně omezeno jen na websocket, aby se případně odhalilo chybné nastavení proxy serveru // Záměrně omezeno jen na websocket, aby se případně odhalilo chybné nastavení proxy serveru
export const socket = socketio.connect(getBaseUrl(), { transports: ["websocket"] }); export const socket = socketio.connect(socketUrl, { transports: ["websocket"] });
export const SocketContext = React.createContext(); export const SocketContext = React.createContext();
// Konstanty websocket eventů, musí odpovídat těm na serveru! // Konstanty websocket eventů, musí odpovídat těm na serveru!

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,9 @@
version: '3.8' version: '3.8'
services: services:
server: luncher:
build:
context: ./server
client:
build:
context: ./client
nginx:
depends_on:
- server
- client
restart: always restart: always
build: build:
context: ./nginx context: ./
ports: ports:
- 3005:80 - 3001:3001

View File

@ -1,2 +0,0 @@
FROM nginx
COPY ./default.conf /etc/nginx/conf.d/default.conf

View File

@ -1,37 +0,0 @@
upstream client {
server client:3000;
}
upstream server {
server server:3001;
}
server {
listen 80;
location / {
proxy_pass http://client;
}
location /static {
proxy_pass http://client;
}
location /sockjs-node {
proxy_pass http://client;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /socket.io {
proxy_pass http://server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /api {
proxy_pass http://server;
}
}

View File

@ -1,8 +1,8 @@
{ {
"private": true, "private": true,
"workspaces": [ "workspaces": [
"api", "client",
"server", "server",
"client" "types"
] ]
} }

View File

@ -1,6 +1,5 @@
export NODE_ENV=development export NODE_ENV=development
yarn install yarn install
cd api && yarn build &
cd server && yarn start & cd server && yarn start &
cd client && yarn start & cd client && yarn start &
wait wait

View File

@ -1,18 +0,0 @@
FROM node:18-alpine3.18
ENV LANG cs_CZ.UTF-8
WORKDIR /app
COPY package.json .
COPY yarn.lock .
COPY .env.production .
COPY tsconfig.json .
COPY src ./src
RUN yarn install --frozen-lockfile
RUN yarn build
EXPOSE 3001
CMD [ "node", "/app/dist/index.js" ]

View File

@ -1,3 +0,0 @@
#!/bin/bash
yarn install --frozen-lockfile && yarn build
docker build -t luncher-server .

View File

@ -25,7 +25,6 @@
"dotenv": "^16.1.3", "dotenv": "^16.1.3",
"express": "^4.18.2", "express": "^4.18.2",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"@luncher/api": "1.0.0",
"simple-json-db": "^2.0.0", "simple-json-db": "^2.0.0",
"socket.io": "^4.6.1" "socket.io": "^4.6.1"
} }

View File

@ -9,10 +9,15 @@ import path from 'path';
import { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku } from "./restaurants"; import { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku } from "./restaurants";
import { getQr } from "./qr"; import { getQr } from "./qr";
import { generateToken, getLogin, verify } from "./auth"; import { generateToken, getLogin, verify } from "./auth";
import { Restaurants } from "@luncher/api/dist/Types"; import { Restaurants } from "../../types";
const ENVIRONMENT = process.env.NODE_ENV || 'production'; const ENVIRONMENT = process.env.NODE_ENV || 'production';
dotenv.config({ path: path.resolve(__dirname, `../.env.${ENVIRONMENT}`) }); dotenv.config({ path: path.resolve(__dirname, `./.env.${ENVIRONMENT}`) });
// Validace nastavení JWT tokenu - nemá bez něj smysl vůbec povolit server spustit
if (!process.env.JWT_SECRET) {
throw Error("Není vyplněna proměnná prostředí JWT_SECRET");
}
const app = express(); const app = express();
const server = require("http").createServer(app); const server = require("http").createServer(app);
@ -30,6 +35,8 @@ app.use(cors({
origin: '*' origin: '*'
})); }));
app.use(express.static('public'))
const parseToken = (req: any) => { const parseToken = (req: any) => {
if (req?.headers?.authorization) { if (req?.headers?.authorization) {
return req.headers.authorization.split(' ')[1]; return req.headers.authorization.split(' ')[1];
@ -225,4 +232,8 @@ server.listen(PORT, () => {
console.log(`Server listening on ${HOST}, port ${PORT}`); console.log(`Server listening on ${HOST}, port ${PORT}`);
}); });
console.log(process.env.API_KEY) // Umožníme vypnutí serveru přes SIGINT, jinak Docker čeká než ho sestřelí
process.on('SIGINT', function () {
console.log("\nSIGINT (Ctrl-C), vypínám server");
process.exit(0);
});

View File

@ -1,5 +1,5 @@
/** Notifikace pro gotify*/ /** Notifikace pro gotify*/
import { GotifyServer, NotififaceInput, NotifikaceData } from '@luncher/api/dist/Types'; import { GotifyServer, NotififaceInput, NotifikaceData } from '../../types';
import axios, { AxiosError } from 'axios'; import axios, { AxiosError } from 'axios';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import path from 'path'; import path from 'path';

View File

@ -4,7 +4,7 @@ import path from "path";
import fs from "fs"; import fs from "fs";
import { load } from 'cheerio'; import { load } from 'cheerio';
import { formatDate } from "./utils"; import { formatDate } from "./utils";
import { Food } from "@luncher/api/dist/Types"; import { Food } from "../../types";
// Fráze v názvech jídel, které naznačují že se jedná o polévku // Fráze v názvech jídel, které naznačují že se jedná o polévku
const SOUP_NAMES = ['polévka', 'česnečka', 'česnekový krém', 'cibulačka', 'vývar'] const SOUP_NAMES = ['polévka', 'česnečka', 'česnekový krém', 'cibulačka', 'vývar']

View File

@ -2,7 +2,7 @@ import { db } from "./database";
import { formatDate, getHumanDate, getIsWeekend } from "./utils"; import { formatDate, getHumanDate, getIsWeekend } from "./utils";
import { callNotifikace } from "./notifikace"; import { callNotifikace } from "./notifikace";
import { generateQr } from "./qr"; import { generateQr } from "./qr";
import { ClientData, PizzaDayState, UdalostEnum, Pizza, PizzaSize, Order, PizzaOrder, Locations } from "@luncher/api/dist/Types"; import { ClientData, PizzaDayState, UdalostEnum, Pizza, PizzaSize, Order, PizzaOrder, Locations } from "../../types";
/** Vrátí dnešní datum, případně fiktivní datum pro účely vývoje a testování. */ /** Vrátí dnešní datum, případně fiktivní datum pro účely vývoje a testování. */
function getToday(): Date { function getToday(): Date {

View File

@ -1,4 +1,8 @@
{ {
"include": [
"src/**/*",
"../types/**/*"
],
"compilerOptions": { "compilerOptions": {
"target": "ES2016", "target": "ES2016",
"module": "CommonJS", "module": "CommonJS",
@ -6,7 +10,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"outDir": "./dist", "outDir": "./dist",
"rootDir": "./src", "rootDir": "../",
"strict": true, "strict": true
} }
} }

File diff suppressed because it is too large Load Diff

1
types/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './Types';

15
types/tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"declaration": true,
// "emitDeclarationOnly": true,
// "outDir": "./dist",
"noEmit": true
},
"include": [
"index.ts",
"Types.ts"
],
"exclude": [
"node_modules"
]
}

784
yarn.lock

File diff suppressed because it is too large Load Diff