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:
|
||||
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:
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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')
|
||||
});
|
||||
|
||||
|
||||
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 {
|
||||
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({
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Reference in New Issue
Block a user