diff --git a/index.js b/index.js index e727903..d551f09 100644 --- a/index.js +++ b/index.js @@ -10,9 +10,9 @@ import njk from 'nunjucks' import initLog from './libs/utils/log.js' import routes from './routes/routes.js' - import { generateDB } from './libs/database/generate.js' import { dbInit } from './libs/database/init.js' +import { getStats, setStat } from './libs/serverStats.js' import { downloadEssentialData } from './libs/download/dowload.js' const start = Date.now() @@ -21,10 +21,13 @@ const log = initLog("[ MAIN ]") // Read the .env file dotenv.config() +dbInit() +setStat("startup", start) +setStat("lastDBUpdate", start) + if (process.env.DOWNLOAD_FILES === "enabled") await downloadEssentialData() -dbInit() generateDB() const Server = express() diff --git a/libs/serverStats.js b/libs/serverStats.js new file mode 100644 index 0000000..876a48f --- /dev/null +++ b/libs/serverStats.js @@ -0,0 +1,47 @@ +import { sqlite } from './database/init.js' + +let globStats = { + startup: Date.now(), + updatingDB: false, + lastDBUpdate: Date.now() +} + +/** + * This function returns current server stats. + * + * @returns {object} Stats for server. + * @author BurnyLlama + */ +export function getStats() { + const uptimeMillis = Date.now() - globStats.startup + const uptimeSeconds = `${uptimeMillis / 1000} s` + + const dbBytes = sqlite.prepare("SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()").get().size + + const db = { + size: { + bytes: dbBytes, + kbytes: dbBytes / 1024, + mbytes: dbBytes / 1024 ** 2, + gbytes: dbBytes / 1024 ** 3, + }, + currentlyUpdating: globStats.updatingDB, + lastUpdate: new Date(globStats.lastDBUpdate).toLocaleString() + } + + const stats = { uptimeMillis, uptimeSeconds, db } + console.log(stats) + return stats +} + +/** + * This sets a "global" stat... + * + * @param {string} stat The stat you want to set. + * @param {*} value The calue you want 'stat' to have. + * + * @author BurnyLlama + */ +export function setStat(stat, value) { + globStats[stat] = value +} \ No newline at end of file diff --git a/routes/routes.js b/routes/routes.js index 252e5f5..86a67fd 100644 --- a/routes/routes.js +++ b/routes/routes.js @@ -1,6 +1,7 @@ import { Router } from 'express' import wrapper from '../libs/database/wrapper.js' import tx from '../libs/routex.js' +import { getStats } from '../libs/serverStats.js' const routes = Router() @@ -33,4 +34,13 @@ routes.get( } ) +routes.get( + '/status', + (req, res) => { + const stats = getStats() + + tx(req, res)('pages/stats.njk', { stats }, true, { currentSection: null }) + } +) + export default routes \ No newline at end of file diff --git a/src/sass/maps.scss b/src/sass/maps.scss index ac45074..eca0714 100644 --- a/src/sass/maps.scss +++ b/src/sass/maps.scss @@ -36,15 +36,6 @@ .map-info { margin: .25rem 2rem; - - td { - padding: .25rem 0; - vertical-align: middle; - } - - td:first-child { - width: 9ch; - } } a.map-more-info { @@ -77,10 +68,6 @@ a.map-more-info { width: 100%; height: auto; } - - td:first-child { - width: 14ch; - } } .leaderboard { diff --git a/src/sass/theme.scss b/src/sass/theme.scss index 1f43d90..ad82d2e 100644 --- a/src/sass/theme.scss +++ b/src/sass/theme.scss @@ -142,6 +142,26 @@ input { } + +// +// TABLES +// +tr { + width: max-content; + + td { + padding: .25rem 0; + vertical-align: middle; + width: max-content; + } + + td:first-child { + padding: .25rem 1.5rem .25rem 0; + } + +} + + // // FOOTER // diff --git a/static/css/maps.css b/static/css/maps.css index 81988d2..fb6f407 100644 --- a/static/css/maps.css +++ b/static/css/maps.css @@ -1 +1 @@ -.map{background-color:#20203f;border-radius:1rem;box-shadow:.25rem 0 2rem #00001f;display:flex;flex-direction:column;justify-content:center;margin:2rem 1rem;transition:transform .3s,box-shadow .3s}.map:hover{transform:scale(1.05);box-shadow:.5rem 0 4rem #00001f}.map-image{border-radius:1rem 1rem 0 0;width:360px;height:225px}.map-name{font-size:1.2rem;font-weight:bold;margin:1rem 2rem .5rem 2rem}.map-info{margin:.25rem 2rem}.map-info td{padding:.25rem 0;vertical-align:middle}.map-info td:first-child{width:9ch}a.map-more-info{margin:.75rem auto 1.25rem auto;padding:.15rem .5rem;border:.1rem solid #fba7c6;border-radius:.5rem;color:#fba7c6;text-decoration:none transparent;transition:color .3s,background-color .3s}a.map-more-info:hover{color:#10102f;background-color:#fba7c6}.map-detailed{padding-bottom:1rem}.map-detailed:hover{transform:none}.map-detailed .map-image{width:100%;height:auto}.map-detailed td:first-child{width:14ch}.leaderboard{width:max-content;margin:2rem;padding:1.5rem 2rem;background-color:#20203f;border-radius:1rem;box-shadow:.25rem 0 2rem #00001f}.rank{display:flex;margin:1.5rem 0}.rank .country-image{height:1.5rem;margin:auto .5rem auto 0}.rank .rank-position{color:#ee588f;margin:auto .5rem auto 0;font-weight:bold}.rank .rank-player{margin:auto 2rem auto 0}.rank .rank-time{color:#9090af;font-family:"Manrope Light";margin:auto 0 auto auto}.leaderboard-history{width:min(70ch,70vw);margin:2rem;padding:1.5rem 2rem;background-color:#20203f;border-radius:1rem;box-shadow:.25rem 0 2rem #00001f} +.map{background-color:#20203f;border-radius:1rem;box-shadow:.25rem 0 2rem #00001f;display:flex;flex-direction:column;justify-content:center;margin:2rem 1rem;transition:transform .3s,box-shadow .3s}.map:hover{transform:scale(1.05);box-shadow:.5rem 0 4rem #00001f}.map-image{border-radius:1rem 1rem 0 0;width:360px;height:225px}.map-name{font-size:1.2rem;font-weight:bold;margin:1rem 2rem .5rem 2rem}.map-info{margin:.25rem 2rem}a.map-more-info{margin:.75rem auto 1.25rem auto;padding:.15rem .5rem;border:.1rem solid #fba7c6;border-radius:.5rem;color:#fba7c6;text-decoration:none transparent;transition:color .3s,background-color .3s}a.map-more-info:hover{color:#10102f;background-color:#fba7c6}.map-detailed{padding-bottom:1rem}.map-detailed:hover{transform:none}.map-detailed .map-image{width:100%;height:auto}.leaderboard{width:max-content;margin:2rem;padding:1.5rem 2rem;background-color:#20203f;border-radius:1rem;box-shadow:.25rem 0 2rem #00001f}.rank{display:flex;margin:1.5rem 0}.rank .country-image{height:1.5rem;margin:auto .5rem auto 0}.rank .rank-position{color:#ee588f;margin:auto .5rem auto 0;font-weight:bold}.rank .rank-player{margin:auto 2rem auto 0}.rank .rank-time{color:#9090af;font-family:"Manrope Light";margin:auto 0 auto auto}.leaderboard-history{width:min(70ch,70vw);margin:2rem;padding:1.5rem 2rem;background-color:#20203f;border-radius:1rem;box-shadow:.25rem 0 2rem #00001f} diff --git a/static/css/theme.css b/static/css/theme.css index dfdd6ed..a2d8f8c 100644 --- a/static/css/theme.css +++ b/static/css/theme.css @@ -1 +1 @@ -*{margin:0;padding:0;border:0 none transparent;box-sizing:border-box;scroll-behavior:smooth;font-family:"Manrope Regular",sans-serif;font-size:1rem;line-height:1.5}html,body{color:#e0e0ff;background-color:#10102f;height:100%}nav{display:flex;align-items:center;background-color:#20203f;border-radius:0 0 2rem 2rem;box-shadow:.5rem 0 4rem #00001f;margin:0 0 2rem 0;padding:1rem 2rem}nav .logo{width:3rem;height:3rem;margin-right:2rem;border-radius:100%;background-color:#ee588f}nav a{color:#fba7c6;border-bottom:.1rem solid transparent;margin:.25rem 1rem .5rem 1rem;padding:.15rem .5rem .25rem .5rem;font-size:1.25rem;text-decoration:none transparent;transition:border .3s}nav a:hover{border-bottom:.1rem solid #fba7c6}nav a.current{color:#ee588f}nav a.current:hover{border-bottom:.1rem solid #ee588f}main.flex-container{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-around;margin:0 10vw}header,h1,h2,h3,h4,h5,h6{color:#fba7c6}header{font-size:4rem}h1{font-size:1.5rem;font-weight:normal}h2{font-size:1.35rem;font-weight:normal}h3{font-size:1.2rem;font-weight:normal}h4{font-size:1.05rem;font-weight:normal}h5{font-size:.9rem;font-weight:normal}a{color:#a3cefa}form{display:flex;flex-direction:column;justify-content:center;align-items:center}input{color:#e0e0ff;background-color:#20203f;margin:1em 0;padding:.75em 1.25em;width:min(80ch,80vw);outline:0 none transparent;border:.2rem solid transparent;border-radius:.5rem;box-shadow:.25rem 0 2rem #00001f;transition:border .3s,background-color .5s,border-radius .5s}input:hover{background-color:#30304f;border-radius:1rem}input:active,input:focus{border:.2rem solid #ee588f}footer{display:flex;flex-direction:column;align-items:center;margin:5vh 2rem}footer a{color:#74b6fb;font-size:.8rem} +*{margin:0;padding:0;border:0 none transparent;box-sizing:border-box;scroll-behavior:smooth;font-family:"Manrope Regular",sans-serif;font-size:1rem;line-height:1.5}html,body{color:#e0e0ff;background-color:#10102f;height:100%}nav{display:flex;align-items:center;background-color:#20203f;border-radius:0 0 2rem 2rem;box-shadow:.5rem 0 4rem #00001f;margin:0 0 2rem 0;padding:1rem 2rem}nav .logo{width:3rem;height:3rem;margin-right:2rem;border-radius:100%;background-color:#ee588f}nav a{color:#fba7c6;border-bottom:.1rem solid transparent;margin:.25rem 1rem .5rem 1rem;padding:.15rem .5rem .25rem .5rem;font-size:1.25rem;text-decoration:none transparent;transition:border .3s}nav a:hover{border-bottom:.1rem solid #fba7c6}nav a.current{color:#ee588f}nav a.current:hover{border-bottom:.1rem solid #ee588f}main.flex-container{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-around;margin:0 10vw}header,h1,h2,h3,h4,h5,h6{color:#fba7c6}header{font-size:4rem}h1{font-size:1.5rem;font-weight:normal}h2{font-size:1.35rem;font-weight:normal}h3{font-size:1.2rem;font-weight:normal}h4{font-size:1.05rem;font-weight:normal}h5{font-size:.9rem;font-weight:normal}a{color:#a3cefa}form{display:flex;flex-direction:column;justify-content:center;align-items:center}input{color:#e0e0ff;background-color:#20203f;margin:1em 0;padding:.75em 1.25em;width:min(80ch,80vw);outline:0 none transparent;border:.2rem solid transparent;border-radius:.5rem;box-shadow:.25rem 0 2rem #00001f;transition:border .3s,background-color .5s,border-radius .5s}input:hover{background-color:#30304f;border-radius:1rem}input:active,input:focus{border:.2rem solid #ee588f}tr{width:max-content}tr td{padding:.25rem 0;vertical-align:middle;width:max-content}tr td:first-child{padding:.25rem 1.5rem .25rem 0}footer{display:flex;flex-direction:column;align-items:center;margin:5vh 2rem}footer a{color:#74b6fb;font-size:.8rem} diff --git a/views/components/_footer.njk b/views/components/_footer.njk index 2d1ae68..5a15234 100644 --- a/views/components/_footer.njk +++ b/views/components/_footer.njk @@ -3,6 +3,6 @@ Made with <3 by team qwik

- Source CodeAPI ReferenceInstance Info + Source CodeAPI ReferenceServer Status

\ No newline at end of file diff --git a/views/pages/stats.njk b/views/pages/stats.njk new file mode 100644 index 0000000..018e3f5 --- /dev/null +++ b/views/pages/stats.njk @@ -0,0 +1,33 @@ +{% extends "../templates/basic.njk" %} +{% import "../components/_utils.njk" as utils %} +{% set stats = data.stats %} + +{% block body %} +
+
+

+ Current server status +

+ + + + + + + + + + + + + + + + + + + +
Uptime:{{ utils.fancyTime(stats.uptimeMillis / 1000) }}
Uptime seconds:{{ stats.uptimeSeconds }}
Database size:{{ stats.db.size.kbytes }} KB || {{ stats.db.size.mbytes | round(2) }} MB || {{ stats.db.size.gbytes | round(2) }} GB
Database status:{{ "Currently updating the database..." if stats.db.currentlyUpdating else "Last update was at " + stats.db.lastUpdate }}
+
+
+{% endblock %} \ No newline at end of file