This commit is contained in:
Aleksander Wilczyński 2024-08-07 19:51:25 +02:00
parent a6f0f8a81e
commit 1b021c0a1f
Signed by untrusted user: alekswilc
GPG Key ID: D4464A248E5F27FE
15 changed files with 618 additions and 425 deletions

BIN
.gitea/assets/image-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,16 +1,13 @@
{ {
"name": "play-history", "name": "simrail-logs",
"version": "1.0.0", "main": "dist/index.js",
"main": "index.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"postbuild": "copyfiles --error --up 1 src/http/views/*.* dist", "postbuild": "copyfiles --error --up 1 src/http/views/**/* dist",
"dev": "npm run build && doppler run node dist" "dev": "npm run build && doppler run node dist"
}, },
"keywords": [], "author": "Aleksander <alekswilc> Wilczyński",
"author": "", "license": "AGPL-3.0-only",
"license": "ISC",
"description": "",
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",

View File

@ -3,26 +3,17 @@ Prosty projekt, logujący wyjścia z posterunków.
### Hej! Podoba Ci się projekt? Polajkuj post na [forum](https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/), a będzie mi miło! Dla Ciebie to jedno kliknięcie, a dla mnie motywacja do rozwijania projektu ### Hej! Podoba Ci się projekt? Polajkuj post na [forum](https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/), a będzie mi miło! Dla Ciebie to jedno kliknięcie, a dla mnie motywacja do rozwijania projektu
## Aplikacja
https://simrail.alekswilc.dev/
## Cele ## Cele
- Ułatwienie zgłaszania graczy, którzy robią "Hit and Run" (psuje i wychodze z posterunku) - Ułatwienie zgłaszania graczy, którzy robią "Hit and Run" (psuje i wychodze z posterunku)
- Statystyki
## Dalszy rozwój ## Dalszy rozwój
- Obsługa pociagów, a nie tylko posterunków - Obsługa pociagów, a nie tylko posterunków (#8)
# Jak korzystać?
- Otwórz https://simrail.alekswilc.dev/
- Znajdź interesujący Cie rekord.
- Naciśnij przycisk więcej
- Naciśnij na okienko z angielskim zapisem informacji (lub przycisk kopiuj link)
- Wklej na kanał #multiplayer-help-request z opisem sytuacji.
- Gotowe, kolejny troll jest zgłoszony.
![alt text](.gitea/assets/image.png)
# Zgłaszanie błedów # Zgłaszanie błedów
Aplikacja jest w wersji testowej, i obsługuje tylko serwer PL2
Jak zgłosić błąd? Jak zgłosić błąd?
- Otwórz https://git.alekswilc.dev/alekswilc/simrail-logs/issues - Otwórz https://git.alekswilc.dev/alekswilc/simrail-logs/issues
- Załóż konto (to tylko kilka kliknięć, a twoje zgłoszenie bardzo mi pomoże) - Załóż konto (to tylko kilka kliknięć, a twoje zgłoszenie bardzo mi pomoże)

View File

@ -1,103 +0,0 @@
import express from 'express';
import { MLog } from '../mongo/logs.js';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import dayjs from 'dayjs';
import { PlayerUtil } from '../util/PlayerUtil.js';
import { msToTime } from '../util/time.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export class ApiModule {
public static load() {
const app = express();
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views')
app.get('/', async (req, res) => {
const records = await MLog.find()
.sort({ leftDate: -1 })
.limit(30)
res.render('index.ejs', {
records,
dayjs,
msToTime
});
})
const generateSearch = (regex: RegExp) => [
{
stationName: { $regex: regex },
},
{
userUsername: { $regex: regex },
},
{
stationShort: { $regex: regex },
},
{
userSteamId: { $regex: regex },
},
{
server: { $regex: regex },
}
]
app.get('/search', async (req, res) => {
if (!req.query.q) return res.redirect('/');
const s = req.query.q.toString().split(',').map(x => new RegExp(x, "i"));
const records = await MLog.aggregate([
{
$match: {
$and: [
...s.map(x => ({ $or: generateSearch(x) }))
]
}
}
])
.sort({ leftDate: -1 })
.limit(30)
res.render('search.ejs', {
records,
dayjs,
q: req.query.q,
msToTime
});
})
app.get('/details/:id', async (req, res) => {
if (!req.params.id) return res.redirect('/');
const record = await MLog.findOne({ id: req.params.id });
const player = await PlayerUtil.getPlayer(record?.userSteamId!);
res.render('details.ejs', {
record,
dayjs,
player,
msToTime
});
})
app.get('/api/last', async (req, res) => {
const records = await MLog.find()
.sort({ leftDate: -1 })
.limit(30)
res.json({ code: 200, records });
})
app.get('/api/search', async (req, res) => {
if (!req.query.q) return res.send('invalid');
const records = await MLog.find({ $text: { $search: req.query.q as string } })
.sort({ leftDate: -1 })
.limit(30)
res.json({ code: 200, records });
})
app.listen(2005);
}
}

129
src/http/routes/stations.ts Normal file
View File

@ -0,0 +1,129 @@
import { Router } from 'express';
import { MLog, raw_schema } from '../../mongo/logs.js';
import dayjs from 'dayjs';
import { PlayerUtil } from '../../util/PlayerUtil.js';
import { msToTime } from '../../util/time.js';
import { PipelineStage } from 'mongoose';
const generateSearch = (regex: RegExp) => [
{
stationName: { $regex: regex },
},
{
userUsername: { $regex: regex },
},
{
stationShort: { $regex: regex },
},
{
userSteamId: { $regex: regex },
},
{
server: { $regex: regex },
}
]
export class StationsRoute {
static load() {
const app = Router();
app.get('/', async (req, res) => {
const s = req.query.q?.toString().split(',').map(x => new RegExp(x, "i"));
const filter: PipelineStage[] = [];
s && filter.push({
$match: {
$and: [
...s.map(x => ({ $or: generateSearch(x) }))
]
}
})
const records = await MLog.aggregate(filter)
.sort({ leftDate: -1 })
.limit(30)
res.render('stations/index.ejs', {
records,
dayjs,
q: req.query.q,
msToTime
});
})
app.get('/details/:id', async (req, res) => {
if (!req.params.id) return res.redirect('/stations/');
const record = await MLog.findOne({ id: req.params.id });
const player = await PlayerUtil.getPlayer(record?.userSteamId!);
res.render('stations/details.ejs', {
record,
dayjs,
player,
msToTime
});
})
app.get('/leaderboard/', async (req, res) => {
const s = req.query.q?.toString().split(',').map(x => new RegExp(x, "i"));
const data = Object.keys(raw_schema)
.reduce((o, key) => ({ ...o, [key]: `$${key}` }), {});
const filter: PipelineStage[] = [
{
$project: {
// record.leftDate - record.joinedDate
result: { $subtract: ['$leftDate', '$joinedDate'] },
...data
}
},
];
s && filter.unshift(
{
$match: {
$and: [
...s.map(x => ({ $or: generateSearch(x) }))
]
}
}
)
const records = await MLog.aggregate(filter)
.sort({ result: -1 })
.limit(30)
res.render('stations/leaderboard.ejs', {
records,
dayjs,
q: req.query.q,
msToTime
});
})
// API ENDPOINTS
// CREATE AN ISSUE IF YOU NEED API ACCESS: https://git.alekswilc.dev/alekswilc/simrail-logs/issues
/*
app.get('/api/last', async (req, res) => {
const records = await MLog.find()
.sort({ leftDate: -1 })
.limit(30)
res.json({ code: 200, records });
})
app.get('/api/search', async (req, res) => {
if (!req.query.q) return res.send('invalid');
const records = await MLog.find({ $text: { $search: req.query.q as string } })
.sort({ leftDate: -1 })
.limit(30)
res.json({ code: 200, records });
})*/
return app;
}
}

26
src/http/server.ts Normal file
View File

@ -0,0 +1,26 @@
import express from 'express';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { StationsRoute } from './routes/stations.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export class ApiModule {
public static load() {
const app = express();
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views')
app.get('/', (_, res) => res.render('home'));
// backward compatible
app.get('/details/:id', (req, res) => res.redirect('/stations/details/'+req.params.id));
app.use('/stations/', StationsRoute.load());
app.listen(2005);
}
}

View File

@ -0,0 +1,7 @@
<div style="font-size: 13.5px;">
<p>Made with <span style="color: red;">❤️</span> by <a href="https://www.alekswilc.dev/">alekswilc</a> for SimRail Community</p>
<p><a href="https://git.alekswilc.dev/alekswilc/">Open Source</a></p>
<% if (thanks) { %>
<p>Podziękowania dla <a href="https://discord.com/users/1079005960653254656">AQUALYTH</a>, osób lajkujących <a href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">posta</a>, osób użytkujących strone i całego community Simrail <span style="color: red;">❤️</span></p>
<% } %>
</div>

View File

@ -0,0 +1,21 @@
<header>
<h1><a style="color: white; text-decoration: none;" href="/">SimRail Logs</a></h1>
<% if (section==='stations' ) { %>
<nav style="margin-bottom: 0;">
<a href="/">Strona Główna</a> /
<a href="https://git.alekswilc.dev/alekswilc/simrail-logs/issues/8">Logi pociągów</a>
</nav>
<nav>
<a href="/stations/">Logi posterunków</a> /
<a href="/stations/leaderboard">Tablica godzin</a>
</nav>
<% } else {%>
<a href="/stations/">Logi posterunków</a> /
<a href="https://git.alekswilc.dev/alekswilc/simrail-logs/issues/8">Logi pociągów</a>
<% } %>
<br />
<p style="color: darkorange; font-size: 14px;">Hej! Podoba Ci się projekt? Polajkuj post na <a
href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">forum</a>, a będzie
mi miło! Dla Ciebie to jedno kliknięcie, a dla mnie motywacja do rozwijania projektu.</p>
</header>

113
src/http/views/home.ejs Normal file
View File

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>simrail.alekswilc.dev</title>
<meta name="description" content="Simrail Utils">
<meta property="og:title" content="simrail.alekswilc.dev">
<meta property="og:url" content="https://simrail.alekswilc.dev">
<meta property="og:description" content="Simrail Utils">
<meta property="og:type" content="website">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
<style>
p {
margin: 1%;
}
</style>
</head>
<body>
<%- include('_modules/header.ejs', { section:'home' }) %>
<p>Prosty projekt, logujący wyjścia z posterunków.</p>
<h3>Cele projektu</h3>
<p>Główne założenia</p>
<ul>
<li>
<p>Ułatwienie zgłaszania graczy, którzy robią "Hit and Run" (psuje i wychodze z posterunku)</p>
</li>
<li>
<p>Statystyki</p>
</li>
</ul>
<h3>Rozwój aplikacji</h3>
<p>Planowane funkcjonalności</p>
<ul>
<li>
<p>Obsługa pociągów (<a href="https://git.alekswilc.dev/alekswilc/simrail-logs/issues/8">#8</a>)</p>
</li>
</ul>
<h3>Informacje dodatkowe</h3>
<details>
<summary>
Zgłaszanie błędów i propozycji
</summary>
<h6>Zgłaszanie błedów</h6>
<ul>
<li>Otwórz <a
href="https://git.alekswilc.dev/alekswilc/simrail-logs/issues">https://git.alekswilc.dev/alekswilc/simrail-logs/issues</a>
</li>
<li>Zaloguj się (np poprzez Discorda)</li>
<li>Naciśnij przycisk "New issue" ("Nowe zgłoszenie")</li>
<li>W tytule napisz krótki opis (np. Problem z wyświetleniem strony)</li>
<li>Opisz problem (kiedy występuje, podaj linki, postaraj sie napisać jak moge odtworzyć ten błąd, jak
powinna zachowywać sie aplikacja bez tego błędu)</li>
<li>Poczekaj na informacje z mojej strony</li>
<li>Dziekuje :)</li>
</ul>
<img src="https://git.alekswilc.dev/alekswilc/simrail-logs/raw/branch/main/.gitea/assets/image-1.png">
<h6>Zgłaszanie propozycji</h6>
<ul>
<li>Otwórz <a
href="https://git.alekswilc.dev/alekswilc/simrail-logs/issues">https://git.alekswilc.dev/alekswilc/simrail-logs/issues</a>
</li>
<li>Zaloguj się (np poprzez Discorda)</li>
<li>Naciśnij przycisk "New issue" ("Nowe zgłoszenie")</li>
<li>W tytule napisz krótki opis (np. Logi wejścia i wyjścia z pociągów)</li>
<li>Opisz propozycje (dokładnie co z dyżurkami ale dla pociągów)</li>
<li>Poczekaj na informacje z mojej strony</li>
<li>Dziekuje :)</li>
</ul>
<img src="https://git.alekswilc.dev/alekswilc/simrail-logs/raw/branch/main/.gitea/assets/image-2.png">
</details>
<h2>Licencja</h2>
<details>
<summary>
Copyright (C) 2024 Aleksander <alekswilc> Wilczyński
</summary>
<code style="white-space: pre-line">This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see https://www.gnu.org/licenses/.</code>
</details>
<hr>
<%- include('_modules/footer.ejs', { thanks: true }) %>
</body>
</html>

View File

@ -1,89 +1,86 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>simrail.alekswilc.dev</title> <title>simrail.alekswilc.dev</title>
<meta name="description" <meta name="description"
content="<%= record.stationName %> | <%= record.userUsername %> | <%= dayjs(record.leftDate).format('hh:mm DD/MM/YYYY') %>"> content="<%= record.stationName %> | <%= record.userUsername %> | <%= dayjs(record.leftDate).format('hh:mm DD/MM/YYYY') %>">
<meta property="og:title" content="Simrail Log"> <meta property="og:title" content="Simrail Log">
<meta property="og:url" content="https://simrail.alekswilc.dev/details/<%= record.id %>/"> <meta property="og:url" content="https://simrail.alekswilc.dev/details/<%= record.id %>/">
<meta property="og:description" <meta property="og:description"
content="<%= record.stationName %> | <%= record.userUsername %> | <%= dayjs(record.leftDate).format('hh:mm DD/MM/YYYY') %>""> content="<%= record.stationName %> | <%= record.userUsername %> | <%= dayjs(record.leftDate).format('hh:mm DD/MM/YYYY') %>"">
<meta property=" og:type" content="website"> <meta property=" og:type" content="website">
<meta property="og:image" content="<%= record.userAvatar %>" /> <meta property="og:image" content="<%= record.userAvatar %>" />
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:card" content="summary_large_image">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
<style> <style>
p { p {
margin: 0; margin: 0;
} }
.details { .details {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.clickable { .clickable {
cursor: pointer; cursor: pointer;
} }
</style> </style>
</head> </head>
<script> <script>
function copydata() { function copydata() {
navigator.clipboard.writeText(document.getElementById('data').textContent.replace(/ /g, '').split('\n').filter(x => x).join('')) navigator.clipboard.writeText(document.getElementById('data').textContent.replace(/ /g, '').split('\n').filter(x => x).join(''))
} }
function copylink() { function copylink() {
navigator.clipboard.writeText("https://simrail.alekswilc.dev/details/<%= record.id %>/") navigator.clipboard.writeText("https://simrail.alekswilc.dev/details/<%= record.id %>/")
} }
</script> </script>
<body> <body>
<header> <%- include('../_modules/header.ejs', { section: 'stations' }) %>
<h1><a style="color: white; text-decoration: none;" href="/">SimRail Logs</a></h1>
<p><a href="https://git.alekswilc.dev/alekswilc/simrail-logs">Dokumentacja</a></p> <div class="details">
<br />
<p style="color: darkorange;">Hej! Podoba Ci się projekt? Polajkuj post na <a href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">forum</a>, a będzie mi miło! Dla Ciebie to jedno kliknięcie, a dla mnie motywacja do rozwijania projektu</p> <p>Użytkownik: <a href="<%= player.profileurl %>">
</header> <%= record.userUsername %>
</a></p>
<div class="details"> <p>Stacja: <%= record.stationName %>
</p>
<p>Użytkownik: <a href="<%= player.profileurl %>"> <p>Serwer: <%= record.server.toUpperCase() %>
<%= record.userUsername %> </p>
</a></p> <p>Data wejścia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') : '--:-- --/--/--'
<p>Stacja: <%= record.stationName %> %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)</p>
</p> <p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%= dayjs(record.leftDate).fromNow()
<p>Serwer: <%= record.server.toUpperCase() %> %>)</p>
</p> <p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<p>Data wejścia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') : '--:-- --/--/--' </p>
%> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)</p>
<p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%= dayjs(record.leftDate).fromNow() <br />
%>)</p> <code class="clickable" style="white-space: pre-line" onclick="copydata()" id="data">;station: <%= record.stationName %>
<p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %> ;steam: <%= record.userSteamId %>
</p> ;server: <%= record.server %>
;name: <%= record.userUsername %>
<br /> ;joined: <%=record.joinedDate ? dayjs(record.joinedDate).format() : 'no-data'%>
<code class="clickable" style="white-space: pre-line" onclick="copydata()" id="data">;station: <%= record.stationName %> ;left: <%=dayjs(record.leftDate).format()%>
;steam: <%= record.userSteamId %> ;url: https://simrail.alekswilc.dev/details/<%= record.id %>/
;server: <%= record.server %> </code>
;name: <%= record.userUsername %> <br />
;joined: <%=record.joinedDate ? dayjs(record.joinedDate).format() : 'no-data'%> <p><button onclick="copylink()">Kopiuj link</button></p>
;left: <%=dayjs(record.leftDate).format()%>
;url: https://simrail.alekswilc.dev/details/<%= record.id %>/
</code>
<br /> </div>
<p><button onclick="copylink()">Kopiuj link</button></p> <hr>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</div>
</body>
</html> </html>

View File

@ -1,85 +1,91 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>simrail.alekswilc.dev</title> <title>simrail.alekswilc.dev</title>
<meta name="description" content="Simrail Utils"> <meta name="description" content="Simrail Utils">
<meta property="og:title" content="simrail.alekswilc.dev"> <meta property="og:title" content="simrail.alekswilc.dev">
<meta property="og:url" content="https://simrail.alekswilc.dev"> <meta property="og:url" content="https://simrail.alekswilc.dev">
<meta property="og:description" content="Simrail Utils"> <meta property="og:description" content="Simrail Utils">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
<style> <style>
p { p {
margin: 1%; margin: 1%;
} }
</style> </style>
</head> </head>
<body> <body>
<header> <%- include('../_modules/header.ejs', { section: 'stations' }) %>
<h1><a style="color: white; text-decoration: none;" href="/">SimRail Logs</a></h1>
<p><a href="https://git.alekswilc.dev/alekswilc/simrail-logs">Dokumentacja</a></p> <h2>Wyszukaj posterunek, osobe lub serwer</h2>
<br />
<p style="color: darkorange;">Hej! Podoba Ci się projekt? Polajkuj post na <a href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">forum</a>, a będzie mi miło! Dla Ciebie to jedno kliknięcie, a dla mnie motywacja do rozwijania projektu</p> <div class="container">
</header> <input type="text" id="search" value="<%=q%>">
<button onclick="search()">Szukaj</button>
<h2>Wyszukaj posterunek, osobe lub serwer</h2> <button onclick="clearSearch()">Wyczyść</button>
<div class="container"> <p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p>
<input type="text" id="search"> </div>
<button onclick="search()">Szukaj</button>
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p> <ul>
</div> <% records.forEach(record=> { %>
<li>
<h2>Ostatnie opuszczenia posterunków</h2> <details>
<summary>[<span style="color:lightskyblue">
<ul> <%= record.server.toUpperCase() %>
<% records.forEach(record=> { %> </span>] <span style="color: lightskyblue">
<li> <%= record.stationName %>
<details> </span> - <span style="color:hotpink">
<summary>[<span style="color:lightskyblue"> <%= record.userUsername %>
<%= record.server.toUpperCase() %> </span>
</span>] <span style="color: lightskyblue"> <p style="margin-bottom: 0; opacity: 0.5;">
<%= record.stationName %> <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %>
</span> - <span style="color:hotpink"> </p>
<%= record.userUsername %> </summary>
</span> <p>Data dołączenia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY')
<p style="margin-bottom: 0; opacity: 0.5;"> : '--:-- --/--/--' %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
<%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> </p>
</p> <p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%=
</summary> dayjs(record.leftDate).fromNow() %>)</p>
<p>Data dołączenia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') <p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
: '--:-- --/--/--' %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>) </p>
</p>
<p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%= <a href="/details/<%= record.id %>">
dayjs(record.leftDate).fromNow() %>)</p> <button>Więcej</button>
<p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %> </a>
</p> </details>
</li>
<a href="/details/<%= record.id %>"> <% }) %>
<button>Więcej</button> </ul>
</a>
</details> <% if (!records.length) { %>
</li> <h4>Nie znaleziono wyników dla twojego zapytania.</h4>
<% }) %> <% } %>
</ul>
<hr>
<script> <%- include('../_modules/footer.ejs', { thanks: false }) %>
function search() {
location.href = '/search?q=' + document.getElementById('search').value <script>
} function search() {
document.getElementById('search').addEventListener("keyup", (event) => { location.href = '/stations/?q=' + document.getElementById('search').value
if (event.key === "Enter") }
search(); function clearSearch() {
console.log('test')
}); location.href = '/stations/';
</script> }
document.getElementById('search').addEventListener("keyup", (event) => {
</body> if (event.key === "Enter")
search();
});
</script>
</body>
</html> </html>

View File

@ -1,87 +1,92 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>simrail.alekswilc.dev</title> <title>simrail.alekswilc.dev</title>
<meta name="description" content="Simrail Utils"> <meta name="description" content="Simrail Utils">
<meta property="og:title" content="simrail.alekswilc.dev"> <meta property="og:title" content="simrail.alekswilc.dev">
<meta property="og:url" content="https://simrail.alekswilc.dev/search?q=<%=q%>"> <meta property="og:url" content="https://simrail.alekswilc.dev/search?q=<%=q%>">
<meta property="og:description" content="Simrail Utils"> <meta property="og:description" content="Simrail Utils">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
<style> <style>
p { p {
margin: 1%; margin: 1%;
} }
</style> </style>
</head> </head>
<body> <body>
<header> <%- include('../_modules/header.ejs', { section: 'stations' }) %>
<h1><a style="color: white; text-decoration: none;" href="/">SimRail Logs</a></h1>
<p><a href="https://git.alekswilc.dev/alekswilc/simrail-logs">Dokumentacja</a></p> <h2>Tablica godzin</h2>
<br /> <div class="container">
<p style="color: darkorange;">Hej! Podoba Ci się projekt? Polajkuj post na <a href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">forum</a>, a będzie mi miło! Dla Ciebie to jedno kliknięcie, a dla mnie motywacja do rozwijania projektu</p> <input type="text" id="search" value="<%=q%>">
</header> <button onclick="search()">Filtruj</button>
<button onclick="clearSearch()">Wyczyść</button>
<h2>Wyszukaj posterunek, osobe lub serwer</h2>
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p>
</div>
<div class="container"> <ul>
<input type="text" id="search" value="<%=q%>"> <% records.forEach(record=> { %>
<button onclick="search()">Szukaj</button> <li>
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p> <details>
</div> <summary>[<span style="color:lightskyblue">
<%= record.server.toUpperCase() %>
<h2>Wyniki wyszukiwania</h2> </span>] <span style="color: lightskyblue">
<%= record.stationName %>
<ul> </span> - <span style="color:hotpink">
<% records.forEach(record=> { %> <%= record.userUsername %>
<li> </span>- <span style="color:lightseagreen">
<details> <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<summary>[<span style="color:lightskyblue"> </span>
<%= record.server.toUpperCase() %> <p style="margin-bottom: 0; opacity: 0.5;">
</span>] <span style="color: lightskyblue"> <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %>
<%= record.stationName %> </p>
</span> - <span style="color:hotpink"> </summary>
<%= record.userUsername %> <p>Data dołączenia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY')
</span> : '--:-- --/--/--' %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
<p style="margin-bottom: 0; opacity: 0.5;"> </p>
<%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> <p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%=
</p> dayjs(record.leftDate).fromNow() %>)</p>
</summary> <p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<p>Data dołączenia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') </p>
: '--:-- --/--/--' %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
</p> <a href="/details/<%= record.id %>">
<p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%= <button>Więcej</button>
dayjs(record.leftDate).fromNow() %>)</p> </a>
<p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %> </details>
</p> </li>
<% }) %>
<a href="/details/<%= record.id %>"> </ul>
<button>Więcej</button>
</a> <% if (!records.length) { %>
</details> <h4>Nie znaleziono wyników dla twojego zapytania.</h4>
</li> <% } %>
<% }) %>
</ul>
<script>
<script> function search() {
function search() { location.href = '/stations/leaderboard?q=' + document.getElementById('search').value
location.href = '/search?q=' + document.getElementById('search').value }
}
document.getElementById('search').addEventListener("keyup", (event) => { function clearSearch() {
if (event.key === "Enter") location.href = '/stations/leaderboard'
search(); }
}); document.getElementById('search').addEventListener("keyup", (event) => {
</script> if (event.key === "Enter")
search();
</body>
});
</script>
<hr>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</html> </html>

View File

@ -3,7 +3,7 @@ import './util/time.js';
import { SimrailClient, SimrailClientEvents } from './util/SimrailClient.js'; import { SimrailClient, SimrailClientEvents } from './util/SimrailClient.js';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { StationsModule } from './modules/stations.js'; import { StationsModule } from './modules/stations.js';
import { ApiModule } from './http/api.js'; import { ApiModule } from './http/server.js';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { IPlayer } from './types/player.js'; import { IPlayer } from './types/player.js';
import { Station, Server } from '@simrail/types'; import { Station, Server } from '@simrail/types';

View File

@ -1,47 +1,47 @@
import { Model, model, Schema } from 'mongoose'; import { Model, model, Schema } from 'mongoose';
const schema = new Schema<ILog>( export const raw_schema = {
{ id: {
id: { type: String,
type: String, required: true
required: true },
}, userSteamId: {
userSteamId: { type: String,
type: String, required: true
required: true },
}, userUsername: {
userUsername: { type: String,
type: String, required: true
required: true },
}, userAvatar: {
userAvatar: { type: String,
type: String, required: true
required: true },
}, joinedDate: {
joinedDate: { type: Number,
type: Number, required: false,
required: false, default: undefined
default: undefined },
}, leftDate: {
leftDate: { type: Number,
type: Number, required: true
required: true },
}, stationName: {
stationName: { type: String,
type: String, required: true
required: true },
}, stationShort: {
stationShort: { type: String,
type: String, required: true
required: true },
}, server: {
server: { type: String,
type: String, required: true
required: true },
}, }
}
); const schema = new Schema<ILog>(raw_schema);
schema.index({ stationName: 'text', userUsername: 'text', stationShort: 'text', userSteamId: 'text', server: 'text' }) schema.index({ stationName: 'text', userUsername: 'text', stationShort: 'text', userSteamId: 'text', server: 'text' })
export type TMLog = Model<ILog> export type TMLog = Model<ILog>

View File

@ -10,5 +10,9 @@ export const msToTime = (duration: number) => {
const minutes = Math.floor((duration / (1000 * 60)) % 60); const minutes = Math.floor((duration / (1000 * 60)) % 60);
const hours = Math.floor((duration / (1000 * 60 * 60)) % 24); const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
if (minutes === 0 && hours === 0 && duration > 0)
return "1m";
return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m` : ''}`; return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m` : ''}`;
} }