ui
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
.mysql
|
||||||
16
Dockerfile
Normal file
16
Dockerfile
Normal 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" ]
|
||||||
@ -1,4 +1,16 @@
|
|||||||
services:
|
services:
|
||||||
|
app:
|
||||||
|
image: labyrint
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
restart: on-failure:3
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer:latest
|
image: adminer:latest
|
||||||
restart: always
|
restart: always
|
||||||
@ -11,8 +23,6 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: password
|
MYSQL_ROOT_PASSWORD: password
|
||||||
ports:
|
|
||||||
- 8081:3306
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./.mysql:/var/lib/mysql
|
- ./.mysql:/var/lib/mysql
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { defineConfig } from "drizzle-kit";
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
export const dbCredentials = {
|
export const dbCredentials = {
|
||||||
host: "127.0.0.1",
|
//host: "127.0.0.1",
|
||||||
//host: "db",
|
host: "db",
|
||||||
port: 8081,
|
//port: 8081,
|
||||||
//port: 3306,
|
port: 3306,
|
||||||
user: "root",
|
user: "root",
|
||||||
password: "password",
|
password: "password",
|
||||||
database: "sous-podzim2024-app"
|
database: "sous-podzim2024-app"
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import { datetime, int, mysqlTable, varchar } from "drizzle-orm/mysql-core";
|
|||||||
|
|
||||||
export const personTable = mysqlTable('person', {
|
export const personTable = mysqlTable('person', {
|
||||||
personId: int('personId').primaryKey().autoincrement(),
|
personId: int('personId').primaryKey().autoincrement(),
|
||||||
name: varchar('name', { length: 255 }),
|
name: varchar('name', { length: 255 }).unique().notNull(),
|
||||||
secret: varchar('secret', { length: 255 }),
|
secret: varchar('secret', { length: 255 }).unique().notNull(),
|
||||||
gambleTime: datetime('gampleTime')
|
gambleTime: datetime('gampleTime')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
51
src/index.ts
51
src/index.ts
@ -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 {
|
function isGambleAvailable(time: Date | null): boolean {
|
||||||
if (time == null) {
|
if (time == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 hour delay before showing
|
// 3 hour delay before showing
|
||||||
if (new Date(time.getTime() + 3 * 60 * 60 * 1000) < new Date()) {
|
if (getNextGambleTime(time) < new Date()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,13 +53,23 @@ function isDetailAvailable(time: Date | null): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 minute delay before hiding
|
// 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 true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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) => {
|
app.get("/gamble/:id", async (req, res) => {
|
||||||
let id = parseInt(req.params.id);
|
let id = parseInt(req.params.id);
|
||||||
if (id == undefined) {
|
if (id == undefined) {
|
||||||
@ -77,13 +91,14 @@ app.get("/gamble/:id", async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isGambleAvailable(person.gambleTime)) {
|
if (!isGambleAvailable(person.gambleTime)) {
|
||||||
ejs.renderFile('src/templates/error.ejs', { error: "Gamble is currently not available" }, function (err, str) {
|
if (person.gambleTime == null) {
|
||||||
if (err) {
|
sendError(res, 423, "Gamble není dostupný");
|
||||||
res.status(500).send(err);
|
} else {
|
||||||
}
|
sendError(res, 423, "Gamble bude dostupný až ve " + getNextGambleTime(person.gambleTime).toLocaleString('cs', {
|
||||||
|
hour: "2-digit",
|
||||||
res.send(str);
|
minute: "2-digit",
|
||||||
});
|
}));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,17 +130,17 @@ app.post("/gamble/:id", upload.none(), async (req, res) => {
|
|||||||
.groupBy(personTable.personId))[0];
|
.groupBy(personTable.personId))[0];
|
||||||
|
|
||||||
if (!person) {
|
if (!person) {
|
||||||
res.status(404).send("Invalid person id");
|
sendError(res, 404, "Osoba nenalezena");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isGambleAvailable(person.gambleTime)) {
|
if (!isGambleAvailable(person.gambleTime)) {
|
||||||
res.status(423).send("Gamble currently locked");
|
sendError(res, 423, "Nelze zadat další gamble");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (person.secret != req.body['secret']) {
|
if (person.secret != req.body['secret']) {
|
||||||
res.sendStatus(403);
|
sendError(res, 423, "Nesprávé heslo");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +165,7 @@ app.post("/gamble/:id", upload.none(), async (req, res) => {
|
|||||||
app.get("/person/:id", async (req, res) => {
|
app.get("/person/:id", async (req, res) => {
|
||||||
let id = parseInt(req.params.id);
|
let id = parseInt(req.params.id);
|
||||||
if (id == undefined) {
|
if (id == undefined) {
|
||||||
res.status(404).send("Invalid person id");
|
sendError(res, 404, "Osoba nenalezena");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,18 +180,12 @@ app.get("/person/:id", async (req, res) => {
|
|||||||
.groupBy(personTable.personId))[0];
|
.groupBy(personTable.personId))[0];
|
||||||
|
|
||||||
if (!person) {
|
if (!person) {
|
||||||
res.status(404).send("Invalid person id");
|
sendError(res, 404, "Osoba nenalezena");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDetailAvailable(person.gambleTime)) {
|
if (!isDetailAvailable(person.gambleTime)) {
|
||||||
ejs.renderFile('src/templates/error.ejs', { error: "Detail not available" }, function (err, str) {
|
sendError(res, 404, "Detail bude dostupný až po dalším gamblu");
|
||||||
if (err) {
|
|
||||||
res.status(500).send(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(str);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let points = await db.query.pointsTable.findMany({
|
let points = await db.query.pointsTable.findMany({
|
||||||
|
|||||||
@ -6,11 +6,23 @@
|
|||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<link rel="stylesheet" href="/bootstrap.min.css" />
|
<link rel="stylesheet" href="/bootstrap.min.css" />
|
||||||
<script src="/bootstrap.bundle.min.js"></script>
|
<script src="/bootstrap.bundle.min.js"></script>
|
||||||
<title>Index</title>
|
<title>Nestabilní body</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-bs-theme="dark">
|
<body data-bs-theme="dark">
|
||||||
<a href="/">index</a>
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
Error: <%= error %>
|
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -6,16 +6,30 @@
|
|||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<link rel="stylesheet" href="/bootstrap.min.css" />
|
<link rel="stylesheet" href="/bootstrap.min.css" />
|
||||||
<script src="/bootstrap.bundle.min.js"></script>
|
<script src="/bootstrap.bundle.min.js"></script>
|
||||||
<title>Index</title>
|
<title>Nestabilní body</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-bs-theme="dark">
|
<body data-bs-theme="dark">
|
||||||
<form action="/gamble/<%= person.personId %>" method="post" enctype="multipart/form-data">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<label for="fname">Points:</label><br>
|
<div class="container-fluid">
|
||||||
<input type="number" id="points" name="points"><br>
|
<a class="nav-link" href="/">Hlavní stránka</a>
|
||||||
<label for="fname">Secret</label><br>
|
</div>
|
||||||
<input type="text" id="secret" name="secret"><br>
|
</nav>
|
||||||
<input type="submit" value="Submit">
|
<div class="container">
|
||||||
</form>
|
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -6,20 +6,22 @@
|
|||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<link rel="stylesheet" href="/bootstrap.min.css" />
|
<link rel="stylesheet" href="/bootstrap.min.css" />
|
||||||
<script src="/bootstrap.bundle.min.js"></script>
|
<script src="/bootstrap.bundle.min.js"></script>
|
||||||
<title>Index</title>
|
<title>Nestabilní body</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-bs-theme="dark">
|
<body data-bs-theme="dark">
|
||||||
People
|
<div class="w-100 text-center p-5">
|
||||||
<div class="row p-5">
|
<h1>Nestabilní body</h1>
|
||||||
<div class="list-group col-3">
|
</div>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row p-5">
|
||||||
<% people.forEach((person) => {%>
|
<% people.forEach((person) => {%>
|
||||||
<a href='/gamble/<%= person.personId %>'
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
class="list-group-item list-group-item-action">
|
<a href='/gamble/<%= person.personId %>'
|
||||||
<div class="me-auto">
|
class="btn btn-outline-secondary w-100 m-2 text-body fw-bold">
|
||||||
<span class="fw-bold"><%= person.name %></span>
|
<%= person.name %>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
</div>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,24 +11,19 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-bs-theme="dark">
|
<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="container">
|
||||||
<div class="row">
|
<div class="w-100 text-center p-5">
|
||||||
<div class="col col-12 col-lg-4">
|
<h1 class="mb-3">Detail osoby</h1>
|
||||||
<div class="h-100 d-flex flex-column justify-content-center">
|
<h2><%= person.name %></h2>
|
||||||
<div class="border rounded p-3 m-1">
|
<h2><%= person.pointsTotal %> bodů</h2>
|
||||||
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>
|
</div>
|
||||||
|
<canvas id="chart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -66,5 +61,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href='/';
|
||||||
|
}, 5*60*1000);
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user