This commit is contained in:
2024-09-20 14:31:19 +02:00
parent 466981457d
commit bc39f6b6d6
10 changed files with 129 additions and 65 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
.mysql

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM oven/bun:1 AS base
WORKDIR /usr/src/app
COPY package.json bun.lockb .
#RUN bun install --frozen-lockfile --production
RUN bun install --frozen-lockfile --development
COPY . .
#ENV NODE_ENV=production
ENV NODE_ENV=development
USER bun
EXPOSE 8080/tcp
#ENTRYPOINT [ "bun", "run", "src/index.ts" ]
ENTRYPOINT [ "bun", "run", "dev" ]

View File

@ -1,4 +1,16 @@
services:
app:
image: labyrint
build:
context: .
restart: on-failure:3
ports:
- 8080:8080
volumes:
- .:/usr/src/app
depends_on:
db:
condition: service_healthy
adminer:
image: adminer:latest
restart: always
@ -11,8 +23,6 @@ services:
restart: always
environment:
MYSQL_ROOT_PASSWORD: password
ports:
- 8081:3306
volumes:
- ./.mysql:/var/lib/mysql
healthcheck:

View File

@ -1,10 +1,10 @@
import { defineConfig } from "drizzle-kit";
export const dbCredentials = {
host: "127.0.0.1",
//host: "db",
port: 8081,
//port: 3306,
//host: "127.0.0.1",
host: "db",
//port: 8081,
port: 3306,
user: "root",
password: "password",
database: "sous-podzim2024-app"

View File

@ -3,8 +3,8 @@ import { datetime, int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
export const personTable = mysqlTable('person', {
personId: int('personId').primaryKey().autoincrement(),
name: varchar('name', { length: 255 }),
secret: varchar('secret', { length: 255 }),
name: varchar('name', { length: 255 }).unique().notNull(),
secret: varchar('secret', { length: 255 }).unique().notNull(),
gambleTime: datetime('gampleTime')
});

View File

@ -30,13 +30,17 @@ app.get("/", async (req, res) => {
});
});
function getNextGambleTime(time: Date): Date {
return new Date(time.getTime() + 3 * 60 * 60 * 1000);
}
function isGambleAvailable(time: Date | null): boolean {
if (time == null) {
return true;
}
// 3 hour delay before showing
if (new Date(time.getTime() + 3 * 60 * 60 * 1000) < new Date()) {
if (getNextGambleTime(time) < new Date()) {
return true;
}
@ -49,13 +53,23 @@ function isDetailAvailable(time: Date | null): boolean {
}
// 2 minute delay before hiding
if (new Date(time.getTime() + 1 * 60 * 1000) > new Date()) {
if (new Date(time.getTime() + 2 * 60 * 1000) > new Date()) {
return true;
}
return false;
}
function sendError(res: express.Response, code: number, error: string): void {
ejs.renderFile('src/templates/error.ejs', { error: error }, function (err, str) {
if (err) {
res.status(500).send(err);
}
res.status(code).send(str);
});
}
app.get("/gamble/:id", async (req, res) => {
let id = parseInt(req.params.id);
if (id == undefined) {
@ -77,13 +91,14 @@ app.get("/gamble/:id", async (req, res) => {
}
if (!isGambleAvailable(person.gambleTime)) {
ejs.renderFile('src/templates/error.ejs', { error: "Gamble is currently not available" }, function (err, str) {
if (err) {
res.status(500).send(err);
}
res.send(str);
});
if (person.gambleTime == null) {
sendError(res, 423, "Gamble není dostupný");
} else {
sendError(res, 423, "Gamble bude dostupný až ve " + getNextGambleTime(person.gambleTime).toLocaleString('cs', {
hour: "2-digit",
minute: "2-digit",
}));
}
return;
}
@ -115,17 +130,17 @@ app.post("/gamble/:id", upload.none(), async (req, res) => {
.groupBy(personTable.personId))[0];
if (!person) {
res.status(404).send("Invalid person id");
sendError(res, 404, "Osoba nenalezena");
return;
}
if (!isGambleAvailable(person.gambleTime)) {
res.status(423).send("Gamble currently locked");
sendError(res, 423, "Nelze zadat další gamble");
return;
}
if (person.secret != req.body['secret']) {
res.sendStatus(403);
sendError(res, 423, "Nesprávé heslo");
return;
}
@ -150,7 +165,7 @@ app.post("/gamble/:id", upload.none(), async (req, res) => {
app.get("/person/:id", async (req, res) => {
let id = parseInt(req.params.id);
if (id == undefined) {
res.status(404).send("Invalid person id");
sendError(res, 404, "Osoba nenalezena");
return;
}
@ -165,18 +180,12 @@ app.get("/person/:id", async (req, res) => {
.groupBy(personTable.personId))[0];
if (!person) {
res.status(404).send("Invalid person id");
sendError(res, 404, "Osoba nenalezena");
return;
}
if (!isDetailAvailable(person.gambleTime)) {
ejs.renderFile('src/templates/error.ejs', { error: "Detail not available" }, function (err, str) {
if (err) {
res.status(500).send(err);
}
res.send(str);
});
sendError(res, 404, "Detail bude dostupný až po dalším gamblu");
}
let points = await db.query.pointsTable.findMany({

View File

@ -6,11 +6,23 @@
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="/bootstrap.min.css" />
<script src="/bootstrap.bundle.min.js"></script>
<title>Index</title>
<title>Nestabilní body</title>
</head>
<body data-bs-theme="dark">
<a href="/">index</a>
Error: <%= error %>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="nav-link" href="/">Hlavní stránka</a>
</div>
</nav>
<div class="w-100 text-center p-5">
<h1>Error</h1>
<h2><%= error %></h2>
</div>
<script>
setTimeout(() => {
window.location.href='/';
}, 20*1000);
</script>
</body>
</html>

View File

@ -6,16 +6,30 @@
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="/bootstrap.min.css" />
<script src="/bootstrap.bundle.min.js"></script>
<title>Index</title>
<title>Nestabilní body</title>
</head>
<body data-bs-theme="dark">
<form action="/gamble/<%= person.personId %>" method="post" enctype="multipart/form-data">
<label for="fname">Points:</label><br>
<input type="number" id="points" name="points"><br>
<label for="fname">Secret</label><br>
<input type="text" id="secret" name="secret"><br>
<input type="submit" value="Submit">
</form>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="nav-link" href="/">Hlavní stránka</a>
</div>
</nav>
<div class="container">
<div class="w-100 text-center p-5">
<h1 class="mb-3">Gamble</h1>
<h2>Osoba: <%= person.name %></h2>
</div>
<form action="/gamble/<%= person.personId %>" method="post" enctype="multipart/form-data">
<label for="personSecret" class="form-label">Secret</label>
<input type="password" autocomplete="off" id="personSecret" name="secret" class="form-control"><br>
<input type="submit" value="Submit" class="btn btn-primary">
</form>
</div>
<script>
setTimeout(() => {
window.location.href='/';
}, 5*60*1000);
</script>
</body>
</html>

View File

@ -6,20 +6,22 @@
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="/bootstrap.min.css" />
<script src="/bootstrap.bundle.min.js"></script>
<title>Index</title>
<title>Nestabilní body</title>
</head>
<body data-bs-theme="dark">
People
<div class="row p-5">
<div class="list-group col-3">
<div class="w-100 text-center p-5">
<h1>Nestabilní body</h1>
</div>
<div class="container-fluid">
<div class="row p-5">
<% people.forEach((person) => {%>
<a href='/gamble/<%= person.personId %>'
class="list-group-item list-group-item-action">
<div class="me-auto">
<span class="fw-bold"><%= person.name %></span>
</div>
</a>
<div class="col-12 col-md-6 col-lg-3">
<a href='/gamble/<%= person.personId %>'
class="btn btn-outline-secondary w-100 m-2 text-body fw-bold">
<%= person.name %>
</a>
</div>
<% }); %>
</div>
</div>

View File

@ -11,24 +11,19 @@
</head>
<body data-bs-theme="dark">
<a href="/">Index</a>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="nav-link" href="/">Hlavní stránka</a>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col col-12 col-lg-4">
<div class="h-100 d-flex flex-column justify-content-center">
<div class="border rounded p-3 m-1">
Name: <%= person.name %>
</div>
<div class="border rounded p-3 m-1">
Points: <%= person.pointsTotal %>
</div>
</div>
</div>
<div class="col col-12 col-lg-8 text-center">
<canvas id="chart"></canvas>
</div>
<div class="w-100 text-center p-5">
<h1 class="mb-3">Detail osoby</h1>
<h2><%= person.name %></h2>
<h2><%= person.pointsTotal %> bodů</h2>
</div>
<canvas id="chart"></canvas>
</div>
<script>
@ -66,5 +61,10 @@
}
});
</script>
<script>
setTimeout(() => {
window.location.href='/';
}, 5*60*1000);
</script>
</body>
</html>