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 Code • API Reference • Instance Info
+ Source Code • API Reference • Server 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