Skip to content

Commit

Permalink
Merge pull request #481 from gnmyt/updates/code-optimizations
Browse files Browse the repository at this point in the history
🚀 Optimierungen am Code
  • Loading branch information
gnmyt authored Dec 1, 2023
2 parents 4a624f7 + a84ca10 commit c30db99
Show file tree
Hide file tree
Showing 26 changed files with 143 additions and 200 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy_docker_dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ jobs:
uses: docker/build-push-action@v3
with:
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64 #,linux/arm/v7 (add back once rollup supports it)
tags: germannewsmaker/myspeed:development
37 changes: 15 additions & 22 deletions server/controller/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ const config = require("../models/Config");
const {triggerEvent} = require("./integrations");

const configDefaults = {
"ping": "25",
"download": "100",
"upload": "50",
"cron": "0 * * * *",
"serverId": "none",
"password": "none",
"passwordLevel": "none",
"acceptOoklaLicense": "false"
ping: "25",
download: "100",
upload: "50",
cron: "0 * * * *",
serverId: "none",
password: "none",
passwordLevel: "none",
acceptOoklaLicense: "false"
}

module.exports.insertDefaults = async () => {
Expand All @@ -22,26 +22,19 @@ module.exports.insertDefaults = async () => {
await config.bulkCreate(insert, {validate: true});
}

// Lists all config entries
module.exports.list = async () => {
module.exports.listAll = async () => {
return await config.findAll();
}

// Gets a specific config entry
module.exports.get = async (key) => {
return await config.findByPk(key);
module.exports.getValue = async (key) => {
return (await config.findByPk(key)).value;
}

// Updates a specific config entry
module.exports.update = async (key, newValue) => {
if ((await this.get(key)) === undefined) return undefined;
module.exports.updateValue = async (key, newValue) => {
if ((await this.getValue(key)) === undefined) return undefined;

triggerEvent("configUpdated", {key: key, value: key === "password" ? "protected" :newValue}).then(() => {});
triggerEvent("configUpdated", {key: key, value: key === "password" ? "protected" : newValue})
.then(undefined);

return await config.update({value: newValue}, {where: {key: key}});
}

// Resets a specific config entry to the default value
module.exports.resetDefault = async (key) => {
await this.update(key, configDefaults[key]);
}
33 changes: 8 additions & 25 deletions server/controller/node.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,20 @@
const axios = require('axios');
const nodes = require('../models/Node');

// Gets all node entries
module.exports.list = async () => {
return await nodes.findAll().then((result) => result.map((node) => ({...node, password: node.password !== null})));
}
module.exports.listAll = async () => await nodes.findAll()
.then((result) => result.map((node) => ({...node, password: node.password !== null})));

// Create a new node entry
module.exports.create = async (name, url, password) => {
return await nodes.create({name: name, url: url, password: password});
}
module.exports.create = async (name, url, password) => await nodes.create({name: name, url: url, password: password});

// Delete a node entry
module.exports.delete = async (nodeId) => {
return await nodes.destroy({where: {id: nodeId}});
}
module.exports.delete = async (nodeId) => await nodes.destroy({where: {id: nodeId}});

// Get a specific node entry
module.exports.get = async (nodeId) => {
return await nodes.findOne({where: {id: nodeId}});
}
module.exports.getOne = async (nodeId) => await nodes.findOne({where: {id: nodeId}});

// Update the name of the node entry
module.exports.updateName = async (nodeId, name) => {
return await nodes.update({name: name}, {where: {id: nodeId}});
}
module.exports.updateName = async (nodeId, name) => await nodes.update({name: name}, {where: {id: nodeId}});

// Update the password of the node entry
module.exports.updatePassword = async (nodeId, password) => {
return await nodes.update({password: password}, {where: {id: nodeId}});
}
module.exports.updatePassword = async (nodeId, password) => await nodes.update({password: password}, {where: {id: nodeId}});

module.exports.checkNode = async (url, password) => {
module.exports.checkStatus = async (url, password) => {
if (password === "none") password = undefined;
const api = await axios.get(url + "/api/config", {headers: {password: password}}).catch(() => {
return "INVALID_URL";
Expand Down
10 changes: 4 additions & 6 deletions server/controller/pause.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
let currentState = false;
let updateTimer;

// Update the current state directly
module.exports.updateState = function(newState) {
module.exports.updateState = (newState) => {
this.currentState = newState;
}

// Update the current state in a specific time
module.exports.resumeIn = function(time) {
if (isNaN(time)) return;
module.exports.resumeIn = (hours) => {
if (isNaN(hours)) return;

if (updateTimer !== null)
clearTimeout(updateTimer);

this.updateState(true);
updateTimer = setTimeout(() => this.updateState(false), time * 3600000); // time in hours
updateTimer = setTimeout(() => this.updateState(false), hours * 3600000); // time in hours

return true;
}
Expand Down
6 changes: 2 additions & 4 deletions server/controller/recommendations.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
const recommendations = require('../models/Recommendations');
const {triggerEvent} = require("./integrations");

// Gets the current recommendations
module.exports.get = async () => {
module.exports.getCurrent = async () => {
return await recommendations.findOne();
}

// Sets new recommendations
module.exports.set = async (ping, download, upload) => {
module.exports.update = async (ping, download, upload) => {
const configuration = {ping: Math.round(ping), download: parseFloat(download.toFixed(2)),
upload: parseFloat(upload.toFixed(2))};

Expand Down
64 changes: 15 additions & 49 deletions server/controller/speedtests.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
const tests = require('../models/Speedtests');
const {Op, Sequelize} = require("sequelize");
const {mapFixed, mapRounded, calculateTestAverages} = require("../util/helpers");

// Inserts a new speedtest into the database
module.exports.create = async (ping, download, upload, time, type = "auto", error = null) => {
return (await tests.create({ping, download, upload, error, type, time})).id;
}

// Gets a specific speedtest by id
module.exports.get = async (id) => {
module.exports.getOne = async (id) => {
let speedtest = await tests.findByPk(id);
if (speedtest === null) return null;
if (speedtest.error === null) delete speedtest.error;
return speedtest
}

// Lists all speedtests from the database
module.exports.list = async (hours = 24, start, limit) => {
module.exports.listTests = async (hours = 24, start, limit) => {
const whereClause = start ? {id: {[Op.lt]: start}} : undefined;

let dbEntries = (await tests.findAll({where: whereClause, order: [["created", "DESC"]], limit}))
.filter((entry) => new Date(entry.created) > new Date().getTime() - hours * 3600000);

for (let dbEntry of dbEntries)
if (dbEntry.error === null) delete dbEntry.error
if (dbEntry.error === null) delete dbEntry.error;

return dbEntries;
}

// Lists all speedtests from the database grouped by days
module.exports.listByDays = async (days) => {
let dbEntries = (await tests.findAll({order: [["created", "DESC"]]})).filter((entry) => entry.error === null)
.filter((entry) => new Date(entry.created) > new Date().getTime() - days * 24 * 3600000);
Expand All @@ -42,7 +39,6 @@ module.exports.listByDays = async (days) => {
return averages;
}

// Calculates the average speedtests and lists them
module.exports.listAverage = async (days) => {
const averages = await this.listByDays(days);
let result = [];
Expand All @@ -51,22 +47,15 @@ module.exports.listAverage = async (days) => {
result.push(averages[Object.keys(averages)[0]][0]);

for (let day in averages) {
let avgNumbers = {ping: 0, down: 0, up: 0, time: 0};
let currentDay = averages[day];

currentDay.forEach((current) => {
avgNumbers.ping += current.ping;
avgNumbers.down += current.download;
avgNumbers.up += current.upload;
avgNumbers.time += current.time;
});
let avgNumbers = calculateTestAverages(currentDay);

const created = new Date(currentDay[0].created);
result.push({
ping: Math.round(avgNumbers["ping"] / currentDay.length),
download: parseFloat((avgNumbers["down"] / currentDay.length).toFixed(2)),
upload: parseFloat((avgNumbers["up"] / currentDay.length).toFixed(2)),
time: Math.round(avgNumbers["time"] / currentDay.length),
ping: Math.round(avgNumbers["ping"]),
download: parseFloat((avgNumbers["down"]).toFixed(2)),
upload: parseFloat((avgNumbers["up"]).toFixed(2)),
time: Math.round(avgNumbers["time"]),
type: "average",
amount: currentDay.length,
created: created.getFullYear() + "-" + (created.getMonth() + 1) + "-" + created.getDate()
Expand All @@ -76,18 +65,6 @@ module.exports.listAverage = async (days) => {
return result;
}

const mapFixed = (entries, type) => ({
min: Math.min(...entries.map((entry) => entry[type])),
max: Math.max(...entries.map((entry) => entry[type])),
avg: parseFloat((entries.reduce((a, b) => a + b[type], 0) / entries.length).toFixed(2))
});

const mapRounded = (entries, type) => ({
min: Math.min(...entries.map((entry) => entry[type])),
max: Math.max(...entries.map((entry) => entry[type])),
avg: Math.round(entries.reduce((a, b) => a + b[type], 0) / entries.length)
});

module.exports.listStatistics = async (days) => {
let dbEntries = (await tests.findAll({order: [["created", "DESC"]]}))
.filter((entry) => new Date(entry.created) > new Date().getTime() - (days <= 30 ? days : 30 ) * 24 * 3600000);
Expand All @@ -97,6 +74,9 @@ module.exports.listStatistics = async (days) => {

let notFailed = dbEntries.filter((entry) => entry.error === null);

let data = ["ping", "download", "upload", "time"]
.map((item) => days >= 3 ? avgEntries.map(entry => entry[item]) : notFailed.map(entry => entry[item]));

return {
tests: {
total: dbEntries.length,
Expand All @@ -107,32 +87,18 @@ module.exports.listStatistics = async (days) => {
download: mapFixed(notFailed, "download"),
upload: mapFixed(notFailed, "upload"),
time: mapRounded(notFailed, "time"),
data: {
ping: days >= 3 ? avgEntries.map((entry) => entry.ping) : notFailed.map((entry) => entry.ping),
download: days >= 3 ? avgEntries.map((entry) => entry.download) : notFailed.map((entry) => entry.download),
upload: days >= 3 ? avgEntries.map((entry) => entry.upload) : notFailed.map((entry) => entry.upload),
time: days >= 3 ? avgEntries.map((entry) => entry.time) : notFailed.map((entry) => entry.time)
},
data,
labels: days >= 3 ? avgEntries.map((entry) => new Date(entry.created).toLocaleDateString())
: notFailed.map((entry) => new Date(entry.created).toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"}))
};
}

// Gets the latest speedtest from the database
module.exports.latest = async () => {
let speedtest = await tests.findOne({order: [["created", "DESC"]]});
if (speedtest != null && speedtest.error === null) delete speedtest.error;
return speedtest
}

// Deletes a specific speedtest
module.exports.delete = async (id) => {
if (await this.get(id) === null) return false;
module.exports.deleteOne = async (id) => {
if (await this.getOne(id) === null) return false;
await tests.destroy({where: {id: id}});
return true;
}

// Removes speedtests older than 30 days
module.exports.removeOld = async () => {
await tests.destroy({
where: {
Expand Down
14 changes: 7 additions & 7 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ app.disable('x-powered-by');
const port = process.env.port || 5216;

// Create the data folder and the servers file
require('./config/createFolder');
require('./config/loadServers');
require('./util/createFolders');
require('./util/loadServers');

process.on('uncaughtException', err => require('./config/errorHandler')(err));
process.on('uncaughtException', err => require('./util/errorHandler')(err));

// Register middlewares
app.use(express.json());
Expand Down Expand Up @@ -52,26 +52,26 @@ const run = async () => {
await require('./controller/integrations').initialize();

// Load the cli
await require('./config/loadCli').load();
await require('./util/loadCli').load();

await config.insertDefaults();

// Start all timer
timerTask.startTimer((await config.get("cron")).value);
timerTask.startTimer(await config.getValue("cron"));
setInterval(async () => require('./tasks/speedtest').removeOld(), 60000);

// Start integration interval
integrationTask.startTimer();

// Make a speedtest
timerTask.runTask();
timerTask.runTask().then(undefined);

app.listen(port, () => console.log(`Server listening on port ${port}`));
}

db.authenticate().then(() => {
console.log("Successfully connected to the database file");
run();
run().then(undefined);
}).catch(err => {
console.error("Could not open the database file. Maybe it is damaged?: " + err.message);
process.exit(111);
Expand Down
2 changes: 1 addition & 1 deletion server/integrations/discord.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const axios = require("axios");
const {replaceVariables} = require("../util/variables");
const {replaceVariables} = require("../util/helpers");

const defaults = {
finished: ":sparkles: **A speedtest is finished**\n > :ping_pong: `Ping`: %ping% ms\n > :arrow_up: `Upload`: %upload% Mbps\n > :arrow_down: `Download`: %download% Mbps",
Expand Down
2 changes: 1 addition & 1 deletion server/integrations/gotify.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const axios = require("axios");
const {replaceVariables} = require("../util/variables");
const {replaceVariables} = require("../util/helpers");

const defaults = {
finished: "A speedtest is finished:\nPing: %ping% ms\nUpload: %upload% Mbps\nDownload: %download% Mbps",
Expand Down
2 changes: 1 addition & 1 deletion server/integrations/telegram.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const axios = require("axios");
const {replaceVariables} = require("../util/variables");
const {replaceVariables} = require("../util/helpers");

const defaults = {
finished: "✨ *A speedtest is finished*\n🏓 `Ping`: %ping% ms\n🔼 `Upload`: %upload% Mbps\n🔽 `Download`: %download% Mbps",
Expand Down
4 changes: 2 additions & 2 deletions server/middlewares/password.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const config = require('../controller/config');
const bcrypt = require('bcrypt');

module.exports = (allowViewAccess) => async (req, res, next) => {
let passwordHash = (await config.get("password")).value;
let passwordLevel = (await config.get("passwordLevel")).value;
let passwordHash = await config.getValue("password");
let passwordLevel = await config.getValue("passwordLevel");

if (passwordHash === "none") {
req.viewMode = false;
Expand Down
7 changes: 3 additions & 4 deletions server/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ const timer = require('../tasks/timer');
const cron = require('cron-validator');
const password = require('../middlewares/password');

// Gets all config entries
app.get("/", password(true), async (req, res) => {
let configValues = {};
(await config.list()).forEach(row => {
(await config.listAll()).forEach(row => {
if (row.key !== "password" && !(req.viewMode && ["serverId", "cron", "passwordLevel"].includes(row.key)))
configValues[row.key] = row.value;
});
Expand All @@ -17,7 +16,6 @@ app.get("/", password(true), async (req, res) => {
res.json(configValues);
});

// Updates a specific config entry
app.patch("/:key", password(false), async (req, res) => {
if (!req.body.value?.toString()) return res.status(400).json({message: "You need to provide the new value"});

Expand All @@ -38,7 +36,8 @@ app.patch("/:key", password(false), async (req, res) => {
if (req.params.key === "cron" && !cron.isValidCron(req.body.value.toString()))
return res.status(500).json({message: "Not a valid cron expression"});

if (!await config.update(req.params.key, req.body.value.toString())) return res.status(404).json({message: "The provided key does not exist"});
if (!await config.updateValue(req.params.key, req.body.value.toString()))
return res.status(404).json({message: "The provided key does not exist"});

if (req.params.key === "cron") {
timer.stopTimer();
Expand Down
Loading

0 comments on commit c30db99

Please sign in to comment.