feat: #8, #16, bug fixes

This commit is contained in:
Aleksander Wilczyński 2024-08-18 00:31:09 +02:00
parent 873a38768e
commit b4a1a8db8e
Signed by untrusted user: alekswilc
GPG Key ID: D4464A248E5F27FE
23 changed files with 784 additions and 117 deletions

View File

@ -11,7 +11,7 @@ https://simrail.alekswilc.dev/
- Statystyki
## Dalszy rozwój
- Obsługa pociagów, a nie tylko posterunków ([#8](https://git.alekswilc.dev/alekswilc/simrail-logs/issues/8))
- Nowy wygląd aplikacji
# Zgłaszanie błedów
Jak zgłosić błąd?

View File

@ -0,0 +1,81 @@
import { Router } from 'express';
import dayjs from 'dayjs';
import { PlayerUtil } from '../../util/PlayerUtil.js';
import { msToTime } from '../../util/time.js';
import { PipelineStage } from 'mongoose';
import { MProfile, raw_schema } from '../../mongo/profile.js';
const generateSearch = (regex: RegExp) => [
{
steam: { $regex: regex },
},
{
steamName: { $regex: regex },
},
]
export class LeaderboardRoute {
static load() {
const app = Router();
app.get('/train', 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 MProfile.aggregate(filter)
.sort({ trainPoints: -1 })
.limit(10)
res.render('leaderboard/index.ejs', {
records,
dayjs,
msToTime,
type: 'train',
q: req.query.q,
});
})
app.get('/station', 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 MProfile.aggregate(filter)
.sort({ dispatcherTime: -1 })
.limit(10)
res.render('leaderboard/index.ejs', {
records,
dayjs,
msToTime,
type: 'station',
q: req.query.q,
});
})
return app;
}
}

View File

@ -0,0 +1,32 @@
import { Router } from 'express';
import dayjs from 'dayjs';
import { PlayerUtil } from '../../util/PlayerUtil.js';
import { msToTime } from '../../util/time.js';
import { MProfile } from '../../mongo/profile.js';
import { MBlacklist } from '../../mongo/blacklist.js';
export class ProfilesRoute {
static load() {
const app = Router();
app.get('/:id', async (req, res) => {
if (!req.params.id) return res.redirect('/');
const player = await MProfile.findOne({ steam: req.params.id });
if (!player) return res.render('profiles/private.ejs');
const blacklist = await MBlacklist.findOne({ steam: req.params.id! });
if (blacklist && blacklist.status) return res.render('profiles/private.ejs');
const steam = await PlayerUtil.getPlayer(player?.steam!);
const steamStats = await PlayerUtil.getPlayerStats(player?.steam!);
res.render('profiles/index.ejs', {
player, steam, steamStats: steamStats,
msToTime
});
})
return app;
}
}

View File

@ -4,6 +4,7 @@ import dayjs from 'dayjs';
import { PlayerUtil } from '../../util/PlayerUtil.js';
import { msToTime } from '../../util/time.js';
import { PipelineStage } from 'mongoose';
import { MBlacklist } from '../../mongo/blacklist.js';
const generateSearch = (regex: RegExp) => [
{
@ -55,6 +56,8 @@ export class StationsRoute {
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 blacklist = await MBlacklist.findOne({ steam: record?.userSteamId! });
if (blacklist && blacklist.status) return res.redirect('/stations/');
const player = await PlayerUtil.getPlayer(record?.userSteamId!);
res.render('stations/details.ejs', {
@ -65,45 +68,7 @@ export class StationsRoute {
});
})
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
/*

126
src/http/routes/trains.ts Normal file
View File

@ -0,0 +1,126 @@
import { Router } from 'express';
import dayjs from 'dayjs';
import { PlayerUtil } from '../../util/PlayerUtil.js';
import { msToTime } from '../../util/time.js';
import { PipelineStage } from 'mongoose';
import { MTrainLog, raw_schema } from '../../mongo/trainLogs.js';
import { MBlacklist } from '../../mongo/blacklist.js';
const generateSearch = (regex: RegExp) => [
{
trainNumber: { $regex: regex },
},
{
userSteamId: { $regex: regex },
},
{
server: { $regex: regex },
}
]
export class TrainsRoute {
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 MTrainLog.aggregate(filter)
.sort({ leftDate: -1 })
.limit(30)
res.render('trains/index.ejs', {
records,
dayjs,
q: req.query.q,
msToTime
});
})
app.get('/details/:id', async (req, res) => {
if (!req.params.id) return res.redirect('/trains/');
const record = await MTrainLog.findOne({ id: req.params.id });
const player = await PlayerUtil.getPlayer(record?.userSteamId!);
const blacklist = await MBlacklist.findOne({ steam: record?.userSteamId! });
if (blacklist && blacklist.status) return res.redirect('/trains/');
res.render('trains/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 MTrainLog.aggregate(filter)
.sort({ result: -1 })
.limit(30)
res.render('trains/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;
}
}

View File

@ -2,6 +2,10 @@ import express from 'express';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { StationsRoute } from './routes/stations.js';
import { TrainsRoute } from './routes/trains.js';
import { ProfilesRoute } from './routes/profile.js';
import { LeaderboardRoute } from './routes/leaderboard.js';
import { MProfile } from '../mongo/profile.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@ -20,6 +24,28 @@ export class ApiModule {
app.get('/details/:id', (req, res) => res.redirect('/stations/details/'+req.params.id));
app.use('/stations/', StationsRoute.load());
app.use('/trains/', TrainsRoute.load());
app.use('/profiles/', ProfilesRoute.load());
app.use('/leaderboard/', LeaderboardRoute.load())
app.get('/migrate', async () => {
const _ = await MProfile.find({});
_.forEach(async(x) => {
x.trainPoints = Object.values(x.trainStats).length === 1 ?
Object.values(x.trainStats)[0].score :
Object.values(x.trainStats).reduce((acc, curr) => acc + curr.score, 0)
x.trainDistance = Object.values(x.trainStats).length === 1 ?
Object.values(x.trainStats)[0].distance :
Object.values(x.trainStats).reduce((acc, curr) => acc + curr.distance, 0)
await MProfile.updateOne({ id: x.id }, { trainPoints: x.trainPoints, trainDistance: x.trainDistance })
})
})
app.listen(2005);
}

View File

@ -1,19 +1,11 @@
<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>
<% } %>
<a href="/trains/">Logi pociągów</a> /
<a href="/leaderboard/train">Tablica pociągow</a> /
<a href="/leaderboard/station">Tablica posterunkó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

View File

@ -39,7 +39,7 @@
<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>
<p>Nowy wygląd aplikacji</p>
</li>
</ul>

View File

@ -0,0 +1,77 @@
<!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/leaderboard/">
<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: 'leaderboard' }) %>
<h2 id="but">Tablica wyników</h2>
<div class="container">
<input type="text" id="search" value="<%-q%>">
<button onclick="search()">Szukaj</button>
<button onclick="clearSearch()">Wyczyść</button>
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p>
</div>
<br />
<ul>
<% records.forEach(record=> { %>
<li>
<%- type === 'train' ? include('train.ejs', { record, msToTime }) : include('station.ejs', { record, msToTime }) %>
</li>
<% }) %>
</ul>
<% if (!records.length) { %>
<h4>Nie znaleziono wyników dla twojego zapytania.</h4>
<% } %>
<script>
function select() {
const isTrain = "<%- type %>" === 'train'
location.href = '/leaderboard/' + (isTrain ? 'station' : 'train')
}
document.getElementById('but').textContent = ("<%- type %>" === 'train') ? 'Tablica pociągów' : 'Tablica posterunków'
function search() {
location.href = '/leaderboard/<%- type %>/?q=' + document.getElementById('search').value
}
function clearSearch() {
location.href = '/leaderboard/<%- type %>';
}
document.getElementById('search').addEventListener("keyup", (event) => {
if (event.key === "Enter")
search();
});
</script>
<hr>
<p style="color: orange;">Dane do rankingu zbierane są od dnia 19.08.2024.</p>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</html>

View File

@ -0,0 +1,9 @@
<details>
<summary><span style="color:hotpink">
<%- record.steamName %>
</span> <span style="color: lightskyblue">
<%- msToTime(record.dispatcherTime) %>
</span> </summary>
<p>Spędzona liczba godzin: <%- msToTime(record.dispatcherTime) || 'Brak' %></p>
<button onclick="location.href = '/profiles/<%- record.steam %>'">Więcej</button>
</details>

View File

@ -0,0 +1,11 @@
<details>
<summary><span style="color:hotpink">
<%- record.steamName %>
</span> - <span style="color:lightskyblue">
<%- record.trainPoints %> pkt.
</span></summary>
<p>Spędzona liczba godzin: <%- msToTime(record.trainTime) %></p>
<p>Przejechane kilometry: <%- record.trainDistance / 1000 %>km</p>
<p>Zdobyte punkty: <%- record.trainPoints %></p>
<button onclick="location.href = '/profiles/<%- record.steam %>'">Więcej</button>
</details>

View File

@ -0,0 +1,117 @@
<!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="<%- steam.personaname %>">
<meta property="og:title" content="Simrail Log">
<meta property="og:url" content="https://simrail.alekswilc.dev/profiles/<%- player.steam %>/">
<meta property="og:description"
content="<%- steam.personaname %>">
<meta property=" og:type" content="website">
<meta property="og:image" content="<%- steam.avatarfull %>" />
<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/@exampledev/new.css@1.1.2/new.min.css">
<style>
p {
margin: 0;
}
.details {
display: flex;
flex-direction: column;
}
.clickable {
cursor: pointer;
}
</style>
</head>
<script>
function copylink() {
navigator.clipboard.writeText("https://simrail.alekswilc.dev/players/details/<%- player.steam %>/")
}
</script>
<body>
<%- include('../_modules/header.ejs', { section: 'profiles' }) %>
<div class="details">
<h1><a href="<%- steam.profileurl %>"><%- steam.personaname %></a></h1>
<%if (steamStats.stats) {%>
<details open>
<summary>Statystyki Steam</summary>
<p>Zdobyte punkty: <%- steamStats.stats.find(x => x.name === 'SCORE')?.value ?? "0" %></p>
<p>Przejechane kilometry: <%- (steamStats.stats.find(x => x.name === 'DISTANCE_M')?.value / 1000) ?? "0" %></p>
<p>Czas spędzony jako dyżurny ruchu: <%- msToTime((steamStats.stats.find(x => x.name === 'DISPATCHER_TIME')?.value ?? 0)*1_000_000) || 'Nigdy nie wszedł w tryb dyżurnego ruchu.' %></p>
<br />
<p style="font-size: smaller;">UWAGA: powyższe statystyki udostępnia platforma STEAM, mogą one być z łatwością manipulowane.</p>
</details>
<%}%>
<h1>Statystyki pociągów</h1>
<% if (player.trainTime) {%>
<p>Spędzony czas: <%- msToTime(player.trainTime) %></p>
<p>Przejechane kilometry: <%- player.trainDistance / 1000 %>km</p>
<p>Zdobyte punkty: <%- player.trainPoints %></p>
<p>Średnia prędkość: <%- ((player.trainDistance / (player.trainTime / 1000)) * 3.6).toFixed(2) %> km/h</p>
<%}%>
<% if (player.trainStats && Object.keys(player.trainStats).length) {%>
<ul>
<% Object.keys(player.trainStats).forEach(name => {%>
<li>
<details open>
<summary><%- name %></summary>
<p>Przejechany dystans: <%- player.trainStats[name].distance / 1000 %>km</p>
<p>Spędzony czas: <%- msToTime(player.trainStats[name].time) %></p>
<p>Zdobyte punkty: <%- player.trainStats[name].score %></p>
<p>Średnia prędkość: <%- ((player.trainStats[name].distance / (player.trainStats[name].time / 1000)) * 3.6).toFixed(2) %> km/h</p>
</details>
</li>
<% }) %>
</ul>
<%} else {%>
<p>Brak danych</p>
<%}%>
<h1>Statystyki posterunków</h1>
<% if (player.dispatcherTime) {%>
<p>Spędzony czas: <%- msToTime(player.dispatcherTime) %></p>
<%}%>
<% if (player.dispatcherStats && Object.keys(player.dispatcherStats).length) {%>
<ul>
<% Object.keys(player.dispatcherStats).forEach(name => {%>
<li>
<details open>
<summary><%- name %></summary>
<p>Spędzony czas: <%- msToTime(player.dispatcherStats[name].time) %></p>
</details>
</li>
<% }) %>
</ul>
<%} else {%>
<p>Brak danych</p>
<%}%>
<br />
<p><button onclick="copylink()">Kopiuj link</button></p>
</div>
<hr>
<p style="color: orange;">Dane do rankingu zbierane są od dnia 19.08.2024.</p>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!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="Profil prywatny">
<meta property="og:title" content="Simrail Log">
<meta property="og:url" content="https://simrail.alekswilc.dev/">
<meta property="og:description"
content="Profil prywatny">
<meta property=" og:type" content="website">
<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/@exampledev/new.css@1.1.2/new.min.css">
<style>
p {
margin: 0;
}
.details {
display: flex;
flex-direction: column;
}
.clickable {
cursor: pointer;
}
</style>
</head>
<body>
<%- include('../_modules/header.ejs', { section: 'profiles' }) %>
<div class="details">
<p>Profil gracza jest prywatny.</p>
</div>
<hr>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</html>

View File

@ -6,13 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>simrail.alekswilc.dev</title>
<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: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"
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:image" content="<%= record.userAvatar %>" />
<meta property="og:image" content="<%- record.userAvatar %>" />
<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/@exampledev/new.css@1.1.2/new.min.css">
@ -40,7 +40,7 @@
}
function copylink() {
navigator.clipboard.writeText("https://simrail.alekswilc.dev/stations/details/<%= record.id %>/")
navigator.clipboard.writeText("https://simrail.alekswilc.dev/stations/details/<%- record.id %>/")
}
</script>
@ -49,28 +49,28 @@
<div class="details">
<p>Użytkownik: <a href="<%= player.profileurl %>">
<%= record.userUsername %>
<p>Użytkownik: <a href="/profiles/<%- record.userSteamId %>">
<%- record.userUsername %>
</a></p>
<p>Stacja: <%= record.stationName %>
<p>Stacja: <%- record.stationName %>
</p>
<p>Serwer: <%= record.server.toUpperCase() %>
<p>Serwer: <%- record.server.toUpperCase() %>
</p>
<p>Data wejścia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') : '--:-- --/--/--'
%> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)</p>
<p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%= dayjs(record.leftDate).fromNow()
<p>Data wejścia: <%- record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') : '--:-- --/--/--'
%> (<%- record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)</p>
<p>Data wyjścia: <%- dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%- dayjs(record.leftDate).fromNow()
%>)</p>
<p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<p>Spędzony czas: <%- record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
</p>
<br />
<code class="clickable" style="white-space: pre-line" onclick="copydata()" id="data">;station: <%= record.stationName %>
;steam: <%= record.userSteamId %>
;server: <%= record.server %>
;name: <%= record.userUsername %>
;joined: <%=record.joinedDate ? dayjs(record.joinedDate).format() : 'no-data'%>
;left: <%=dayjs(record.leftDate).format()%>
;url: https://simrail.alekswilc.dev/stations/details/<%= record.id %>/
<code class="clickable" style="white-space: pre-line" onclick="copydata()" id="data">;station: <%- record.stationName %>
;steam: <%- record.userSteamId %>
;server: <%- record.server %>
;name: <%- record.userUsername %>
;joined: <%-record.joinedDate ? dayjs(record.joinedDate).format() : 'no-data'%>
;left: <%-dayjs(record.leftDate).format()%>
;url: https://simrail.alekswilc.dev/stations/details/<%- record.id %>/
</code>
<br />
<p><button onclick="copylink()">Kopiuj link</button></p>

View File

@ -26,7 +26,7 @@
<h2>Wyszukaj posterunek, osobe lub serwer</h2>
<div class="container">
<input type="text" id="search" value="<%=q%>">
<input type="text" id="search" value="<%-q%>">
<button onclick="search()">Szukaj</button>
<button onclick="clearSearch()">Wyczyść</button>
@ -38,25 +38,25 @@
<li>
<details>
<summary>[<span style="color:lightskyblue">
<%= record.server.toUpperCase() %>
<%- record.server.toUpperCase() %>
</span>] <span style="color: lightskyblue">
<%= record.stationName %>
<%- record.stationName %>
</span> - <span style="color:hotpink">
<%= record.userUsername %>
<%- record.userUsername %>
</span>
<p style="margin-bottom: 0; opacity: 0.5;">
<%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %>
<%- dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %>
</p>
</summary>
<p>Data dołączenia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY')
: '--:-- --/--/--' %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
<p>Data dołączenia: <%- record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY')
: '--:-- --/--/--' %> (<%- record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
</p>
<p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%=
<p>Data wyjścia: <%- dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%-
dayjs(record.leftDate).fromNow() %>)</p>
<p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<p>Spędzony czas: <%- record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
</p>
<a href="/stations/details/<%= record.id %>">
<a href="/stations/details/<%- record.id %>">
<button>Więcej</button>
</a>
</details>

View File

@ -0,0 +1,94 @@
<!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="<%- record.stationName %> | <%- record.userUsername %> | <%- dayjs(record.leftDate).format('hh:mm DD/MM/YYYY') %>">
<meta property="og:title" content="Simrail Log">
<meta property="og:url" content="https://simrail.alekswilc.dev/details/<%- record.id %>/">
<meta property="og:description"
content="<%- record.stationName %> | <%- record.userUsername %> | <%- dayjs(record.leftDate).format('hh:mm DD/MM/YYYY') %>"">
<meta property=" og:type" content="website">
<meta property="og:image" content="<%- record.userAvatar %>" />
<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/@exampledev/new.css@1.1.2/new.min.css">
<style>
p {
margin: 0;
}
.details {
display: flex;
flex-direction: column;
}
.clickable {
cursor: pointer;
}
</style>
</head>
<script>
function copydata() {
navigator.clipboard.writeText(document.getElementById('data').textContent.replace(/ /g, '').split('\n').filter(x => x).join(''))
}
function copylink() {
navigator.clipboard.writeText("https://simrail.alekswilc.dev/trains/details/<%- record.id %>/")
}
</script>
<body>
<%- include('../_modules/header.ejs', { section: 'trains' }) %>
<div class="details">
<p>Użytkownik: <a href="/profiles/<%- record.userSteamId %>">
<%- record.userUsername %>
</a></p>
<p>Stacja: <%- record.stationName %>
</p>
<p>Pociąg: <%- record.trainName %> <%- record.trainNumber %>
</p>
<p>Data wejścia: <%- record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY') : '--:-- --/--/--'
%> (<%- record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)</p>
<p>Data wyjścia: <%- dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%- dayjs(record.leftDate).fromNow()
%>)</p>
<p>Spędzony czas: <%- record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
</p>
<% if (record.distance) { %>
<p>Przejechane kilometry: <%- record.distance / 1000 %></p>
<p>Zdobyte punkty: <%- record.points %></p>
<p>Średnia prędkość: <%- ((record.distance / ((record.leftDate - record.joinedDate) / 1000)) * 3.6).toFixed(2) %> km/h</p>
<% } %>
</p>
<br />
<code class="clickable" style="white-space: pre-line" onclick="copydata()" id="data">;train: <%- record.trainNumber %>
;steam: <%- record.userSteamId %>
;server: <%- record.server %>
;name: <%- record.userUsername %>
;joined: <%-record.joinedDate ? dayjs(record.joinedDate).format() : 'no-data'%>
;left: <%-dayjs(record.leftDate).format()%><%if (record.distance) {%>
;distance: <%- record.distance / 1000 %>
;points: <%- record.points %><%}%>
;url: https://simrail.alekswilc.dev/trains/details/<%- record.id %>/
</code>
<br />
<p><button onclick="copylink()">Kopiuj link</button></p>
</div>
<hr>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</html>

View File

@ -7,7 +7,7 @@
<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/search?q=<%=q%>">
<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">
@ -18,46 +18,51 @@
margin: 1%;
}
</style>
</head>
<body>
<%- include('../_modules/header.ejs', { section: 'stations' }) %>
<%- include('../_modules/header.ejs', { section: 'trains' }) %>
<h2>Wyszukaj pociąg, osobe lub serwer</h2>
<h2>Tablica godzin</h2>
<div class="container">
<input type="text" id="search" value="<%=q%>">
<button onclick="search()">Filtruj</button>
<input type="text" id="search" value="<%-q%>">
<button onclick="search()">Szukaj</button>
<button onclick="clearSearch()">Wyczyść</button>
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p>
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,1413</p>
</div>
<ul>
<% records.forEach(record=> { %>
<li>
<details>
<summary>[<span style="color:lightskyblue">
<%= record.server.toUpperCase() %>
<%- record.server.toUpperCase() %>
</span>] <span style="color: lightskyblue">
<%= record.stationName %>
<%- record.trainName %>
</span> - <span style="color: lightskyblue">
<%- record.trainNumber %>
</span> - <span style="color:hotpink">
<%= record.userUsername %>
</span>- <span style="color:lightseagreen">
<%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<%- record.userUsername %>
</span>
<p style="margin-bottom: 0; opacity: 0.5;">
<%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %>
<%- dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %>
</p>
</summary>
<p>Data dołączenia: <%= record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY')
: '--:-- --/--/--' %> (<%= record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
<p>Data dołączenia: <%- record.joinedDate ? dayjs(record.joinedDate).format('HH:mm DD/MM/YYYY')
: '--:-- --/--/--' %> (<%- record.joinedDate ? dayjs(record.joinedDate).fromNow() : '--' %>)
</p>
<p>Data wyjścia: <%= dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%=
<p>Data wyjścia: <%- dayjs(record.leftDate).format('HH:mm DD/MM/YYYY') %> (<%-
dayjs(record.leftDate).fromNow() %>)</p>
<p>Spędzony czas: <%= record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<p>Spędzony czas: <%- record.joinedDate ? msToTime(record.leftDate - record.joinedDate) : '--' %>
<% if (record.distance) { %>
<p>Przejechane kilometry: <%- record.distance / 1000 %></p>
<p>Zdobyte punkty: <%- record.points %></p>
<% } %>
</p>
<a href="/details/<%= record.id %>">
<a href="/trains/details/<%- record.id %>">
<button>Więcej</button>
</a>
</details>
@ -69,24 +74,24 @@
<h4>Nie znaleziono wyników dla twojego zapytania.</h4>
<% } %>
<hr>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
<script>
function search() {
location.href = '/stations/leaderboard?q=' + document.getElementById('search').value
location.href = '/trains/?q=' + document.getElementById('search').value
}
function clearSearch() {
location.href = '/stations/leaderboard'
console.log('test')
location.href = '/trains/';
}
document.getElementById('search').addEventListener("keyup", (event) => {
if (event.key === "Enter")
search();
});
</script>
<hr>
<%- include('../_modules/footer.ejs', { thanks: false }) %>
</body>
</html>

View File

@ -44,6 +44,12 @@ import { TrainsModule } from './modules/trains.js';
StationsModule.load();
TrainsModule.load();
ApiModule.load();
process.on('unhandledRejection', (reason, promise) => {
console.error(reason);
console.error(promise);
})
})();

View File

@ -13,22 +13,26 @@ export class StationsModule {
const stats = await PlayerUtil.getPlayerStats(player.steamid);
const date = new Date();
if (stats) {
const time = date.getTime() - joinedAt;
const userProfile = await MProfile.findOne({ steam: player.steamid }) ?? await MProfile.create({ steam: player.steamid, id: v4() });
const time = (date.getTime() - joinedAt) ?? 0;
const userProfile = await MProfile.findOne({ steam: player.steamid }) ?? await MProfile.create({ steam: player.steamid, id: v4(), steamName: player.personaname });
if (!userProfile.dispatcherStats) userProfile.dispatcherStats = {};
if (userProfile.dispatcherStats[station.Name]) {
userProfile.dispatcherStats[station.Name].time = userProfile.dispatcherStats[station.Name].time + time;
} else {
userProfile.dispatcherStats[station.Name] = {
time
}
}
if (Number.isNaN(userProfile.dispatcherStats[station.Name].time)) userProfile.dispatcherStats[station.Name].time = 0;
console.log(userProfile.dispatcherStats);
if (!userProfile.dispatcherTime) userProfile.dispatcherTime = 0;
console.log(await MProfile.findOneAndUpdate({ id: userProfile.id }, { dispatcherStats: userProfile.dispatcherStats }))
userProfile.dispatcherTime = userProfile.dispatcherTime + time;
await MProfile.findOneAndUpdate({ id: userProfile.id }, { dispatcherStats: userProfile.dispatcherStats, dispatcherTime: userProfile.dispatcherTime })
}
MLog.create({

View File

@ -12,7 +12,8 @@ export class TrainsModule {
client.on(SimrailClientEvents.TrainLeft, async (server: Server, train: Train, player: IPlayer, joinedAt: number, leftAt: number, points: number, distance: number, vehicle: string) => {
if (distance) {
const userProfile = await MProfile.findOne({ steam: player.steamid }) ?? await MProfile.create({ steam: player.steamid, id: v4() });
const time = (leftAt - joinedAt) ?? 0;
const userProfile = await MProfile.findOne({ steam: player.steamid }) ?? await MProfile.create({ steam: player.steamid, id: v4(), steamName: player.personaname });
const vehicleName = getVehicle(vehicle) ?? vehicle;
@ -21,14 +22,26 @@ export class TrainsModule {
if (userProfile.trainStats[vehicleName]) {
userProfile.trainStats[vehicleName].distance = userProfile.trainStats[vehicleName].distance + distance;
userProfile.trainStats[vehicleName].score = userProfile.trainStats[vehicleName].score + points;
userProfile.trainStats[vehicleName].time = userProfile.trainStats[vehicleName].time + time;
} else {
userProfile.trainStats[vehicleName] = {
distance, score: points
distance, score: points, time
}
}
await MProfile.findOneAndUpdate({ id: userProfile.id }, { trainStats: userProfile.trainStats })
if (!userProfile.trainTime) userProfile.trainTime = 0;
userProfile.trainTime = userProfile.trainTime + time;
if (!userProfile.trainPoints) userProfile.trainPoints = 0;
userProfile.trainPoints = userProfile.trainPoints + points;
if (!userProfile.trainDistance) userProfile.trainDistance = 0;
userProfile.trainDistance = userProfile.trainDistance + distance;
await MProfile.findOneAndUpdate({ id: userProfile.id }, { trainStats: userProfile.trainStats, trainTime: userProfile.trainTime, trainPoints: userProfile.trainPoints, trainDistance: userProfile.trainDistance });
}
MTrainLog.create({
@ -40,7 +53,8 @@ export class TrainsModule {
leftDate: leftAt,
trainNumber: train.TrainNoLocal,
server: server.ServerCode,
distance, points
distance, points,
trainName: train.TrainName
});
})
}

24
src/mongo/blacklist.ts Normal file
View File

@ -0,0 +1,24 @@
import { Model, model, Schema } from 'mongoose';
export const raw_schema = {
steam: {
type: String,
required: true
},
status: {
type: Boolean,
default: false
}
}
const schema = new Schema<IBlacklist>(raw_schema);
export type TMBlacklist = Model<IBlacklist>
export const MBlacklist = model<IBlacklist>('blacklist', schema);
export interface IBlacklist {
steam: string
status: boolean
}

View File

@ -10,6 +10,11 @@ export const raw_schema = {
type: String,
required: true
},
steamName: {
type: String,
required: true
},
trainStats: {
type: Object,
required: false,
@ -20,6 +25,26 @@ export const raw_schema = {
required: false,
default: {}
},
trainTime: {
type: Number,
required: false,
default: 0
},
trainPoints: {
type: Number,
required: false,
default: 0
},
trainDistance: {
type: Number,
required: false,
default: 0
},
dispatcherTime: {
type: Number,
required: false,
default: 0
},
}
const schema = new Schema<IProfile>(raw_schema);
@ -36,6 +61,7 @@ export interface IProfile {
[trainName: string]: {
score: number,
distance: number
time: number,
}
}
dispatcherStats: {
@ -43,4 +69,10 @@ export interface IProfile {
time: number
}
}
dispatcherTime: number;
trainTime: number
trainPoints: number
steamName: string
trainDistance: number
}

View File

@ -45,6 +45,10 @@ export const raw_schema = {
type: String,
required: true
},
trainName: {
type: String,
default: null
}
}
const schema = new Schema<ITrainLog>(raw_schema);
@ -61,6 +65,7 @@ export interface ITrainLog {
joinedDate?: number
leftDate: number
trainNumber: string
trainName: string
distance: number
points: number
server: string