v3 release #75
packages
backend/src
http
index.tsmodules
mongo
types
util
frontend/src
@ -1,56 +1,78 @@
|
||||
import { assert } from 'node:console';
|
||||
import { assert } from "node:console";
|
||||
|
||||
export interface IResponse<T> {
|
||||
success: boolean
|
||||
export interface IResponse<T>
|
||||
{
|
||||
success: boolean;
|
||||
data: T;
|
||||
code: number
|
||||
code: number;
|
||||
}
|
||||
|
||||
export class BaseResponseBuilder<T> {
|
||||
protected success: IResponse<T>['success'] = undefined!;
|
||||
protected data: IResponse<T>['data'] = undefined!;
|
||||
protected code: IResponse<T>['code'] = undefined!;
|
||||
export class BaseResponseBuilder<T>
|
||||
{
|
||||
protected success: IResponse<T>["success"] = undefined!;
|
||||
protected data: IResponse<T>["data"] = undefined!;
|
||||
protected code: IResponse<T>["code"] = undefined!;
|
||||
|
||||
|
||||
public constructor(data?: Partial<IResponse<T>>) {
|
||||
if (!data) return;
|
||||
if ('success' in data) this.success = data.success as IResponse<T>['success'];
|
||||
if ('data' in data) this.data = data.data as IResponse<T>['data'];
|
||||
if ('code' in data) this.code = data.code as IResponse<T>['code'];
|
||||
public constructor(data?: Partial<IResponse<T>>)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ("success" in data)
|
||||
{
|
||||
this.success = data.success as IResponse<T>["success"];
|
||||
}
|
||||
if ("data" in data)
|
||||
{
|
||||
this.data = data.data as IResponse<T>["data"];
|
||||
}
|
||||
if ("code" in data)
|
||||
{
|
||||
this.code = data.code as IResponse<T>["code"];
|
||||
}
|
||||
}
|
||||
|
||||
public setData(data: T) {
|
||||
public setData(data: T)
|
||||
{
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public setCode(code: number) {
|
||||
public setCode(code: number)
|
||||
{
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
public toJSON()
|
||||
{
|
||||
const { success, data, code } = this;
|
||||
assert(typeof(success) === 'boolean', 'expected success to be boolean');
|
||||
assert(typeof(code) === 'number', 'expected success to be number');
|
||||
assert(typeof (success) === "boolean", "expected success to be boolean");
|
||||
assert(typeof (code) === "number", "expected success to be number");
|
||||
|
||||
return {
|
||||
success, data, code
|
||||
}
|
||||
success, data, code,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class SuccessResponseBuilder<T> extends BaseResponseBuilder<T> {
|
||||
public constructor(options?: IResponse<T>) {
|
||||
export class SuccessResponseBuilder<T> extends BaseResponseBuilder<T>
|
||||
{
|
||||
public constructor(options?: IResponse<T>)
|
||||
{
|
||||
super(options);
|
||||
this.success = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ErrorResponseBuilder<T> extends BaseResponseBuilder<T> {
|
||||
public constructor(options?: IResponse<T>) {
|
||||
export class ErrorResponseBuilder<T> extends BaseResponseBuilder<T>
|
||||
{
|
||||
public constructor(options?: IResponse<T>)
|
||||
{
|
||||
super(options);
|
||||
this.success = false;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import { PipelineStage } from 'mongoose';
|
||||
import { IProfile, MProfile, raw_schema } from '../../mongo/profile.js';
|
||||
import { SuccessResponseBuilder } from '../responseBuilder.js';
|
||||
import { removeProperties } from '../../util/functions.js';
|
||||
import { Router } from "express";
|
||||
import { PipelineStage } from "mongoose";
|
||||
import { IProfile, MProfile, raw_schema } from "../../mongo/profile.js";
|
||||
import { SuccessResponseBuilder } from "../responseBuilder.js";
|
||||
import { removeProperties } from "../../util/functions.js";
|
||||
|
||||
const generateSearch = (regex: RegExp) => [
|
||||
{
|
||||
@ -11,61 +11,65 @@ const generateSearch = (regex: RegExp) => [
|
||||
{
|
||||
steamName: { $regex: regex },
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
export class LeaderboardRoute {
|
||||
static load() {
|
||||
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"));
|
||||
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) }))
|
||||
]
|
||||
}
|
||||
})
|
||||
...s.map(x => ({ $or: generateSearch(x) })),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const records = await MProfile.aggregate(filter)
|
||||
.sort({ trainPoints: -1 })
|
||||
.limit(10)
|
||||
.limit(10);
|
||||
|
||||
res.json(
|
||||
new SuccessResponseBuilder<{ records: Omit<IProfile, '_id' | '__v'>[] }>()
|
||||
new SuccessResponseBuilder<{ records: Omit<IProfile, "_id" | "__v">[] }>()
|
||||
.setCode(200)
|
||||
.setData({ records: records.map(x => removeProperties<Omit<IProfile, '_id' | '__v'>>(x, ['_id', '__v'])) })
|
||||
.toJSON()
|
||||
.setData({ records: records.map(x => removeProperties<Omit<IProfile, "_id" | "__v">>(x, [ "_id", "__v" ])) })
|
||||
.toJSON(),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
app.get('/station', async (req, res) => {
|
||||
const s = req.query.q?.toString().split(',').map(x => new RegExp(x, "i"));
|
||||
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) }))
|
||||
]
|
||||
}
|
||||
})
|
||||
...s.map(x => ({ $or: generateSearch(x) })),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const records = await MProfile.aggregate(filter)
|
||||
.sort({ dispatcherTime: -1 })
|
||||
.limit(10)
|
||||
.limit(10);
|
||||
|
||||
res.json(
|
||||
new SuccessResponseBuilder<{ records: Omit<IProfile, '_id' | '__v'>[] }>()
|
||||
new SuccessResponseBuilder<{ records: Omit<IProfile, "_id" | "__v">[] }>()
|
||||
.setCode(200)
|
||||
.setData({ records: records.map(x => removeProperties<Omit<IProfile, '_id' | '__v'>>(x, ['_id', '__v'])) })
|
||||
.toJSON()
|
||||
.setData({ records: records.map(x => removeProperties<Omit<IProfile, "_id" | "__v">>(x, [ "_id", "__v" ])) })
|
||||
.toJSON(),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
40
packages/backend/src/http/routes/log.ts
Normal file
40
packages/backend/src/http/routes/log.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Router } from "express";
|
||||
import { ITrainLog, MTrainLog } from "../../mongo/trainLogs.js";
|
||||
import { MBlacklist } from "../../mongo/blacklist.js";
|
||||
import { ErrorResponseBuilder, SuccessResponseBuilder } from "../responseBuilder.js";
|
||||
import { removeProperties } from "../../util/functions.js";
|
||||
import { ILog, MLog } from "../../mongo/logs.js";
|
||||
|
||||
|
||||
export class LogRoute
|
||||
{
|
||||
static load()
|
||||
{
|
||||
const app = Router();
|
||||
|
||||
app.get("/:id", async (req, res) =>
|
||||
{
|
||||
const { id } = req.params;
|
||||
if (!id)
|
||||
{
|
||||
res.status(400).json(new ErrorResponseBuilder()
|
||||
.setCode(400)
|
||||
.setData("Missing Id parameter").toJSON());
|
||||
return;
|
||||
}
|
||||
|
||||
const log = await MLog.findOne({ id }) || await MTrainLog.findOne({ id });
|
||||
if (!log)
|
||||
{
|
||||
res.status(404).json(new ErrorResponseBuilder()
|
||||
.setCode(404)
|
||||
.setData("Invalid Id parameter").toJSON());
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json(new SuccessResponseBuilder().setCode(200).setData(removeProperties<Omit<(ILog | ITrainLog), "_id" | "__v">>(log.toJSON(), [ "_id", "__v" ])));
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
@ -1,23 +1,38 @@
|
||||
import { Router } from 'express';
|
||||
import { msToTime } from '../../util/time.js';
|
||||
import { MProfile } from '../../mongo/profile.js';
|
||||
import { MBlacklist } from '../../mongo/blacklist.js';
|
||||
import { SteamUtil } from '../../util/SteamUtil.js';
|
||||
import { ErrorResponseBuilder, SuccessResponseBuilder } from '../responseBuilder.js';
|
||||
import { Router } from "express";
|
||||
import { msToTime } from "../../util/time.js";
|
||||
import { MProfile } from "../../mongo/profile.js";
|
||||
import { MBlacklist } from "../../mongo/blacklist.js";
|
||||
import { SteamUtil } from "../../util/SteamUtil.js";
|
||||
import { ErrorResponseBuilder, SuccessResponseBuilder } from "../responseBuilder.js";
|
||||
|
||||
export class ProfilesRoute {
|
||||
static load() {
|
||||
export class ProfilesRoute
|
||||
{
|
||||
static load()
|
||||
{
|
||||
const app = Router();
|
||||
|
||||
|
||||
app.get('/:id', async (req, res) => {
|
||||
if (!req.params.id) return res.redirect('/');
|
||||
app.get("/:id", async (req, res) =>
|
||||
{
|
||||
if (!req.params.id)
|
||||
{
|
||||
res.redirect("/");
|
||||
return;
|
||||
}
|
||||
const player = await MProfile.findOne({ steam: req.params.id });
|
||||
if (!player) return res.status(404).json(new ErrorResponseBuilder()
|
||||
.setCode(404).setData("Profile not found! (propably private)"));
|
||||
if (!player)
|
||||
{
|
||||
res.status(404).json(new ErrorResponseBuilder()
|
||||
.setCode(404).setData("Profile not found! (propably private)"));
|
||||
return;
|
||||
}
|
||||
const blacklist = await MBlacklist.findOne({ steam: req.params.id! });
|
||||
if (blacklist && blacklist.status) return res.status(403).json(new ErrorResponseBuilder()
|
||||
.setCode(403).setData("Profile blacklisted!"));
|
||||
if (blacklist && blacklist.status)
|
||||
{
|
||||
res.status(403).json(new ErrorResponseBuilder()
|
||||
.setCode(403).setData("Profile blacklisted!"));
|
||||
return;
|
||||
}
|
||||
const steam = await SteamUtil.getPlayer(player?.steam!);
|
||||
const steamStats = await SteamUtil.getPlayerStats(player?.steam!);
|
||||
|
||||
@ -26,16 +41,16 @@ export class ProfilesRoute {
|
||||
new SuccessResponseBuilder()
|
||||
.setCode(200)
|
||||
.setData({
|
||||
player, steam, steamStats
|
||||
player, steam, steamStats,
|
||||
})
|
||||
.toJSON()
|
||||
)
|
||||
.toJSON(),
|
||||
);
|
||||
|
||||
res.render('profiles/index.ejs', {
|
||||
res.render("profiles/index.ejs", {
|
||||
player, steam, steamStats: steamStats,
|
||||
msToTime,
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import { ILog, MLog } from '../../mongo/logs.js';
|
||||
import dayjs from 'dayjs';
|
||||
import { msToTime } from '../../util/time.js';
|
||||
import { PipelineStage } from 'mongoose';
|
||||
import { MBlacklist } from '../../mongo/blacklist.js';
|
||||
import { SteamUtil } from '../../util/SteamUtil.js';
|
||||
import { GitUtil } from '../../util/git.js';
|
||||
import { removeProperties } from '../../util/functions.js';
|
||||
import { SuccessResponseBuilder } from '../responseBuilder.js';
|
||||
import { Router } from "express";
|
||||
import { ILog, MLog } from "../../mongo/logs.js";
|
||||
import dayjs from "dayjs";
|
||||
import { msToTime } from "../../util/time.js";
|
||||
import { PipelineStage } from "mongoose";
|
||||
import { MBlacklist } from "../../mongo/blacklist.js";
|
||||
import { SteamUtil } from "../../util/SteamUtil.js";
|
||||
import { GitUtil } from "../../util/git.js";
|
||||
import { removeProperties } from "../../util/functions.js";
|
||||
import { SuccessResponseBuilder } from "../responseBuilder.js";
|
||||
|
||||
const generateSearch = (regex: RegExp) => [
|
||||
{
|
||||
@ -24,15 +24,18 @@ const generateSearch = (regex: RegExp) => [
|
||||
},
|
||||
{
|
||||
server: { $regex: regex },
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export class StationsRoute {
|
||||
static load() {
|
||||
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"));
|
||||
app.get("/", async (req, res) =>
|
||||
{
|
||||
const s = req.query.q?.toString().split(",").map(x => new RegExp(x, "i"));
|
||||
|
||||
const filter: PipelineStage[] = [];
|
||||
|
||||
@ -40,30 +43,37 @@ export class StationsRoute {
|
||||
s && filter.push({
|
||||
$match: {
|
||||
$and: [
|
||||
...s.map(x => ({ $or: generateSearch(x) }))
|
||||
]
|
||||
}
|
||||
})
|
||||
...s.map(x => ({ $or: generateSearch(x) })),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const records = await MLog.aggregate(filter)
|
||||
.sort({ leftDate: -1 })
|
||||
.limit(30)
|
||||
.limit(30);
|
||||
res.json(
|
||||
new SuccessResponseBuilder<{ records: Omit<ILog, '_id' | '__v'>[] }>()
|
||||
new SuccessResponseBuilder<{ records: Omit<ILog, "_id" | "__v">[] }>()
|
||||
.setCode(200)
|
||||
.setData({ records: records.map(x => removeProperties<Omit<ILog, '_id' | '__v'>>(x, ['_id', '__v'])) })
|
||||
.toJSON()
|
||||
.setData({ records: records.map(x => removeProperties<Omit<ILog, "_id" | "__v">>(x, [ "_id", "__v" ])) })
|
||||
.toJSON(),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/details/:id', async (req, res) => {
|
||||
if (!req.params.id) return res.redirect('/stations/');
|
||||
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/');
|
||||
if (blacklist && blacklist.status)
|
||||
{
|
||||
return res.redirect("/stations/");
|
||||
}
|
||||
const player = await SteamUtil.getPlayer(record?.userSteamId!);
|
||||
|
||||
res.render('stations/details.ejs', {
|
||||
res.render("stations/details.ejs", {
|
||||
record,
|
||||
dayjs,
|
||||
player,
|
||||
|
@ -1,21 +1,24 @@
|
||||
import { Router } from 'express';
|
||||
import dayjs from 'dayjs';
|
||||
import { msToTime } from '../../util/time.js';
|
||||
import { PipelineStage } from 'mongoose';
|
||||
import { ITrainLog, MTrainLog, raw_schema } from '../../mongo/trainLogs.js';
|
||||
import { MBlacklist } from '../../mongo/blacklist.js';
|
||||
import { SteamUtil } from '../../util/SteamUtil.js';
|
||||
import { GitUtil } from '../../util/git.js';
|
||||
import { SuccessResponseBuilder } from '../responseBuilder.js';
|
||||
import { removeProperties } from '../../util/functions.js';
|
||||
import { MLog } from '../../mongo/logs.js';
|
||||
import { MProfile } from '../../mongo/profile.js';
|
||||
import { Router } from "express";
|
||||
import dayjs from "dayjs";
|
||||
import { msToTime } from "../../util/time.js";
|
||||
import { PipelineStage } from "mongoose";
|
||||
import { ITrainLog, MTrainLog, raw_schema } from "../../mongo/trainLogs.js";
|
||||
import { MBlacklist } from "../../mongo/blacklist.js";
|
||||
import { SteamUtil } from "../../util/SteamUtil.js";
|
||||
import { GitUtil } from "../../util/git.js";
|
||||
import { SuccessResponseBuilder } from "../responseBuilder.js";
|
||||
import { removeProperties } from "../../util/functions.js";
|
||||
import { MLog } from "../../mongo/logs.js";
|
||||
import { MProfile } from "../../mongo/profile.js";
|
||||
|
||||
export class StatsRoute {
|
||||
static load() {
|
||||
export class StatsRoute
|
||||
{
|
||||
static load()
|
||||
{
|
||||
const app = Router();
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
app.get("/", async (req, res) =>
|
||||
{
|
||||
const { commit, version } = GitUtil.getData();
|
||||
|
||||
const trains = await MTrainLog.countDocuments();
|
||||
@ -23,12 +26,15 @@ export class StatsRoute {
|
||||
const profiles = await MProfile.countDocuments();
|
||||
|
||||
res.json(
|
||||
new SuccessResponseBuilder<{ git: { commit?: string, version?: string }, stats: { trains: number, dispatchers: number, profiles: number } }>()
|
||||
new SuccessResponseBuilder<{
|
||||
git: { commit?: string, version?: string },
|
||||
stats: { trains: number, dispatchers: number, profiles: number }
|
||||
}>()
|
||||
.setCode(200)
|
||||
.setData({ git: { commit, version }, stats: { trains, dispatchers, profiles } })
|
||||
.toJSON()
|
||||
.toJSON(),
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
return app;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import dayjs from 'dayjs';
|
||||
import { msToTime } from '../../util/time.js';
|
||||
import { PipelineStage } from 'mongoose';
|
||||
import { ITrainLog, MTrainLog, raw_schema } from '../../mongo/trainLogs.js';
|
||||
import { MBlacklist } from '../../mongo/blacklist.js';
|
||||
import { SteamUtil } from '../../util/SteamUtil.js';
|
||||
import { GitUtil } from '../../util/git.js';
|
||||
import { SuccessResponseBuilder } from '../responseBuilder.js';
|
||||
import { removeProperties } from '../../util/functions.js';
|
||||
import { Router } from "express";
|
||||
import dayjs from "dayjs";
|
||||
import { msToTime } from "../../util/time.js";
|
||||
import { PipelineStage } from "mongoose";
|
||||
import { ITrainLog, MTrainLog, raw_schema } from "../../mongo/trainLogs.js";
|
||||
import { MBlacklist } from "../../mongo/blacklist.js";
|
||||
import { SteamUtil } from "../../util/SteamUtil.js";
|
||||
import { GitUtil } from "../../util/git.js";
|
||||
import { SuccessResponseBuilder } from "../responseBuilder.js";
|
||||
import { removeProperties } from "../../util/functions.js";
|
||||
|
||||
const generateSearch = (regex: RegExp) => [
|
||||
{
|
||||
@ -22,14 +22,17 @@ const generateSearch = (regex: RegExp) => [
|
||||
{
|
||||
userUsername: { $regex: regex },
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
export class TrainsRoute {
|
||||
static load() {
|
||||
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"));
|
||||
app.get("/", async (req, res) =>
|
||||
{
|
||||
const s = req.query.q?.toString().split(",").map(x => new RegExp(x, "i"));
|
||||
|
||||
const filter: PipelineStage[] = [];
|
||||
|
||||
@ -37,21 +40,21 @@ export class TrainsRoute {
|
||||
s && filter.push({
|
||||
$match: {
|
||||
$and: [
|
||||
...s.map(x => ({ $or: generateSearch(x) }))
|
||||
]
|
||||
}
|
||||
})
|
||||
...s.map(x => ({ $or: generateSearch(x) })),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const records = await MTrainLog.aggregate(filter)
|
||||
.sort({ leftDate: -1 })
|
||||
.limit(30)
|
||||
.limit(30);
|
||||
|
||||
|
||||
res.json(
|
||||
new SuccessResponseBuilder<{ records: Omit<ITrainLog, '_id' | '__v'>[] }>()
|
||||
new SuccessResponseBuilder<{ records: Omit<ITrainLog, "_id" | "__v">[] }>()
|
||||
.setCode(200)
|
||||
.setData({ records: records.map(x => removeProperties<Omit<ITrainLog, '_id' | '__v'>>(x, ['_id', '__v'])) })
|
||||
.toJSON()
|
||||
.setData({ records: records.map(x => removeProperties<Omit<ITrainLog, "_id" | "__v">>(x, [ "_id", "__v" ])) })
|
||||
.toJSON(),
|
||||
);
|
||||
|
||||
// res.render('trains/index.ejs', {
|
||||
@ -61,37 +64,45 @@ export class TrainsRoute {
|
||||
// msToTime,
|
||||
// ...GitUtil.getData()
|
||||
// });
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/details/:id', async (req, res) => {
|
||||
if (!req.params.id) return res.redirect('/trains/');
|
||||
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 SteamUtil.getPlayer(record?.userSteamId!);
|
||||
const blacklist = await MBlacklist.findOne({ steam: record?.userSteamId! });
|
||||
if (blacklist && blacklist.status) return res.redirect('/trains/');
|
||||
if (blacklist && blacklist.status)
|
||||
{
|
||||
return res.redirect("/trains/");
|
||||
}
|
||||
|
||||
res.render('trains/details.ejs', {
|
||||
res.render("trains/details.ejs", {
|
||||
record,
|
||||
dayjs,
|
||||
player,
|
||||
msToTime,
|
||||
...GitUtil.getData()
|
||||
...GitUtil.getData(),
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/leaderboard/', async (req, res) => {
|
||||
const s = req.query.q?.toString().split(',').map(x => new RegExp(x, "i"));
|
||||
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}` }), {});
|
||||
.reduce((o, key) => ({ ...o, [ key ]: `$${ key }` }), {});
|
||||
|
||||
const filter: PipelineStage[] = [
|
||||
{
|
||||
$project: {
|
||||
// record.leftDate - record.joinedDate
|
||||
result: { $subtract: ['$leftDate', '$joinedDate'] },
|
||||
...data
|
||||
}
|
||||
result: { $subtract: [ "$leftDate", "$joinedDate" ] },
|
||||
...data,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@ -99,17 +110,17 @@ export class TrainsRoute {
|
||||
{
|
||||
$match: {
|
||||
$and: [
|
||||
...s.map(x => ({ $or: generateSearch(x) }))
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
...s.map(x => ({ $or: generateSearch(x) })),
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
const records = await MTrainLog.aggregate(filter)
|
||||
.sort({ result: -1 })
|
||||
.limit(30)
|
||||
res.render('trains/leaderboard.ejs', {
|
||||
.limit(30);
|
||||
res.render("trains/leaderboard.ejs", {
|
||||
records,
|
||||
dayjs,
|
||||
q: req.query.q,
|
||||
@ -117,27 +128,6 @@ export class TrainsRoute {
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
@ -1,34 +1,39 @@
|
||||
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';
|
||||
import { GitUtil } from '../util/git.js';
|
||||
import cors from 'cors';
|
||||
import { StatsRoute } from './routes/stats.js';
|
||||
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";
|
||||
import { GitUtil } from "../util/git.js";
|
||||
import cors from "cors";
|
||||
import { StatsRoute } from "./routes/stats.js";
|
||||
import { LogRoute } from "./routes/log.js";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
|
||||
export class ApiModule {
|
||||
public static load() {
|
||||
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', GitUtil.getData()));
|
||||
app.set("view engine", "ejs");
|
||||
app.set("views", __dirname + "/views");
|
||||
app.get("/", (_, res) => res.render("home", GitUtil.getData()));
|
||||
app.use(cors());
|
||||
// backward compatible
|
||||
app.get('/details/:id', (req, res) => res.redirect('/stations/details/' + req.params.id));
|
||||
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.use('/stats/', StatsRoute.load());
|
||||
app.use("/stations/", StationsRoute.load());
|
||||
app.use("/trains/", TrainsRoute.load());
|
||||
app.use("/profiles/", ProfilesRoute.load());
|
||||
app.use("/leaderboard/", LeaderboardRoute.load());
|
||||
app.use("/stats/", StatsRoute.load());
|
||||
app.use("/log/", LogRoute.load());
|
||||
|
||||
app.listen(2005);
|
||||
}
|
||||
|
@ -2,15 +2,18 @@
|
||||
<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>
|
||||
<p><% if (version) { %>
|
||||
Wersja: <a href="https://git.alekswilc.dev/alekswilc/simrail-logs/releases/tag/<%- version %>"><%- version %></a>
|
||||
<% } %>
|
||||
<p>
|
||||
<% if (version) { %>
|
||||
Wersja: <a
|
||||
href="https://git.alekswilc.dev/alekswilc/simrail-logs/releases/tag/<%- version %>"><%- version %></a>
|
||||
<% } %>
|
||||
Commit: <a href="https://git.alekswilc.dev/alekswilc/simrail-logs/commit/<%- commit %>"><%- commit %></a>
|
||||
</p>
|
||||
<% if (thanks) { %>
|
||||
<p>Podziękowania dla społeczności discorda <a href="https://discord.gg/yDhy3pDrVr">Simrail24</a>, <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
|
||||
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 społeczności Simrail <span style="color: red;">❤️</span></p>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
@ -1,13 +1,14 @@
|
||||
<header>
|
||||
<h1><a style="color: white; text-decoration: none;" href="/">SimRail Logs</a></h1>
|
||||
|
||||
<a href="/stations/">Logi posterunkó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 />
|
||||
<a href="/stations/">Logi posterunkó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
|
||||
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>
|
@ -21,101 +21,103 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%- include('_modules/header.ejs', { section:'home' }) %>
|
||||
<%- 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>
|
||||
<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>Nowy wygląd aplikacji</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Rozwój aplikacji</h3>
|
||||
<p>Planowane funkcjonalności</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Nowy wygląd aplikacji</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Podziękowania dla</h3>
|
||||
<ul>
|
||||
<li>Społeczności discorda <a href="https://discord.gg/yDhy3pDrVr">Simrail24</a></li>
|
||||
<li><a href="https://discord.com/users/1079005960653254656">AQUALYTH</a></li>
|
||||
<li>osób lajkujących <a href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">posta</a></li>
|
||||
<li>osób użytkujących strone</li>
|
||||
<li>Społeczności SimRail <span style="color: red;">❤️</span></li>
|
||||
</ul>
|
||||
<h3>Podziękowania dla</h3>
|
||||
<ul>
|
||||
<li>Społeczności discorda <a href="https://discord.gg/yDhy3pDrVr">Simrail24</a></li>
|
||||
<li><a href="https://discord.com/users/1079005960653254656">AQUALYTH</a></li>
|
||||
<li>osób lajkujących <a href="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/">posta</a>
|
||||
</li>
|
||||
<li>osób użytkujących strone</li>
|
||||
<li>Społeczności SimRail <span style="color: red;">❤️</span></li>
|
||||
</ul>
|
||||
|
||||
<h3>Informacje dodatkowe</h3>
|
||||
<details>
|
||||
<summary>
|
||||
Zgłaszanie błędów i propozycji
|
||||
</summary>
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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.
|
||||
|
||||
<hr>
|
||||
<%- include('_modules/footer.ejs', { thanks: true, version, commit }) %>
|
||||
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, version, commit }) %>
|
||||
|
||||
|
||||
</body>
|
||||
|
@ -22,56 +22,65 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'leaderboard' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'leaderboard' }) %>
|
||||
|
||||
<h2 id="but">Tablica wyników</h2>
|
||||
<div class="container">
|
||||
<input type="text" id="search" value="<%-q%>">
|
||||
<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>
|
||||
<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,Łazy</p>
|
||||
|
||||
</div>
|
||||
<br />
|
||||
<ul>
|
||||
<% records.forEach(record=> { %>
|
||||
<li>
|
||||
<%- type === 'train' ? include('train.ejs', { record, msToTime }) : include('station.ejs', { record, msToTime }) %>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
</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>
|
||||
<% } %>
|
||||
<% 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");
|
||||
}
|
||||
|
||||
<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();
|
||||
}
|
||||
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>
|
||||
</script>
|
||||
<hr>
|
||||
|
||||
<p style="color: orange;">Dane do rankingu zbierane są od dnia 19.08.2024.</p>
|
||||
<p style="color: orange;">Dane do rankingu zbierane są od dnia 19.08.2024.</p>
|
||||
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -3,7 +3,7 @@
|
||||
<%- record.steamName %>
|
||||
</span> <span style="color: lightskyblue">
|
||||
<%- msToTime(record.dispatcherTime) %>
|
||||
</span> </summary>
|
||||
<p>Spędzona liczba godzin: <%- msToTime(record.dispatcherTime, true) || 'Brak' %></p>
|
||||
</span></summary>
|
||||
<p>Spędzona liczba godzin: <%- msToTime(record.dispatcherTime, true) || 'Brak' %></p>
|
||||
<button onclick="location.href = '/profiles/<%- record.steam %>'">Więcej</button>
|
||||
</details>
|
@ -6,13 +6,13 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>simrail.alekswilc.dev</title>
|
||||
<meta name="description"
|
||||
content="<%- steam.personaname %>">
|
||||
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 %>">
|
||||
content="<%- steam.personaname %>">
|
||||
<meta property=" og:type" content="website">
|
||||
<meta property="og:image" content="<%- steam.avatarfull %>" />
|
||||
<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">
|
||||
@ -34,83 +34,89 @@
|
||||
</head>
|
||||
|
||||
<script>
|
||||
function copylink() {
|
||||
navigator.clipboard.writeText("https://simrail.alekswilc.dev/players/details/<%- player.steam %>/")
|
||||
function copylink()
|
||||
{
|
||||
navigator.clipboard.writeText("https://simrail.alekswilc.dev/players/details/<%- player.steam %>/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'profiles' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'profiles' }) %>
|
||||
|
||||
<div class="details">
|
||||
<h1><a href="<%- steam.profileurl %>"><%- steam.personaname %></a></h1>
|
||||
<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, true) || '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>
|
||||
|
||||
<%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, true) || '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>
|
||||
<%}%>
|
||||
</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, true) %></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>
|
||||
|
||||
<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, true) %></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>
|
||||
<% } %>
|
||||
|
||||
<% }) %>
|
||||
</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, true) %></p>
|
||||
</details>
|
||||
</li>
|
||||
|
||||
<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, true) %></p>
|
||||
</details>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } else { %>
|
||||
<p>Brak danych</p>
|
||||
<% } %>
|
||||
|
||||
<% }) %>
|
||||
</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, version, commit }) %>
|
||||
<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, version, commit }) %>
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -6,11 +6,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>simrail.alekswilc.dev</title>
|
||||
<meta name="description"
|
||||
content="Profil prywatny">
|
||||
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">
|
||||
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">
|
||||
@ -33,14 +33,14 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'profiles' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'profiles' }) %>
|
||||
|
||||
<div class="details">
|
||||
<p>Profil gracza jest prywatny.</p>
|
||||
<div class="details">
|
||||
<p>Profil gracza jest prywatny.</p>
|
||||
|
||||
</div>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
</div>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -6,13 +6,14 @@
|
||||
<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: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">
|
||||
@ -34,52 +35,56 @@
|
||||
</head>
|
||||
|
||||
<script>
|
||||
function copydata() {
|
||||
navigator.clipboard.writeText(document.getElementById('data').textContent.replace(/ /g, '').split('\n').filter(x => x).join(''))
|
||||
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/stations/details/<%- record.id %>/")
|
||||
function copylink()
|
||||
{
|
||||
navigator.clipboard.writeText("https://simrail.alekswilc.dev/stations/details/<%- record.id %>/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'stations' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'stations' }) %>
|
||||
|
||||
<div class="details">
|
||||
<div class="details">
|
||||
|
||||
<p>Użytkownik: <a href="/profiles/<%- record.userSteamId %>">
|
||||
<%- record.userUsername %>
|
||||
</a></p>
|
||||
<p>Stacja: <%- record.stationName %>
|
||||
</p>
|
||||
<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>
|
||||
<p>Spędzony czas: <%- record.joinedDate ? msToTime(record.leftDate - record.joinedDate, true) : '--' %>
|
||||
</p>
|
||||
<p>Użytkownik: <a href="/profiles/<%- record.userSteamId %>">
|
||||
<%- record.userUsername %>
|
||||
</a></p>
|
||||
<p>Stacja: <%- record.stationName %>
|
||||
</p>
|
||||
<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>
|
||||
<p>Spędzony czas: <%- record.joinedDate ? msToTime(record.leftDate - record.joinedDate, true) : '--' %>
|
||||
</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>
|
||||
<br />
|
||||
<p><button onclick="copylink()">Kopiuj link</button></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>
|
||||
<br/>
|
||||
<p>
|
||||
<button onclick="copylink()">Kopiuj link</button>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
</div>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -21,70 +21,77 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'stations' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'stations' }) %>
|
||||
|
||||
<h2>Wyszukaj posterunek, osobe lub serwer</h2>
|
||||
<h2>Wyszukaj posterunek, osobe lub serwer</h2>
|
||||
|
||||
<div class="container">
|
||||
<input type="text" id="search" value="<%-q%>">
|
||||
<button onclick="search()">Szukaj</button>
|
||||
<button onclick="clearSearch()">Wyczyść</button>
|
||||
<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>
|
||||
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,Łazy</p>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<% records.forEach(record=> { %>
|
||||
<li>
|
||||
<details>
|
||||
<summary>[<span style="color:lightskyblue">
|
||||
<ul>
|
||||
<% records.forEach(record=> { %>
|
||||
<li>
|
||||
<details>
|
||||
<summary>[<span style="color:lightskyblue">
|
||||
<%- record.server.toUpperCase() %>
|
||||
</span>] <span style="color: lightskyblue">
|
||||
<%- record.stationName %>
|
||||
</span> - <span style="color:hotpink">
|
||||
<%- record.userUsername %>
|
||||
</span>
|
||||
<p style="margin-bottom: 0; opacity: 0.5;">
|
||||
<%- 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>
|
||||
<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>
|
||||
<p style="margin-bottom: 0; opacity: 0.5;">
|
||||
<%- 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>
|
||||
<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>
|
||||
|
||||
<a href="/stations/details/<%- record.id %>">
|
||||
<button>Więcej</button>
|
||||
</a>
|
||||
</details>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<a href="/stations/details/<%- record.id %>">
|
||||
<button>Więcej</button>
|
||||
</a>
|
||||
</details>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
|
||||
<% if (!records.length) { %>
|
||||
<h4>Nie znaleziono wyników dla twojego zapytania.</h4>
|
||||
<% } %>
|
||||
<% if (!records.length) { %>
|
||||
<h4>Nie znaleziono wyników dla twojego zapytania.</h4>
|
||||
<% } %>
|
||||
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
|
||||
<script>
|
||||
function search() {
|
||||
location.href = '/stations/?q=' + document.getElementById('search').value
|
||||
<script>
|
||||
function search()
|
||||
{
|
||||
location.href = "/stations/?q=" + document.getElementById("search").value;
|
||||
}
|
||||
|
||||
function clearSearch()
|
||||
{
|
||||
console.log("test");
|
||||
location.href = "/stations/";
|
||||
}
|
||||
|
||||
document.getElementById("search").addEventListener("keyup", (event) =>
|
||||
{
|
||||
if (event.key === "Enter")
|
||||
{
|
||||
search();
|
||||
}
|
||||
function clearSearch() {
|
||||
console.log('test')
|
||||
location.href = '/stations/';
|
||||
}
|
||||
document.getElementById('search').addEventListener("keyup", (event) => {
|
||||
if (event.key === "Enter")
|
||||
search();
|
||||
|
||||
});
|
||||
</script>
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -6,13 +6,14 @@
|
||||
<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: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">
|
||||
@ -34,59 +35,67 @@
|
||||
</head>
|
||||
|
||||
<script>
|
||||
function copydata() {
|
||||
navigator.clipboard.writeText(document.getElementById('data').textContent.replace(/ /g, '').split('\n').filter(x => x).join(''))
|
||||
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 %>/")
|
||||
function copylink()
|
||||
{
|
||||
navigator.clipboard.writeText("https://simrail.alekswilc.dev/trains/details/<%- record.id %>/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'trains' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'trains' }) %>
|
||||
|
||||
<div class="details">
|
||||
<div class="details">
|
||||
|
||||
<p>Użytkownik: <a href="/profiles/<%- record.userSteamId %>">
|
||||
<%- record.userUsername %>
|
||||
</a></p>
|
||||
<p>Użytkownik: <a href="/profiles/<%- record.userSteamId %>">
|
||||
<%- record.userUsername %>
|
||||
</a></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, true) : '--' %>
|
||||
</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, true) : '--' %>
|
||||
</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) { %>
|
||||
<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>
|
||||
;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, version, commit }) %>
|
||||
</div>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -21,23 +21,23 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%- include('../_modules/header.ejs', { section: 'trains' }) %>
|
||||
<%- include('../_modules/header.ejs', { section: 'trains' }) %>
|
||||
|
||||
<h2>Wyszukaj pociąg, osobe lub serwer</h2>
|
||||
<h2>Wyszukaj pociąg, osobe lub serwer</h2>
|
||||
|
||||
<div class="container">
|
||||
<input type="text" id="search" value="<%-q%>">
|
||||
<button onclick="search()">Szukaj</button>
|
||||
<button onclick="clearSearch()">Wyczyść</button>
|
||||
<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,1413</p>
|
||||
</div>
|
||||
<p>Użyj przecinka, aby wyszukać wiele wartości: pl2,1413</p>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<% records.forEach(record=> { %>
|
||||
<li>
|
||||
<details>
|
||||
<summary>[<span style="color:lightskyblue">
|
||||
<ul>
|
||||
<% records.forEach(record=> { %>
|
||||
<li>
|
||||
<details>
|
||||
<summary>[<span style="color:lightskyblue">
|
||||
<%- record.server.toUpperCase() %>
|
||||
</span>] <span style="color: lightskyblue">
|
||||
<%- record.trainName %>
|
||||
@ -46,51 +46,58 @@
|
||||
</span> - <span style="color:hotpink">
|
||||
<%- record.userUsername %>
|
||||
</span>
|
||||
<p style="margin-bottom: 0; opacity: 0.5;">
|
||||
<%- 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>
|
||||
<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) : '--' %>
|
||||
<% if (record.distance) { %>
|
||||
<p>Przejechane kilometry: <%- record.distance / 1000 %></p>
|
||||
<p>Zdobyte punkty: <%- record.points %></p>
|
||||
<% } %>
|
||||
</p>
|
||||
<p style="margin-bottom: 0; opacity: 0.5;">
|
||||
<%- 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>
|
||||
<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) : '--' %>
|
||||
<% if (record.distance) { %>
|
||||
<p>Przejechane kilometry: <%- record.distance / 1000 %></p>
|
||||
<p>Zdobyte punkty: <%- record.points %></p>
|
||||
<% } %>
|
||||
</p>
|
||||
|
||||
<a href="/trains/details/<%- record.id %>">
|
||||
<button>Więcej</button>
|
||||
</a>
|
||||
</details>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<a href="/trains/details/<%- record.id %>">
|
||||
<button>Więcej</button>
|
||||
</a>
|
||||
</details>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
|
||||
<% if (!records.length) { %>
|
||||
<h4>Nie znaleziono wyników dla twojego zapytania.</h4>
|
||||
<% } %>
|
||||
<% if (!records.length) { %>
|
||||
<h4>Nie znaleziono wyników dla twojego zapytania.</h4>
|
||||
<% } %>
|
||||
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
<hr>
|
||||
<%- include('../_modules/footer.ejs', { thanks: false, version, commit }) %>
|
||||
|
||||
<script>
|
||||
function search() {
|
||||
location.href = '/trains/?q=' + document.getElementById('search').value
|
||||
<script>
|
||||
function search()
|
||||
{
|
||||
location.href = "/trains/?q=" + document.getElementById("search").value;
|
||||
}
|
||||
|
||||
function clearSearch()
|
||||
{
|
||||
console.log("test");
|
||||
location.href = "/trains/";
|
||||
}
|
||||
|
||||
document.getElementById("search").addEventListener("keyup", (event) =>
|
||||
{
|
||||
if (event.key === "Enter")
|
||||
{
|
||||
search();
|
||||
}
|
||||
function clearSearch() {
|
||||
console.log('test')
|
||||
location.href = '/trains/';
|
||||
}
|
||||
document.getElementById('search').addEventListener("keyup", (event) => {
|
||||
if (event.key === "Enter")
|
||||
search();
|
||||
|
||||
});
|
||||
</script>
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
@ -1,30 +1,30 @@
|
||||
import { createClient, RedisClientType } from 'redis';
|
||||
import './util/time.js';
|
||||
import { SimrailClient, SimrailClientEvents } from './util/SimrailClient.js';
|
||||
import dayjs from 'dayjs';
|
||||
import { StationsModule } from './modules/stations.js';
|
||||
import { ApiModule } from './http/server.js';
|
||||
import mongoose from 'mongoose';
|
||||
import { IPlayer } from './types/player.js';
|
||||
import { Station, Server, Train } from '@simrail/types';
|
||||
import { TrainsModule } from './modules/trains.js';
|
||||
import { createClient, RedisClientType } from "redis";
|
||||
import "./util/time.js";
|
||||
import { SimrailClient, SimrailClientEvents } from "./util/SimrailClient.js";
|
||||
import dayjs from "dayjs";
|
||||
import { StationsModule } from "./modules/stations.js";
|
||||
import { ApiModule } from "./http/server.js";
|
||||
import mongoose from "mongoose";
|
||||
import { IPlayer } from "./types/player.js";
|
||||
import { Station, Server, Train } from "@simrail/types";
|
||||
import { TrainsModule } from "./modules/trains.js";
|
||||
|
||||
|
||||
; (async () => {
|
||||
;(async () =>
|
||||
{
|
||||
global.redis = await createClient({
|
||||
url: process.env.REDIS_URI!
|
||||
url: process.env.REDIS_URI!,
|
||||
})
|
||||
.on('error', err => console.log('Redis Client Error', err))
|
||||
.on("error", err => console.log("Redis Client Error", err))
|
||||
.connect() as RedisClientType;
|
||||
console.log('Redis connected');
|
||||
console.log("Redis connected");
|
||||
|
||||
await mongoose.connect(process.env.MONGO_URI!);
|
||||
|
||||
console.log('MongoDB connected');
|
||||
console.log("MongoDB connected");
|
||||
global.client = new SimrailClient();
|
||||
|
||||
|
||||
|
||||
/*
|
||||
client.on(SimrailClientEvents.StationJoined, (server: Server, station: Station, player: IPlayer) => {
|
||||
console.log(`${server.ServerCode} | ${station.Name} | ${player.personaname} joined`);
|
||||
|
@ -1,38 +1,54 @@
|
||||
import { Server, Station } from '@simrail/types';
|
||||
import { MLog } from '../mongo/logs.js';
|
||||
import { IPlayer } from '../types/player.js';
|
||||
import { SimrailClientEvents } from '../util/SimrailClient.js';
|
||||
import { v4 } from 'uuid';
|
||||
import { MProfile } from '../mongo/profile.js';
|
||||
import { SteamUtil } from '../util/SteamUtil.js';
|
||||
import { Server, Station } from "@simrail/types";
|
||||
import { MLog } from "../mongo/logs.js";
|
||||
import { IPlayer } from "../types/player.js";
|
||||
import { SimrailClientEvents } from "../util/SimrailClient.js";
|
||||
import { v4 } from "uuid";
|
||||
import { MProfile } from "../mongo/profile.js";
|
||||
import { SteamUtil } from "../util/SteamUtil.js";
|
||||
|
||||
export class StationsModule {
|
||||
public static load() {
|
||||
export class StationsModule
|
||||
{
|
||||
public static load()
|
||||
{
|
||||
|
||||
client.on(SimrailClientEvents.StationLeft, async (server: Server, station: Station, player: IPlayer, joinedAt: number) => {
|
||||
client.on(SimrailClientEvents.StationLeft, async (server: Server, station: Station, player: IPlayer, joinedAt: number) =>
|
||||
{
|
||||
const stats = await SteamUtil.getPlayerStats(player.steamid);
|
||||
const date = new Date();
|
||||
if (stats) {
|
||||
if (stats)
|
||||
{
|
||||
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 (!userProfile.dispatcherStats)
|
||||
{
|
||||
userProfile.dispatcherStats = {};
|
||||
}
|
||||
|
||||
if (Number.isNaN(userProfile.dispatcherStats[station.Name].time)) userProfile.dispatcherStats[station.Name].time = 0;
|
||||
if (userProfile.dispatcherStats[ station.Name ])
|
||||
{
|
||||
userProfile.dispatcherStats[ station.Name ].time = userProfile.dispatcherStats[ station.Name ].time + time;
|
||||
}
|
||||
else
|
||||
{
|
||||
userProfile.dispatcherStats[ station.Name ] = {
|
||||
time,
|
||||
};
|
||||
}
|
||||
|
||||
if (!userProfile.dispatcherTime) userProfile.dispatcherTime = 0;
|
||||
if (Number.isNaN(userProfile.dispatcherStats[ station.Name ].time))
|
||||
{
|
||||
userProfile.dispatcherStats[ station.Name ].time = 0;
|
||||
}
|
||||
|
||||
if (!userProfile.dispatcherTime)
|
||||
{
|
||||
userProfile.dispatcherTime = 0;
|
||||
}
|
||||
|
||||
userProfile.dispatcherTime = userProfile.dispatcherTime + time;
|
||||
|
||||
await MProfile.findOneAndUpdate({ id: userProfile.id }, { dispatcherStats: userProfile.dispatcherStats, dispatcherTime: userProfile.dispatcherTime })
|
||||
await MProfile.findOneAndUpdate({ id: userProfile.id }, { dispatcherStats: userProfile.dispatcherStats, dispatcherTime: userProfile.dispatcherTime });
|
||||
}
|
||||
|
||||
MLog.create({
|
||||
|
@ -1,43 +1,62 @@
|
||||
import { Server, Station, Train } from '@simrail/types';
|
||||
import { MLog } from '../mongo/logs.js';
|
||||
import { IPlayer } from '../types/player.js';
|
||||
import { SimrailClientEvents } from '../util/SimrailClient.js';
|
||||
import { v4 } from 'uuid';
|
||||
import { getVehicle } from '../util/contants.js';
|
||||
import { MProfile } from '../mongo/profile.js';
|
||||
import { MTrainLog } from '../mongo/trainLogs.js';
|
||||
import { Server, Station, Train } from "@simrail/types";
|
||||
import { MLog } from "../mongo/logs.js";
|
||||
import { IPlayer } from "../types/player.js";
|
||||
import { SimrailClientEvents } from "../util/SimrailClient.js";
|
||||
import { v4 } from "uuid";
|
||||
import { getVehicle } from "../util/contants.js";
|
||||
import { MProfile } from "../mongo/profile.js";
|
||||
import { MTrainLog } from "../mongo/trainLogs.js";
|
||||
|
||||
export class TrainsModule {
|
||||
public static load() {
|
||||
export class TrainsModule
|
||||
{
|
||||
public static load()
|
||||
{
|
||||
|
||||
client.on(SimrailClientEvents.TrainLeft, async (server: Server, train: Train, player: IPlayer, joinedAt: number, leftAt: number, points: number, distance: number, vehicle: string) => {
|
||||
if (distance) {
|
||||
client.on(SimrailClientEvents.TrainLeft, async (server: Server, train: Train, player: IPlayer, joinedAt: number, leftAt: number, points: number, distance: number, vehicle: string) =>
|
||||
{
|
||||
if (distance)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!userProfile.trainStats) userProfile.trainStats = {};
|
||||
|
||||
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, time
|
||||
}
|
||||
if (!userProfile.trainStats)
|
||||
{
|
||||
userProfile.trainStats = {};
|
||||
}
|
||||
|
||||
if (!userProfile.trainTime) userProfile.trainTime = 0;
|
||||
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, time,
|
||||
};
|
||||
}
|
||||
|
||||
if (!userProfile.trainTime)
|
||||
{
|
||||
userProfile.trainTime = 0;
|
||||
}
|
||||
|
||||
userProfile.trainTime = userProfile.trainTime + time;
|
||||
|
||||
if (!userProfile.trainPoints) userProfile.trainPoints = 0;
|
||||
if (!userProfile.trainPoints)
|
||||
{
|
||||
userProfile.trainPoints = 0;
|
||||
}
|
||||
|
||||
userProfile.trainPoints = userProfile.trainPoints + points;
|
||||
|
||||
if (!userProfile.trainDistance) userProfile.trainDistance = 0;
|
||||
if (!userProfile.trainDistance)
|
||||
{
|
||||
userProfile.trainDistance = 0;
|
||||
}
|
||||
|
||||
userProfile.trainDistance = userProfile.trainDistance + distance;
|
||||
|
||||
|
@ -1,24 +1,25 @@
|
||||
import { Model, model, Schema } from 'mongoose';
|
||||
import { Model, model, Schema } from "mongoose";
|
||||
|
||||
|
||||
export const raw_schema = {
|
||||
steam: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
status: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = new Schema<IBlacklist>(raw_schema);
|
||||
|
||||
export type TMBlacklist = Model<IBlacklist>
|
||||
|
||||
export const MBlacklist = model<IBlacklist>('blacklist', schema);
|
||||
export const MBlacklist = model<IBlacklist>("blacklist", schema);
|
||||
|
||||
export interface IBlacklist {
|
||||
steam: string
|
||||
status: boolean
|
||||
export interface IBlacklist
|
||||
{
|
||||
steam: string;
|
||||
status: boolean;
|
||||
}
|
@ -1,61 +1,62 @@
|
||||
import { Model, model, Schema } from 'mongoose';
|
||||
import { Model, model, Schema } from "mongoose";
|
||||
|
||||
|
||||
export const raw_schema = {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
userSteamId: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
userUsername: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
userAvatar: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
joinedDate: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
leftDate: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
stationName: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
stationShort: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
server: {
|
||||
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 const MLog = model<ILog>('logs', schema);
|
||||
export const MLog = model<ILog>("logs", schema);
|
||||
|
||||
export interface ILog {
|
||||
id: string
|
||||
userSteamId: string
|
||||
userUsername: string
|
||||
userAvatar: string
|
||||
joinedDate?: number
|
||||
leftDate: number
|
||||
stationName: string
|
||||
stationShort: string
|
||||
server: string
|
||||
export interface ILog
|
||||
{
|
||||
id: string;
|
||||
userSteamId: string;
|
||||
userUsername: string;
|
||||
userAvatar: string;
|
||||
joinedDate?: number;
|
||||
leftDate: number;
|
||||
stationName: string;
|
||||
stationShort: string;
|
||||
server: string;
|
||||
}
|
@ -1,78 +1,79 @@
|
||||
import { Model, model, Schema } from 'mongoose';
|
||||
import { Model, model, Schema } from "mongoose";
|
||||
|
||||
|
||||
export const raw_schema = {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
steam: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
steamName: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
|
||||
trainStats: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {}
|
||||
default: {},
|
||||
},
|
||||
dispatcherStats: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {}
|
||||
default: {},
|
||||
},
|
||||
trainTime: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
trainPoints: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
trainDistance: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
dispatcherTime: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const schema = new Schema<IProfile>(raw_schema);
|
||||
|
||||
|
||||
export type TMProfile = Model<IProfile>
|
||||
|
||||
export const MProfile = model<IProfile>('profile', schema);
|
||||
export const MProfile = model<IProfile>("profile", schema);
|
||||
|
||||
export interface IProfile {
|
||||
id: string
|
||||
steam: string
|
||||
export interface IProfile
|
||||
{
|
||||
id: string;
|
||||
steam: string;
|
||||
trainStats: {
|
||||
[trainName: string]: {
|
||||
[ trainName: string ]: {
|
||||
score: number,
|
||||
distance: number
|
||||
time: number,
|
||||
}
|
||||
}
|
||||
};
|
||||
dispatcherStats: {
|
||||
[name: string]: {
|
||||
[ name: string ]: {
|
||||
time: number
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatcherTime: number;
|
||||
trainTime: number
|
||||
trainPoints: number
|
||||
steamName: string
|
||||
trainDistance: number
|
||||
trainTime: number;
|
||||
trainPoints: number;
|
||||
steamName: string;
|
||||
trainDistance: number;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import { Model, model, Schema } from 'mongoose';
|
||||
import { Model, model, Schema } from "mongoose";
|
||||
|
||||
|
||||
export const raw_schema = {
|
||||
steam: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
stats: {
|
||||
type: Object,
|
||||
@ -18,19 +18,20 @@ export const raw_schema = {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const schema = new Schema<ISteam>(raw_schema);
|
||||
|
||||
|
||||
export type TMSteam = Model<ISteam>
|
||||
|
||||
export const MSteam = model<ISteam>('steam', schema);
|
||||
export const MSteam = model<ISteam>("steam", schema);
|
||||
|
||||
export interface ISteam {
|
||||
steam: string
|
||||
stats: object
|
||||
personaname: string
|
||||
avatarfull: string
|
||||
lastUpdated: number
|
||||
export interface ISteam
|
||||
{
|
||||
steam: string;
|
||||
stats: object;
|
||||
personaname: string;
|
||||
avatarfull: string;
|
||||
lastUpdated: number;
|
||||
}
|
@ -1,72 +1,73 @@
|
||||
import { Model, model, Schema } from 'mongoose';
|
||||
import { Model, model, Schema } from "mongoose";
|
||||
|
||||
|
||||
export const raw_schema = {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
trainNumber: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
userSteamId: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
userUsername: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
userAvatar: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
joinedDate: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
leftDate: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
distance: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
points: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
server: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
trainName: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = new Schema<ITrainLog>(raw_schema);
|
||||
|
||||
export type TMTrainLog = Model<ITrainLog>
|
||||
|
||||
export const MTrainLog = model<ITrainLog>('train_logs', schema);
|
||||
export const MTrainLog = model<ITrainLog>("train_logs", schema);
|
||||
|
||||
export interface ITrainLog {
|
||||
id: string
|
||||
userSteamId: string
|
||||
userUsername: string
|
||||
userAvatar: string
|
||||
joinedDate?: number
|
||||
leftDate: number
|
||||
trainNumber: string
|
||||
trainName: string
|
||||
distance: number
|
||||
points: number
|
||||
server: string
|
||||
export interface ITrainLog
|
||||
{
|
||||
id: string;
|
||||
userSteamId: string;
|
||||
userUsername: string;
|
||||
userAvatar: string;
|
||||
joinedDate?: number;
|
||||
leftDate: number;
|
||||
trainNumber: string;
|
||||
trainName: string;
|
||||
distance: number;
|
||||
points: number;
|
||||
server: string;
|
||||
}
|
6
packages/backend/src/types/typings.d.ts
vendored
6
packages/backend/src/types/typings.d.ts
vendored
@ -1,9 +1,9 @@
|
||||
import { RedisClientType } from 'redis'
|
||||
import { SimrailClient } from '../util/SimrailClient.ts'
|
||||
import { RedisClientType } from "redis";
|
||||
import { SimrailClient } from "../util/SimrailClient.ts";
|
||||
/* eslint no-var: off */
|
||||
declare global
|
||||
{
|
||||
declare var redis: RedisClientType,
|
||||
declare var redis: RedisClientType,
|
||||
client: SimrailClient;
|
||||
|
||||
}
|
@ -1,40 +1,56 @@
|
||||
import { IPlayerPayload, IPlayerStatsPayload } from '../types/player.js';
|
||||
import { IPlayerPayload, IPlayerStatsPayload } from "../types/player.js";
|
||||
|
||||
const STEAM_API_KEY = process.env.STEAM_APIKEY;
|
||||
|
||||
const fetchFuckingSteamApi = (url: string) => {
|
||||
const fetchFuckingSteamApi = (url: string) =>
|
||||
{
|
||||
let retries = 0;
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
const req = () => {
|
||||
if (retries > 5) throw new Error('request failed to api steam');
|
||||
return new Promise((res, rej) =>
|
||||
{
|
||||
const req = () =>
|
||||
{
|
||||
if (retries > 5)
|
||||
{
|
||||
throw new Error("request failed to api steam");
|
||||
}
|
||||
fetch(url, { signal: AbortSignal.timeout(10000) }).then(x => x.json())
|
||||
.then(x => res(x))
|
||||
.catch(() => {
|
||||
console.log('STEAM request failed! ', url.replace(STEAM_API_KEY!, '[XXX]'), retries)
|
||||
.catch(() =>
|
||||
{
|
||||
console.log("STEAM request failed! ", url.replace(STEAM_API_KEY!, "[XXX]"), retries);
|
||||
|
||||
retries++;
|
||||
setTimeout(() => req(), 1000);
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
req();
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export class PlayerUtil {
|
||||
public static async getPlayer(steamId: number | string) {
|
||||
const data = await fetchFuckingSteamApi(`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=${STEAM_API_KEY}&format=json&steamids=${steamId}`) as IPlayerPayload;
|
||||
export class PlayerUtil
|
||||
{
|
||||
public static async getPlayer(steamId: number | string)
|
||||
{
|
||||
const data = await fetchFuckingSteamApi(`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=${ STEAM_API_KEY }&format=json&steamids=${ steamId }`) as IPlayerPayload;
|
||||
|
||||
if (!data.response.players) return;
|
||||
return data.response.players[0];
|
||||
if (!data.response.players)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return data.response.players[ 0 ];
|
||||
}
|
||||
|
||||
public static async getPlayerStats(steamId: string) {
|
||||
const data = await fetchFuckingSteamApi(`http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=1422130&key=${STEAM_API_KEY}&steamid=${steamId}`) as IPlayerStatsPayload;
|
||||
public static async getPlayerStats(steamId: string)
|
||||
{
|
||||
const data = await fetchFuckingSteamApi(`http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=1422130&key=${ STEAM_API_KEY }&steamid=${ steamId }`) as IPlayerStatsPayload;
|
||||
|
||||
|
||||
if (!data.playerstats?.stats) return;
|
||||
if (!data.playerstats?.stats)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return data.playerstats;
|
||||
}
|
||||
}
|
@ -1,23 +1,27 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { EventEmitter } from "node:events";
|
||||
|
||||
import { IPlayer } from '../types/player.js';
|
||||
import { PlayerUtil } from './PlayerUtil.js';
|
||||
import { Station, ApiResponse, Server, Train } from '@simrail/types';
|
||||
import { IPlayer } from "../types/player.js";
|
||||
import { PlayerUtil } from "./PlayerUtil.js";
|
||||
import { Station, ApiResponse, Server, Train } from "@simrail/types";
|
||||
|
||||
export enum SimrailClientEvents {
|
||||
StationJoined = 'stationJoined',
|
||||
StationLeft = 'stationLeft',
|
||||
TrainJoined = 'trainJoined',
|
||||
TrainLeft = 'trainLeft',
|
||||
export enum SimrailClientEvents
|
||||
{
|
||||
StationJoined = "stationJoined",
|
||||
StationLeft = "stationLeft",
|
||||
TrainJoined = "trainJoined",
|
||||
TrainLeft = "trainLeft",
|
||||
|
||||
}
|
||||
|
||||
export declare interface SimrailClient {
|
||||
export declare interface SimrailClient
|
||||
{
|
||||
on(event: SimrailClientEvents.StationJoined, listener: (server: Server, station: Station, player: IPlayer) => void): this;
|
||||
|
||||
on(event: SimrailClientEvents.StationLeft, listener: (server: Server, station: Station, player: IPlayer, joinedAt: number) => void): this;
|
||||
|
||||
|
||||
on(event: SimrailClientEvents.TrainJoined, listener: (server: Server, train: Train, player: IPlayer, startDistance: number) => void): this;
|
||||
|
||||
on(event: SimrailClientEvents.TrainLeft, listener: (server: Server, train: Train, player: IPlayer, joinedAt: number, leftAt: number, points: number, distance: number, vehicle: string) => void): this;
|
||||
|
||||
//on(event: string, listener: Function): this;
|
||||
@ -35,113 +39,165 @@ export type OccupiedTrain = {
|
||||
StartPlayerPoints: number;
|
||||
}
|
||||
|
||||
export class SimrailClient extends EventEmitter {
|
||||
public stations: Record<Server['ServerCode'], Station[]> = {};
|
||||
public stationsOccupied: Record<Server['ServerCode'], Record<string, OccupiedStation | null>> = {};
|
||||
export class SimrailClient extends EventEmitter
|
||||
{
|
||||
public stations: Record<Server["ServerCode"], Station[]> = {};
|
||||
public stationsOccupied: Record<Server["ServerCode"], Record<string, OccupiedStation | null>> = {};
|
||||
|
||||
public trains: Record<Server['ServerCode'], Train[]> = {};
|
||||
public trainsOccupied: Record<Server['ServerCode'], Record<string, OccupiedTrain | null>> = {};
|
||||
public trains: Record<Server["ServerCode"], Train[]> = {};
|
||||
public trainsOccupied: Record<Server["ServerCode"], Record<string, OccupiedTrain | null>> = {};
|
||||
|
||||
public constructor() {
|
||||
public constructor()
|
||||
{
|
||||
super();
|
||||
this.setup();
|
||||
setTimeout(() => setInterval(() => this.update(), 500), 1000)
|
||||
setTimeout(() => setInterval(() => this.update(), 500), 1000);
|
||||
}
|
||||
|
||||
public getStation(server: Server['ServerCode'], name: string) {
|
||||
if (!this.stationsOccupied[server] || !this.stationsOccupied[server][name]) return null;
|
||||
const player = PlayerUtil.getPlayer(this.stationsOccupied[server][name].SteamId);
|
||||
return { player, joinedAt: this.stationsOccupied[name].joinedAt };
|
||||
public getStation(server: Server["ServerCode"], name: string)
|
||||
{
|
||||
if (!this.stationsOccupied[ server ] || !this.stationsOccupied[ server ][ name ])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
const player = PlayerUtil.getPlayer(this.stationsOccupied[ server ][ name ].SteamId);
|
||||
return { player, joinedAt: this.stationsOccupied[ name ].joinedAt };
|
||||
}
|
||||
|
||||
public getTrain(server: Server['ServerCode'], name: string) {
|
||||
if (!this.trainsOccupied[server] || !this.trainsOccupied[server][name]) return null;
|
||||
const player = PlayerUtil.getPlayer(this.trainsOccupied[server][name].SteamId);
|
||||
return { player, joinedAt: this.trainsOccupied[server][name].JoinedAt, startPlayerDistance: this.trainsOccupied[server][name].StartPlayerDistance };
|
||||
public getTrain(server: Server["ServerCode"], name: string)
|
||||
{
|
||||
if (!this.trainsOccupied[ server ] || !this.trainsOccupied[ server ][ name ])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
const player = PlayerUtil.getPlayer(this.trainsOccupied[ server ][ name ].SteamId);
|
||||
return { player, joinedAt: this.trainsOccupied[ server ][ name ].JoinedAt, startPlayerDistance: this.trainsOccupied[ server ][ name ].StartPlayerDistance };
|
||||
}
|
||||
|
||||
|
||||
private async setup() {
|
||||
if (!await redis.json.get('stations'))
|
||||
redis.json.set('stations', '$', []);
|
||||
if (!await redis.json.get('trains'))
|
||||
redis.json.set('trains', '$', []);
|
||||
if (!await redis.json.get('trains_occupied'))
|
||||
redis.json.set('trains_occupied', '$', {});
|
||||
private async setup()
|
||||
{
|
||||
if (!await redis.json.get("stations"))
|
||||
{
|
||||
redis.json.set("stations", "$", []);
|
||||
}
|
||||
if (!await redis.json.get("trains"))
|
||||
{
|
||||
redis.json.set("trains", "$", []);
|
||||
}
|
||||
if (!await redis.json.get("trains_occupied"))
|
||||
{
|
||||
redis.json.set("trains_occupied", "$", {});
|
||||
}
|
||||
|
||||
if (!await redis.json.get('stations_occupied'))
|
||||
redis.json.set('stations_occupied', '$', {});
|
||||
if (!await redis.json.get("stations_occupied"))
|
||||
{
|
||||
redis.json.set("stations_occupied", "$", {});
|
||||
}
|
||||
|
||||
this.stations = (await redis.json.get('stations') as unknown as SimrailClient['stations']);
|
||||
this.stationsOccupied = (await redis.json.get('stations_occupied') as unknown as SimrailClient['stationsOccupied']);
|
||||
this.trains = (await redis.json.get('trains') as unknown as SimrailClient['trains']);
|
||||
this.trainsOccupied = (await redis.json.get('trains_occupied') as unknown as SimrailClient['trainsOccupied']);
|
||||
this.stations = (await redis.json.get("stations") as unknown as SimrailClient["stations"]);
|
||||
this.stationsOccupied = (await redis.json.get("stations_occupied") as unknown as SimrailClient["stationsOccupied"]);
|
||||
this.trains = (await redis.json.get("trains") as unknown as SimrailClient["trains"]);
|
||||
this.trainsOccupied = (await redis.json.get("trains_occupied") as unknown as SimrailClient["trainsOccupied"]);
|
||||
}
|
||||
|
||||
private async processStation(server: Server, stations: ApiResponse<Station>) {
|
||||
if (stations.result) {
|
||||
if (!this.stations[server.ServerCode]) this.stations[server.ServerCode] = [];
|
||||
if (!this.stationsOccupied[server.ServerCode]) this.stationsOccupied[server.ServerCode] = {};
|
||||
private async processStation(server: Server, stations: ApiResponse<Station>)
|
||||
{
|
||||
if (stations.result)
|
||||
{
|
||||
if (!this.stations[ server.ServerCode ])
|
||||
{
|
||||
this.stations[ server.ServerCode ] = [];
|
||||
}
|
||||
if (!this.stationsOccupied[ server.ServerCode ])
|
||||
{
|
||||
this.stationsOccupied[ server.ServerCode ] = {};
|
||||
}
|
||||
|
||||
if (!this.stations[server.ServerCode].length) {
|
||||
this.stations[server.ServerCode] = stations.data;
|
||||
redis.json.set('stations', '$', this.stations);
|
||||
if (!this.stations[ server.ServerCode ].length)
|
||||
{
|
||||
this.stations[ server.ServerCode ] = stations.data;
|
||||
redis.json.set("stations", "$", this.stations);
|
||||
|
||||
}
|
||||
|
||||
stations.data.forEach(async (x) => {
|
||||
const data = this.stations[server.ServerCode].find(y => y.Name === x.Name);
|
||||
if (!data) return;
|
||||
stations.data.forEach(async (x) =>
|
||||
{
|
||||
const data = this.stations[ server.ServerCode ].find(y => y.Name === x.Name);
|
||||
if (!data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.DispatchedBy[0]?.SteamId !== x.DispatchedBy[0]?.SteamId) {
|
||||
if (!data.DispatchedBy[0]?.SteamId) {
|
||||
if (data.DispatchedBy[ 0 ]?.SteamId !== x.DispatchedBy[ 0 ]?.SteamId)
|
||||
{
|
||||
if (!data.DispatchedBy[ 0 ]?.SteamId)
|
||||
{
|
||||
// join
|
||||
const date = new Date();
|
||||
const player = await PlayerUtil.getPlayer(x.DispatchedBy[0]?.SteamId);
|
||||
const player = await PlayerUtil.getPlayer(x.DispatchedBy[ 0 ]?.SteamId);
|
||||
|
||||
this.emit(SimrailClientEvents.StationJoined, server, x, player);
|
||||
this.stationsOccupied[server.ServerCode][data.Prefix] = {
|
||||
SteamId: x.DispatchedBy[0]?.SteamId,
|
||||
JoinedAt: date.getTime()
|
||||
this.stationsOccupied[ server.ServerCode ][ data.Prefix ] = {
|
||||
SteamId: x.DispatchedBy[ 0 ]?.SteamId,
|
||||
JoinedAt: date.getTime(),
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
// leave
|
||||
const player = await PlayerUtil.getPlayer(data.DispatchedBy[0]?.SteamId)
|
||||
const player = await PlayerUtil.getPlayer(data.DispatchedBy[ 0 ]?.SteamId);
|
||||
|
||||
this.emit(SimrailClientEvents.StationLeft, server, x, player, this.stationsOccupied[server.ServerCode][data.Prefix]?.JoinedAt);
|
||||
delete this.stationsOccupied[server.ServerCode][data.Prefix];
|
||||
this.emit(SimrailClientEvents.StationLeft, server, x, player, this.stationsOccupied[ server.ServerCode ][ data.Prefix ]?.JoinedAt);
|
||||
delete this.stationsOccupied[ server.ServerCode ][ data.Prefix ];
|
||||
|
||||
}
|
||||
})
|
||||
redis.json.set('stations_occupied', '$', this.stationsOccupied);
|
||||
});
|
||||
redis.json.set("stations_occupied", "$", this.stationsOccupied);
|
||||
|
||||
this.stations[server.ServerCode] = stations.data;
|
||||
redis.json.set('stations', '$', this.stations);
|
||||
this.stations[ server.ServerCode ] = stations.data;
|
||||
redis.json.set("stations", "$", this.stations);
|
||||
}
|
||||
}
|
||||
|
||||
private async processTrain(server: Server, trains: ApiResponse<Train>) {
|
||||
if (trains.result) {
|
||||
if (!this.trains[server.ServerCode]) this.trains[server.ServerCode] = [];
|
||||
if (!this.trainsOccupied[server.ServerCode]) this.trainsOccupied[server.ServerCode] = {};
|
||||
private async processTrain(server: Server, trains: ApiResponse<Train>)
|
||||
{
|
||||
if (trains.result)
|
||||
{
|
||||
if (!this.trains[ server.ServerCode ])
|
||||
{
|
||||
this.trains[ server.ServerCode ] = [];
|
||||
}
|
||||
if (!this.trainsOccupied[ server.ServerCode ])
|
||||
{
|
||||
this.trainsOccupied[ server.ServerCode ] = {};
|
||||
}
|
||||
|
||||
if (!this.trains[server.ServerCode].length) {
|
||||
this.trains[server.ServerCode] = trains.data;
|
||||
if (!this.trains[ server.ServerCode ].length)
|
||||
{
|
||||
this.trains[ server.ServerCode ] = trains.data;
|
||||
|
||||
redis.json.set('trains', '$', this.trains);
|
||||
redis.json.set("trains", "$", this.trains);
|
||||
return;
|
||||
}
|
||||
|
||||
trains.data.forEach(async (x) => {
|
||||
const data = this.trains[server.ServerCode].find(y => y.id === x.id);
|
||||
if (!data) return;
|
||||
trains.data.forEach(async (x) =>
|
||||
{
|
||||
const data = this.trains[ server.ServerCode ].find(y => y.id === x.id);
|
||||
if (!data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.TrainData.ControlledBySteamID !== x.TrainData.ControlledBySteamID) {
|
||||
|
||||
if (!data.TrainData.ControlledBySteamID) {
|
||||
if (!x.TrainData.ControlledBySteamID) return;
|
||||
if (data.TrainData.ControlledBySteamID !== x.TrainData.ControlledBySteamID)
|
||||
{
|
||||
|
||||
if (!data.TrainData.ControlledBySteamID)
|
||||
{
|
||||
if (!x.TrainData.ControlledBySteamID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// join
|
||||
const date = new Date();
|
||||
|
||||
@ -150,62 +206,72 @@ export class SimrailClient extends EventEmitter {
|
||||
const playerStats = await PlayerUtil.getPlayerStats(x.TrainData.ControlledBySteamID!);
|
||||
|
||||
|
||||
this.emit(SimrailClientEvents.TrainJoined, server, x, player, playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value);
|
||||
this.emit(SimrailClientEvents.TrainJoined, server, x, player, playerStats?.stats.find(x => x.name === "DISTANCE_M")?.value);
|
||||
|
||||
this.trainsOccupied[server.ServerCode][x.TrainNoLocal] = {
|
||||
this.trainsOccupied[ server.ServerCode ][ x.TrainNoLocal ] = {
|
||||
SteamId: x.TrainData.ControlledBySteamID!,
|
||||
JoinedAt: date.getTime(),
|
||||
StartPlayerDistance: playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value!,
|
||||
StartPlayerDistance: playerStats?.stats.find(x => x.name === "DISTANCE_M")?.value!,
|
||||
StartPlayerPoints: playerStats?.stats.find(x => x.name === "SCORE")?.value!,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.TrainData.ControlledBySteamID) return;
|
||||
if (!data.TrainData.ControlledBySteamID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const date = new Date();
|
||||
|
||||
const player = await PlayerUtil.getPlayer(data.TrainData.ControlledBySteamID!);
|
||||
|
||||
const playerId = data.TrainData.ControlledBySteamID!;
|
||||
const trainOccupied = this.trainsOccupied[server.ServerCode][data.TrainNoLocal] && JSON.parse(JSON.stringify(this.trainsOccupied[server.ServerCode][data.TrainNoLocal])) || null;
|
||||
const trainOccupied = this.trainsOccupied[ server.ServerCode ][ data.TrainNoLocal ] && JSON.parse(JSON.stringify(this.trainsOccupied[ server.ServerCode ][ data.TrainNoLocal ])) || null;
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
setTimeout(() =>
|
||||
{
|
||||
|
||||
PlayerUtil.getPlayerStats(playerId).then(playerStats => {
|
||||
|
||||
PlayerUtil.getPlayerStats(playerId).then(playerStats =>
|
||||
{
|
||||
const oldKm = trainOccupied?.StartPlayerDistance ?? 0;
|
||||
|
||||
const distance = oldKm ? (playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value ?? 0) - oldKm : 0;
|
||||
const distance = oldKm ? (playerStats?.stats.find(x => x.name === "DISTANCE_M")?.value ?? 0) - oldKm : 0;
|
||||
|
||||
const oldPoints = trainOccupied?.StartPlayerPoints ?? 0;
|
||||
|
||||
const points = oldPoints ? (playerStats?.stats.find(x => x.name === 'SCORE')?.value ?? 0) - oldPoints : 0;
|
||||
const points = oldPoints ? (playerStats?.stats.find(x => x.name === "SCORE")?.value ?? 0) - oldPoints : 0;
|
||||
|
||||
|
||||
this.emit(SimrailClientEvents.TrainLeft, server, data, player, trainOccupied?.JoinedAt, date.getTime(), points, distance, x.Vehicles[0]);
|
||||
})
|
||||
}, 80_000)
|
||||
delete this.trainsOccupied[server.ServerCode][data.TrainNoLocal];
|
||||
this.emit(SimrailClientEvents.TrainLeft, server, data, player, trainOccupied?.JoinedAt, date.getTime(), points, distance, x.Vehicles[ 0 ]);
|
||||
});
|
||||
}, 80_000);
|
||||
delete this.trainsOccupied[ server.ServerCode ][ data.TrainNoLocal ];
|
||||
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
this.trains[server.ServerCode] = trains.data;
|
||||
redis.json.set('trains', '$', this.trains);
|
||||
redis.json.set('trains_occupied', '$', this.trainsOccupied);
|
||||
this.trains[ server.ServerCode ] = trains.data;
|
||||
redis.json.set("trains", "$", this.trains);
|
||||
redis.json.set("trains_occupied", "$", this.trainsOccupied);
|
||||
}
|
||||
}
|
||||
|
||||
private async update() {
|
||||
const servers = (await fetch('https://panel.simrail.eu:8084/servers-open').then(x => x.json()).catch(x => ({ data: [], result: false })) as ApiResponse<Server>)
|
||||
.data?.filter(x => x.ServerName.includes('Polski')) ?? []; // no plans to support other servers
|
||||
private async update()
|
||||
{
|
||||
const servers = (await fetch("https://panel.simrail.eu:8084/servers-open").then(x => x.json()).catch(x => ({ data: [], result: false })) as ApiResponse<Server>)
|
||||
.data?.filter(x => x.ServerName.includes("Polski")) ?? []; // no plans to support other servers
|
||||
|
||||
if (!servers.length) console.log('SimrailAPI is down')
|
||||
if (!servers.length)
|
||||
{
|
||||
console.log("SimrailAPI is down");
|
||||
}
|
||||
|
||||
// TODO: maybe node:worker_threads?
|
||||
// TODO: check performance
|
||||
servers.forEach(async (server) => {
|
||||
servers.forEach(async (server) =>
|
||||
{
|
||||
const stations = (await fetch('https://panel.simrail.eu:8084/stations-open?serverCode=' + server.ServerCode).then(x => x.json()).catch(() => ({ result: false }))) as ApiResponse<Station>;
|
||||
const trains = (await fetch('https://panel.simrail.eu:8084/trains-open?serverCode=' + server.ServerCode).then(x => x.json()).catch(() => ({ result: false }))) as ApiResponse<Train>;
|
||||
|
||||
@ -213,7 +279,6 @@ export class SimrailClient extends EventEmitter {
|
||||
this.processTrain(server, trains);
|
||||
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
import { MSteam } from '../mongo/steam.js';
|
||||
import { PlayerUtil } from './PlayerUtil.js';
|
||||
import { MSteam } from "../mongo/steam.js";
|
||||
import { PlayerUtil } from "./PlayerUtil.js";
|
||||
|
||||
export class SteamUtil {
|
||||
public static async updatePlayerDataInDatabase(steam: string) {
|
||||
export class SteamUtil
|
||||
{
|
||||
public static async updatePlayerDataInDatabase(steam: string)
|
||||
{
|
||||
const data = await PlayerUtil.getPlayer(steam);
|
||||
const stats = await PlayerUtil.getPlayerStats(steam);
|
||||
const steamApi = await MSteam.findOne({ steam });
|
||||
|
||||
if (steamApi) {
|
||||
if (steamApi)
|
||||
{
|
||||
await MSteam.findOneAndUpdate({ steam }, { stats, personaname: data?.personaname, avatarfull: data?.avatarfull });
|
||||
return;
|
||||
}
|
||||
@ -16,21 +19,28 @@ export class SteamUtil {
|
||||
}
|
||||
|
||||
|
||||
public static async getPlayer(steam: string) {
|
||||
public static async getPlayer(steam: string)
|
||||
{
|
||||
const steamApi = await MSteam.findOne({ steam });
|
||||
this.updatePlayerDataInDatabase(steam);
|
||||
void this.updatePlayerDataInDatabase(steam);
|
||||
|
||||
if (steamApi) return { personname: steamApi.personaname, avatarfull: steamApi.avatarfull };
|
||||
if (steamApi)
|
||||
{
|
||||
return { personname: steamApi.personaname, avatarfull: steamApi.avatarfull };
|
||||
}
|
||||
|
||||
return await PlayerUtil.getPlayer(steam);
|
||||
}
|
||||
|
||||
public static async getPlayerStats(steam: string) {
|
||||
public static async getPlayerStats(steam: string)
|
||||
{
|
||||
const steamApi = await MSteam.findOne({ steam });
|
||||
this.updatePlayerDataInDatabase(steam);
|
||||
void this.updatePlayerDataInDatabase(steam);
|
||||
|
||||
if (steamApi)
|
||||
return steamApi.stats;
|
||||
if (steamApi)
|
||||
{
|
||||
return steamApi.stats;
|
||||
}
|
||||
|
||||
return await PlayerUtil.getPlayerStats(steam);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import wcmatch from 'wildcard-match'
|
||||
import wcmatch from "wildcard-match";
|
||||
|
||||
/*
|
||||
E186_134 = "Traxx/E186-134",
|
||||
@ -48,80 +48,81 @@ E186_134 = "Traxx/E186-134",
|
||||
|
||||
export const trainsList = [
|
||||
{
|
||||
train: 'Traxx (E186)',
|
||||
train: "Traxx (E186)",
|
||||
pattern: [
|
||||
'Traxx/E186-*',
|
||||
]
|
||||
"Traxx/E186-*",
|
||||
],
|
||||
},
|
||||
{
|
||||
train: 'Dragon2 (E6ACTa, E6ACTadb)',
|
||||
train: "Dragon2 (E6ACTa, E6ACTadb)",
|
||||
pattern: [
|
||||
'Dragon2/E6ACTa-*',
|
||||
'Dragon2/E6ACTadb-*'
|
||||
]
|
||||
"Dragon2/E6ACTa-*",
|
||||
"Dragon2/E6ACTadb-*",
|
||||
],
|
||||
},
|
||||
{
|
||||
train: 'Dragon2 (ET25)',
|
||||
train: "Dragon2 (ET25)",
|
||||
pattern: [
|
||||
'Dragon2/ET25-*',
|
||||
]
|
||||
"Dragon2/ET25-*",
|
||||
],
|
||||
},
|
||||
{
|
||||
train: 'Pendolino (ED250)',
|
||||
train: "Pendolino (ED250)",
|
||||
pattern: [
|
||||
'Pendolino/ED250-*',
|
||||
]
|
||||
"Pendolino/ED250-*",
|
||||
],
|
||||
},
|
||||
{
|
||||
train: 'EN57',
|
||||
train: "EN57",
|
||||
pattern: [
|
||||
'EN57/EN57-*',
|
||||
]
|
||||
"EN57/EN57-*",
|
||||
],
|
||||
},
|
||||
{
|
||||
train: 'EN71',
|
||||
train: "EN71",
|
||||
pattern: [
|
||||
'EN57/EN71-*',
|
||||
]
|
||||
"EN57/EN71-*",
|
||||
],
|
||||
},
|
||||
{
|
||||
train: 'EN96',
|
||||
train: "EN96",
|
||||
pattern: [
|
||||
'Elf/EN76-*',
|
||||
]
|
||||
"Elf/EN76-*",
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
train: 'EP07',
|
||||
train: "EP07",
|
||||
pattern: [
|
||||
'4E/EP07-*',
|
||||
]
|
||||
"4E/EP07-*",
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
train: 'EP08',
|
||||
train: "EP08",
|
||||
pattern: [
|
||||
'4E/EP08-*',
|
||||
]
|
||||
"4E/EP08-*",
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
train: 'ET22',
|
||||
train: "ET22",
|
||||
pattern: [
|
||||
'201E/ET22-*',
|
||||
]
|
||||
"201E/ET22-*",
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
train: 'EU07',
|
||||
train: "EU07",
|
||||
pattern: [
|
||||
'4E/EU07-*',
|
||||
]
|
||||
}
|
||||
]
|
||||
"4E/EU07-*",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const getVehicle = (name: string) => {
|
||||
export const getVehicle = (name: string) =>
|
||||
{
|
||||
return trainsList.find(x => wcmatch(x.pattern)(name))?.train;
|
||||
};
|
@ -1,8 +1,9 @@
|
||||
// TODO: typings
|
||||
export const removeProperties = <T>(data: any, names: string[]) => {
|
||||
|
||||
export const removeProperties = <T>(data: any, names: string[]) =>
|
||||
{
|
||||
for (const name of names)
|
||||
delete data[name];
|
||||
|
||||
{
|
||||
delete data[ name ];
|
||||
}
|
||||
return data as T;
|
||||
}
|
||||
};
|
@ -1,35 +1,45 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { execSync } from "child_process";
|
||||
|
||||
export class GitUtil {
|
||||
export class GitUtil
|
||||
{
|
||||
private static cache: { lastUpdated: number, version?: string, commit?: string } = undefined!;
|
||||
|
||||
public static getLatestVersion() {
|
||||
try {
|
||||
const data = execSync('git describe --tags --exact-match').toString();
|
||||
return data.replace('\n', '');
|
||||
} catch {
|
||||
public static getLatestVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
const data = execSync("git describe --tags --exact-match").toString();
|
||||
return data.replace("\n", "");
|
||||
} catch
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public static getLatestCommit() {
|
||||
try {
|
||||
const data = execSync('git rev-parse --short HEAD').toString();
|
||||
return data.replace('\n', '');
|
||||
} catch {
|
||||
public static getLatestCommit()
|
||||
{
|
||||
try
|
||||
{
|
||||
const data = execSync("git rev-parse --short HEAD").toString();
|
||||
return data.replace("\n", "");
|
||||
} catch
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static getData() {
|
||||
if (this.cache && (this.cache.lastUpdated - Date.now()) < 30_000)
|
||||
public static getData()
|
||||
{
|
||||
if (this.cache && (this.cache.lastUpdated - Date.now()) < 30_000)
|
||||
{
|
||||
return this.cache;
|
||||
|
||||
}
|
||||
|
||||
const data = {
|
||||
version: this.getLatestVersion(),
|
||||
commit: this.getLatestCommit(),
|
||||
lastUpdated: Date.now()
|
||||
lastUpdated: Date.now(),
|
||||
};
|
||||
|
||||
this.cache = data;
|
||||
|
@ -1,20 +1,22 @@
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime.js';
|
||||
import duration from 'dayjs/plugin/duration.js';
|
||||
import pl from 'dayjs/locale/pl.js';
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime.js";
|
||||
import duration from "dayjs/plugin/duration.js";
|
||||
import pl from "dayjs/locale/pl.js";
|
||||
|
||||
|
||||
|
||||
dayjs.extend(duration)
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.locale(pl);
|
||||
|
||||
|
||||
export const msToTime = (duration: number, long = false) => {
|
||||
const time = dayjs.duration(duration, 'milliseconds');
|
||||
export const msToTime = (duration: number, long = false) =>
|
||||
{
|
||||
const time = dayjs.duration(duration, "milliseconds");
|
||||
|
||||
if (long)
|
||||
return `${time.humanize()} (${time.format('Y[y] M[mth] D[d] H[h] m[m] s[s]').split(' ').map(x => x.split('')[0] === '0' ? null : x).filter(x => x).join(' ')})`;
|
||||
if (long)
|
||||
{
|
||||
return `${ time.humanize() } (${ time.format("Y[y] M[mth] D[d] H[h] m[m] s[s]").split(" ").map(x => x.split("")[ 0 ] === "0" ? null : x).filter(x => x).join(" ") })`;
|
||||
}
|
||||
|
||||
return time.humanize();
|
||||
}
|
63
packages/frontend/src/pages/log/Log.tsx
Normal file
63
packages/frontend/src/pages/log/Log.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ContentLoader } from "../../components/mini/loaders/ContentLoader.tsx";
|
||||
import { WarningAlert } from "../../components/mini/alerts/Warning.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PageTitle } from "../../components/mini/util/PageTitle.tsx";
|
||||
import { TLogResponse, TLogStationData, TLogTrainData } from "../../types/log.ts";
|
||||
import { StationLog } from "../../components/pages/log/StationLog.tsx";
|
||||
|
||||
export const Log = () =>
|
||||
{
|
||||
const { id } = useParams();
|
||||
|
||||
const [ error, setError ] = useState<0 | 1 | 2 | 3>(0);
|
||||
const [ trainData, setTrainData ] = useState<TLogTrainData>(undefined!);
|
||||
const [ stationData, setStationData ] = useState<TLogStationData>(undefined!);
|
||||
|
||||
trainData; // suppress error
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
fetch(`${ import.meta.env.VITE_API_URL }/log/${ id }`).then(x => x.json()).then((data: TLogResponse) =>
|
||||
{
|
||||
switch (data.code)
|
||||
{
|
||||
case 404:
|
||||
setError(2);
|
||||
break;
|
||||
case 403:
|
||||
// NOT_IMPLEMENTED
|
||||
setError(3);
|
||||
break;
|
||||
case 200:
|
||||
setError(1);
|
||||
|
||||
"trainNumber" in data.data ? setTrainData(data.data) : setStationData(data.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* LOADING */ }
|
||||
{ error === 0 && <ContentLoader/> }
|
||||
{/* NOT FOUND */ }
|
||||
{ error === 2 && <PageTitle title={ `simrail.alekswilc.dev | Profile not found` }/> }
|
||||
{ error === 2 && <WarningAlert title={ t("log.errors.notfound.title") }
|
||||
description={ t("log.errors.notfound.description") }/> }
|
||||
{/* BLACKLISTED PROFILE */ }
|
||||
{ error === 3 && <PageTitle title={ `simrail.alekswilc.dev | Blacklisted profile` }/> }
|
||||
{ error === 3 && <WarningAlert title={ t("log.errors.blacklist.title") }
|
||||
description={ t("log.errors.blacklist.description") }/> }
|
||||
{/* SUCCESS */ }
|
||||
{ error === 1 && stationData && <PageTitle
|
||||
title={ `simrail.alekswilc.dev | ${ stationData.userUsername } | ${ stationData.stationName }` }/> }
|
||||
{ error === 1 && stationData && < StationLog data={ stationData }/> }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
35
packages/frontend/src/types/log.ts
Normal file
35
packages/frontend/src/types/log.ts
Normal file
@ -0,0 +1,35 @@
|
||||
export interface TLogResponse
|
||||
{
|
||||
success: boolean;
|
||||
data: TLogTrainData | TLogStationData;
|
||||
code: number;
|
||||
}
|
||||
|
||||
export interface TLogTrainData
|
||||
{
|
||||
id: string;
|
||||
trainNumber: string;
|
||||
userSteamId: string;
|
||||
userUsername: string;
|
||||
userAvatar: string;
|
||||
leftDate: number;
|
||||
distance: number;
|
||||
points: number;
|
||||
server: string;
|
||||
trainName: string;
|
||||
joinedDate?: number;
|
||||
|
||||
}
|
||||
|
||||
export interface TLogStationData
|
||||
{
|
||||
id: string;
|
||||
userSteamId: string;
|
||||
userUsername: string;
|
||||
userAvatar: string;
|
||||
leftDate: number;
|
||||
stationName: string;
|
||||
stationShort: string;
|
||||
server: string;
|
||||
joinedDate?: number;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user